mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: close MainMenu and Library dropdown on item select (#6152)
This commit is contained in:
parent
d4afd66268
commit
1db078a3dc
12 changed files with 178 additions and 62 deletions
|
@ -4,16 +4,23 @@ import { Island } from "../Island";
|
|||
import { useDevice } from "../App";
|
||||
import clsx from "clsx";
|
||||
import Stack from "../Stack";
|
||||
import React from "react";
|
||||
import { DropdownMenuContentPropsContext } from "./common";
|
||||
|
||||
const MenuContent = ({
|
||||
children,
|
||||
onClickOutside,
|
||||
className = "",
|
||||
onSelect,
|
||||
style,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
onClickOutside?: () => void;
|
||||
className?: string;
|
||||
/**
|
||||
* Called when any menu item is selected (clicked on).
|
||||
*/
|
||||
onSelect?: (event: Event) => void;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
const device = useDevice();
|
||||
|
@ -24,28 +31,32 @@ const MenuContent = ({
|
|||
const classNames = clsx(`dropdown-menu ${className}`, {
|
||||
"dropdown-menu--mobile": device.isMobile,
|
||||
}).trim();
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className={classNames}
|
||||
style={style}
|
||||
data-testid="dropdown-menu"
|
||||
>
|
||||
{/* the zIndex ensures this menu has higher stacking order,
|
||||
<DropdownMenuContentPropsContext.Provider value={{ onSelect }}>
|
||||
<div
|
||||
ref={menuRef}
|
||||
className={classNames}
|
||||
style={style}
|
||||
data-testid="dropdown-menu"
|
||||
>
|
||||
{/* the zIndex ensures this menu has higher stacking order,
|
||||
see https://github.com/excalidraw/excalidraw/pull/1445 */}
|
||||
{device.isMobile ? (
|
||||
<Stack.Col className="dropdown-menu-container">{children}</Stack.Col>
|
||||
) : (
|
||||
<Island
|
||||
className="dropdown-menu-container"
|
||||
padding={2}
|
||||
style={{ zIndex: 1 }}
|
||||
>
|
||||
{children}
|
||||
</Island>
|
||||
)}
|
||||
</div>
|
||||
{device.isMobile ? (
|
||||
<Stack.Col className="dropdown-menu-container">{children}</Stack.Col>
|
||||
) : (
|
||||
<Island
|
||||
className="dropdown-menu-container"
|
||||
padding={2}
|
||||
style={{ zIndex: 1 }}
|
||||
>
|
||||
{children}
|
||||
</Island>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuContentPropsContext.Provider>
|
||||
);
|
||||
};
|
||||
export default MenuContent;
|
||||
MenuContent.displayName = "DropdownMenuContent";
|
||||
|
||||
export default MenuContent;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
import {
|
||||
getDrodownMenuItemClassName,
|
||||
useHandleDropdownMenuItemClick,
|
||||
} from "./common";
|
||||
import MenuItemContent from "./DropdownMenuItemContent";
|
||||
|
||||
export const getDrodownMenuItemClassName = (className = "") => {
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
|
||||
};
|
||||
|
||||
const DropdownMenuItem = ({
|
||||
icon,
|
||||
onSelect,
|
||||
|
@ -14,15 +14,17 @@ const DropdownMenuItem = ({
|
|||
...rest
|
||||
}: {
|
||||
icon?: JSX.Element;
|
||||
onSelect: () => void;
|
||||
onSelect: (event: Event) => void;
|
||||
children: React.ReactNode;
|
||||
shortcut?: string;
|
||||
className?: string;
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
|
||||
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||
|
||||
return (
|
||||
<button
|
||||
{...rest}
|
||||
onClick={onSelect}
|
||||
onClick={handleClick}
|
||||
type="button"
|
||||
className={getDrodownMenuItemClassName(className)}
|
||||
title={rest.title ?? rest["aria-label"]}
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
import MenuItemContent from "./DropdownMenuItemContent";
|
||||
import React from "react";
|
||||
import { getDrodownMenuItemClassName } from "./DropdownMenuItem";
|
||||
import {
|
||||
getDrodownMenuItemClassName,
|
||||
useHandleDropdownMenuItemClick,
|
||||
} from "./common";
|
||||
|
||||
const DropdownMenuItemLink = ({
|
||||
icon,
|
||||
shortcut,
|
||||
href,
|
||||
children,
|
||||
onSelect,
|
||||
className = "",
|
||||
...rest
|
||||
}: {
|
||||
href: string;
|
||||
icon?: JSX.Element;
|
||||
children: React.ReactNode;
|
||||
shortcut?: string;
|
||||
className?: string;
|
||||
href: string;
|
||||
onSelect?: (event: Event) => void;
|
||||
} & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||
|
||||
return (
|
||||
<a
|
||||
{...rest}
|
||||
|
@ -23,6 +31,7 @@ const DropdownMenuItemLink = ({
|
|||
rel="noreferrer"
|
||||
className={getDrodownMenuItemClassName(className)}
|
||||
title={rest.title ?? rest["aria-label"]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||
{children}
|
||||
|
|
31
src/components/dropdownMenu/common.ts
Normal file
31
src/components/dropdownMenu/common.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React, { useContext } from "react";
|
||||
import { EVENT } from "../../constants";
|
||||
import { composeEventHandlers } from "../../utils";
|
||||
|
||||
export const DropdownMenuContentPropsContext = React.createContext<{
|
||||
onSelect?: (event: Event) => void;
|
||||
}>({});
|
||||
|
||||
export const getDrodownMenuItemClassName = (className = "") => {
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
|
||||
};
|
||||
|
||||
export const useHandleDropdownMenuItemClick = (
|
||||
origOnClick:
|
||||
| React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement>
|
||||
| undefined,
|
||||
onSelect: ((event: Event) => void) | undefined,
|
||||
) => {
|
||||
const DropdownMenuContentProps = useContext(DropdownMenuContentPropsContext);
|
||||
|
||||
return composeEventHandlers(origOnClick, (event) => {
|
||||
const itemSelectEvent = new CustomEvent(EVENT.MENU_ITEM_SELECT, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
onSelect?.(itemSelectEvent);
|
||||
if (!itemSelectEvent.defaultPrevented) {
|
||||
DropdownMenuContentProps.onSelect?.(itemSelectEvent);
|
||||
}
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue