/** * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ import "./menu-actions.scss"; import React, { isValidElement } from "react"; import { observable, makeObservable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { autoBind, cssNames } from "../../utils"; import type { IconProps } from "../icon"; import { Icon } from "../icon"; import type { MenuProps } from "./menu"; import { Menu, MenuItem } from "./menu"; import isString from "lodash/isString"; import type { TooltipDecoratorProps } from "../tooltip"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable"; export interface MenuActionsProps extends Partial { className?: string; toolbar?: boolean; // display menu as toolbar with icons autoCloseOnSelect?: boolean; triggerIcon?: string | (IconProps & TooltipDecoratorProps) | React.ReactNode; /** * @deprecated Provide your own remove `` as part of the `children` passed to this component */ removeConfirmationMessage?: React.ReactNode | (() => React.ReactNode); /** * @deprecated Provide your own update `` as part of the `children` passed to this component */ updateAction?: () => void | Promise; /** * @deprecated Provide your own remove `` as part of the `children` passed to this component */ removeAction?: () => void | Promise; onOpen?: () => void; id?: string; } interface Dependencies { openConfirmDialog: OpenConfirmDialog; } @observer class NonInjectedMenuActions extends React.Component { static defaultProps = { removeConfirmationMessage: "Remove item?", }; @observable isOpen = !!this.props.toolbar; toggle = () => { if (this.props.toolbar) return; this.isOpen = !this.isOpen; }; constructor(props: MenuActionsProps & Dependencies) { super(props); makeObservable(this); autoBind(this); } componentDidMount(): void { disposeOnUnmount(this, [ reaction(() => this.isOpen, (isOpen) => { if (isOpen) { this.props.onOpen?.(); } }, { fireImmediately: true, }), ]); } remove() { const { removeAction, openConfirmDialog } = this.props; let { removeConfirmationMessage } = this.props; if (typeof removeConfirmationMessage === "function") { removeConfirmationMessage = removeConfirmationMessage(); } openConfirmDialog({ ok: removeAction, labelOk: "Remove", message:
{removeConfirmationMessage}
, }); } renderTriggerIcon() { if (this.props.toolbar) return null; const { triggerIcon = "more_vert" } = this.props; let className: string; if (isValidElement(triggerIcon)) { className = cssNames(triggerIcon.props.className, { active: this.isOpen }); return React.cloneElement(triggerIcon, { id: this.props.id, className }); } const iconProps: IconProps & TooltipDecoratorProps = { id: this.props.id, interactive: true, material: isString(triggerIcon) ? triggerIcon : undefined, active: this.isOpen, ...(typeof triggerIcon === "object" ? triggerIcon : {}), }; if (iconProps.tooltip && this.isOpen) { delete iconProps.tooltip; // don't show tooltip for icon when menu is open } return ( ); } render() { const { className, toolbar, autoCloseOnSelect, children, updateAction, removeAction, triggerIcon, removeConfirmationMessage, ...menuProps } = this.props; const autoClose = !toolbar; return ( <> {this.renderTriggerIcon()} {children} {updateAction && ( Edit )} {removeAction && ( Delete )} ); } } export const MenuActions = withInjectables(NonInjectedMenuActions, { getProps: (di, props) => ({ id: di.inject(getRandomIdInjectable)(), openConfirmDialog: di.inject(openConfirmDialogInjectable), ...props, }), });