diff options
Diffstat (limited to 'packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx')
| -rw-r--r-- | packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx new file mode 100644 index 0000000..a203124 --- /dev/null +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx @@ -0,0 +1,88 @@ +import { Island } from "../Island"; +import { useDevice } from "../App"; +import clsx from "clsx"; +import Stack from "../Stack"; +import React, { useEffect, useRef } from "react"; +import { DropdownMenuContentPropsContext } from "./common"; +import { useOutsideClick } from "../../hooks/useOutsideClick"; +import { KEYS } from "../../keys"; +import { EVENT } from "../../constants"; +import { useStable } from "../../hooks/useStable"; + +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(); + const menuRef = useRef<HTMLDivElement>(null); + + const callbacksRef = useStable({ onClickOutside }); + + useOutsideClick(menuRef, () => { + callbacksRef.onClickOutside?.(); + }); + + useEffect(() => { + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === KEYS.ESCAPE) { + event.stopImmediatePropagation(); + callbacksRef.onClickOutside?.(); + } + }; + + const option = { + // so that we can stop propagation of the event before it reaches + // event handlers that were bound before this one + capture: true, + }; + + document.addEventListener(EVENT.KEYDOWN, onKeyDown, option); + return () => { + document.removeEventListener(EVENT.KEYDOWN, onKeyDown, option); + }; + }, [callbacksRef]); + + const classNames = clsx(`dropdown-menu ${className}`, { + "dropdown-menu--mobile": device.editor.isMobile, + }).trim(); + + return ( + <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.editor.isMobile ? ( + <Stack.Col className="dropdown-menu-container">{children}</Stack.Col> + ) : ( + <Island + className="dropdown-menu-container" + padding={2} + style={{ zIndex: 2 }} + > + {children} + </Island> + )} + </div> + </DropdownMenuContentPropsContext.Provider> + ); +}; +MenuContent.displayName = "DropdownMenuContent"; + +export default MenuContent; |
