'use strict'; const React = require("react"); const useMeasure = require("react-use-measure"); const matchValue = require("match-value"); const insecureNanoid = require("nanoid/non-secure").nanoid; const defaultStyle = require("./style.css"); const useTheme = require("../../util/themeable"); const useGuaranteedMemo = require("../../util/use-guaranteed-memo"); const isEventInsideRef = require("../../util/is-event-inside-ref"); const comparePath = require("../../util/compare-path"); const MenuItemContext = require("../../contexts/menu-item"); const MenuPositionContext = require("../../contexts/menu-position"); const MenuPathContext = require("../../contexts/menu-path"); module.exports = function MenuItem({ label, title, menu, onClick }) { let { withTheme } = useTheme({ control: "menu", defaultStyle }); let [ boundsRef, bounds ] = useMeasure({ debounce: 10 }); let submenuRef = React.useRef(); let itemContext = React.useContext(MenuItemContext); let path = React.useContext(MenuPathContext); let id = useGuaranteedMemo(() => insecureNanoid()); // insecure is fine here, these IDs are not interesting to an attacker let ownPath = path.concat([ id ]); // FIXME: memoize? let isSelected = itemContext.isActive && comparePath(itemContext.selectedPath, ownPath); let submenuPosition = matchValue(itemContext.menuType, { menuBar: { x: bounds.x, y: bounds.y + bounds.height }, menu: { x: bounds.x + bounds.width, /* FIXME: Account for border of containing menu? */ y: bounds.y } }); let handlers = { onClick: (event) => { if (!isEventInsideRef(submenuRef, event)) { if (onClick != null) { onClick(); // TODO: Pass through event? Allow cancellation? To deal with advanced cases like checkboxes within menus, if we want that } if (itemContext.onClick != null) { let hasMenu = (menu != null); let isInMenu = false; // Let the menu intercept and change this itemContext.onClick(ownPath, hasMenu, isInMenu); } } }, onMouseEnter: (_event) => { // FIXME: Only outside of submenu? if (itemContext.onMouseEnter != null) { itemContext.onMouseEnter(ownPath); } }, onMouseLeave: (_event) => { // FIXME: Only outside of submenu? if (itemContext.onMouseLeave != null) { itemContext.onMouseLeave(ownPath); } }, }; let buttonClasses = withTheme([ "item", { item_selected: isSelected }, { item_directPress: (menu == null) } ]); return (
{label} {(menu != null) ? {(isSelected) ? menu : null } : null }
); };