@vuu-ui/vuu-popups 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu-provider.js","sources":["../../src/menu/context-menu-provider.tsx"],"sourcesContent":["import type {\n ContextMenuContextType,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { createContext, ReactNode, useCallback, useMemo } from \"react\";\n\nexport const ContextMenuContext = createContext<ContextMenuContextType | null>(\n null,\n);\n\nexport interface ContextMenuConfiguration {\n menuActionHandler?: MenuActionHandler;\n menuBuilder: MenuBuilder;\n}\n\nexport interface ContextMenuProviderProps extends ContextMenuConfiguration {\n children: ReactNode;\n label?: string;\n}\n\ninterface ProviderProps extends ContextMenuProviderProps {\n context: ContextMenuContextType | null;\n}\n\nconst Provider = ({\n children,\n context,\n menuActionHandler,\n menuBuilder,\n}: ProviderProps) => {\n const menuBuilders = useMemo(() => {\n if (context?.menuBuilders && menuBuilder) {\n return context.menuBuilders.concat(menuBuilder);\n } else if (menuBuilder) {\n return [menuBuilder];\n } else {\n return context?.menuBuilders || [];\n }\n }, [context, menuBuilder]);\n\n const handleMenuAction = useCallback(\n (reason) => {\n if (menuActionHandler?.(reason)) {\n return true;\n }\n\n if (context?.menuActionHandler?.(reason)) {\n return true;\n }\n },\n [context, menuActionHandler],\n );\n\n return (\n <ContextMenuContext.Provider\n value={{\n menuActionHandler: handleMenuAction,\n menuBuilders,\n }}\n >\n {children}\n </ContextMenuContext.Provider>\n );\n};\n\n// Need an option for local menu to override higher-level menu, rather than extend\nexport const ContextMenuProvider = ({\n children,\n label,\n menuActionHandler,\n menuBuilder,\n}: ContextMenuProviderProps) => {\n return (\n <ContextMenuContext.Consumer>\n {(parentContext) => (\n <Provider\n context={parentContext}\n label={label}\n menuActionHandler={menuActionHandler}\n menuBuilder={menuBuilder}\n >\n {children}\n </Provider>\n )}\n </ContextMenuContext.Consumer>\n );\n};\n"],"names":["createContext","useMemo","useCallback","jsx"],"mappings":";;;;;AAOO,MAAM,kBAAqB,GAAAA,mBAAA;AAAA,EAChC;AACF;AAgBA,MAAM,WAAW,CAAC;AAAA,EAChB,QAAA;AAAA,EACA,OAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAqB,KAAA;AACnB,EAAM,MAAA,YAAA,GAAeC,cAAQ,MAAM;AACjC,IAAI,IAAA,OAAA,EAAS,gBAAgB,WAAa,EAAA;AACxC,MAAO,OAAA,OAAA,CAAQ,YAAa,CAAA,MAAA,CAAO,WAAW,CAAA;AAAA,eACrC,WAAa,EAAA;AACtB,MAAA,OAAO,CAAC,WAAW,CAAA;AAAA,KACd,MAAA;AACL,MAAO,OAAA,OAAA,EAAS,gBAAgB,EAAC;AAAA;AACnC,GACC,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AAEzB,EAAA,MAAM,gBAAmB,GAAAC,iBAAA;AAAA,IACvB,CAAC,MAAW,KAAA;AACV,MAAI,IAAA,iBAAA,GAAoB,MAAM,CAAG,EAAA;AAC/B,QAAO,OAAA,IAAA;AAAA;AAGT,MAAI,IAAA,OAAA,EAAS,iBAAoB,GAAA,MAAM,CAAG,EAAA;AACxC,QAAO,OAAA,IAAA;AAAA;AACT,KACF;AAAA,IACA,CAAC,SAAS,iBAAiB;AAAA,GAC7B;AAEA,EACE,uBAAAC,cAAA;AAAA,IAAC,kBAAmB,CAAA,QAAA;AAAA,IAAnB;AAAA,MACC,KAAO,EAAA;AAAA,QACL,iBAAmB,EAAA,gBAAA;AAAA,QACnB;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ,CAAA;AAGO,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA,KAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAgC,KAAA;AAC9B,EAAA,uBACGA,cAAA,CAAA,kBAAA,CAAmB,QAAnB,EAAA,EACE,WAAC,aACA,qBAAAA,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAS,EAAA,aAAA;AAAA,MACT,KAAA;AAAA,MACA,iBAAA;AAAA,MACA,WAAA;AAAA,MAEC;AAAA;AAAA,GAGP,EAAA,CAAA;AAEJ;;;;;"}
1
+ {"version":3,"file":"context-menu-provider.js","sources":["../../src/menu/context-menu-provider.tsx"],"sourcesContent":["import type {\n ContextMenuContextType,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { createContext, ReactNode, useCallback, useMemo } from \"react\";\n\nexport const ContextMenuContext = createContext<ContextMenuContextType | null>(\n null,\n);\n\nexport interface ContextMenuConfiguration {\n menuActionHandler?: MenuActionHandler;\n menuBuilder: MenuBuilder;\n}\n\nexport interface ContextMenuProviderProps extends ContextMenuConfiguration {\n children: ReactNode;\n label?: string;\n}\n\ninterface ProviderProps extends ContextMenuProviderProps {\n context: ContextMenuContextType | null;\n}\n\nconst Provider = ({\n children,\n context,\n menuActionHandler,\n menuBuilder,\n}: ProviderProps) => {\n const menuBuilders = useMemo(() => {\n if (context?.menuBuilders && menuBuilder) {\n return context.menuBuilders.concat(menuBuilder);\n } else if (menuBuilder) {\n return [menuBuilder];\n } else {\n return context?.menuBuilders || [];\n }\n }, [context, menuBuilder]);\n\n const handleMenuAction = useCallback<MenuActionHandler>(\n (reason) => {\n if (menuActionHandler?.(reason)) {\n return true;\n }\n\n if (context?.menuActionHandler?.(reason)) {\n return true;\n }\n },\n [context, menuActionHandler],\n );\n\n return (\n <ContextMenuContext.Provider\n value={{\n menuActionHandler: handleMenuAction,\n menuBuilders,\n }}\n >\n {children}\n </ContextMenuContext.Provider>\n );\n};\n\n// Need an option for local menu to override higher-level menu, rather than extend\nexport const ContextMenuProvider = ({\n children,\n label,\n menuActionHandler,\n menuBuilder,\n}: ContextMenuProviderProps) => {\n return (\n <ContextMenuContext.Consumer>\n {(parentContext) => (\n <Provider\n context={parentContext}\n label={label}\n menuActionHandler={menuActionHandler}\n menuBuilder={menuBuilder}\n >\n {children}\n </Provider>\n )}\n </ContextMenuContext.Consumer>\n );\n};\n"],"names":["createContext","useMemo","useCallback","jsx"],"mappings":";;;;;AAOO,MAAM,kBAAqB,GAAAA,mBAAA;AAAA,EAChC;AACF;AAgBA,MAAM,WAAW,CAAC;AAAA,EAChB,QAAA;AAAA,EACA,OAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAqB,KAAA;AACnB,EAAM,MAAA,YAAA,GAAeC,cAAQ,MAAM;AACjC,IAAI,IAAA,OAAA,EAAS,gBAAgB,WAAa,EAAA;AACxC,MAAO,OAAA,OAAA,CAAQ,YAAa,CAAA,MAAA,CAAO,WAAW,CAAA;AAAA,eACrC,WAAa,EAAA;AACtB,MAAA,OAAO,CAAC,WAAW,CAAA;AAAA,KACd,MAAA;AACL,MAAO,OAAA,OAAA,EAAS,gBAAgB,EAAC;AAAA;AACnC,GACC,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AAEzB,EAAA,MAAM,gBAAmB,GAAAC,iBAAA;AAAA,IACvB,CAAC,MAAW,KAAA;AACV,MAAI,IAAA,iBAAA,GAAoB,MAAM,CAAG,EAAA;AAC/B,QAAO,OAAA,IAAA;AAAA;AAGT,MAAI,IAAA,OAAA,EAAS,iBAAoB,GAAA,MAAM,CAAG,EAAA;AACxC,QAAO,OAAA,IAAA;AAAA;AACT,KACF;AAAA,IACA,CAAC,SAAS,iBAAiB;AAAA,GAC7B;AAEA,EACE,uBAAAC,cAAA;AAAA,IAAC,kBAAmB,CAAA,QAAA;AAAA,IAAnB;AAAA,MACC,KAAO,EAAA;AAAA,QACL,iBAAmB,EAAA,gBAAA;AAAA,QACnB;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ,CAAA;AAGO,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA,KAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAgC,KAAA;AAC9B,EAAA,uBACGA,cAAA,CAAA,kBAAA,CAAmB,QAAnB,EAAA,EACE,WAAC,aACA,qBAAAA,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAS,EAAA,aAAA;AAAA,MACT,KAAA;AAAA,MACA,iBAAA;AAAA,MACA,WAAA;AAAA,MAEC;AAAA;AAAA,GAGP,EAAA,CAAA;AAEJ;;;;;"}
@@ -88,14 +88,16 @@ const useCascade = ({
88
88
  setOpenMenus([{ id: rootId, left: posX, top: posY }]);
89
89
  } else {
90
90
  menuState.current[hostMenuId] = "popup-open";
91
- const el = document.getElementById(itemId);
92
- if (el !== null) {
93
- const { left, top } = getPosition(el, openMenus.current);
94
- setOpenMenus(
95
- openMenus.current.concat({ id: targetMenuId, left, top })
96
- );
97
- } else {
98
- throw Error(`openMenu no menuItem ${itemId}`);
91
+ if (itemId) {
92
+ const el = document.getElementById(itemId);
93
+ if (el !== null) {
94
+ const { left, top } = getPosition(el, openMenus.current);
95
+ setOpenMenus(
96
+ openMenus.current.concat({ id: targetMenuId, left, top })
97
+ );
98
+ } else {
99
+ throw Error(`openMenu no menuItem ${itemId}`);
100
+ }
99
101
  }
100
102
  }
101
103
  },
@@ -1 +1 @@
1
- {"version":3,"file":"use-cascade.js","sources":["../../src/menu/use-cascade.ts"],"sourcesContent":["import {\n MouseEvent,\n SyntheticEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { closestListItem } from \"./list-dom-utils\";\nimport { MenuItemProps, MenuOpenHandler } from \"./MenuList\";\n// import {mousePosition} from './aim/utils';\n// import {aiming} from './aim/aim';\n\nconst nudge = (\n menus: RuntimeMenuDescriptor[],\n distance: number,\n pos: \"left\" | \"top\"\n) => {\n return menus.map((m, i) =>\n i === menus.length - 1\n ? {\n ...m,\n [pos]: m[pos] - distance,\n }\n : m\n );\n};\nconst nudgeLeft = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"left\");\nconst nudgeUp = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"top\");\n\nconst flipSides = (id: string, menus: RuntimeMenuDescriptor[]) => {\n const [parentMenu, menu] = menus.slice(-2);\n const el = document.getElementById(`${id}-${menu.id}`);\n if (el === null) {\n throw Error(`useCascade.flipSides element with id ${menu.id} not found`);\n }\n const { width } = el.getBoundingClientRect();\n return menus.map((m) =>\n m === menu\n ? {\n ...m,\n left: parentMenu.left - (width - 2),\n }\n : m\n );\n};\n\n// const closedNode = (el: HTMLElement) =>\n// el.ariaHasPopup === \"true\" && el.ariaExpanded !== \"true\";\nconst getPosition = (el: HTMLElement, openMenus: RuntimeMenuDescriptor[]) => {\n const [{ left, top: menuTop }] = openMenus.slice(-1);\n // const {top, right, bottom, left} = el.getBoundingClientRect();\n // this will not work for MenuList within window, we need the\n // const {offsetLeft: left, offsetTop: menuTop} = el.closest('.vuuMenuList');\n const { offsetWidth: width, offsetTop: top } = el;\n return { left: left + width, top: top + menuTop };\n};\n\nexport type RuntimeMenuDescriptor = {\n id: string;\n left: number;\n top: number;\n};\n\n/** menuitem-vuu-1-0 vuu-1 */\nexport const getHostMenuId = (id: string, rootId: string) => {\n const pos = id.lastIndexOf(\"-\");\n if (id.startsWith(\"menuitem\")) {\n return pos > -1 ? id.slice(9, pos) : rootId;\n } else {\n return pos > -1 ? id.slice(0, pos) : rootId;\n }\n};\n\nconst getTargetMenuId = (id: string) => id.slice(9);\n\nconst getMenuItemDetails = (\n { ariaExpanded, ariaHasPopup, id }: HTMLElement,\n rootId: string\n) => {\n if (id.startsWith(\"menuitem\")) {\n return {\n hostMenuId: getHostMenuId(id, rootId),\n targetMenuId: getTargetMenuId(id),\n menuItemId: id,\n isGroup: ariaHasPopup === \"true\",\n isOpen: ariaExpanded === \"true\",\n };\n } else {\n throw Error(`getMenuItemDetails #${id} is not a menuitem`);\n }\n};\n\nexport interface CascadeHookProps {\n id: string;\n onActivate: (menuId: string) => void;\n onMouseEnterItem: (evt: MouseEvent, itemId: string) => void;\n position: { x: number; y: number };\n}\n\nexport interface CascadeHooksResult {\n closeMenu: () => void;\n handleRender: () => void;\n listItemProps: Partial<MenuItemProps>;\n openMenu: MenuOpenHandler;\n openMenus: RuntimeMenuDescriptor[];\n}\n\ntype MenuStatus = \"no-popup\" | \"popup-open\" | \"pending-close\" | \"popup-pending\";\ntype MenuState = { [key: string]: MenuStatus };\n\nexport const useCascade = ({\n id: rootId,\n onActivate,\n onMouseEnterItem,\n position: { x: posX, y: posY },\n}: CascadeHookProps): CascadeHooksResult => {\n const [, forceRefresh] = useState({});\n const openMenus = useRef<RuntimeMenuDescriptor[]>([\n { id: rootId, left: posX, top: posY },\n ]);\n\n const menuIsOpen = useCallback(\n (menuId: string) =>\n openMenus.current.findIndex((menu) => menu.id === menuId) !== -1,\n []\n );\n\n const getOpenMenuStatus = useCallback((menuId: string) => {\n const state = menuState.current[menuId];\n if (state === undefined) {\n throw Error(`getOpenMenuState no entry for menu ${menuId}`);\n }\n return state;\n }, []);\n\n const setOpenMenus = useCallback((menus: RuntimeMenuDescriptor[]) => {\n openMenus.current = menus;\n forceRefresh({});\n }, []);\n\n const menuOpenPendingTimeout = useRef<number | undefined>();\n const menuClosePendingTimeout = useRef<number | undefined>();\n const menuState = useRef<MenuState>({ [rootId]: \"no-popup\" });\n // const prevLevel = useRef(0);\n\n // const prevAim = useRef({mousePos: null, distance: true});\n\n const openMenu = useCallback(\n (hostMenuId = rootId, targetMenuId: string, itemId = null) => {\n if (hostMenuId === rootId && itemId === null) {\n setOpenMenus([{ id: rootId, left: posX, top: posY }]);\n } else {\n menuState.current[hostMenuId] = \"popup-open\";\n const el = document.getElementById(itemId) as HTMLElement;\n if (el !== null) {\n const { left, top } = getPosition(el, openMenus.current);\n setOpenMenus(\n openMenus.current.concat({ id: targetMenuId, left, top })\n );\n } else {\n throw Error(`openMenu no menuItem ${itemId}`);\n }\n }\n },\n [rootId, posX, posY, setOpenMenus]\n );\n\n const closeMenu = useCallback(\n (menuId?: string) => {\n if (menuId === rootId) {\n setOpenMenus([]);\n } else {\n const menus = openMenus.current.slice();\n const lastMenu = menus.pop() as RuntimeMenuDescriptor;\n menuState.current[lastMenu.id] = \"no-popup\";\n const parentMenu = menus.at(-1);\n if (parentMenu) {\n menuState.current[parentMenu.id] = \"no-popup\";\n }\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus]\n );\n\n const closeMenus = useCallback(\n (menuItemId) => {\n const menus = openMenus.current.slice();\n const menuItemMenuId = menuItemId.slice(9);\n let { id: lastMenuId } = menus.at(-1) as RuntimeMenuDescriptor;\n while (menus.length > 1 && !menuItemMenuId.startsWith(lastMenuId)) {\n const parentMenuId = getHostMenuId(lastMenuId, rootId);\n menus.pop();\n menuState.current[lastMenuId] = \"no-popup\";\n menuState.current[parentMenuId] = \"no-popup\";\n ({ id: lastMenuId } = menus[menus.length - 1]);\n }\n if (menus.length < openMenus.current.length) {\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus]\n );\n\n const clearAnyScheduledOpenTasks = useCallback(() => {\n if (menuOpenPendingTimeout.current) {\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n }\n }, []);\n\n const scheduleOpen = useCallback(\n (\n hostMenuId: string,\n targetMenuId: string,\n menuItemId: string,\n delay = 300\n ) => {\n clearAnyScheduledOpenTasks();\n // do we need to set target state to pending-open ?s\n\n menuOpenPendingTimeout.current = window.setTimeout(() => {\n // console.log(\n // `scheduleOpen<timeout> opening menu ${targetMenuId} from menu ${hostMenuId} via menuitem ${menuItemId}`\n // );\n closeMenus(menuItemId);\n menuState.current[hostMenuId] = \"popup-open\";\n menuState.current[targetMenuId] = \"no-popup\";\n openMenu(hostMenuId, targetMenuId, menuItemId);\n }, delay);\n },\n [clearAnyScheduledOpenTasks, closeMenus, openMenu]\n );\n\n const scheduleClose = useCallback(\n (hostMenuId: string, openMenuId: string, itemId: string) => {\n // console.log(\n // `scheduleClose openMenuId ${openMenuId} from parent menu ${hostMenuId} itemId ${itemId}`\n // );\n menuState.current[openMenuId] = \"pending-close\";\n menuClosePendingTimeout.current = window.setTimeout(() => {\n // console.log(`call closeMenus from scheduleClose`);\n closeMenus(itemId);\n }, 400);\n },\n [closeMenus]\n );\n\n const handleRender = useCallback(() => {\n const { current: menus } = openMenus;\n const menu = menus.at(-1);\n const el = menu ? document.getElementById(menu.id) : undefined;\n if (el) {\n const { right, bottom } = el.getBoundingClientRect();\n const { clientHeight, clientWidth } = document.body;\n if (right > clientWidth) {\n const newMenus =\n menus.length > 1\n ? flipSides(rootId, menus)\n : nudgeLeft(menus, right - clientWidth);\n setOpenMenus(newMenus);\n } else if (bottom > clientHeight) {\n const newMenus = nudgeUp(menus, bottom - clientHeight);\n setOpenMenus(newMenus);\n }\n\n if (typeof el.tabIndex === \"number\") {\n el.focus();\n }\n }\n }, [rootId, setOpenMenus]);\n\n // TODO introduce a delay parameter that allows click to requeat an immediate render\n const triggerChildMenu = useCallback<MenuOpenHandler>(\n (menuItemEl, immediate = false) => {\n const { hostMenuId, targetMenuId, menuItemId, isGroup, isOpen } =\n getMenuItemDetails(menuItemEl, rootId);\n const {\n current: { [hostMenuId]: state },\n } = menuState;\n\n const delay = immediate ? 0 : undefined;\n\n // console.log(\n // `%ctriggerChildMenu\n // rootId ${rootId}\n // menuItem ${menuItemId}\n // host menu: ${hostMenuId}\n // target menu: ${targetMenuId}\n // item index: ${menuItemId}\n // state ${state}\n // isGroup ${isGroup} isOpen ${isOpen}\n // openMenus: ${JSON.stringify(openMenus.current)}\n // full state='${JSON.stringify(menuState.current)}`,\n // \"color: green; font-weight: bold;\"\n // );\n\n if (state === \"no-popup\" && isGroup) {\n menuState.current[hostMenuId] = \"popup-pending\";\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-pending\" && !isGroup) {\n menuState.current[hostMenuId] = \"no-popup\";\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n } else if (state === \"popup-pending\" && isGroup) {\n clearTimeout(menuOpenPendingTimeout.current);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-open\") {\n if (menuIsOpen(targetMenuId)) {\n const menuStatus = getOpenMenuStatus(targetMenuId);\n // Close any child menus of the target menu. This can happen if we have\n // opened child menus, then moused out of the menu entirely, to re-enter\n // at a higher level\n closeMenus(menuItemId);\n\n switch (menuStatus) {\n case \"pending-close\":\n // cancel the close\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[targetMenuId] = \"no-popup\";\n clearAnyScheduledOpenTasks();\n break;\n default:\n }\n } else {\n // TODO review the below, suspectb it's over complicating things\n const [parentOfLastOpenedMenu, lastOpenedMenu] =\n openMenus.current.slice(-2);\n if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n menuState.current[lastOpenedMenu.id] !== \"pending-close\" /*&&\n sameLevel*/\n ) {\n scheduleClose(hostMenuId, lastOpenedMenu.id, menuItemId);\n if (isGroup && !isOpen) {\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n }\n } else if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n isGroup &&\n menuItemId !== lastOpenedMenu.id &&\n menuState.current[lastOpenedMenu.id] === \"pending-close\"\n ) {\n // if there is already an item queued for opening cancel it\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (isGroup) {\n // closeMenus(menuId, itemId);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (\n !(\n (menuState.current[lastOpenedMenu.id] === \"pending-close\") /*&&\n sameLevel*/\n )\n ) {\n closeMenus(menuItemId);\n }\n }\n }\n\n if (state === \"pending-close\") {\n clearAnyScheduledOpenTasks();\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[hostMenuId] = \"popup-open\";\n }\n },\n [\n clearAnyScheduledOpenTasks,\n closeMenus,\n getOpenMenuStatus,\n menuIsOpen,\n rootId,\n scheduleClose,\n scheduleOpen,\n ]\n );\n\n const listItemProps: Partial<MenuItemProps> = useMemo(\n () => ({\n onMouseEnter: (evt: MouseEvent) => {\n const menuItemEl = closestListItem(evt.target as HTMLElement);\n triggerChildMenu(menuItemEl);\n onMouseEnterItem(evt, menuItemEl.id);\n },\n\n onClick: (evt: SyntheticEvent) => {\n const listItemEl = closestListItem(evt.target as HTMLElement);\n const { isGroup, menuItemId } = getMenuItemDetails(listItemEl, rootId);\n if (isGroup) {\n triggerChildMenu(listItemEl);\n } else {\n onActivate(menuItemId);\n }\n },\n }),\n [onActivate, onMouseEnterItem, rootId, triggerChildMenu]\n );\n\n return {\n closeMenu,\n handleRender,\n listItemProps,\n openMenu: triggerChildMenu,\n openMenus: openMenus.current,\n };\n};\n"],"names":["useState","useRef","useCallback","useMemo","closestListItem"],"mappings":"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"}
1
+ {"version":3,"file":"use-cascade.js","sources":["../../src/menu/use-cascade.ts"],"sourcesContent":["import {\n MouseEvent,\n SyntheticEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { closestListItem } from \"./list-dom-utils\";\nimport { MenuItemProps, MenuOpenHandler } from \"./MenuList\";\n// import {mousePosition} from './aim/utils';\n// import {aiming} from './aim/aim';\n\nconst nudge = (\n menus: RuntimeMenuDescriptor[],\n distance: number,\n pos: \"left\" | \"top\",\n) => {\n return menus.map((m, i) =>\n i === menus.length - 1\n ? {\n ...m,\n [pos]: m[pos] - distance,\n }\n : m,\n );\n};\nconst nudgeLeft = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"left\");\nconst nudgeUp = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"top\");\n\nconst flipSides = (id: string, menus: RuntimeMenuDescriptor[]) => {\n const [parentMenu, menu] = menus.slice(-2);\n const el = document.getElementById(`${id}-${menu.id}`);\n if (el === null) {\n throw Error(`useCascade.flipSides element with id ${menu.id} not found`);\n }\n const { width } = el.getBoundingClientRect();\n return menus.map((m) =>\n m === menu\n ? {\n ...m,\n left: parentMenu.left - (width - 2),\n }\n : m,\n );\n};\n\n// const closedNode = (el: HTMLElement) =>\n// el.ariaHasPopup === \"true\" && el.ariaExpanded !== \"true\";\nconst getPosition = (el: HTMLElement, openMenus: RuntimeMenuDescriptor[]) => {\n const [{ left, top: menuTop }] = openMenus.slice(-1);\n // const {top, right, bottom, left} = el.getBoundingClientRect();\n // this will not work for MenuList within window, we need the\n // const {offsetLeft: left, offsetTop: menuTop} = el.closest('.vuuMenuList');\n const { offsetWidth: width, offsetTop: top } = el;\n return { left: left + width, top: top + menuTop };\n};\n\nexport type RuntimeMenuDescriptor = {\n id: string;\n left: number;\n top: number;\n};\n\n/** menuitem-vuu-1-0 vuu-1 */\nexport const getHostMenuId = (id: string, rootId: string) => {\n const pos = id.lastIndexOf(\"-\");\n if (id.startsWith(\"menuitem\")) {\n return pos > -1 ? id.slice(9, pos) : rootId;\n } else {\n return pos > -1 ? id.slice(0, pos) : rootId;\n }\n};\n\nconst getTargetMenuId = (id: string) => id.slice(9);\n\nconst getMenuItemDetails = (\n { ariaExpanded, ariaHasPopup, id }: HTMLElement,\n rootId: string,\n) => {\n if (id.startsWith(\"menuitem\")) {\n return {\n hostMenuId: getHostMenuId(id, rootId),\n targetMenuId: getTargetMenuId(id),\n menuItemId: id,\n isGroup: ariaHasPopup === \"true\",\n isOpen: ariaExpanded === \"true\",\n };\n } else {\n throw Error(`getMenuItemDetails #${id} is not a menuitem`);\n }\n};\n\nexport interface CascadeHookProps {\n id: string;\n onActivate: (menuId: string) => void;\n onMouseEnterItem: (evt: MouseEvent, itemId: string) => void;\n position: { x: number; y: number };\n}\n\nexport interface CascadeHooksResult {\n closeMenu: () => void;\n handleRender: () => void;\n listItemProps: Partial<MenuItemProps>;\n openMenu: MenuOpenHandler;\n openMenus: RuntimeMenuDescriptor[];\n}\n\ntype MenuStatus = \"no-popup\" | \"popup-open\" | \"pending-close\" | \"popup-pending\";\ntype MenuState = { [key: string]: MenuStatus };\n\nexport const useCascade = ({\n id: rootId,\n onActivate,\n onMouseEnterItem,\n position: { x: posX, y: posY },\n}: CascadeHookProps): CascadeHooksResult => {\n const [, forceRefresh] = useState({});\n const openMenus = useRef<RuntimeMenuDescriptor[]>([\n { id: rootId, left: posX, top: posY },\n ]);\n\n const menuIsOpen = useCallback(\n (menuId: string) =>\n openMenus.current.findIndex((menu) => menu.id === menuId) !== -1,\n [],\n );\n\n const getOpenMenuStatus = useCallback((menuId: string) => {\n const state = menuState.current[menuId];\n if (state === undefined) {\n throw Error(`getOpenMenuState no entry for menu ${menuId}`);\n }\n return state;\n }, []);\n\n const setOpenMenus = useCallback((menus: RuntimeMenuDescriptor[]) => {\n openMenus.current = menus;\n forceRefresh({});\n }, []);\n\n const menuOpenPendingTimeout = useRef<number | undefined>();\n const menuClosePendingTimeout = useRef<number | undefined>();\n const menuState = useRef<MenuState>({ [rootId]: \"no-popup\" });\n // const prevLevel = useRef(0);\n\n // const prevAim = useRef({mousePos: null, distance: true});\n\n const openMenu = useCallback(\n (\n hostMenuId = rootId,\n targetMenuId: string,\n itemId: string | null = null,\n ) => {\n if (hostMenuId === rootId && itemId === null) {\n setOpenMenus([{ id: rootId, left: posX, top: posY }]);\n } else {\n menuState.current[hostMenuId] = \"popup-open\";\n if (itemId) {\n const el = document.getElementById(itemId) as HTMLElement;\n if (el !== null) {\n const { left, top } = getPosition(el, openMenus.current);\n setOpenMenus(\n openMenus.current.concat({ id: targetMenuId, left, top }),\n );\n } else {\n throw Error(`openMenu no menuItem ${itemId}`);\n }\n }\n }\n },\n [rootId, posX, posY, setOpenMenus],\n );\n\n const closeMenu = useCallback(\n (menuId?: string) => {\n if (menuId === rootId) {\n setOpenMenus([]);\n } else {\n const menus = openMenus.current.slice();\n const lastMenu = menus.pop() as RuntimeMenuDescriptor;\n menuState.current[lastMenu.id] = \"no-popup\";\n const parentMenu = menus.at(-1);\n if (parentMenu) {\n menuState.current[parentMenu.id] = \"no-popup\";\n }\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus],\n );\n\n const closeMenus = useCallback(\n (menuItemId: string) => {\n const menus = openMenus.current.slice();\n const menuItemMenuId = menuItemId.slice(9);\n let { id: lastMenuId } = menus.at(-1) as RuntimeMenuDescriptor;\n while (menus.length > 1 && !menuItemMenuId.startsWith(lastMenuId)) {\n const parentMenuId = getHostMenuId(lastMenuId, rootId);\n menus.pop();\n menuState.current[lastMenuId] = \"no-popup\";\n menuState.current[parentMenuId] = \"no-popup\";\n ({ id: lastMenuId } = menus[menus.length - 1]);\n }\n if (menus.length < openMenus.current.length) {\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus],\n );\n\n const clearAnyScheduledOpenTasks = useCallback(() => {\n if (menuOpenPendingTimeout.current) {\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n }\n }, []);\n\n const scheduleOpen = useCallback(\n (\n hostMenuId: string,\n targetMenuId: string,\n menuItemId: string,\n delay = 300,\n ) => {\n clearAnyScheduledOpenTasks();\n // do we need to set target state to pending-open ?s\n\n menuOpenPendingTimeout.current = window.setTimeout(() => {\n // console.log(\n // `scheduleOpen<timeout> opening menu ${targetMenuId} from menu ${hostMenuId} via menuitem ${menuItemId}`\n // );\n closeMenus(menuItemId);\n menuState.current[hostMenuId] = \"popup-open\";\n menuState.current[targetMenuId] = \"no-popup\";\n openMenu(hostMenuId, targetMenuId, menuItemId);\n }, delay);\n },\n [clearAnyScheduledOpenTasks, closeMenus, openMenu],\n );\n\n const scheduleClose = useCallback(\n (hostMenuId: string, openMenuId: string, itemId: string) => {\n // console.log(\n // `scheduleClose openMenuId ${openMenuId} from parent menu ${hostMenuId} itemId ${itemId}`\n // );\n menuState.current[openMenuId] = \"pending-close\";\n menuClosePendingTimeout.current = window.setTimeout(() => {\n // console.log(`call closeMenus from scheduleClose`);\n closeMenus(itemId);\n }, 400);\n },\n [closeMenus],\n );\n\n const handleRender = useCallback(() => {\n const { current: menus } = openMenus;\n const menu = menus.at(-1);\n const el = menu ? document.getElementById(menu.id) : undefined;\n if (el) {\n const { right, bottom } = el.getBoundingClientRect();\n const { clientHeight, clientWidth } = document.body;\n if (right > clientWidth) {\n const newMenus =\n menus.length > 1\n ? flipSides(rootId, menus)\n : nudgeLeft(menus, right - clientWidth);\n setOpenMenus(newMenus);\n } else if (bottom > clientHeight) {\n const newMenus = nudgeUp(menus, bottom - clientHeight);\n setOpenMenus(newMenus);\n }\n\n if (typeof el.tabIndex === \"number\") {\n el.focus();\n }\n }\n }, [rootId, setOpenMenus]);\n\n // TODO introduce a delay parameter that allows click to requeat an immediate render\n const triggerChildMenu = useCallback<MenuOpenHandler>(\n (menuItemEl, immediate = false) => {\n const { hostMenuId, targetMenuId, menuItemId, isGroup, isOpen } =\n getMenuItemDetails(menuItemEl, rootId);\n const {\n current: { [hostMenuId]: state },\n } = menuState;\n\n const delay = immediate ? 0 : undefined;\n\n // console.log(\n // `%ctriggerChildMenu\n // rootId ${rootId}\n // menuItem ${menuItemId}\n // host menu: ${hostMenuId}\n // target menu: ${targetMenuId}\n // item index: ${menuItemId}\n // state ${state}\n // isGroup ${isGroup} isOpen ${isOpen}\n // openMenus: ${JSON.stringify(openMenus.current)}\n // full state='${JSON.stringify(menuState.current)}`,\n // \"color: green; font-weight: bold;\"\n // );\n\n if (state === \"no-popup\" && isGroup) {\n menuState.current[hostMenuId] = \"popup-pending\";\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-pending\" && !isGroup) {\n menuState.current[hostMenuId] = \"no-popup\";\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n } else if (state === \"popup-pending\" && isGroup) {\n clearTimeout(menuOpenPendingTimeout.current);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-open\") {\n if (menuIsOpen(targetMenuId)) {\n const menuStatus = getOpenMenuStatus(targetMenuId);\n // Close any child menus of the target menu. This can happen if we have\n // opened child menus, then moused out of the menu entirely, to re-enter\n // at a higher level\n closeMenus(menuItemId);\n\n switch (menuStatus) {\n case \"pending-close\":\n // cancel the close\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[targetMenuId] = \"no-popup\";\n clearAnyScheduledOpenTasks();\n break;\n default:\n }\n } else {\n // TODO review the below, suspectb it's over complicating things\n const [parentOfLastOpenedMenu, lastOpenedMenu] =\n openMenus.current.slice(-2);\n if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n menuState.current[lastOpenedMenu.id] !== \"pending-close\" /*&&\n sameLevel*/\n ) {\n scheduleClose(hostMenuId, lastOpenedMenu.id, menuItemId);\n if (isGroup && !isOpen) {\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n }\n } else if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n isGroup &&\n menuItemId !== lastOpenedMenu.id &&\n menuState.current[lastOpenedMenu.id] === \"pending-close\"\n ) {\n // if there is already an item queued for opening cancel it\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (isGroup) {\n // closeMenus(menuId, itemId);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (\n !(\n (menuState.current[lastOpenedMenu.id] === \"pending-close\") /*&&\n sameLevel*/\n )\n ) {\n closeMenus(menuItemId);\n }\n }\n }\n\n if (state === \"pending-close\") {\n clearAnyScheduledOpenTasks();\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[hostMenuId] = \"popup-open\";\n }\n },\n [\n clearAnyScheduledOpenTasks,\n closeMenus,\n getOpenMenuStatus,\n menuIsOpen,\n rootId,\n scheduleClose,\n scheduleOpen,\n ],\n );\n\n const listItemProps: Partial<MenuItemProps> = useMemo(\n () => ({\n onMouseEnter: (evt: MouseEvent) => {\n const menuItemEl = closestListItem(evt.target as HTMLElement);\n triggerChildMenu(menuItemEl);\n onMouseEnterItem(evt, menuItemEl.id);\n },\n\n onClick: (evt: SyntheticEvent) => {\n const listItemEl = closestListItem(evt.target as HTMLElement);\n const { isGroup, menuItemId } = getMenuItemDetails(listItemEl, rootId);\n if (isGroup) {\n triggerChildMenu(listItemEl);\n } else {\n onActivate(menuItemId);\n }\n },\n }),\n [onActivate, onMouseEnterItem, rootId, triggerChildMenu],\n );\n\n return {\n closeMenu,\n handleRender,\n listItemProps,\n openMenu: triggerChildMenu,\n openMenus: openMenus.current,\n };\n};\n"],"names":["useState","useRef","useCallback","useMemo","closestListItem"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":["isValidNumber","useRef","useState","useCallback","isNavigationKey","hasPopup","isRoot","useMemo"],"mappings":";;;;;;;AAmDO,MAAM,wBAAwB,CAAC;AAAA,EACpC,sBAAyB,GAAA,KAAA;AAAA,EACzB,KAAA;AAAA,EACA,qBAAA;AAAA,EACA,gBAAkB,EAAA,oBAAA;AAAA,EAClB,UAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAqD,KAAA;AACnD,EAAA,IACEA,sBAAc,CAAA,oBAAoB,CAClC,IAAAA,sBAAA,CAAc,qBAAqB,CACnC,EAAA;AACA,IAAM,MAAA,KAAA;AAAA,MACJ;AAAA,KACF;AAAA;AAGF,EAAM,MAAA,sBAAA,GAAyBA,uBAAc,oBAAoB,CAAA;AACjE,EAAA,MAAM,mBAAsB,GAAAC,YAAA;AAAA,IAC1B,qBAAA,IACE,oBACC,KAAA,sBAAA,GAAyB,CAAI,GAAA,CAAA,CAAA;AAAA,GAClC;AACA,EAAA,MAAM,GAAG,WAAW,CAAA,GAAIC,eAAkB,IAAI,CAAA;AAE9C,EAAA,MAAM,iBAAoB,GAAAC,iBAAA;AAAA,IACxB,CAAC,GAAQ,KAAA;AACP,MAAA,mBAAA,CAAoB,OAAU,GAAA,GAAA;AAC9B,MAAA,WAAA,GAAc,GAAG,CAAA;AACjB,MAAA,WAAA,CAAY,EAAE,CAAA;AAAA,KAChB;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,mBAAsB,GAAAA,iBAAA;AAAA,IAC1B,CAAC,GAAQ,KAAA;AACP,MAAI,IAAA,GAAA,KAAQ,oBAAoB,OAAS,EAAA;AACvC,QAAA,IAAI,CAAC,sBAAwB,EAAA;AAC3B,UAAA,iBAAA,CAAkB,GAAG,CAAA;AAAA;AACvB;AACF,KACF;AAAA,IACA,CAAC,wBAAwB,iBAAiB;AAAA,GAC5C;AAGA,EAAM,MAAA,kBAAA,GAAqBF,aAAO,IAAI,CAAA;AACtC,EAAM,MAAA,WAAA,GAAcA,aAAO,KAAK,CAAA;AAChC,EAAA,MAAM,cAAiB,GAAA,CAAC,KAAoB,KAAA,WAAA,CAAY,OAAU,GAAA,KAAA;AAElE,EAAM,MAAA,gBAAA,GAAmB,sBACrB,GAAA,oBAAA,GACA,mBAAoB,CAAA,OAAA;AAExB,EAAA,MAAM,kBAAqB,GAAAE,iBAAA;AAAA,IACzB,CAAC,CAAqB,KAAA;AACpB,MAAA,MAAM,UAAU,WAAY,CAAA,KAAA,EAAO,CAAE,CAAA,GAAA,EAAK,oBAAoB,OAAO,CAAA;AACrE,MAAI,IAAA,OAAA,KAAY,oBAAoB,OAAS,EAAA;AAC3C,QAAA,mBAAA,CAAoB,OAAO,CAAA;AAAA;AAC7B,KACF;AAAA,IACA,CAAC,OAAO,mBAAmB;AAAA,GAC7B;AAEA,EAAA,MAAM,aAAgB,GAAAA,iBAAA;AAAA,IACpB,CAAC,CAAqB,KAAA;AACpB,MAAI,IAAAC,uBAAA,CAAgB,CAAC,CAAG,EAAA;AACtB,QAAA,CAAA,CAAE,cAAe,EAAA;AACjB,QAAA,CAAA,CAAE,eAAgB,EAAA;AAClB,QAAA,kBAAA,CAAmB,OAAU,GAAA,IAAA;AAC7B,QAAA,kBAAA,CAAmB,CAAC,CAAA;AAAA,OACtB,MAAA,IAAA,CACG,CAAE,CAAA,GAAA,KAAQ,YAAgB,IAAA,CAAA,CAAE,GAAQ,KAAA,OAAA,KACrCC,cAAS,CAAA,CAAA,CAAE,MAAuB,EAAA,gBAAgB,CAClD,EAAA;AACA,QAAA,MAAM,SAAS,CAAE,CAAA,MAAA;AACjB,QAAA,MAAM,aAAa,MAAO,CAAA,aAAA;AAAA,UACxB,yBAAyB,gBAAgB,CAAA,EAAA;AAAA,SAC3C;AAEA,QAAA,IAAI,UAAY,EAAA;AACd,UAAA,UAAA,GAAa,YAAY,IAAI,CAAA;AAAA;AAC/B,OACF,MAAA,IAAW,EAAE,GAAQ,KAAA,WAAA,IAAe,CAACC,YAAO,CAAA,CAAA,CAAE,MAAqB,CAAG,EAAA;AACpE,QAAA,WAAA,CAAY,GAAG,kBAAkB,CAAA;AAAA,OACnC,MAAA,IAAW,CAAE,CAAA,GAAA,KAAQ,OAAS,EAAA;AAC5B,QAAA,CAAA,CAAE,cAAe,EAAA;AACjB,QAAA,CAAA,CAAE,eAAgB,EAAA;AAClB,QAAA,UAAA,IAAc,WAAW,gBAAgB,CAAA;AAAA,OAC3C,MAAA,IAAW,CAAE,CAAA,GAAA,KAAQ,KAAO,EAAA;AAC1B,QAAA,WAAA,CAAY,GAAG,UAAU,CAAA;AAAA;AAC3B,KACF;AAAA,IACA,CAAC,gBAAA,EAAkB,kBAAoB,EAAA,UAAA,EAAY,aAAa,UAAU;AAAA,GAC5E;AAEA,EAAA,MAAM,SAAmC,GAAAC,aAAA;AAAA,IACvC,OAAO;AAAA,MACL,SAAS,MAAM;AACb,QAAA,IAAI,qBAAqB,CAAI,CAAA,EAAA;AAC3B,UAAA,iBAAA,CAAkB,CAAC,CAAA;AAAA;AACrB,OACF;AAAA,MACA,SAAW,EAAA,aAAA;AAAA,MACX,oBAAoB,MAAM;AACxB,QAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,OACrB;AAAA;AAAA,MAGA,aAAa,MAAM;AACjB,QAAA,IAAI,mBAAmB,OAAS,EAAA;AAC9B,UAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAAA;AAC/B,OACF;AAAA,MACA,cAAc,MAAM;AAClB,QAAA,kBAAA,CAAmB,OAAU,GAAA,IAAA;AAC7B,QAAA,cAAA,CAAe,KAAK,CAAA;AACpB,QAAA,mBAAA,CAAoB,CAAE,CAAA,CAAA;AAAA;AACxB,KACF,CAAA;AAAA,IACA,CAAC,aAAA,EAAe,gBAAkB,EAAA,iBAAA,EAAmB,mBAAmB;AAAA,GAC1E;AAEA,EAAO,OAAA;AAAA,IACL,YAAA,EAAc,kBAAmB,CAAA,OAAA,GAAU,gBAAmB,GAAA,CAAA,CAAA;AAAA,IAC9D,sBAAA;AAAA,IACA,gBAAA;AAAA,IACA,mBAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAGA,SAAS,WAAA,CAAY,KAAe,EAAA,GAAA,EAAa,GAAa,EAAA;AAC5D,EAAA,IAAI,QAAQ,SAAW,EAAA;AACrB,IAAA,IAAI,MAAM,CAAG,EAAA;AACX,MAAA,OAAO,GAAM,GAAA,CAAA;AAAA,KACR,MAAA;AACL,MAAO,OAAA,GAAA;AAAA;AACT,GACK,MAAA;AACL,IAAA,IAAI,QAAQ,IAAM,EAAA;AAChB,MAAO,OAAA,CAAA;AAAA,KACT,MAAA,IAAW,GAAQ,KAAA,KAAA,GAAQ,CAAG,EAAA;AAC5B,MAAO,OAAA,GAAA;AAAA,KACF,MAAA;AACL,MAAA,OAAO,GAAM,GAAA,CAAA;AAAA;AACf;AAEJ;;;;"}
1
+ {"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx: number) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx: number) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":["isValidNumber","useRef","useState","useCallback","isNavigationKey","hasPopup","isRoot","useMemo"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useContextMenu.js","sources":["../../src/menu/useContextMenu.tsx"],"sourcesContent":["import {\n ContextMenuItemDescriptor,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n isGroupMenuItemDescriptor,\n useThemeAttributes,\n} from \"@vuu-ui/vuu-utils\";\nimport { cloneElement, useCallback, useContext, useMemo } from \"react\";\nimport {\n MenuActionClosePopup,\n PopupCloseReason,\n PopupService,\n reasonIsMenuAction,\n} from \"../popup\";\nimport { ContextMenu, ContextMenuProps } from \"./ContextMenu\";\nimport { MenuItem, MenuItemGroup } from \"./MenuList\";\nimport { ContextMenuContext } from \"./context-menu-provider\";\n\nexport type ContextMenuOptions = {\n [key: string]: unknown;\n columns?: ColumnDescriptor[];\n contextMenu?: JSX.Element;\n ContextMenuProps?: Partial<ContextMenuProps> & {\n className?: string;\n };\n controlledComponentId?: string;\n};\n\nexport type EventLike = {\n clientX: number;\n clientY: number;\n preventDefault?: () => void;\n stopPropagation?: () => void;\n};\n\nexport type ShowContextMenu = (\n e: EventLike,\n location: string,\n options: ContextMenuOptions,\n) => void;\n\n// The argument allows a top-level menuBuilder to operate outside the Context\nexport const useContextMenu = (\n menuBuilder?: MenuBuilder,\n menuActionHandler?: MenuActionHandler,\n): [ShowContextMenu, () => void] => {\n const ctx = useContext(ContextMenuContext);\n\n const [themeClass, densityClass, dataMode] = useThemeAttributes();\n const themeAttributes = useMemo(\n () => ({\n themeClass,\n densityClass,\n dataMode,\n }),\n [dataMode, densityClass, themeClass],\n );\n\n const buildMenuOptions = useCallback(\n (menuBuilders: MenuBuilder[], location, options) => {\n let results: ContextMenuItemDescriptor[] = [];\n for (const menuBuilder of menuBuilders) {\n // Maybe we should leave the concatenation to the menuBuilder, then it can control menuItem order\n results = results.concat(menuBuilder(location, options));\n }\n return results;\n },\n [],\n );\n\n const handleShowContextMenu = useCallback<ShowContextMenu>(\n (e, location, { ContextMenuProps, contextMenu, ...options }) => {\n e.stopPropagation?.();\n e.preventDefault?.();\n\n if (contextMenu) {\n return showContextMenuComponent(\n {\n x: e.clientX,\n y: e.clientY,\n },\n contextMenu,\n );\n }\n\n const menuBuilders: MenuBuilder[] = [];\n if (menuBuilder) {\n menuBuilders.push(menuBuilder);\n }\n if (\n ctx &&\n Array.isArray(ctx?.menuBuilders) &&\n ctx.menuBuilders.length > 0\n ) {\n menuBuilders.push(...ctx.menuBuilders);\n }\n\n if (menuBuilders.length > 0) {\n const menuItemDescriptors = buildMenuOptions(\n menuBuilders,\n location,\n options,\n );\n\n // const menuHandler = menuActionHandler ?? ctx?.menuActionHandler;\n const menuHandler: MenuActionHandler = (\n action: MenuActionClosePopup,\n ) => {\n if (menuActionHandler?.(action) === true) {\n return true;\n } else {\n return ctx?.menuActionHandler(action);\n }\n };\n\n if (menuItemDescriptors.length && menuHandler) {\n // because showPopup is going to be used to render the context menu, it will not\n // have access to the ContextMenuContext. Pass the theme attributes here\n showContextMenu(e, menuItemDescriptors, menuHandler, {\n PortalProps: {\n themeAttributes,\n },\n ...ContextMenuProps,\n });\n }\n } else {\n console.warn(\n \"useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)\",\n );\n }\n },\n [buildMenuOptions, ctx, menuActionHandler, menuBuilder, themeAttributes],\n );\n\n const hideContextMenu = useCallback(() => {\n console.log(\"hide context menu\");\n }, []);\n\n return [handleShowContextMenu, hideContextMenu];\n};\n\nconst NO_OPTIONS = {};\n\nconst showContextMenuComponent = (\n position: { x: number; y: number },\n contextMenu: JSX.Element,\n) => {\n PopupService.showPopup({\n focus: true,\n left: 0,\n top: 0,\n component: cloneElement(contextMenu, { position }),\n });\n};\n\nconst showContextMenu = (\n e: EventLike,\n menuDescriptors: ContextMenuItemDescriptor[],\n handleContextMenuAction: MenuActionHandler,\n {\n position: positionProp,\n ...contextMenuProps\n }: ContextMenuOptions[\"ContextMenuProps\"] = NO_OPTIONS,\n) => {\n const menuItems = (menuDescriptors: ContextMenuItemDescriptor[]) => {\n const fromDescriptor = (menuItem: ContextMenuItemDescriptor, i: number) =>\n isGroupMenuItemDescriptor(menuItem) ? (\n <MenuItemGroup key={i} label={menuItem.label}>\n {menuItem.children.map(fromDescriptor)}\n </MenuItemGroup>\n ) : (\n <MenuItem\n key={i}\n action={menuItem.action}\n className={menuItem.className}\n data-icon={menuItem.icon}\n options={menuItem.options}\n >\n {menuItem.label}\n </MenuItem>\n );\n\n return menuDescriptors.map(fromDescriptor);\n };\n\n const handleClose = (reason?: PopupCloseReason) => {\n if (reasonIsMenuAction(reason)) {\n if (reason?.closedBy === \"popup-service\") {\n return;\n }\n handleContextMenuAction(reason);\n // TODO this results in onClose being called twice on component\n // cant simply be removed, some refactoring work needed\n PopupService.hidePopup(reason);\n }\n contextMenuProps?.onClose?.(reason);\n };\n\n const position = positionProp ?? {\n x: e.clientX,\n y: e.clientY,\n };\n\n const component = (\n <ContextMenu\n {...contextMenuProps}\n onClose={handleClose}\n position={position}\n >\n {menuItems(menuDescriptors)}\n </ContextMenu>\n );\n PopupService.showPopup({ left: 0, top: 0, component, focus: true });\n};\n"],"names":["useContext","ContextMenuContext","useThemeAttributes","useMemo","useCallback","menuBuilder","ContextMenuProps","PopupService","cloneElement","menuDescriptors","isGroupMenuItemDescriptor","jsx","MenuItemGroup","MenuItem","reasonIsMenuAction","ContextMenu"],"mappings":"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"}
1
+ {"version":3,"file":"useContextMenu.js","sources":["../../src/menu/useContextMenu.tsx"],"sourcesContent":["import {\n ContextMenuItemDescriptor,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n isGroupMenuItemDescriptor,\n useThemeAttributes,\n} from \"@vuu-ui/vuu-utils\";\nimport { cloneElement, useCallback, useContext, useMemo } from \"react\";\nimport {\n MenuActionClosePopup,\n PopupCloseReason,\n PopupService,\n reasonIsMenuAction,\n} from \"../popup\";\nimport { ContextMenu, ContextMenuProps } from \"./ContextMenu\";\nimport { MenuItem, MenuItemGroup } from \"./MenuList\";\nimport { ContextMenuContext } from \"./context-menu-provider\";\n\nexport type ContextMenuOptions = {\n [key: string]: unknown;\n columns?: ColumnDescriptor[];\n contextMenu?: JSX.Element;\n ContextMenuProps?: Partial<ContextMenuProps> & {\n className?: string;\n };\n controlledComponentId?: string;\n};\n\nexport type EventLike = {\n clientX: number;\n clientY: number;\n preventDefault?: () => void;\n stopPropagation?: () => void;\n};\n\nexport type ShowContextMenu = (\n e: EventLike,\n location: string,\n options: ContextMenuOptions,\n) => void;\n\n// The argument allows a top-level menuBuilder to operate outside the Context\nexport const useContextMenu = (\n menuBuilder?: MenuBuilder,\n menuActionHandler?: MenuActionHandler,\n): [ShowContextMenu, () => void] => {\n const ctx = useContext(ContextMenuContext);\n\n const [themeClass, densityClass, dataMode] = useThemeAttributes();\n const themeAttributes = useMemo(\n () => ({\n themeClass,\n densityClass,\n dataMode,\n }),\n [dataMode, densityClass, themeClass],\n );\n\n const buildMenuOptions = useCallback(\n (menuBuilders: MenuBuilder[], location: string, options: unknown) => {\n let results: ContextMenuItemDescriptor[] = [];\n for (const menuBuilder of menuBuilders) {\n // Maybe we should leave the concatenation to the menuBuilder, then it can control menuItem order\n results = results.concat(menuBuilder(location, options));\n }\n return results;\n },\n [],\n );\n\n const handleShowContextMenu = useCallback<ShowContextMenu>(\n (e, location, { ContextMenuProps, contextMenu, ...options }) => {\n e.stopPropagation?.();\n e.preventDefault?.();\n\n if (contextMenu) {\n return showContextMenuComponent(\n {\n x: e.clientX,\n y: e.clientY,\n },\n contextMenu,\n );\n }\n\n const menuBuilders: MenuBuilder[] = [];\n if (menuBuilder) {\n menuBuilders.push(menuBuilder);\n }\n if (\n ctx &&\n Array.isArray(ctx?.menuBuilders) &&\n ctx.menuBuilders.length > 0\n ) {\n menuBuilders.push(...ctx.menuBuilders);\n }\n\n if (menuBuilders.length > 0) {\n const menuItemDescriptors = buildMenuOptions(\n menuBuilders,\n location,\n options,\n );\n\n // const menuHandler = menuActionHandler ?? ctx?.menuActionHandler;\n const menuHandler: MenuActionHandler = (\n action: MenuActionClosePopup,\n ) => {\n if (menuActionHandler?.(action) === true) {\n return true;\n } else {\n return ctx?.menuActionHandler(action);\n }\n };\n\n if (menuItemDescriptors.length && menuHandler) {\n // because showPopup is going to be used to render the context menu, it will not\n // have access to the ContextMenuContext. Pass the theme attributes here\n showContextMenu(e, menuItemDescriptors, menuHandler, {\n PortalProps: {\n themeAttributes,\n },\n ...ContextMenuProps,\n });\n }\n } else {\n console.warn(\n \"useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)\",\n );\n }\n },\n [buildMenuOptions, ctx, menuActionHandler, menuBuilder, themeAttributes],\n );\n\n const hideContextMenu = useCallback(() => {\n console.log(\"hide context menu\");\n }, []);\n\n return [handleShowContextMenu, hideContextMenu];\n};\n\nconst NO_OPTIONS = {};\n\nconst showContextMenuComponent = (\n position: { x: number; y: number },\n contextMenu: JSX.Element,\n) => {\n PopupService.showPopup({\n focus: true,\n left: 0,\n top: 0,\n component: cloneElement(contextMenu, { position }),\n });\n};\n\nconst showContextMenu = (\n e: EventLike,\n menuDescriptors: ContextMenuItemDescriptor[],\n handleContextMenuAction: MenuActionHandler,\n {\n position: positionProp,\n ...contextMenuProps\n }: ContextMenuOptions[\"ContextMenuProps\"] = NO_OPTIONS,\n) => {\n const menuItems = (menuDescriptors: ContextMenuItemDescriptor[]) => {\n const fromDescriptor = (menuItem: ContextMenuItemDescriptor, i: number) =>\n isGroupMenuItemDescriptor(menuItem) ? (\n <MenuItemGroup key={i} label={menuItem.label}>\n {menuItem.children.map(fromDescriptor)}\n </MenuItemGroup>\n ) : (\n <MenuItem\n key={i}\n action={menuItem.action}\n className={menuItem.className}\n data-icon={menuItem.icon}\n options={menuItem.options}\n >\n {menuItem.label}\n </MenuItem>\n );\n\n return menuDescriptors.map(fromDescriptor);\n };\n\n const handleClose = (reason?: PopupCloseReason) => {\n if (reasonIsMenuAction(reason)) {\n if (reason?.closedBy === \"popup-service\") {\n return;\n }\n handleContextMenuAction(reason);\n // TODO this results in onClose being called twice on component\n // cant simply be removed, some refactoring work needed\n PopupService.hidePopup(reason);\n }\n contextMenuProps?.onClose?.(reason);\n };\n\n const position = positionProp ?? {\n x: e.clientX,\n y: e.clientY,\n };\n\n const component = (\n <ContextMenu\n {...contextMenuProps}\n onClose={handleClose}\n position={position}\n >\n {menuItems(menuDescriptors)}\n </ContextMenu>\n );\n PopupService.showPopup({ left: 0, top: 0, component, focus: true });\n};\n"],"names":["useContext","ContextMenuContext","useThemeAttributes","useMemo","useCallback","menuBuilder","ContextMenuProps","PopupService","cloneElement","menuDescriptors","isGroupMenuItemDescriptor","jsx","MenuItemGroup","MenuItem","reasonIsMenuAction","ContextMenu"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePopupMenu.js","sources":["../../src/popup-menu/usePopupMenu.ts"],"sourcesContent":["import {\n AriaAttributes,\n MouseEvent,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { MenuOpenHandler } from \"../menu\";\nimport { PopupMenuProps } from \"./PopupMenu\";\nimport { getPositionRelativeToAnchor } from \"../popup/getPositionRelativeToAnchor\";\nimport { useContextMenu } from \"../menu\";\nimport { PopupCloseCallback, reasonIsClickAway } from \"../popup/popup-service\";\nimport { PopupPlacement } from \"../popup/Popup\";\n\nexport interface PopupMenuHookProps\n extends Pick<\n PopupMenuProps,\n | \"anchorElement\"\n | \"menuActionHandler\"\n | \"menuBuilder\"\n | \"menuClassName\"\n | \"menuOptions\"\n | \"onMenuClose\"\n | \"onMenuOpen\"\n > {\n id: string;\n menuLocation: string;\n popupPlacement: PopupPlacement;\n tabIndex: number;\n}\n\nexport const usePopupMenu = ({\n anchorElement,\n id,\n menuActionHandler,\n menuBuilder,\n menuClassName,\n menuLocation,\n menuOptions,\n onMenuClose,\n onMenuOpen,\n popupPlacement,\n tabIndex,\n}: PopupMenuHookProps) => {\n const [menuOpen, _setMenuOpen] = useState(false);\n const suppressShowMenuRef = useRef(false);\n const rootRef = useRef<HTMLButtonElement>(null);\n\n const setMenuOpen = useCallback(\n (isOpen) => {\n _setMenuOpen(isOpen);\n if (isOpen) {\n onMenuOpen?.();\n }\n },\n [onMenuOpen]\n );\n\n const [showContextMenu] = useContextMenu(menuBuilder, menuActionHandler);\n\n const handleOpenMenu = useCallback<MenuOpenHandler>((el) => {\n console.log(`menu Open `, {\n el,\n });\n }, []);\n\n const handleMenuClose = useCallback<PopupCloseCallback>(\n (reason) => {\n setMenuOpen(false);\n // If user has clicked the MenuButton whilst menu is open, we want to close it.\n // The PopupService will close it for us as a 'click-away' event. We don't want\n // that click on the button to re-open it.\n if (reasonIsClickAway(reason)) {\n const target = reason.mouseEvt.target as HTMLElement;\n if (target === rootRef.current) {\n suppressShowMenuRef.current = true;\n }\n onMenuClose?.(reason);\n } else {\n requestAnimationFrame(() => {\n onMenuClose?.(reason);\n if (tabIndex !== -1 && reason?.type !== \"tab-away\") {\n rootRef.current?.focus();\n }\n });\n }\n },\n [onMenuClose, setMenuOpen, tabIndex]\n );\n\n const showMenu = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n if (suppressShowMenuRef.current) {\n suppressShowMenuRef.current = false;\n } else {\n const anchorEl = anchorElement?.current ?? rootRef.current;\n if (anchorEl) {\n const {\n left: x,\n top: y,\n width,\n } = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);\n setMenuOpen(true);\n\n showContextMenu(e, menuLocation, {\n ContextMenuProps: {\n className: menuClassName,\n id: `${id}-menu`,\n onClose: handleMenuClose,\n openMenu: handleOpenMenu,\n position: {\n x,\n y,\n },\n style: { width: width ? width - 2 : undefined },\n },\n ...menuOptions,\n });\n }\n }\n },\n [\n anchorElement,\n handleMenuClose,\n handleOpenMenu,\n id,\n menuClassName,\n menuLocation,\n menuOptions,\n popupPlacement,\n setMenuOpen,\n showContextMenu,\n ]\n );\n\n const ariaAttributes: AriaAttributes = {\n \"aria-controls\": menuOpen ? `${id}-menu` : undefined,\n \"aria-expanded\": menuOpen,\n \"aria-haspopup\": \"menu\",\n };\n\n const buttonProps = {\n id,\n onClick: showMenu,\n tabIndex,\n };\n\n return { ariaAttributes, buttonProps, menuOpen, rootRef };\n};\n"],"names":["useState","useRef","useCallback","useContextMenu","reasonIsClickAway","getPositionRelativeToAnchor"],"mappings":"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"}
1
+ {"version":3,"file":"usePopupMenu.js","sources":["../../src/popup-menu/usePopupMenu.ts"],"sourcesContent":["import {\n AriaAttributes,\n MouseEvent,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { MenuOpenHandler } from \"../menu\";\nimport { PopupMenuProps } from \"./PopupMenu\";\nimport { getPositionRelativeToAnchor } from \"../popup/getPositionRelativeToAnchor\";\nimport { useContextMenu } from \"../menu\";\nimport { PopupCloseCallback, reasonIsClickAway } from \"../popup/popup-service\";\nimport { PopupPlacement } from \"../popup/Popup\";\n\nexport interface PopupMenuHookProps\n extends Pick<\n PopupMenuProps,\n | \"anchorElement\"\n | \"menuActionHandler\"\n | \"menuBuilder\"\n | \"menuClassName\"\n | \"menuOptions\"\n | \"onMenuClose\"\n | \"onMenuOpen\"\n > {\n id: string;\n menuLocation: string;\n popupPlacement: PopupPlacement;\n tabIndex: number;\n}\n\nexport const usePopupMenu = ({\n anchorElement,\n id,\n menuActionHandler,\n menuBuilder,\n menuClassName,\n menuLocation,\n menuOptions,\n onMenuClose,\n onMenuOpen,\n popupPlacement,\n tabIndex,\n}: PopupMenuHookProps) => {\n const [menuOpen, _setMenuOpen] = useState(false);\n const suppressShowMenuRef = useRef(false);\n const rootRef = useRef<HTMLButtonElement>(null);\n\n const setMenuOpen = useCallback(\n (isOpen: boolean) => {\n _setMenuOpen(isOpen);\n if (isOpen) {\n onMenuOpen?.();\n }\n },\n [onMenuOpen],\n );\n\n const [showContextMenu] = useContextMenu(menuBuilder, menuActionHandler);\n\n const handleOpenMenu = useCallback<MenuOpenHandler>((el) => {\n console.log(`menu Open `, {\n el,\n });\n }, []);\n\n const handleMenuClose = useCallback<PopupCloseCallback>(\n (reason) => {\n setMenuOpen(false);\n // If user has clicked the MenuButton whilst menu is open, we want to close it.\n // The PopupService will close it for us as a 'click-away' event. We don't want\n // that click on the button to re-open it.\n if (reasonIsClickAway(reason)) {\n const target = reason.mouseEvt.target as HTMLElement;\n if (target === rootRef.current) {\n suppressShowMenuRef.current = true;\n }\n onMenuClose?.(reason);\n } else {\n requestAnimationFrame(() => {\n onMenuClose?.(reason);\n if (tabIndex !== -1 && reason?.type !== \"tab-away\") {\n rootRef.current?.focus();\n }\n });\n }\n },\n [onMenuClose, setMenuOpen, tabIndex],\n );\n\n const showMenu = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n if (suppressShowMenuRef.current) {\n suppressShowMenuRef.current = false;\n } else {\n const anchorEl = anchorElement?.current ?? rootRef.current;\n if (anchorEl) {\n const {\n left: x,\n top: y,\n width,\n } = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);\n setMenuOpen(true);\n\n showContextMenu(e, menuLocation, {\n ContextMenuProps: {\n className: menuClassName,\n id: `${id}-menu`,\n onClose: handleMenuClose,\n openMenu: handleOpenMenu,\n position: {\n x,\n y,\n },\n style: { width: width ? width - 2 : undefined },\n },\n ...menuOptions,\n });\n }\n }\n },\n [\n anchorElement,\n handleMenuClose,\n handleOpenMenu,\n id,\n menuClassName,\n menuLocation,\n menuOptions,\n popupPlacement,\n setMenuOpen,\n showContextMenu,\n ],\n );\n\n const ariaAttributes: AriaAttributes = {\n \"aria-controls\": menuOpen ? `${id}-menu` : undefined,\n \"aria-expanded\": menuOpen,\n \"aria-haspopup\": \"menu\",\n };\n\n const buttonProps = {\n id,\n onClick: showMenu,\n tabIndex,\n };\n\n return { ariaAttributes, buttonProps, menuOpen, rootRef };\n};\n"],"names":["useState","useRef","useCallback","useContextMenu","reasonIsClickAway","getPositionRelativeToAnchor"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu-provider.js","sources":["../../src/menu/context-menu-provider.tsx"],"sourcesContent":["import type {\n ContextMenuContextType,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { createContext, ReactNode, useCallback, useMemo } from \"react\";\n\nexport const ContextMenuContext = createContext<ContextMenuContextType | null>(\n null,\n);\n\nexport interface ContextMenuConfiguration {\n menuActionHandler?: MenuActionHandler;\n menuBuilder: MenuBuilder;\n}\n\nexport interface ContextMenuProviderProps extends ContextMenuConfiguration {\n children: ReactNode;\n label?: string;\n}\n\ninterface ProviderProps extends ContextMenuProviderProps {\n context: ContextMenuContextType | null;\n}\n\nconst Provider = ({\n children,\n context,\n menuActionHandler,\n menuBuilder,\n}: ProviderProps) => {\n const menuBuilders = useMemo(() => {\n if (context?.menuBuilders && menuBuilder) {\n return context.menuBuilders.concat(menuBuilder);\n } else if (menuBuilder) {\n return [menuBuilder];\n } else {\n return context?.menuBuilders || [];\n }\n }, [context, menuBuilder]);\n\n const handleMenuAction = useCallback(\n (reason) => {\n if (menuActionHandler?.(reason)) {\n return true;\n }\n\n if (context?.menuActionHandler?.(reason)) {\n return true;\n }\n },\n [context, menuActionHandler],\n );\n\n return (\n <ContextMenuContext.Provider\n value={{\n menuActionHandler: handleMenuAction,\n menuBuilders,\n }}\n >\n {children}\n </ContextMenuContext.Provider>\n );\n};\n\n// Need an option for local menu to override higher-level menu, rather than extend\nexport const ContextMenuProvider = ({\n children,\n label,\n menuActionHandler,\n menuBuilder,\n}: ContextMenuProviderProps) => {\n return (\n <ContextMenuContext.Consumer>\n {(parentContext) => (\n <Provider\n context={parentContext}\n label={label}\n menuActionHandler={menuActionHandler}\n menuBuilder={menuBuilder}\n >\n {children}\n </Provider>\n )}\n </ContextMenuContext.Consumer>\n );\n};\n"],"names":[],"mappings":";;;AAOO,MAAM,kBAAqB,GAAA,aAAA;AAAA,EAChC;AACF;AAgBA,MAAM,WAAW,CAAC;AAAA,EAChB,QAAA;AAAA,EACA,OAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAqB,KAAA;AACnB,EAAM,MAAA,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAI,IAAA,OAAA,EAAS,gBAAgB,WAAa,EAAA;AACxC,MAAO,OAAA,OAAA,CAAQ,YAAa,CAAA,MAAA,CAAO,WAAW,CAAA;AAAA,eACrC,WAAa,EAAA;AACtB,MAAA,OAAO,CAAC,WAAW,CAAA;AAAA,KACd,MAAA;AACL,MAAO,OAAA,OAAA,EAAS,gBAAgB,EAAC;AAAA;AACnC,GACC,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AAEzB,EAAA,MAAM,gBAAmB,GAAA,WAAA;AAAA,IACvB,CAAC,MAAW,KAAA;AACV,MAAI,IAAA,iBAAA,GAAoB,MAAM,CAAG,EAAA;AAC/B,QAAO,OAAA,IAAA;AAAA;AAGT,MAAI,IAAA,OAAA,EAAS,iBAAoB,GAAA,MAAM,CAAG,EAAA;AACxC,QAAO,OAAA,IAAA;AAAA;AACT,KACF;AAAA,IACA,CAAC,SAAS,iBAAiB;AAAA,GAC7B;AAEA,EACE,uBAAA,GAAA;AAAA,IAAC,kBAAmB,CAAA,QAAA;AAAA,IAAnB;AAAA,MACC,KAAO,EAAA;AAAA,QACL,iBAAmB,EAAA,gBAAA;AAAA,QACnB;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ,CAAA;AAGO,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA,KAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAgC,KAAA;AAC9B,EAAA,uBACG,GAAA,CAAA,kBAAA,CAAmB,QAAnB,EAAA,EACE,WAAC,aACA,qBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAS,EAAA,aAAA;AAAA,MACT,KAAA;AAAA,MACA,iBAAA;AAAA,MACA,WAAA;AAAA,MAEC;AAAA;AAAA,GAGP,EAAA,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"context-menu-provider.js","sources":["../../src/menu/context-menu-provider.tsx"],"sourcesContent":["import type {\n ContextMenuContextType,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { createContext, ReactNode, useCallback, useMemo } from \"react\";\n\nexport const ContextMenuContext = createContext<ContextMenuContextType | null>(\n null,\n);\n\nexport interface ContextMenuConfiguration {\n menuActionHandler?: MenuActionHandler;\n menuBuilder: MenuBuilder;\n}\n\nexport interface ContextMenuProviderProps extends ContextMenuConfiguration {\n children: ReactNode;\n label?: string;\n}\n\ninterface ProviderProps extends ContextMenuProviderProps {\n context: ContextMenuContextType | null;\n}\n\nconst Provider = ({\n children,\n context,\n menuActionHandler,\n menuBuilder,\n}: ProviderProps) => {\n const menuBuilders = useMemo(() => {\n if (context?.menuBuilders && menuBuilder) {\n return context.menuBuilders.concat(menuBuilder);\n } else if (menuBuilder) {\n return [menuBuilder];\n } else {\n return context?.menuBuilders || [];\n }\n }, [context, menuBuilder]);\n\n const handleMenuAction = useCallback<MenuActionHandler>(\n (reason) => {\n if (menuActionHandler?.(reason)) {\n return true;\n }\n\n if (context?.menuActionHandler?.(reason)) {\n return true;\n }\n },\n [context, menuActionHandler],\n );\n\n return (\n <ContextMenuContext.Provider\n value={{\n menuActionHandler: handleMenuAction,\n menuBuilders,\n }}\n >\n {children}\n </ContextMenuContext.Provider>\n );\n};\n\n// Need an option for local menu to override higher-level menu, rather than extend\nexport const ContextMenuProvider = ({\n children,\n label,\n menuActionHandler,\n menuBuilder,\n}: ContextMenuProviderProps) => {\n return (\n <ContextMenuContext.Consumer>\n {(parentContext) => (\n <Provider\n context={parentContext}\n label={label}\n menuActionHandler={menuActionHandler}\n menuBuilder={menuBuilder}\n >\n {children}\n </Provider>\n )}\n </ContextMenuContext.Consumer>\n );\n};\n"],"names":[],"mappings":";;;AAOO,MAAM,kBAAqB,GAAA,aAAA;AAAA,EAChC;AACF;AAgBA,MAAM,WAAW,CAAC;AAAA,EAChB,QAAA;AAAA,EACA,OAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAqB,KAAA;AACnB,EAAM,MAAA,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAI,IAAA,OAAA,EAAS,gBAAgB,WAAa,EAAA;AACxC,MAAO,OAAA,OAAA,CAAQ,YAAa,CAAA,MAAA,CAAO,WAAW,CAAA;AAAA,eACrC,WAAa,EAAA;AACtB,MAAA,OAAO,CAAC,WAAW,CAAA;AAAA,KACd,MAAA;AACL,MAAO,OAAA,OAAA,EAAS,gBAAgB,EAAC;AAAA;AACnC,GACC,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AAEzB,EAAA,MAAM,gBAAmB,GAAA,WAAA;AAAA,IACvB,CAAC,MAAW,KAAA;AACV,MAAI,IAAA,iBAAA,GAAoB,MAAM,CAAG,EAAA;AAC/B,QAAO,OAAA,IAAA;AAAA;AAGT,MAAI,IAAA,OAAA,EAAS,iBAAoB,GAAA,MAAM,CAAG,EAAA;AACxC,QAAO,OAAA,IAAA;AAAA;AACT,KACF;AAAA,IACA,CAAC,SAAS,iBAAiB;AAAA,GAC7B;AAEA,EACE,uBAAA,GAAA;AAAA,IAAC,kBAAmB,CAAA,QAAA;AAAA,IAAnB;AAAA,MACC,KAAO,EAAA;AAAA,QACL,iBAAmB,EAAA,gBAAA;AAAA,QACnB;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ,CAAA;AAGO,MAAM,sBAAsB,CAAC;AAAA,EAClC,QAAA;AAAA,EACA,KAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAgC,KAAA;AAC9B,EAAA,uBACG,GAAA,CAAA,kBAAA,CAAmB,QAAnB,EAAA,EACE,WAAC,aACA,qBAAA,GAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,OAAS,EAAA,aAAA;AAAA,MACT,KAAA;AAAA,MACA,iBAAA;AAAA,MACA,WAAA;AAAA,MAEC;AAAA;AAAA,GAGP,EAAA,CAAA;AAEJ;;;;"}
@@ -86,14 +86,16 @@ const useCascade = ({
86
86
  setOpenMenus([{ id: rootId, left: posX, top: posY }]);
87
87
  } else {
88
88
  menuState.current[hostMenuId] = "popup-open";
89
- const el = document.getElementById(itemId);
90
- if (el !== null) {
91
- const { left, top } = getPosition(el, openMenus.current);
92
- setOpenMenus(
93
- openMenus.current.concat({ id: targetMenuId, left, top })
94
- );
95
- } else {
96
- throw Error(`openMenu no menuItem ${itemId}`);
89
+ if (itemId) {
90
+ const el = document.getElementById(itemId);
91
+ if (el !== null) {
92
+ const { left, top } = getPosition(el, openMenus.current);
93
+ setOpenMenus(
94
+ openMenus.current.concat({ id: targetMenuId, left, top })
95
+ );
96
+ } else {
97
+ throw Error(`openMenu no menuItem ${itemId}`);
98
+ }
97
99
  }
98
100
  }
99
101
  },
@@ -1 +1 @@
1
- {"version":3,"file":"use-cascade.js","sources":["../../src/menu/use-cascade.ts"],"sourcesContent":["import {\n MouseEvent,\n SyntheticEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { closestListItem } from \"./list-dom-utils\";\nimport { MenuItemProps, MenuOpenHandler } from \"./MenuList\";\n// import {mousePosition} from './aim/utils';\n// import {aiming} from './aim/aim';\n\nconst nudge = (\n menus: RuntimeMenuDescriptor[],\n distance: number,\n pos: \"left\" | \"top\"\n) => {\n return menus.map((m, i) =>\n i === menus.length - 1\n ? {\n ...m,\n [pos]: m[pos] - distance,\n }\n : m\n );\n};\nconst nudgeLeft = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"left\");\nconst nudgeUp = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"top\");\n\nconst flipSides = (id: string, menus: RuntimeMenuDescriptor[]) => {\n const [parentMenu, menu] = menus.slice(-2);\n const el = document.getElementById(`${id}-${menu.id}`);\n if (el === null) {\n throw Error(`useCascade.flipSides element with id ${menu.id} not found`);\n }\n const { width } = el.getBoundingClientRect();\n return menus.map((m) =>\n m === menu\n ? {\n ...m,\n left: parentMenu.left - (width - 2),\n }\n : m\n );\n};\n\n// const closedNode = (el: HTMLElement) =>\n// el.ariaHasPopup === \"true\" && el.ariaExpanded !== \"true\";\nconst getPosition = (el: HTMLElement, openMenus: RuntimeMenuDescriptor[]) => {\n const [{ left, top: menuTop }] = openMenus.slice(-1);\n // const {top, right, bottom, left} = el.getBoundingClientRect();\n // this will not work for MenuList within window, we need the\n // const {offsetLeft: left, offsetTop: menuTop} = el.closest('.vuuMenuList');\n const { offsetWidth: width, offsetTop: top } = el;\n return { left: left + width, top: top + menuTop };\n};\n\nexport type RuntimeMenuDescriptor = {\n id: string;\n left: number;\n top: number;\n};\n\n/** menuitem-vuu-1-0 vuu-1 */\nexport const getHostMenuId = (id: string, rootId: string) => {\n const pos = id.lastIndexOf(\"-\");\n if (id.startsWith(\"menuitem\")) {\n return pos > -1 ? id.slice(9, pos) : rootId;\n } else {\n return pos > -1 ? id.slice(0, pos) : rootId;\n }\n};\n\nconst getTargetMenuId = (id: string) => id.slice(9);\n\nconst getMenuItemDetails = (\n { ariaExpanded, ariaHasPopup, id }: HTMLElement,\n rootId: string\n) => {\n if (id.startsWith(\"menuitem\")) {\n return {\n hostMenuId: getHostMenuId(id, rootId),\n targetMenuId: getTargetMenuId(id),\n menuItemId: id,\n isGroup: ariaHasPopup === \"true\",\n isOpen: ariaExpanded === \"true\",\n };\n } else {\n throw Error(`getMenuItemDetails #${id} is not a menuitem`);\n }\n};\n\nexport interface CascadeHookProps {\n id: string;\n onActivate: (menuId: string) => void;\n onMouseEnterItem: (evt: MouseEvent, itemId: string) => void;\n position: { x: number; y: number };\n}\n\nexport interface CascadeHooksResult {\n closeMenu: () => void;\n handleRender: () => void;\n listItemProps: Partial<MenuItemProps>;\n openMenu: MenuOpenHandler;\n openMenus: RuntimeMenuDescriptor[];\n}\n\ntype MenuStatus = \"no-popup\" | \"popup-open\" | \"pending-close\" | \"popup-pending\";\ntype MenuState = { [key: string]: MenuStatus };\n\nexport const useCascade = ({\n id: rootId,\n onActivate,\n onMouseEnterItem,\n position: { x: posX, y: posY },\n}: CascadeHookProps): CascadeHooksResult => {\n const [, forceRefresh] = useState({});\n const openMenus = useRef<RuntimeMenuDescriptor[]>([\n { id: rootId, left: posX, top: posY },\n ]);\n\n const menuIsOpen = useCallback(\n (menuId: string) =>\n openMenus.current.findIndex((menu) => menu.id === menuId) !== -1,\n []\n );\n\n const getOpenMenuStatus = useCallback((menuId: string) => {\n const state = menuState.current[menuId];\n if (state === undefined) {\n throw Error(`getOpenMenuState no entry for menu ${menuId}`);\n }\n return state;\n }, []);\n\n const setOpenMenus = useCallback((menus: RuntimeMenuDescriptor[]) => {\n openMenus.current = menus;\n forceRefresh({});\n }, []);\n\n const menuOpenPendingTimeout = useRef<number | undefined>();\n const menuClosePendingTimeout = useRef<number | undefined>();\n const menuState = useRef<MenuState>({ [rootId]: \"no-popup\" });\n // const prevLevel = useRef(0);\n\n // const prevAim = useRef({mousePos: null, distance: true});\n\n const openMenu = useCallback(\n (hostMenuId = rootId, targetMenuId: string, itemId = null) => {\n if (hostMenuId === rootId && itemId === null) {\n setOpenMenus([{ id: rootId, left: posX, top: posY }]);\n } else {\n menuState.current[hostMenuId] = \"popup-open\";\n const el = document.getElementById(itemId) as HTMLElement;\n if (el !== null) {\n const { left, top } = getPosition(el, openMenus.current);\n setOpenMenus(\n openMenus.current.concat({ id: targetMenuId, left, top })\n );\n } else {\n throw Error(`openMenu no menuItem ${itemId}`);\n }\n }\n },\n [rootId, posX, posY, setOpenMenus]\n );\n\n const closeMenu = useCallback(\n (menuId?: string) => {\n if (menuId === rootId) {\n setOpenMenus([]);\n } else {\n const menus = openMenus.current.slice();\n const lastMenu = menus.pop() as RuntimeMenuDescriptor;\n menuState.current[lastMenu.id] = \"no-popup\";\n const parentMenu = menus.at(-1);\n if (parentMenu) {\n menuState.current[parentMenu.id] = \"no-popup\";\n }\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus]\n );\n\n const closeMenus = useCallback(\n (menuItemId) => {\n const menus = openMenus.current.slice();\n const menuItemMenuId = menuItemId.slice(9);\n let { id: lastMenuId } = menus.at(-1) as RuntimeMenuDescriptor;\n while (menus.length > 1 && !menuItemMenuId.startsWith(lastMenuId)) {\n const parentMenuId = getHostMenuId(lastMenuId, rootId);\n menus.pop();\n menuState.current[lastMenuId] = \"no-popup\";\n menuState.current[parentMenuId] = \"no-popup\";\n ({ id: lastMenuId } = menus[menus.length - 1]);\n }\n if (menus.length < openMenus.current.length) {\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus]\n );\n\n const clearAnyScheduledOpenTasks = useCallback(() => {\n if (menuOpenPendingTimeout.current) {\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n }\n }, []);\n\n const scheduleOpen = useCallback(\n (\n hostMenuId: string,\n targetMenuId: string,\n menuItemId: string,\n delay = 300\n ) => {\n clearAnyScheduledOpenTasks();\n // do we need to set target state to pending-open ?s\n\n menuOpenPendingTimeout.current = window.setTimeout(() => {\n // console.log(\n // `scheduleOpen<timeout> opening menu ${targetMenuId} from menu ${hostMenuId} via menuitem ${menuItemId}`\n // );\n closeMenus(menuItemId);\n menuState.current[hostMenuId] = \"popup-open\";\n menuState.current[targetMenuId] = \"no-popup\";\n openMenu(hostMenuId, targetMenuId, menuItemId);\n }, delay);\n },\n [clearAnyScheduledOpenTasks, closeMenus, openMenu]\n );\n\n const scheduleClose = useCallback(\n (hostMenuId: string, openMenuId: string, itemId: string) => {\n // console.log(\n // `scheduleClose openMenuId ${openMenuId} from parent menu ${hostMenuId} itemId ${itemId}`\n // );\n menuState.current[openMenuId] = \"pending-close\";\n menuClosePendingTimeout.current = window.setTimeout(() => {\n // console.log(`call closeMenus from scheduleClose`);\n closeMenus(itemId);\n }, 400);\n },\n [closeMenus]\n );\n\n const handleRender = useCallback(() => {\n const { current: menus } = openMenus;\n const menu = menus.at(-1);\n const el = menu ? document.getElementById(menu.id) : undefined;\n if (el) {\n const { right, bottom } = el.getBoundingClientRect();\n const { clientHeight, clientWidth } = document.body;\n if (right > clientWidth) {\n const newMenus =\n menus.length > 1\n ? flipSides(rootId, menus)\n : nudgeLeft(menus, right - clientWidth);\n setOpenMenus(newMenus);\n } else if (bottom > clientHeight) {\n const newMenus = nudgeUp(menus, bottom - clientHeight);\n setOpenMenus(newMenus);\n }\n\n if (typeof el.tabIndex === \"number\") {\n el.focus();\n }\n }\n }, [rootId, setOpenMenus]);\n\n // TODO introduce a delay parameter that allows click to requeat an immediate render\n const triggerChildMenu = useCallback<MenuOpenHandler>(\n (menuItemEl, immediate = false) => {\n const { hostMenuId, targetMenuId, menuItemId, isGroup, isOpen } =\n getMenuItemDetails(menuItemEl, rootId);\n const {\n current: { [hostMenuId]: state },\n } = menuState;\n\n const delay = immediate ? 0 : undefined;\n\n // console.log(\n // `%ctriggerChildMenu\n // rootId ${rootId}\n // menuItem ${menuItemId}\n // host menu: ${hostMenuId}\n // target menu: ${targetMenuId}\n // item index: ${menuItemId}\n // state ${state}\n // isGroup ${isGroup} isOpen ${isOpen}\n // openMenus: ${JSON.stringify(openMenus.current)}\n // full state='${JSON.stringify(menuState.current)}`,\n // \"color: green; font-weight: bold;\"\n // );\n\n if (state === \"no-popup\" && isGroup) {\n menuState.current[hostMenuId] = \"popup-pending\";\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-pending\" && !isGroup) {\n menuState.current[hostMenuId] = \"no-popup\";\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n } else if (state === \"popup-pending\" && isGroup) {\n clearTimeout(menuOpenPendingTimeout.current);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-open\") {\n if (menuIsOpen(targetMenuId)) {\n const menuStatus = getOpenMenuStatus(targetMenuId);\n // Close any child menus of the target menu. This can happen if we have\n // opened child menus, then moused out of the menu entirely, to re-enter\n // at a higher level\n closeMenus(menuItemId);\n\n switch (menuStatus) {\n case \"pending-close\":\n // cancel the close\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[targetMenuId] = \"no-popup\";\n clearAnyScheduledOpenTasks();\n break;\n default:\n }\n } else {\n // TODO review the below, suspectb it's over complicating things\n const [parentOfLastOpenedMenu, lastOpenedMenu] =\n openMenus.current.slice(-2);\n if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n menuState.current[lastOpenedMenu.id] !== \"pending-close\" /*&&\n sameLevel*/\n ) {\n scheduleClose(hostMenuId, lastOpenedMenu.id, menuItemId);\n if (isGroup && !isOpen) {\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n }\n } else if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n isGroup &&\n menuItemId !== lastOpenedMenu.id &&\n menuState.current[lastOpenedMenu.id] === \"pending-close\"\n ) {\n // if there is already an item queued for opening cancel it\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (isGroup) {\n // closeMenus(menuId, itemId);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (\n !(\n (menuState.current[lastOpenedMenu.id] === \"pending-close\") /*&&\n sameLevel*/\n )\n ) {\n closeMenus(menuItemId);\n }\n }\n }\n\n if (state === \"pending-close\") {\n clearAnyScheduledOpenTasks();\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[hostMenuId] = \"popup-open\";\n }\n },\n [\n clearAnyScheduledOpenTasks,\n closeMenus,\n getOpenMenuStatus,\n menuIsOpen,\n rootId,\n scheduleClose,\n scheduleOpen,\n ]\n );\n\n const listItemProps: Partial<MenuItemProps> = useMemo(\n () => ({\n onMouseEnter: (evt: MouseEvent) => {\n const menuItemEl = closestListItem(evt.target as HTMLElement);\n triggerChildMenu(menuItemEl);\n onMouseEnterItem(evt, menuItemEl.id);\n },\n\n onClick: (evt: SyntheticEvent) => {\n const listItemEl = closestListItem(evt.target as HTMLElement);\n const { isGroup, menuItemId } = getMenuItemDetails(listItemEl, rootId);\n if (isGroup) {\n triggerChildMenu(listItemEl);\n } else {\n onActivate(menuItemId);\n }\n },\n }),\n [onActivate, onMouseEnterItem, rootId, triggerChildMenu]\n );\n\n return {\n closeMenu,\n handleRender,\n listItemProps,\n openMenu: triggerChildMenu,\n openMenus: openMenus.current,\n };\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-cascade.js","sources":["../../src/menu/use-cascade.ts"],"sourcesContent":["import {\n MouseEvent,\n SyntheticEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { closestListItem } from \"./list-dom-utils\";\nimport { MenuItemProps, MenuOpenHandler } from \"./MenuList\";\n// import {mousePosition} from './aim/utils';\n// import {aiming} from './aim/aim';\n\nconst nudge = (\n menus: RuntimeMenuDescriptor[],\n distance: number,\n pos: \"left\" | \"top\",\n) => {\n return menus.map((m, i) =>\n i === menus.length - 1\n ? {\n ...m,\n [pos]: m[pos] - distance,\n }\n : m,\n );\n};\nconst nudgeLeft = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"left\");\nconst nudgeUp = (menus: RuntimeMenuDescriptor[], distance: number) =>\n nudge(menus, distance, \"top\");\n\nconst flipSides = (id: string, menus: RuntimeMenuDescriptor[]) => {\n const [parentMenu, menu] = menus.slice(-2);\n const el = document.getElementById(`${id}-${menu.id}`);\n if (el === null) {\n throw Error(`useCascade.flipSides element with id ${menu.id} not found`);\n }\n const { width } = el.getBoundingClientRect();\n return menus.map((m) =>\n m === menu\n ? {\n ...m,\n left: parentMenu.left - (width - 2),\n }\n : m,\n );\n};\n\n// const closedNode = (el: HTMLElement) =>\n// el.ariaHasPopup === \"true\" && el.ariaExpanded !== \"true\";\nconst getPosition = (el: HTMLElement, openMenus: RuntimeMenuDescriptor[]) => {\n const [{ left, top: menuTop }] = openMenus.slice(-1);\n // const {top, right, bottom, left} = el.getBoundingClientRect();\n // this will not work for MenuList within window, we need the\n // const {offsetLeft: left, offsetTop: menuTop} = el.closest('.vuuMenuList');\n const { offsetWidth: width, offsetTop: top } = el;\n return { left: left + width, top: top + menuTop };\n};\n\nexport type RuntimeMenuDescriptor = {\n id: string;\n left: number;\n top: number;\n};\n\n/** menuitem-vuu-1-0 vuu-1 */\nexport const getHostMenuId = (id: string, rootId: string) => {\n const pos = id.lastIndexOf(\"-\");\n if (id.startsWith(\"menuitem\")) {\n return pos > -1 ? id.slice(9, pos) : rootId;\n } else {\n return pos > -1 ? id.slice(0, pos) : rootId;\n }\n};\n\nconst getTargetMenuId = (id: string) => id.slice(9);\n\nconst getMenuItemDetails = (\n { ariaExpanded, ariaHasPopup, id }: HTMLElement,\n rootId: string,\n) => {\n if (id.startsWith(\"menuitem\")) {\n return {\n hostMenuId: getHostMenuId(id, rootId),\n targetMenuId: getTargetMenuId(id),\n menuItemId: id,\n isGroup: ariaHasPopup === \"true\",\n isOpen: ariaExpanded === \"true\",\n };\n } else {\n throw Error(`getMenuItemDetails #${id} is not a menuitem`);\n }\n};\n\nexport interface CascadeHookProps {\n id: string;\n onActivate: (menuId: string) => void;\n onMouseEnterItem: (evt: MouseEvent, itemId: string) => void;\n position: { x: number; y: number };\n}\n\nexport interface CascadeHooksResult {\n closeMenu: () => void;\n handleRender: () => void;\n listItemProps: Partial<MenuItemProps>;\n openMenu: MenuOpenHandler;\n openMenus: RuntimeMenuDescriptor[];\n}\n\ntype MenuStatus = \"no-popup\" | \"popup-open\" | \"pending-close\" | \"popup-pending\";\ntype MenuState = { [key: string]: MenuStatus };\n\nexport const useCascade = ({\n id: rootId,\n onActivate,\n onMouseEnterItem,\n position: { x: posX, y: posY },\n}: CascadeHookProps): CascadeHooksResult => {\n const [, forceRefresh] = useState({});\n const openMenus = useRef<RuntimeMenuDescriptor[]>([\n { id: rootId, left: posX, top: posY },\n ]);\n\n const menuIsOpen = useCallback(\n (menuId: string) =>\n openMenus.current.findIndex((menu) => menu.id === menuId) !== -1,\n [],\n );\n\n const getOpenMenuStatus = useCallback((menuId: string) => {\n const state = menuState.current[menuId];\n if (state === undefined) {\n throw Error(`getOpenMenuState no entry for menu ${menuId}`);\n }\n return state;\n }, []);\n\n const setOpenMenus = useCallback((menus: RuntimeMenuDescriptor[]) => {\n openMenus.current = menus;\n forceRefresh({});\n }, []);\n\n const menuOpenPendingTimeout = useRef<number | undefined>();\n const menuClosePendingTimeout = useRef<number | undefined>();\n const menuState = useRef<MenuState>({ [rootId]: \"no-popup\" });\n // const prevLevel = useRef(0);\n\n // const prevAim = useRef({mousePos: null, distance: true});\n\n const openMenu = useCallback(\n (\n hostMenuId = rootId,\n targetMenuId: string,\n itemId: string | null = null,\n ) => {\n if (hostMenuId === rootId && itemId === null) {\n setOpenMenus([{ id: rootId, left: posX, top: posY }]);\n } else {\n menuState.current[hostMenuId] = \"popup-open\";\n if (itemId) {\n const el = document.getElementById(itemId) as HTMLElement;\n if (el !== null) {\n const { left, top } = getPosition(el, openMenus.current);\n setOpenMenus(\n openMenus.current.concat({ id: targetMenuId, left, top }),\n );\n } else {\n throw Error(`openMenu no menuItem ${itemId}`);\n }\n }\n }\n },\n [rootId, posX, posY, setOpenMenus],\n );\n\n const closeMenu = useCallback(\n (menuId?: string) => {\n if (menuId === rootId) {\n setOpenMenus([]);\n } else {\n const menus = openMenus.current.slice();\n const lastMenu = menus.pop() as RuntimeMenuDescriptor;\n menuState.current[lastMenu.id] = \"no-popup\";\n const parentMenu = menus.at(-1);\n if (parentMenu) {\n menuState.current[parentMenu.id] = \"no-popup\";\n }\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus],\n );\n\n const closeMenus = useCallback(\n (menuItemId: string) => {\n const menus = openMenus.current.slice();\n const menuItemMenuId = menuItemId.slice(9);\n let { id: lastMenuId } = menus.at(-1) as RuntimeMenuDescriptor;\n while (menus.length > 1 && !menuItemMenuId.startsWith(lastMenuId)) {\n const parentMenuId = getHostMenuId(lastMenuId, rootId);\n menus.pop();\n menuState.current[lastMenuId] = \"no-popup\";\n menuState.current[parentMenuId] = \"no-popup\";\n ({ id: lastMenuId } = menus[menus.length - 1]);\n }\n if (menus.length < openMenus.current.length) {\n setOpenMenus(menus);\n }\n },\n [rootId, setOpenMenus],\n );\n\n const clearAnyScheduledOpenTasks = useCallback(() => {\n if (menuOpenPendingTimeout.current) {\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n }\n }, []);\n\n const scheduleOpen = useCallback(\n (\n hostMenuId: string,\n targetMenuId: string,\n menuItemId: string,\n delay = 300,\n ) => {\n clearAnyScheduledOpenTasks();\n // do we need to set target state to pending-open ?s\n\n menuOpenPendingTimeout.current = window.setTimeout(() => {\n // console.log(\n // `scheduleOpen<timeout> opening menu ${targetMenuId} from menu ${hostMenuId} via menuitem ${menuItemId}`\n // );\n closeMenus(menuItemId);\n menuState.current[hostMenuId] = \"popup-open\";\n menuState.current[targetMenuId] = \"no-popup\";\n openMenu(hostMenuId, targetMenuId, menuItemId);\n }, delay);\n },\n [clearAnyScheduledOpenTasks, closeMenus, openMenu],\n );\n\n const scheduleClose = useCallback(\n (hostMenuId: string, openMenuId: string, itemId: string) => {\n // console.log(\n // `scheduleClose openMenuId ${openMenuId} from parent menu ${hostMenuId} itemId ${itemId}`\n // );\n menuState.current[openMenuId] = \"pending-close\";\n menuClosePendingTimeout.current = window.setTimeout(() => {\n // console.log(`call closeMenus from scheduleClose`);\n closeMenus(itemId);\n }, 400);\n },\n [closeMenus],\n );\n\n const handleRender = useCallback(() => {\n const { current: menus } = openMenus;\n const menu = menus.at(-1);\n const el = menu ? document.getElementById(menu.id) : undefined;\n if (el) {\n const { right, bottom } = el.getBoundingClientRect();\n const { clientHeight, clientWidth } = document.body;\n if (right > clientWidth) {\n const newMenus =\n menus.length > 1\n ? flipSides(rootId, menus)\n : nudgeLeft(menus, right - clientWidth);\n setOpenMenus(newMenus);\n } else if (bottom > clientHeight) {\n const newMenus = nudgeUp(menus, bottom - clientHeight);\n setOpenMenus(newMenus);\n }\n\n if (typeof el.tabIndex === \"number\") {\n el.focus();\n }\n }\n }, [rootId, setOpenMenus]);\n\n // TODO introduce a delay parameter that allows click to requeat an immediate render\n const triggerChildMenu = useCallback<MenuOpenHandler>(\n (menuItemEl, immediate = false) => {\n const { hostMenuId, targetMenuId, menuItemId, isGroup, isOpen } =\n getMenuItemDetails(menuItemEl, rootId);\n const {\n current: { [hostMenuId]: state },\n } = menuState;\n\n const delay = immediate ? 0 : undefined;\n\n // console.log(\n // `%ctriggerChildMenu\n // rootId ${rootId}\n // menuItem ${menuItemId}\n // host menu: ${hostMenuId}\n // target menu: ${targetMenuId}\n // item index: ${menuItemId}\n // state ${state}\n // isGroup ${isGroup} isOpen ${isOpen}\n // openMenus: ${JSON.stringify(openMenus.current)}\n // full state='${JSON.stringify(menuState.current)}`,\n // \"color: green; font-weight: bold;\"\n // );\n\n if (state === \"no-popup\" && isGroup) {\n menuState.current[hostMenuId] = \"popup-pending\";\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-pending\" && !isGroup) {\n menuState.current[hostMenuId] = \"no-popup\";\n clearTimeout(menuOpenPendingTimeout.current);\n menuOpenPendingTimeout.current = undefined;\n } else if (state === \"popup-pending\" && isGroup) {\n clearTimeout(menuOpenPendingTimeout.current);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (state === \"popup-open\") {\n if (menuIsOpen(targetMenuId)) {\n const menuStatus = getOpenMenuStatus(targetMenuId);\n // Close any child menus of the target menu. This can happen if we have\n // opened child menus, then moused out of the menu entirely, to re-enter\n // at a higher level\n closeMenus(menuItemId);\n\n switch (menuStatus) {\n case \"pending-close\":\n // cancel the close\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[targetMenuId] = \"no-popup\";\n clearAnyScheduledOpenTasks();\n break;\n default:\n }\n } else {\n // TODO review the below, suspectb it's over complicating things\n const [parentOfLastOpenedMenu, lastOpenedMenu] =\n openMenus.current.slice(-2);\n if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n menuState.current[lastOpenedMenu.id] !== \"pending-close\" /*&&\n sameLevel*/\n ) {\n scheduleClose(hostMenuId, lastOpenedMenu.id, menuItemId);\n if (isGroup && !isOpen) {\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n }\n } else if (\n parentOfLastOpenedMenu.id === hostMenuId &&\n isGroup &&\n menuItemId !== lastOpenedMenu.id &&\n menuState.current[lastOpenedMenu.id] === \"pending-close\"\n ) {\n // if there is already an item queued for opening cancel it\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (isGroup) {\n // closeMenus(menuId, itemId);\n scheduleOpen(hostMenuId, targetMenuId, menuItemId, delay);\n } else if (\n !(\n (menuState.current[lastOpenedMenu.id] === \"pending-close\") /*&&\n sameLevel*/\n )\n ) {\n closeMenus(menuItemId);\n }\n }\n }\n\n if (state === \"pending-close\") {\n clearAnyScheduledOpenTasks();\n clearTimeout(menuClosePendingTimeout.current);\n menuClosePendingTimeout.current = undefined;\n menuState.current[hostMenuId] = \"popup-open\";\n }\n },\n [\n clearAnyScheduledOpenTasks,\n closeMenus,\n getOpenMenuStatus,\n menuIsOpen,\n rootId,\n scheduleClose,\n scheduleOpen,\n ],\n );\n\n const listItemProps: Partial<MenuItemProps> = useMemo(\n () => ({\n onMouseEnter: (evt: MouseEvent) => {\n const menuItemEl = closestListItem(evt.target as HTMLElement);\n triggerChildMenu(menuItemEl);\n onMouseEnterItem(evt, menuItemEl.id);\n },\n\n onClick: (evt: SyntheticEvent) => {\n const listItemEl = closestListItem(evt.target as HTMLElement);\n const { isGroup, menuItemId } = getMenuItemDetails(listItemEl, rootId);\n if (isGroup) {\n triggerChildMenu(listItemEl);\n } else {\n onActivate(menuItemId);\n }\n },\n }),\n [onActivate, onMouseEnterItem, rootId, triggerChildMenu],\n );\n\n return {\n closeMenu,\n handleRender,\n listItemProps,\n openMenu: triggerChildMenu,\n openMenus: openMenus.current,\n };\n};\n"],"names":[],"mappings":";;;AAcA,MAAM,KAAQ,GAAA,CACZ,KACA,EAAA,QAAA,EACA,GACG,KAAA;AACH,EAAA,OAAO,KAAM,CAAA,GAAA;AAAA,IAAI,CAAC,CAAG,EAAA,CAAA,KACnB,CAAM,KAAA,KAAA,CAAM,SAAS,CACjB,GAAA;AAAA,MACE,GAAG,CAAA;AAAA,MACH,CAAC,GAAG,GAAG,CAAA,CAAE,GAAG,CAAI,GAAA;AAAA,KAElB,GAAA;AAAA,GACN;AACF,CAAA;AACA,MAAM,YAAY,CAAC,KAAA,EAAgC,aACjD,KAAM,CAAA,KAAA,EAAO,UAAU,MAAM,CAAA;AAC/B,MAAM,UAAU,CAAC,KAAA,EAAgC,aAC/C,KAAM,CAAA,KAAA,EAAO,UAAU,KAAK,CAAA;AAE9B,MAAM,SAAA,GAAY,CAAC,EAAA,EAAY,KAAmC,KAAA;AAChE,EAAA,MAAM,CAAC,UAAY,EAAA,IAAI,CAAI,GAAA,KAAA,CAAM,MAAM,CAAE,CAAA,CAAA;AACzC,EAAM,MAAA,EAAA,GAAK,SAAS,cAAe,CAAA,CAAA,EAAG,EAAE,CAAI,CAAA,EAAA,IAAA,CAAK,EAAE,CAAE,CAAA,CAAA;AACrD,EAAA,IAAI,OAAO,IAAM,EAAA;AACf,IAAA,MAAM,KAAM,CAAA,CAAA,qCAAA,EAAwC,IAAK,CAAA,EAAE,CAAY,UAAA,CAAA,CAAA;AAAA;AAEzE,EAAA,MAAM,EAAE,KAAA,EAAU,GAAA,EAAA,CAAG,qBAAsB,EAAA;AAC3C,EAAA,OAAO,KAAM,CAAA,GAAA;AAAA,IAAI,CAAC,CAChB,KAAA,CAAA,KAAM,IACF,GAAA;AAAA,MACE,GAAG,CAAA;AAAA,MACH,IAAA,EAAM,UAAW,CAAA,IAAA,IAAQ,KAAQ,GAAA,CAAA;AAAA,KAEnC,GAAA;AAAA,GACN;AACF,CAAA;AAIA,MAAM,WAAA,GAAc,CAAC,EAAA,EAAiB,SAAuC,KAAA;AAC3E,EAAM,MAAA,CAAC,EAAE,IAAM,EAAA,GAAA,EAAK,SAAS,CAAA,GAAI,SAAU,CAAA,KAAA,CAAM,CAAE,CAAA,CAAA;AAInD,EAAA,MAAM,EAAE,WAAA,EAAa,KAAO,EAAA,SAAA,EAAW,KAAQ,GAAA,EAAA;AAC/C,EAAA,OAAO,EAAE,IAAM,EAAA,IAAA,GAAO,KAAO,EAAA,GAAA,EAAK,MAAM,OAAQ,EAAA;AAClD,CAAA;AASa,MAAA,aAAA,GAAgB,CAAC,EAAA,EAAY,MAAmB,KAAA;AAC3D,EAAM,MAAA,GAAA,GAAM,EAAG,CAAA,WAAA,CAAY,GAAG,CAAA;AAC9B,EAAI,IAAA,EAAA,CAAG,UAAW,CAAA,UAAU,CAAG,EAAA;AAC7B,IAAA,OAAO,MAAM,CAAK,CAAA,GAAA,EAAA,CAAG,KAAM,CAAA,CAAA,EAAG,GAAG,CAAI,GAAA,MAAA;AAAA,GAChC,MAAA;AACL,IAAA,OAAO,MAAM,CAAK,CAAA,GAAA,EAAA,CAAG,KAAM,CAAA,CAAA,EAAG,GAAG,CAAI,GAAA,MAAA;AAAA;AAEzC;AAEA,MAAM,eAAkB,GAAA,CAAC,EAAe,KAAA,EAAA,CAAG,MAAM,CAAC,CAAA;AAElD,MAAM,qBAAqB,CACzB,EAAE,cAAc,YAAc,EAAA,EAAA,IAC9B,MACG,KAAA;AACH,EAAI,IAAA,EAAA,CAAG,UAAW,CAAA,UAAU,CAAG,EAAA;AAC7B,IAAO,OAAA;AAAA,MACL,UAAA,EAAY,aAAc,CAAA,EAAA,EAAI,MAAM,CAAA;AAAA,MACpC,YAAA,EAAc,gBAAgB,EAAE,CAAA;AAAA,MAChC,UAAY,EAAA,EAAA;AAAA,MACZ,SAAS,YAAiB,KAAA,MAAA;AAAA,MAC1B,QAAQ,YAAiB,KAAA;AAAA,KAC3B;AAAA,GACK,MAAA;AACL,IAAM,MAAA,KAAA,CAAM,CAAuB,oBAAA,EAAA,EAAE,CAAoB,kBAAA,CAAA,CAAA;AAAA;AAE7D,CAAA;AAoBO,MAAM,aAAa,CAAC;AAAA,EACzB,EAAI,EAAA,MAAA;AAAA,EACJ,UAAA;AAAA,EACA,gBAAA;AAAA,EACA,QAAU,EAAA,EAAE,CAAG,EAAA,IAAA,EAAM,GAAG,IAAK;AAC/B,CAA4C,KAAA;AAC1C,EAAA,MAAM,GAAG,YAAY,CAAI,GAAA,QAAA,CAAS,EAAE,CAAA;AACpC,EAAA,MAAM,YAAY,MAAgC,CAAA;AAAA,IAChD,EAAE,EAAI,EAAA,MAAA,EAAQ,IAAM,EAAA,IAAA,EAAM,KAAK,IAAK;AAAA,GACrC,CAAA;AAED,EAAA,MAAM,UAAa,GAAA,WAAA;AAAA,IACjB,CAAC,MACC,KAAA,SAAA,CAAU,OAAQ,CAAA,SAAA,CAAU,CAAC,IAAS,KAAA,IAAA,CAAK,EAAO,KAAA,MAAM,CAAM,KAAA,CAAA,CAAA;AAAA,IAChE;AAAC,GACH;AAEA,EAAM,MAAA,iBAAA,GAAoB,WAAY,CAAA,CAAC,MAAmB,KAAA;AACxD,IAAM,MAAA,KAAA,GAAQ,SAAU,CAAA,OAAA,CAAQ,MAAM,CAAA;AACtC,IAAA,IAAI,UAAU,KAAW,CAAA,EAAA;AACvB,MAAM,MAAA,KAAA,CAAM,CAAsC,mCAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAAA;AAE5D,IAAO,OAAA,KAAA;AAAA,GACT,EAAG,EAAE,CAAA;AAEL,EAAM,MAAA,YAAA,GAAe,WAAY,CAAA,CAAC,KAAmC,KAAA;AACnE,IAAA,SAAA,CAAU,OAAU,GAAA,KAAA;AACpB,IAAA,YAAA,CAAa,EAAE,CAAA;AAAA,GACjB,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,yBAAyB,MAA2B,EAAA;AAC1D,EAAA,MAAM,0BAA0B,MAA2B,EAAA;AAC3D,EAAA,MAAM,YAAY,MAAkB,CAAA,EAAE,CAAC,MAAM,GAAG,YAAY,CAAA;AAK5D,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CACE,UAAA,GAAa,MACb,EAAA,YAAA,EACA,SAAwB,IACrB,KAAA;AACH,MAAI,IAAA,UAAA,KAAe,MAAU,IAAA,MAAA,KAAW,IAAM,EAAA;AAC5C,QAAa,YAAA,CAAA,CAAC,EAAE,EAAI,EAAA,MAAA,EAAQ,MAAM,IAAM,EAAA,GAAA,EAAK,IAAK,EAAC,CAAC,CAAA;AAAA,OAC/C,MAAA;AACL,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,YAAA;AAChC,QAAA,IAAI,MAAQ,EAAA;AACV,UAAM,MAAA,EAAA,GAAK,QAAS,CAAA,cAAA,CAAe,MAAM,CAAA;AACzC,UAAA,IAAI,OAAO,IAAM,EAAA;AACf,YAAA,MAAM,EAAE,IAAM,EAAA,GAAA,KAAQ,WAAY,CAAA,EAAA,EAAI,UAAU,OAAO,CAAA;AACvD,YAAA,YAAA;AAAA,cACE,SAAA,CAAU,QAAQ,MAAO,CAAA,EAAE,IAAI,YAAc,EAAA,IAAA,EAAM,KAAK;AAAA,aAC1D;AAAA,WACK,MAAA;AACL,YAAM,MAAA,KAAA,CAAM,CAAwB,qBAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAAA;AAC9C;AACF;AACF,KACF;AAAA,IACA,CAAC,MAAA,EAAQ,IAAM,EAAA,IAAA,EAAM,YAAY;AAAA,GACnC;AAEA,EAAA,MAAM,SAAY,GAAA,WAAA;AAAA,IAChB,CAAC,MAAoB,KAAA;AACnB,MAAA,IAAI,WAAW,MAAQ,EAAA;AACrB,QAAA,YAAA,CAAa,EAAE,CAAA;AAAA,OACV,MAAA;AACL,QAAM,MAAA,KAAA,GAAQ,SAAU,CAAA,OAAA,CAAQ,KAAM,EAAA;AACtC,QAAM,MAAA,QAAA,GAAW,MAAM,GAAI,EAAA;AAC3B,QAAU,SAAA,CAAA,OAAA,CAAQ,QAAS,CAAA,EAAE,CAAI,GAAA,UAAA;AACjC,QAAM,MAAA,UAAA,GAAa,KAAM,CAAA,EAAA,CAAG,CAAE,CAAA,CAAA;AAC9B,QAAA,IAAI,UAAY,EAAA;AACd,UAAU,SAAA,CAAA,OAAA,CAAQ,UAAW,CAAA,EAAE,CAAI,GAAA,UAAA;AAAA;AAErC,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA;AACpB,KACF;AAAA,IACA,CAAC,QAAQ,YAAY;AAAA,GACvB;AAEA,EAAA,MAAM,UAAa,GAAA,WAAA;AAAA,IACjB,CAAC,UAAuB,KAAA;AACtB,MAAM,MAAA,KAAA,GAAQ,SAAU,CAAA,OAAA,CAAQ,KAAM,EAAA;AACtC,MAAM,MAAA,cAAA,GAAiB,UAAW,CAAA,KAAA,CAAM,CAAC,CAAA;AACzC,MAAA,IAAI,EAAE,EAAI,EAAA,UAAA,EAAe,GAAA,KAAA,CAAM,GAAG,CAAE,CAAA,CAAA;AACpC,MAAA,OAAO,MAAM,MAAS,GAAA,CAAA,IAAK,CAAC,cAAe,CAAA,UAAA,CAAW,UAAU,CAAG,EAAA;AACjE,QAAM,MAAA,YAAA,GAAe,aAAc,CAAA,UAAA,EAAY,MAAM,CAAA;AACrD,QAAA,KAAA,CAAM,GAAI,EAAA;AACV,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,UAAA;AAChC,QAAU,SAAA,CAAA,OAAA,CAAQ,YAAY,CAAI,GAAA,UAAA;AAClC,QAAA,CAAC,EAAE,EAAI,EAAA,UAAA,KAAe,KAAM,CAAA,KAAA,CAAM,SAAS,CAAC,CAAA;AAAA;AAE9C,MAAA,IAAI,KAAM,CAAA,MAAA,GAAS,SAAU,CAAA,OAAA,CAAQ,MAAQ,EAAA;AAC3C,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA;AACpB,KACF;AAAA,IACA,CAAC,QAAQ,YAAY;AAAA,GACvB;AAEA,EAAM,MAAA,0BAAA,GAA6B,YAAY,MAAM;AACnD,IAAA,IAAI,uBAAuB,OAAS,EAAA;AAClC,MAAA,YAAA,CAAa,uBAAuB,OAAO,CAAA;AAC3C,MAAA,sBAAA,CAAuB,OAAU,GAAA,KAAA,CAAA;AAAA;AACnC,GACF,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,YAAe,GAAA,WAAA;AAAA,IACnB,CACE,UAAA,EACA,YACA,EAAA,UAAA,EACA,QAAQ,GACL,KAAA;AACH,MAA2B,0BAAA,EAAA;AAG3B,MAAuB,sBAAA,CAAA,OAAA,GAAU,MAAO,CAAA,UAAA,CAAW,MAAM;AAIvD,QAAA,UAAA,CAAW,UAAU,CAAA;AACrB,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,YAAA;AAChC,QAAU,SAAA,CAAA,OAAA,CAAQ,YAAY,CAAI,GAAA,UAAA;AAClC,QAAS,QAAA,CAAA,UAAA,EAAY,cAAc,UAAU,CAAA;AAAA,SAC5C,KAAK,CAAA;AAAA,KACV;AAAA,IACA,CAAC,0BAA4B,EAAA,UAAA,EAAY,QAAQ;AAAA,GACnD;AAEA,EAAA,MAAM,aAAgB,GAAA,WAAA;AAAA,IACpB,CAAC,UAAoB,EAAA,UAAA,EAAoB,MAAmB,KAAA;AAI1D,MAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,eAAA;AAChC,MAAwB,uBAAA,CAAA,OAAA,GAAU,MAAO,CAAA,UAAA,CAAW,MAAM;AAExD,QAAA,UAAA,CAAW,MAAM,CAAA;AAAA,SAChB,GAAG,CAAA;AAAA,KACR;AAAA,IACA,CAAC,UAAU;AAAA,GACb;AAEA,EAAM,MAAA,YAAA,GAAe,YAAY,MAAM;AACrC,IAAM,MAAA,EAAE,OAAS,EAAA,KAAA,EAAU,GAAA,SAAA;AAC3B,IAAM,MAAA,IAAA,GAAO,KAAM,CAAA,EAAA,CAAG,CAAE,CAAA,CAAA;AACxB,IAAA,MAAM,KAAK,IAAO,GAAA,QAAA,CAAS,cAAe,CAAA,IAAA,CAAK,EAAE,CAAI,GAAA,KAAA,CAAA;AACrD,IAAA,IAAI,EAAI,EAAA;AACN,MAAA,MAAM,EAAE,KAAA,EAAO,MAAO,EAAA,GAAI,GAAG,qBAAsB,EAAA;AACnD,MAAA,MAAM,EAAE,YAAA,EAAc,WAAY,EAAA,GAAI,QAAS,CAAA,IAAA;AAC/C,MAAA,IAAI,QAAQ,WAAa,EAAA;AACvB,QAAM,MAAA,QAAA,GACJ,KAAM,CAAA,MAAA,GAAS,CACX,GAAA,SAAA,CAAU,MAAQ,EAAA,KAAK,CACvB,GAAA,SAAA,CAAU,KAAO,EAAA,KAAA,GAAQ,WAAW,CAAA;AAC1C,QAAA,YAAA,CAAa,QAAQ,CAAA;AAAA,OACvB,MAAA,IAAW,SAAS,YAAc,EAAA;AAChC,QAAA,MAAM,QAAW,GAAA,OAAA,CAAQ,KAAO,EAAA,MAAA,GAAS,YAAY,CAAA;AACrD,QAAA,YAAA,CAAa,QAAQ,CAAA;AAAA;AAGvB,MAAI,IAAA,OAAO,EAAG,CAAA,QAAA,KAAa,QAAU,EAAA;AACnC,QAAA,EAAA,CAAG,KAAM,EAAA;AAAA;AACX;AACF,GACC,EAAA,CAAC,MAAQ,EAAA,YAAY,CAAC,CAAA;AAGzB,EAAA,MAAM,gBAAmB,GAAA,WAAA;AAAA,IACvB,CAAC,UAAY,EAAA,SAAA,GAAY,KAAU,KAAA;AACjC,MAAM,MAAA,EAAE,YAAY,YAAc,EAAA,UAAA,EAAY,SAAS,MAAO,EAAA,GAC5D,kBAAmB,CAAA,UAAA,EAAY,MAAM,CAAA;AACvC,MAAM,MAAA;AAAA,QACJ,OAAS,EAAA,EAAE,CAAC,UAAU,GAAG,KAAM;AAAA,OAC7B,GAAA,SAAA;AAEJ,MAAM,MAAA,KAAA,GAAQ,YAAY,CAAI,GAAA,KAAA,CAAA;AAgB9B,MAAI,IAAA,KAAA,KAAU,cAAc,OAAS,EAAA;AACnC,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,eAAA;AAChC,QAAa,YAAA,CAAA,UAAA,EAAY,YAAc,EAAA,UAAA,EAAY,KAAK,CAAA;AAAA,OAC/C,MAAA,IAAA,KAAA,KAAU,eAAmB,IAAA,CAAC,OAAS,EAAA;AAChD,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,UAAA;AAChC,QAAA,YAAA,CAAa,uBAAuB,OAAO,CAAA;AAC3C,QAAA,sBAAA,CAAuB,OAAU,GAAA,KAAA,CAAA;AAAA,OACnC,MAAA,IAAW,KAAU,KAAA,eAAA,IAAmB,OAAS,EAAA;AAC/C,QAAA,YAAA,CAAa,uBAAuB,OAAO,CAAA;AAC3C,QAAa,YAAA,CAAA,UAAA,EAAY,YAAc,EAAA,UAAA,EAAY,KAAK,CAAA;AAAA,OAC1D,MAAA,IAAW,UAAU,YAAc,EAAA;AACjC,QAAI,IAAA,UAAA,CAAW,YAAY,CAAG,EAAA;AAC5B,UAAM,MAAA,UAAA,GAAa,kBAAkB,YAAY,CAAA;AAIjD,UAAA,UAAA,CAAW,UAAU,CAAA;AAErB,UAAA,QAAQ,UAAY;AAAA,YAClB,KAAK,eAAA;AAEH,cAAA,YAAA,CAAa,wBAAwB,OAAO,CAAA;AAC5C,cAAA,uBAAA,CAAwB,OAAU,GAAA,KAAA,CAAA;AAClC,cAAU,SAAA,CAAA,OAAA,CAAQ,YAAY,CAAI,GAAA,UAAA;AAClC,cAA2B,0BAAA,EAAA;AAC3B,cAAA;AACF;AACF,SACK,MAAA;AAEL,UAAA,MAAM,CAAC,sBAAwB,EAAA,cAAc,IAC3C,SAAU,CAAA,OAAA,CAAQ,MAAM,CAAE,CAAA,CAAA;AAC5B,UACE,IAAA,sBAAA,CAAuB,OAAO,UAC9B,IAAA,SAAA,CAAU,QAAQ,cAAe,CAAA,EAAE,MAAM,eAEzC,EAAA;AACA,YAAc,aAAA,CAAA,UAAA,EAAY,cAAe,CAAA,EAAA,EAAI,UAAU,CAAA;AACvD,YAAI,IAAA,OAAA,IAAW,CAAC,MAAQ,EAAA;AACtB,cAAa,YAAA,CAAA,UAAA,EAAY,YAAc,EAAA,UAAA,EAAY,KAAK,CAAA;AAAA;AAC1D,WAEA,MAAA,IAAA,sBAAA,CAAuB,EAAO,KAAA,UAAA,IAC9B,OACA,IAAA,UAAA,KAAe,cAAe,CAAA,EAAA,IAC9B,SAAU,CAAA,OAAA,CAAQ,cAAe,CAAA,EAAE,MAAM,eACzC,EAAA;AAEA,YAAa,YAAA,CAAA,UAAA,EAAY,YAAc,EAAA,UAAA,EAAY,KAAK,CAAA;AAAA,qBAC/C,OAAS,EAAA;AAElB,YAAa,YAAA,CAAA,UAAA,EAAY,YAAc,EAAA,UAAA,EAAY,KAAK,CAAA;AAAA,qBAExD,EACG,SAAA,CAAU,QAAQ,cAAe,CAAA,EAAE,MAAM,eAG5C,CAAA,EAAA;AACA,YAAA,UAAA,CAAW,UAAU,CAAA;AAAA;AACvB;AACF;AAGF,MAAA,IAAI,UAAU,eAAiB,EAAA;AAC7B,QAA2B,0BAAA,EAAA;AAC3B,QAAA,YAAA,CAAa,wBAAwB,OAAO,CAAA;AAC5C,QAAA,uBAAA,CAAwB,OAAU,GAAA,KAAA,CAAA;AAClC,QAAU,SAAA,CAAA,OAAA,CAAQ,UAAU,CAAI,GAAA,YAAA;AAAA;AAClC,KACF;AAAA,IACA;AAAA,MACE,0BAAA;AAAA,MACA,UAAA;AAAA,MACA,iBAAA;AAAA,MACA,UAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,MAAM,aAAwC,GAAA,OAAA;AAAA,IAC5C,OAAO;AAAA,MACL,YAAA,EAAc,CAAC,GAAoB,KAAA;AACjC,QAAM,MAAA,UAAA,GAAa,eAAgB,CAAA,GAAA,CAAI,MAAqB,CAAA;AAC5D,QAAA,gBAAA,CAAiB,UAAU,CAAA;AAC3B,QAAiB,gBAAA,CAAA,GAAA,EAAK,WAAW,EAAE,CAAA;AAAA,OACrC;AAAA,MAEA,OAAA,EAAS,CAAC,GAAwB,KAAA;AAChC,QAAM,MAAA,UAAA,GAAa,eAAgB,CAAA,GAAA,CAAI,MAAqB,CAAA;AAC5D,QAAA,MAAM,EAAE,OAAS,EAAA,UAAA,EAAe,GAAA,kBAAA,CAAmB,YAAY,MAAM,CAAA;AACrE,QAAA,IAAI,OAAS,EAAA;AACX,UAAA,gBAAA,CAAiB,UAAU,CAAA;AAAA,SACtB,MAAA;AACL,UAAA,UAAA,CAAW,UAAU,CAAA;AAAA;AACvB;AACF,KACF,CAAA;AAAA,IACA,CAAC,UAAA,EAAY,gBAAkB,EAAA,MAAA,EAAQ,gBAAgB;AAAA,GACzD;AAEA,EAAO,OAAA;AAAA,IACL,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAU,EAAA,gBAAA;AAAA,IACV,WAAW,SAAU,CAAA;AAAA,GACvB;AACF;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx: number) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx: number) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useContextMenu.js","sources":["../../src/menu/useContextMenu.tsx"],"sourcesContent":["import {\n ContextMenuItemDescriptor,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n isGroupMenuItemDescriptor,\n useThemeAttributes,\n} from \"@vuu-ui/vuu-utils\";\nimport { cloneElement, useCallback, useContext, useMemo } from \"react\";\nimport {\n MenuActionClosePopup,\n PopupCloseReason,\n PopupService,\n reasonIsMenuAction,\n} from \"../popup\";\nimport { ContextMenu, ContextMenuProps } from \"./ContextMenu\";\nimport { MenuItem, MenuItemGroup } from \"./MenuList\";\nimport { ContextMenuContext } from \"./context-menu-provider\";\n\nexport type ContextMenuOptions = {\n [key: string]: unknown;\n columns?: ColumnDescriptor[];\n contextMenu?: JSX.Element;\n ContextMenuProps?: Partial<ContextMenuProps> & {\n className?: string;\n };\n controlledComponentId?: string;\n};\n\nexport type EventLike = {\n clientX: number;\n clientY: number;\n preventDefault?: () => void;\n stopPropagation?: () => void;\n};\n\nexport type ShowContextMenu = (\n e: EventLike,\n location: string,\n options: ContextMenuOptions,\n) => void;\n\n// The argument allows a top-level menuBuilder to operate outside the Context\nexport const useContextMenu = (\n menuBuilder?: MenuBuilder,\n menuActionHandler?: MenuActionHandler,\n): [ShowContextMenu, () => void] => {\n const ctx = useContext(ContextMenuContext);\n\n const [themeClass, densityClass, dataMode] = useThemeAttributes();\n const themeAttributes = useMemo(\n () => ({\n themeClass,\n densityClass,\n dataMode,\n }),\n [dataMode, densityClass, themeClass],\n );\n\n const buildMenuOptions = useCallback(\n (menuBuilders: MenuBuilder[], location, options) => {\n let results: ContextMenuItemDescriptor[] = [];\n for (const menuBuilder of menuBuilders) {\n // Maybe we should leave the concatenation to the menuBuilder, then it can control menuItem order\n results = results.concat(menuBuilder(location, options));\n }\n return results;\n },\n [],\n );\n\n const handleShowContextMenu = useCallback<ShowContextMenu>(\n (e, location, { ContextMenuProps, contextMenu, ...options }) => {\n e.stopPropagation?.();\n e.preventDefault?.();\n\n if (contextMenu) {\n return showContextMenuComponent(\n {\n x: e.clientX,\n y: e.clientY,\n },\n contextMenu,\n );\n }\n\n const menuBuilders: MenuBuilder[] = [];\n if (menuBuilder) {\n menuBuilders.push(menuBuilder);\n }\n if (\n ctx &&\n Array.isArray(ctx?.menuBuilders) &&\n ctx.menuBuilders.length > 0\n ) {\n menuBuilders.push(...ctx.menuBuilders);\n }\n\n if (menuBuilders.length > 0) {\n const menuItemDescriptors = buildMenuOptions(\n menuBuilders,\n location,\n options,\n );\n\n // const menuHandler = menuActionHandler ?? ctx?.menuActionHandler;\n const menuHandler: MenuActionHandler = (\n action: MenuActionClosePopup,\n ) => {\n if (menuActionHandler?.(action) === true) {\n return true;\n } else {\n return ctx?.menuActionHandler(action);\n }\n };\n\n if (menuItemDescriptors.length && menuHandler) {\n // because showPopup is going to be used to render the context menu, it will not\n // have access to the ContextMenuContext. Pass the theme attributes here\n showContextMenu(e, menuItemDescriptors, menuHandler, {\n PortalProps: {\n themeAttributes,\n },\n ...ContextMenuProps,\n });\n }\n } else {\n console.warn(\n \"useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)\",\n );\n }\n },\n [buildMenuOptions, ctx, menuActionHandler, menuBuilder, themeAttributes],\n );\n\n const hideContextMenu = useCallback(() => {\n console.log(\"hide context menu\");\n }, []);\n\n return [handleShowContextMenu, hideContextMenu];\n};\n\nconst NO_OPTIONS = {};\n\nconst showContextMenuComponent = (\n position: { x: number; y: number },\n contextMenu: JSX.Element,\n) => {\n PopupService.showPopup({\n focus: true,\n left: 0,\n top: 0,\n component: cloneElement(contextMenu, { position }),\n });\n};\n\nconst showContextMenu = (\n e: EventLike,\n menuDescriptors: ContextMenuItemDescriptor[],\n handleContextMenuAction: MenuActionHandler,\n {\n position: positionProp,\n ...contextMenuProps\n }: ContextMenuOptions[\"ContextMenuProps\"] = NO_OPTIONS,\n) => {\n const menuItems = (menuDescriptors: ContextMenuItemDescriptor[]) => {\n const fromDescriptor = (menuItem: ContextMenuItemDescriptor, i: number) =>\n isGroupMenuItemDescriptor(menuItem) ? (\n <MenuItemGroup key={i} label={menuItem.label}>\n {menuItem.children.map(fromDescriptor)}\n </MenuItemGroup>\n ) : (\n <MenuItem\n key={i}\n action={menuItem.action}\n className={menuItem.className}\n data-icon={menuItem.icon}\n options={menuItem.options}\n >\n {menuItem.label}\n </MenuItem>\n );\n\n return menuDescriptors.map(fromDescriptor);\n };\n\n const handleClose = (reason?: PopupCloseReason) => {\n if (reasonIsMenuAction(reason)) {\n if (reason?.closedBy === \"popup-service\") {\n return;\n }\n handleContextMenuAction(reason);\n // TODO this results in onClose being called twice on component\n // cant simply be removed, some refactoring work needed\n PopupService.hidePopup(reason);\n }\n contextMenuProps?.onClose?.(reason);\n };\n\n const position = positionProp ?? {\n x: e.clientX,\n y: e.clientY,\n };\n\n const component = (\n <ContextMenu\n {...contextMenuProps}\n onClose={handleClose}\n position={position}\n >\n {menuItems(menuDescriptors)}\n </ContextMenu>\n );\n PopupService.showPopup({ left: 0, top: 0, component, focus: true });\n};\n"],"names":["menuBuilder","ContextMenuProps","menuDescriptors"],"mappings":"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"}
1
+ {"version":3,"file":"useContextMenu.js","sources":["../../src/menu/useContextMenu.tsx"],"sourcesContent":["import {\n ContextMenuItemDescriptor,\n MenuActionHandler,\n MenuBuilder,\n} from \"@vuu-ui/vuu-data-types\";\nimport { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n isGroupMenuItemDescriptor,\n useThemeAttributes,\n} from \"@vuu-ui/vuu-utils\";\nimport { cloneElement, useCallback, useContext, useMemo } from \"react\";\nimport {\n MenuActionClosePopup,\n PopupCloseReason,\n PopupService,\n reasonIsMenuAction,\n} from \"../popup\";\nimport { ContextMenu, ContextMenuProps } from \"./ContextMenu\";\nimport { MenuItem, MenuItemGroup } from \"./MenuList\";\nimport { ContextMenuContext } from \"./context-menu-provider\";\n\nexport type ContextMenuOptions = {\n [key: string]: unknown;\n columns?: ColumnDescriptor[];\n contextMenu?: JSX.Element;\n ContextMenuProps?: Partial<ContextMenuProps> & {\n className?: string;\n };\n controlledComponentId?: string;\n};\n\nexport type EventLike = {\n clientX: number;\n clientY: number;\n preventDefault?: () => void;\n stopPropagation?: () => void;\n};\n\nexport type ShowContextMenu = (\n e: EventLike,\n location: string,\n options: ContextMenuOptions,\n) => void;\n\n// The argument allows a top-level menuBuilder to operate outside the Context\nexport const useContextMenu = (\n menuBuilder?: MenuBuilder,\n menuActionHandler?: MenuActionHandler,\n): [ShowContextMenu, () => void] => {\n const ctx = useContext(ContextMenuContext);\n\n const [themeClass, densityClass, dataMode] = useThemeAttributes();\n const themeAttributes = useMemo(\n () => ({\n themeClass,\n densityClass,\n dataMode,\n }),\n [dataMode, densityClass, themeClass],\n );\n\n const buildMenuOptions = useCallback(\n (menuBuilders: MenuBuilder[], location: string, options: unknown) => {\n let results: ContextMenuItemDescriptor[] = [];\n for (const menuBuilder of menuBuilders) {\n // Maybe we should leave the concatenation to the menuBuilder, then it can control menuItem order\n results = results.concat(menuBuilder(location, options));\n }\n return results;\n },\n [],\n );\n\n const handleShowContextMenu = useCallback<ShowContextMenu>(\n (e, location, { ContextMenuProps, contextMenu, ...options }) => {\n e.stopPropagation?.();\n e.preventDefault?.();\n\n if (contextMenu) {\n return showContextMenuComponent(\n {\n x: e.clientX,\n y: e.clientY,\n },\n contextMenu,\n );\n }\n\n const menuBuilders: MenuBuilder[] = [];\n if (menuBuilder) {\n menuBuilders.push(menuBuilder);\n }\n if (\n ctx &&\n Array.isArray(ctx?.menuBuilders) &&\n ctx.menuBuilders.length > 0\n ) {\n menuBuilders.push(...ctx.menuBuilders);\n }\n\n if (menuBuilders.length > 0) {\n const menuItemDescriptors = buildMenuOptions(\n menuBuilders,\n location,\n options,\n );\n\n // const menuHandler = menuActionHandler ?? ctx?.menuActionHandler;\n const menuHandler: MenuActionHandler = (\n action: MenuActionClosePopup,\n ) => {\n if (menuActionHandler?.(action) === true) {\n return true;\n } else {\n return ctx?.menuActionHandler(action);\n }\n };\n\n if (menuItemDescriptors.length && menuHandler) {\n // because showPopup is going to be used to render the context menu, it will not\n // have access to the ContextMenuContext. Pass the theme attributes here\n showContextMenu(e, menuItemDescriptors, menuHandler, {\n PortalProps: {\n themeAttributes,\n },\n ...ContextMenuProps,\n });\n }\n } else {\n console.warn(\n \"useContextMenu, no menuBuilders configured. These should be supplied via the ContextMenuProvider(s)\",\n );\n }\n },\n [buildMenuOptions, ctx, menuActionHandler, menuBuilder, themeAttributes],\n );\n\n const hideContextMenu = useCallback(() => {\n console.log(\"hide context menu\");\n }, []);\n\n return [handleShowContextMenu, hideContextMenu];\n};\n\nconst NO_OPTIONS = {};\n\nconst showContextMenuComponent = (\n position: { x: number; y: number },\n contextMenu: JSX.Element,\n) => {\n PopupService.showPopup({\n focus: true,\n left: 0,\n top: 0,\n component: cloneElement(contextMenu, { position }),\n });\n};\n\nconst showContextMenu = (\n e: EventLike,\n menuDescriptors: ContextMenuItemDescriptor[],\n handleContextMenuAction: MenuActionHandler,\n {\n position: positionProp,\n ...contextMenuProps\n }: ContextMenuOptions[\"ContextMenuProps\"] = NO_OPTIONS,\n) => {\n const menuItems = (menuDescriptors: ContextMenuItemDescriptor[]) => {\n const fromDescriptor = (menuItem: ContextMenuItemDescriptor, i: number) =>\n isGroupMenuItemDescriptor(menuItem) ? (\n <MenuItemGroup key={i} label={menuItem.label}>\n {menuItem.children.map(fromDescriptor)}\n </MenuItemGroup>\n ) : (\n <MenuItem\n key={i}\n action={menuItem.action}\n className={menuItem.className}\n data-icon={menuItem.icon}\n options={menuItem.options}\n >\n {menuItem.label}\n </MenuItem>\n );\n\n return menuDescriptors.map(fromDescriptor);\n };\n\n const handleClose = (reason?: PopupCloseReason) => {\n if (reasonIsMenuAction(reason)) {\n if (reason?.closedBy === \"popup-service\") {\n return;\n }\n handleContextMenuAction(reason);\n // TODO this results in onClose being called twice on component\n // cant simply be removed, some refactoring work needed\n PopupService.hidePopup(reason);\n }\n contextMenuProps?.onClose?.(reason);\n };\n\n const position = positionProp ?? {\n x: e.clientX,\n y: e.clientY,\n };\n\n const component = (\n <ContextMenu\n {...contextMenuProps}\n onClose={handleClose}\n position={position}\n >\n {menuItems(menuDescriptors)}\n </ContextMenu>\n );\n PopupService.showPopup({ left: 0, top: 0, component, focus: true });\n};\n"],"names":["menuBuilder","ContextMenuProps","menuDescriptors"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePopupMenu.js","sources":["../../src/popup-menu/usePopupMenu.ts"],"sourcesContent":["import {\n AriaAttributes,\n MouseEvent,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { MenuOpenHandler } from \"../menu\";\nimport { PopupMenuProps } from \"./PopupMenu\";\nimport { getPositionRelativeToAnchor } from \"../popup/getPositionRelativeToAnchor\";\nimport { useContextMenu } from \"../menu\";\nimport { PopupCloseCallback, reasonIsClickAway } from \"../popup/popup-service\";\nimport { PopupPlacement } from \"../popup/Popup\";\n\nexport interface PopupMenuHookProps\n extends Pick<\n PopupMenuProps,\n | \"anchorElement\"\n | \"menuActionHandler\"\n | \"menuBuilder\"\n | \"menuClassName\"\n | \"menuOptions\"\n | \"onMenuClose\"\n | \"onMenuOpen\"\n > {\n id: string;\n menuLocation: string;\n popupPlacement: PopupPlacement;\n tabIndex: number;\n}\n\nexport const usePopupMenu = ({\n anchorElement,\n id,\n menuActionHandler,\n menuBuilder,\n menuClassName,\n menuLocation,\n menuOptions,\n onMenuClose,\n onMenuOpen,\n popupPlacement,\n tabIndex,\n}: PopupMenuHookProps) => {\n const [menuOpen, _setMenuOpen] = useState(false);\n const suppressShowMenuRef = useRef(false);\n const rootRef = useRef<HTMLButtonElement>(null);\n\n const setMenuOpen = useCallback(\n (isOpen) => {\n _setMenuOpen(isOpen);\n if (isOpen) {\n onMenuOpen?.();\n }\n },\n [onMenuOpen]\n );\n\n const [showContextMenu] = useContextMenu(menuBuilder, menuActionHandler);\n\n const handleOpenMenu = useCallback<MenuOpenHandler>((el) => {\n console.log(`menu Open `, {\n el,\n });\n }, []);\n\n const handleMenuClose = useCallback<PopupCloseCallback>(\n (reason) => {\n setMenuOpen(false);\n // If user has clicked the MenuButton whilst menu is open, we want to close it.\n // The PopupService will close it for us as a 'click-away' event. We don't want\n // that click on the button to re-open it.\n if (reasonIsClickAway(reason)) {\n const target = reason.mouseEvt.target as HTMLElement;\n if (target === rootRef.current) {\n suppressShowMenuRef.current = true;\n }\n onMenuClose?.(reason);\n } else {\n requestAnimationFrame(() => {\n onMenuClose?.(reason);\n if (tabIndex !== -1 && reason?.type !== \"tab-away\") {\n rootRef.current?.focus();\n }\n });\n }\n },\n [onMenuClose, setMenuOpen, tabIndex]\n );\n\n const showMenu = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n if (suppressShowMenuRef.current) {\n suppressShowMenuRef.current = false;\n } else {\n const anchorEl = anchorElement?.current ?? rootRef.current;\n if (anchorEl) {\n const {\n left: x,\n top: y,\n width,\n } = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);\n setMenuOpen(true);\n\n showContextMenu(e, menuLocation, {\n ContextMenuProps: {\n className: menuClassName,\n id: `${id}-menu`,\n onClose: handleMenuClose,\n openMenu: handleOpenMenu,\n position: {\n x,\n y,\n },\n style: { width: width ? width - 2 : undefined },\n },\n ...menuOptions,\n });\n }\n }\n },\n [\n anchorElement,\n handleMenuClose,\n handleOpenMenu,\n id,\n menuClassName,\n menuLocation,\n menuOptions,\n popupPlacement,\n setMenuOpen,\n showContextMenu,\n ]\n );\n\n const ariaAttributes: AriaAttributes = {\n \"aria-controls\": menuOpen ? `${id}-menu` : undefined,\n \"aria-expanded\": menuOpen,\n \"aria-haspopup\": \"menu\",\n };\n\n const buttonProps = {\n id,\n onClick: showMenu,\n tabIndex,\n };\n\n return { ariaAttributes, buttonProps, menuOpen, rootRef };\n};\n"],"names":[],"mappings":";;;;;;;;AA+BO,MAAM,eAAe,CAAC;AAAA,EAC3B,aAAA;AAAA,EACA,EAAA;AAAA,EACA,iBAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF,CAA0B,KAAA;AACxB,EAAA,MAAM,CAAC,QAAA,EAAU,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAC/C,EAAM,MAAA,mBAAA,GAAsB,OAAO,KAAK,CAAA;AACxC,EAAM,MAAA,OAAA,GAAU,OAA0B,IAAI,CAAA;AAE9C,EAAA,MAAM,WAAc,GAAA,WAAA;AAAA,IAClB,CAAC,MAAW,KAAA;AACV,MAAA,YAAA,CAAa,MAAM,CAAA;AACnB,MAAA,IAAI,MAAQ,EAAA;AACV,QAAa,UAAA,IAAA;AAAA;AACf,KACF;AAAA,IACA,CAAC,UAAU;AAAA,GACb;AAEA,EAAA,MAAM,CAAC,eAAe,CAAI,GAAA,cAAA,CAAe,aAAa,iBAAiB,CAAA;AAEvE,EAAM,MAAA,cAAA,GAAiB,WAA6B,CAAA,CAAC,EAAO,KAAA;AAC1D,IAAA,OAAA,CAAQ,IAAI,CAAc,UAAA,CAAA,EAAA;AAAA,MACxB;AAAA,KACD,CAAA;AAAA,GACH,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,eAAkB,GAAA,WAAA;AAAA,IACtB,CAAC,MAAW,KAAA;AACV,MAAA,WAAA,CAAY,KAAK,CAAA;AAIjB,MAAI,IAAA,iBAAA,CAAkB,MAAM,CAAG,EAAA;AAC7B,QAAM,MAAA,MAAA,GAAS,OAAO,QAAS,CAAA,MAAA;AAC/B,QAAI,IAAA,MAAA,KAAW,QAAQ,OAAS,EAAA;AAC9B,UAAA,mBAAA,CAAoB,OAAU,GAAA,IAAA;AAAA;AAEhC,QAAA,WAAA,GAAc,MAAM,CAAA;AAAA,OACf,MAAA;AACL,QAAA,qBAAA,CAAsB,MAAM;AAC1B,UAAA,WAAA,GAAc,MAAM,CAAA;AACpB,UAAA,IAAI,QAAa,KAAA,CAAA,CAAA,IAAM,MAAQ,EAAA,IAAA,KAAS,UAAY,EAAA;AAClD,YAAA,OAAA,CAAQ,SAAS,KAAM,EAAA;AAAA;AACzB,SACD,CAAA;AAAA;AACH,KACF;AAAA,IACA,CAAC,WAAa,EAAA,WAAA,EAAa,QAAQ;AAAA,GACrC;AAEA,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAAC,CAA+B,KAAA;AAC9B,MAAA,IAAI,oBAAoB,OAAS,EAAA;AAC/B,QAAA,mBAAA,CAAoB,OAAU,GAAA,KAAA;AAAA,OACzB,MAAA;AACL,QAAM,MAAA,QAAA,GAAW,aAAe,EAAA,OAAA,IAAW,OAAQ,CAAA,OAAA;AACnD,QAAA,IAAI,QAAU,EAAA;AACZ,UAAM,MAAA;AAAA,YACJ,IAAM,EAAA,CAAA;AAAA,YACN,GAAK,EAAA,CAAA;AAAA,YACL;AAAA,WACE,GAAA,2BAAA,CAA4B,QAAU,EAAA,cAAA,EAAgB,GAAG,CAAC,CAAA;AAC9D,UAAA,WAAA,CAAY,IAAI,CAAA;AAEhB,UAAA,eAAA,CAAgB,GAAG,YAAc,EAAA;AAAA,YAC/B,gBAAkB,EAAA;AAAA,cAChB,SAAW,EAAA,aAAA;AAAA,cACX,EAAA,EAAI,GAAG,EAAE,CAAA,KAAA,CAAA;AAAA,cACT,OAAS,EAAA,eAAA;AAAA,cACT,QAAU,EAAA,cAAA;AAAA,cACV,QAAU,EAAA;AAAA,gBACR,CAAA;AAAA,gBACA;AAAA,eACF;AAAA,cACA,OAAO,EAAE,KAAA,EAAO,KAAQ,GAAA,KAAA,GAAQ,IAAI,KAAU,CAAA;AAAA,aAChD;AAAA,YACA,GAAG;AAAA,WACJ,CAAA;AAAA;AACH;AACF,KACF;AAAA,IACA;AAAA,MACE,aAAA;AAAA,MACA,eAAA;AAAA,MACA,cAAA;AAAA,MACA,EAAA;AAAA,MACA,aAAA;AAAA,MACA,YAAA;AAAA,MACA,WAAA;AAAA,MACA,cAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,MAAM,cAAiC,GAAA;AAAA,IACrC,eAAiB,EAAA,QAAA,GAAW,CAAG,EAAA,EAAE,CAAU,KAAA,CAAA,GAAA,KAAA,CAAA;AAAA,IAC3C,eAAiB,EAAA,QAAA;AAAA,IACjB,eAAiB,EAAA;AAAA,GACnB;AAEA,EAAA,MAAM,WAAc,GAAA;AAAA,IAClB,EAAA;AAAA,IACA,OAAS,EAAA,QAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,OAAO,EAAE,cAAA,EAAgB,WAAa,EAAA,QAAA,EAAU,OAAQ,EAAA;AAC1D;;;;"}
1
+ {"version":3,"file":"usePopupMenu.js","sources":["../../src/popup-menu/usePopupMenu.ts"],"sourcesContent":["import {\n AriaAttributes,\n MouseEvent,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { MenuOpenHandler } from \"../menu\";\nimport { PopupMenuProps } from \"./PopupMenu\";\nimport { getPositionRelativeToAnchor } from \"../popup/getPositionRelativeToAnchor\";\nimport { useContextMenu } from \"../menu\";\nimport { PopupCloseCallback, reasonIsClickAway } from \"../popup/popup-service\";\nimport { PopupPlacement } from \"../popup/Popup\";\n\nexport interface PopupMenuHookProps\n extends Pick<\n PopupMenuProps,\n | \"anchorElement\"\n | \"menuActionHandler\"\n | \"menuBuilder\"\n | \"menuClassName\"\n | \"menuOptions\"\n | \"onMenuClose\"\n | \"onMenuOpen\"\n > {\n id: string;\n menuLocation: string;\n popupPlacement: PopupPlacement;\n tabIndex: number;\n}\n\nexport const usePopupMenu = ({\n anchorElement,\n id,\n menuActionHandler,\n menuBuilder,\n menuClassName,\n menuLocation,\n menuOptions,\n onMenuClose,\n onMenuOpen,\n popupPlacement,\n tabIndex,\n}: PopupMenuHookProps) => {\n const [menuOpen, _setMenuOpen] = useState(false);\n const suppressShowMenuRef = useRef(false);\n const rootRef = useRef<HTMLButtonElement>(null);\n\n const setMenuOpen = useCallback(\n (isOpen: boolean) => {\n _setMenuOpen(isOpen);\n if (isOpen) {\n onMenuOpen?.();\n }\n },\n [onMenuOpen],\n );\n\n const [showContextMenu] = useContextMenu(menuBuilder, menuActionHandler);\n\n const handleOpenMenu = useCallback<MenuOpenHandler>((el) => {\n console.log(`menu Open `, {\n el,\n });\n }, []);\n\n const handleMenuClose = useCallback<PopupCloseCallback>(\n (reason) => {\n setMenuOpen(false);\n // If user has clicked the MenuButton whilst menu is open, we want to close it.\n // The PopupService will close it for us as a 'click-away' event. We don't want\n // that click on the button to re-open it.\n if (reasonIsClickAway(reason)) {\n const target = reason.mouseEvt.target as HTMLElement;\n if (target === rootRef.current) {\n suppressShowMenuRef.current = true;\n }\n onMenuClose?.(reason);\n } else {\n requestAnimationFrame(() => {\n onMenuClose?.(reason);\n if (tabIndex !== -1 && reason?.type !== \"tab-away\") {\n rootRef.current?.focus();\n }\n });\n }\n },\n [onMenuClose, setMenuOpen, tabIndex],\n );\n\n const showMenu = useCallback(\n (e: MouseEvent<HTMLElement>) => {\n if (suppressShowMenuRef.current) {\n suppressShowMenuRef.current = false;\n } else {\n const anchorEl = anchorElement?.current ?? rootRef.current;\n if (anchorEl) {\n const {\n left: x,\n top: y,\n width,\n } = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);\n setMenuOpen(true);\n\n showContextMenu(e, menuLocation, {\n ContextMenuProps: {\n className: menuClassName,\n id: `${id}-menu`,\n onClose: handleMenuClose,\n openMenu: handleOpenMenu,\n position: {\n x,\n y,\n },\n style: { width: width ? width - 2 : undefined },\n },\n ...menuOptions,\n });\n }\n }\n },\n [\n anchorElement,\n handleMenuClose,\n handleOpenMenu,\n id,\n menuClassName,\n menuLocation,\n menuOptions,\n popupPlacement,\n setMenuOpen,\n showContextMenu,\n ],\n );\n\n const ariaAttributes: AriaAttributes = {\n \"aria-controls\": menuOpen ? `${id}-menu` : undefined,\n \"aria-expanded\": menuOpen,\n \"aria-haspopup\": \"menu\",\n };\n\n const buttonProps = {\n id,\n onClick: showMenu,\n tabIndex,\n };\n\n return { ariaAttributes, buttonProps, menuOpen, rootRef };\n};\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
- "version": "0.9.3",
2
+ "version": "0.10.0",
3
3
  "description": "VUU popup components - Context Menu, Dialog etc",
4
4
  "author": "heswell",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
7
- "@salt-ds/core": "1.37.1",
7
+ "@salt-ds/core": "1.43.0",
8
8
  "@salt-ds/styles": "0.2.1",
9
9
  "@salt-ds/window": "0.1.1",
10
- "@vuu-ui/vuu-data-types": "0.9.3",
11
- "@vuu-ui/vuu-layout": "0.9.3",
12
- "@vuu-ui/vuu-utils": "0.9.3",
13
- "@vuu-ui/vuu-ui-controls": "0.9.3"
10
+ "@vuu-ui/vuu-data-types": "0.10.0",
11
+ "@vuu-ui/vuu-layout": "0.10.0",
12
+ "@vuu-ui/vuu-utils": "0.10.0",
13
+ "@vuu-ui/vuu-ui-controls": "0.10.0"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "clsx": "^2.0.0",
17
- "react": ">=17.0.2",
18
- "react-dom": ">=17.0.2"
17
+ "react": "^18.3.1",
18
+ "react-dom": "^18.3.1"
19
19
  },
20
20
  "sideEffects": false,
21
21
  "files": [
@@ -13,7 +13,7 @@ export declare const useTooltip: ({ anchorQuery, id: idProp, placement, tooltipC
13
13
  onMouseEnter: (evt: MouseEvent) => void;
14
14
  onMouseLeave: () => void;
15
15
  };
16
- hideTooltip: (defer?: any) => void;
17
- showTooltip: (ref?: any) => void;
16
+ hideTooltip: (defer?: number) => void;
17
+ showTooltip: (ref?: import("react").MutableRefObject<HTMLElement | null>) => void;
18
18
  tooltipProps: TooltipProps | undefined;
19
19
  };