mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
181 lines
5.4 KiB
TypeScript
181 lines
5.4 KiB
TypeScript
/**
|
|
* 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<MenuProps> {
|
|
className?: string;
|
|
toolbar?: boolean; // display menu as toolbar with icons
|
|
autoCloseOnSelect?: boolean;
|
|
triggerIcon?: string | (IconProps & TooltipDecoratorProps) | React.ReactNode;
|
|
/**
|
|
* @deprecated Provide your own remove `<MenuItem>` as part of the `children` passed to this component
|
|
*/
|
|
removeConfirmationMessage?: React.ReactNode | (() => React.ReactNode);
|
|
/**
|
|
* @deprecated Provide your own update `<MenuItem>` as part of the `children` passed to this component
|
|
*/
|
|
updateAction?: () => void | Promise<void>;
|
|
/**
|
|
* @deprecated Provide your own remove `<MenuItem>` as part of the `children` passed to this component
|
|
*/
|
|
removeAction?: () => void | Promise<void>;
|
|
onOpen?: () => void;
|
|
id?: string;
|
|
}
|
|
|
|
interface Dependencies {
|
|
openConfirmDialog: OpenConfirmDialog;
|
|
}
|
|
|
|
@observer
|
|
class NonInjectedMenuActions extends React.Component<MenuActionsProps & Dependencies> {
|
|
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: <div>{removeConfirmationMessage}</div>,
|
|
});
|
|
}
|
|
|
|
renderTriggerIcon() {
|
|
if (this.props.toolbar) return null;
|
|
const { triggerIcon = "more_vert" } = this.props;
|
|
let className: string;
|
|
|
|
if (isValidElement<HTMLElement>(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 (
|
|
<Icon {...iconProps}/>
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
className, toolbar, autoCloseOnSelect, children, updateAction, removeAction, triggerIcon, removeConfirmationMessage,
|
|
...menuProps
|
|
} = this.props;
|
|
const autoClose = !toolbar;
|
|
|
|
return (
|
|
<>
|
|
{this.renderTriggerIcon()}
|
|
|
|
<Menu
|
|
htmlFor={this.props.id}
|
|
isOpen={this.isOpen}
|
|
open={this.toggle}
|
|
close={this.toggle}
|
|
className={cssNames("MenuActions flex", className, {
|
|
toolbar,
|
|
gaps: toolbar, // add spacing for .flex
|
|
})}
|
|
animated={!toolbar}
|
|
usePortal={autoClose}
|
|
closeOnScroll={autoClose}
|
|
closeOnClickItem={autoCloseOnSelect ?? autoClose}
|
|
closeOnClickOutside={autoClose}
|
|
{...menuProps}
|
|
>
|
|
{children}
|
|
{updateAction && (
|
|
<MenuItem onClick={updateAction}>
|
|
<Icon
|
|
material="edit"
|
|
interactive={toolbar}
|
|
tooltip="Edit"
|
|
/>
|
|
<span className="title">Edit</span>
|
|
</MenuItem>
|
|
)}
|
|
{removeAction && (
|
|
<MenuItem onClick={this.remove} data-testid="menu-action-remove">
|
|
<Icon
|
|
material="delete"
|
|
interactive={toolbar}
|
|
tooltip="Delete"
|
|
/>
|
|
<span className="title">Delete</span>
|
|
</MenuItem>
|
|
)}
|
|
</Menu>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export const MenuActions = withInjectables<Dependencies, MenuActionsProps>(NonInjectedMenuActions, {
|
|
getProps: (di, props) => ({
|
|
id: di.inject(getRandomIdInjectable)(),
|
|
openConfirmDialog: di.inject(openConfirmDialogInjectable),
|
|
...props,
|
|
}),
|
|
});
|