@xsolla/xui-context-menu 0.168.0 → 0.168.1
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.
- package/native/index.d.mts +4 -0
- package/native/index.d.ts +4 -0
- package/native/index.js +53 -19
- package/native/index.js.map +1 -1
- package/native/index.mjs +53 -19
- package/native/index.mjs.map +1 -1
- package/package.json +9 -9
- package/web/index.d.mts +4 -0
- package/web/index.d.ts +4 -0
- package/web/index.js +53 -19
- package/web/index.js.map +1 -1
- package/web/index.mjs +53 -19
- package/web/index.mjs.map +1 -1
package/native/index.d.mts
CHANGED
|
@@ -99,6 +99,10 @@ interface ContextMenuContextValue {
|
|
|
99
99
|
closeMenu: () => void;
|
|
100
100
|
registerCell: (id: string, meta: ContextMenuCellMeta) => number;
|
|
101
101
|
unregisterCell: (id: string) => void;
|
|
102
|
+
/** Live position of a cell in the registry (-1 if not registered). */
|
|
103
|
+
getCellIndex: (id: string) => number;
|
|
104
|
+
/** Bumps whenever the cell registry changes; drives index re-derivation. */
|
|
105
|
+
cellsVersion: number;
|
|
102
106
|
activeIndex: number;
|
|
103
107
|
setActiveIndex: (i: number) => void;
|
|
104
108
|
query: string;
|
package/native/index.d.ts
CHANGED
|
@@ -99,6 +99,10 @@ interface ContextMenuContextValue {
|
|
|
99
99
|
closeMenu: () => void;
|
|
100
100
|
registerCell: (id: string, meta: ContextMenuCellMeta) => number;
|
|
101
101
|
unregisterCell: (id: string) => void;
|
|
102
|
+
/** Live position of a cell in the registry (-1 if not registered). */
|
|
103
|
+
getCellIndex: (id: string) => number;
|
|
104
|
+
/** Bumps whenever the cell registry changes; drives index re-derivation. */
|
|
105
|
+
cellsVersion: number;
|
|
102
106
|
activeIndex: number;
|
|
103
107
|
setActiveIndex: (i: number) => void;
|
|
104
108
|
query: string;
|
package/native/index.js
CHANGED
|
@@ -152,7 +152,9 @@ var OptionCell = ({
|
|
|
152
152
|
const sizing = theme.sizing.contextMenu(size);
|
|
153
153
|
const variants = sizeToVariants[size];
|
|
154
154
|
const id = (0, import_xui_core.useId)();
|
|
155
|
-
const
|
|
155
|
+
const registerCell = ctx?.registerCell;
|
|
156
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
157
|
+
const getCellIndex = ctx?.getCellIndex;
|
|
156
158
|
const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
|
|
157
159
|
const [submenuOpen, setSubmenuOpen] = (0, import_react2.useState)(false);
|
|
158
160
|
const [submenuPos, setSubmenuPos] = (0, import_react2.useState)(null);
|
|
@@ -214,15 +216,22 @@ var OptionCell = ({
|
|
|
214
216
|
const onSelectRef = import_react2.default.useRef(onSelect);
|
|
215
217
|
onSelectRef.current = onSelect;
|
|
216
218
|
(0, import_react2.useEffect)(() => {
|
|
217
|
-
if (!
|
|
218
|
-
|
|
219
|
+
if (!registerCell || !unregisterCell) return;
|
|
220
|
+
registerCell(id, {
|
|
221
|
+
type: "option",
|
|
222
|
+
onSelect: () => onSelectRef.current?.()
|
|
223
|
+
});
|
|
224
|
+
return () => unregisterCell(id);
|
|
225
|
+
}, [registerCell, unregisterCell, id]);
|
|
226
|
+
(0, import_react2.useEffect)(() => {
|
|
227
|
+
if (!registerCell) return;
|
|
228
|
+
registerCell(id, {
|
|
219
229
|
type: "option",
|
|
220
230
|
disabled,
|
|
221
231
|
onSelect: () => onSelectRef.current?.()
|
|
222
232
|
});
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}, [ctx, id, disabled]);
|
|
233
|
+
}, [registerCell, id, disabled]);
|
|
234
|
+
const index = getCellIndex ? getCellIndex(id) : -1;
|
|
226
235
|
const isActive = ctx ? index >= 0 && ctx.activeIndex === index : false;
|
|
227
236
|
const inHoverState = isActive || !ctx && isHovered || hasSubmenu && submenuOpen;
|
|
228
237
|
const handleEnter = () => {
|
|
@@ -475,11 +484,13 @@ var HeadingCell = ({
|
|
|
475
484
|
const sizing = theme.sizing.contextMenu(size);
|
|
476
485
|
const variants = sizeToVariants[size];
|
|
477
486
|
const id = (0, import_xui_core.useId)();
|
|
487
|
+
const registerCell = ctx?.registerCell;
|
|
488
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
478
489
|
(0, import_react2.useEffect)(() => {
|
|
479
|
-
if (!
|
|
480
|
-
|
|
481
|
-
return () =>
|
|
482
|
-
}, [
|
|
490
|
+
if (!registerCell || !unregisterCell) return;
|
|
491
|
+
registerCell(id, { type: "heading" });
|
|
492
|
+
return () => unregisterCell(id);
|
|
493
|
+
}, [registerCell, unregisterCell, id]);
|
|
483
494
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
484
495
|
"div",
|
|
485
496
|
{
|
|
@@ -533,11 +544,13 @@ var SearchCell = ({
|
|
|
533
544
|
const size = propSize ?? ctx?.size ?? "md";
|
|
534
545
|
const sizing = theme.sizing.contextMenu(size);
|
|
535
546
|
const id = (0, import_xui_core.useId)();
|
|
547
|
+
const registerCell = ctx?.registerCell;
|
|
548
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
536
549
|
(0, import_react2.useEffect)(() => {
|
|
537
|
-
if (!
|
|
538
|
-
|
|
539
|
-
return () =>
|
|
540
|
-
}, [
|
|
550
|
+
if (!registerCell || !unregisterCell) return;
|
|
551
|
+
registerCell(id, { type: "search" });
|
|
552
|
+
return () => unregisterCell(id);
|
|
553
|
+
}, [registerCell, unregisterCell, id]);
|
|
541
554
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
542
555
|
"input",
|
|
543
556
|
{
|
|
@@ -570,11 +583,13 @@ var DividerCell = ({ themeMode, themeProductContext, "data-testid": testId }) =>
|
|
|
570
583
|
const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
|
|
571
584
|
const ctx = useContextMenu();
|
|
572
585
|
const id = (0, import_xui_core.useId)();
|
|
586
|
+
const registerCell = ctx?.registerCell;
|
|
587
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
573
588
|
(0, import_react2.useEffect)(() => {
|
|
574
|
-
if (!
|
|
575
|
-
|
|
576
|
-
return () =>
|
|
577
|
-
}, [
|
|
589
|
+
if (!registerCell || !unregisterCell) return;
|
|
590
|
+
registerCell(id, { type: "divider" });
|
|
591
|
+
return () => unregisterCell(id);
|
|
592
|
+
}, [registerCell, unregisterCell, id]);
|
|
578
593
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
579
594
|
"div",
|
|
580
595
|
{
|
|
@@ -861,7 +876,11 @@ var ContextMenu = (props) => {
|
|
|
861
876
|
setCellsVersion((v) => v + 1);
|
|
862
877
|
return cellsRef.current.length - 1;
|
|
863
878
|
}
|
|
879
|
+
const prev = cellsRef.current[existing].meta;
|
|
864
880
|
cellsRef.current[existing] = { id, meta };
|
|
881
|
+
if (prev.disabled !== meta.disabled || prev.type !== meta.type) {
|
|
882
|
+
setCellsVersion((v) => v + 1);
|
|
883
|
+
}
|
|
865
884
|
return existing;
|
|
866
885
|
}, []);
|
|
867
886
|
const unregisterCell = (0, import_react5.useCallback)((id) => {
|
|
@@ -871,18 +890,33 @@ var ContextMenu = (props) => {
|
|
|
871
890
|
setCellsVersion((v) => v + 1);
|
|
872
891
|
}
|
|
873
892
|
}, []);
|
|
893
|
+
const getCellIndex = (0, import_react5.useCallback)(
|
|
894
|
+
(id) => cellsRef.current.findIndex((c) => c.id === id),
|
|
895
|
+
[]
|
|
896
|
+
);
|
|
874
897
|
const ctx = (0, import_react5.useMemo)(
|
|
875
898
|
() => ({
|
|
876
899
|
size,
|
|
877
900
|
closeMenu,
|
|
878
901
|
registerCell,
|
|
879
902
|
unregisterCell,
|
|
903
|
+
getCellIndex,
|
|
904
|
+
cellsVersion,
|
|
880
905
|
activeIndex,
|
|
881
906
|
setActiveIndex,
|
|
882
907
|
query,
|
|
883
908
|
setQuery
|
|
884
909
|
}),
|
|
885
|
-
[
|
|
910
|
+
[
|
|
911
|
+
size,
|
|
912
|
+
closeMenu,
|
|
913
|
+
registerCell,
|
|
914
|
+
unregisterCell,
|
|
915
|
+
getCellIndex,
|
|
916
|
+
cellsVersion,
|
|
917
|
+
activeIndex,
|
|
918
|
+
query
|
|
919
|
+
]
|
|
886
920
|
);
|
|
887
921
|
const triggerNode = (0, import_react5.useMemo)(() => {
|
|
888
922
|
if (!trigger) return null;
|
package/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.tsx","../../src/ContextMenu.tsx","../../src/ContextMenuContext.tsx","../../src/ContextMenuItem.tsx","../../src/hooks/useContextMenuPosition.ts","../../src/hooks/useKeyboardNavigation.ts"],"sourcesContent":["export { ContextMenu } from \"./ContextMenu\";\nexport { ContextMenuItem } from \"./ContextMenuItem\";\n\nexport {\n useContextMenu,\n useContextMenuRequired,\n ContextMenuContext,\n} from \"./ContextMenuContext\";\n\nexport { useContextMenuPosition } from \"./hooks/useContextMenuPosition\";\nexport { useKeyboardNavigation } from \"./hooks/useKeyboardNavigation\";\n\nexport type {\n ContextMenuSize,\n ContextMenuItemType,\n ContextMenuItemLeadingControl,\n ContextMenuOptionItemProps,\n ContextMenuSearchItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuDividerItemProps,\n ContextMenuItemProps,\n ContextMenuPanelType,\n ContextMenuPlacement,\n ContextMenuPosition,\n ContextMenuProps,\n ContextMenuContextValue,\n ContextMenuSizing,\n} from \"./types\";\n","import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useResolvedTheme, type ThemeOverrideProps } from \"@xsolla/xui-core\";\nimport { Spinner } from \"@xsolla/xui-spinner\";\nimport { ContextMenuContext } from \"./ContextMenuContext\";\nimport { ContextMenuItem } from \"./ContextMenuItem\";\nimport { useContextMenuPosition } from \"./hooks/useContextMenuPosition\";\nimport { useKeyboardNavigation } from \"./hooks/useKeyboardNavigation\";\nimport type {\n ContextMenuCellMeta,\n ContextMenuContextValue,\n ContextMenuDividerItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuOptionItemProps,\n ContextMenuProps,\n ContextMenuSize,\n} from \"./types\";\n\ntype Props = ContextMenuProps & ThemeOverrideProps;\n\ninterface CellEntry {\n id: string;\n meta: ContextMenuCellMeta;\n}\n\ntype PresetItem =\n | ContextMenuOptionItemProps\n | ContextMenuHeadingItemProps\n | ContextMenuDividerItemProps;\n\nconst SEARCH_DEBOUNCE_MS = 200;\n\nconst EmptyMessage: React.FC<{ children: React.ReactNode; color: string }> = ({\n children,\n color,\n}) => (\n <div\n style={{\n padding: 12,\n color,\n fontSize: 14,\n textAlign: \"center\",\n }}\n >\n {children}\n </div>\n);\n\nexport const ContextMenu: React.FC<Props> = (props) => {\n const {\n type,\n items,\n children,\n size = \"md\",\n searchable,\n loading,\n emptyMessage,\n empty,\n trigger,\n isOpen,\n onOpenChange,\n closeOnSelect,\n width,\n maxHeight,\n placement = \"bottom-start\",\n onSelect,\n \"aria-label\": ariaLabel,\n \"data-testid\": testId,\n testID,\n themeMode,\n themeProductContext,\n } = props;\n\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(false);\n const open = isControlled ? !!isOpen : internalOpen;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const cellsRef = useRef<CellEntry[]>([]);\n const [cellsVersion, setCellsVersion] = useState(0);\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const panelRef = useRef<HTMLDivElement | null>(null);\n\n // Search query state\n const [query, setQuery] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = setTimeout(() => {\n setDebouncedQuery(query);\n }, SEARCH_DEBOUNCE_MS);\n return () => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);\n };\n }, [query]);\n\n const closeMenu = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen]);\n\n const registerCell = useCallback((id: string, meta: ContextMenuCellMeta) => {\n const existing = cellsRef.current.findIndex((c) => c.id === id);\n if (existing === -1) {\n cellsRef.current.push({ id, meta });\n setCellsVersion((v) => v + 1);\n return cellsRef.current.length - 1;\n }\n cellsRef.current[existing] = { id, meta };\n return existing;\n }, []);\n\n const unregisterCell = useCallback((id: string) => {\n const idx = cellsRef.current.findIndex((c) => c.id === id);\n if (idx !== -1) {\n cellsRef.current.splice(idx, 1);\n setCellsVersion((v) => v + 1);\n }\n }, []);\n\n const ctx: ContextMenuContextValue = useMemo(\n () => ({\n size: size as ContextMenuSize,\n closeMenu,\n registerCell,\n unregisterCell,\n activeIndex,\n setActiveIndex,\n query,\n setQuery,\n }),\n [size, closeMenu, registerCell, unregisterCell, activeIndex, query]\n );\n\n const triggerNode = useMemo(() => {\n if (!trigger) return null;\n const inner = isValidElement(trigger)\n ? cloneElement(\n trigger as ReactElement<{\n \"aria-haspopup\"?: string;\n \"aria-expanded\"?: string | boolean;\n }>,\n {\n \"aria-haspopup\": \"menu\",\n \"aria-expanded\": open ? \"true\" : \"false\",\n }\n )\n : trigger;\n return (\n <span\n ref={(node) => {\n if (!node) {\n triggerRef.current = null;\n return;\n }\n const focusable = node.querySelector<HTMLElement>(\n \"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])\"\n );\n triggerRef.current = focusable ?? node;\n }}\n onClick={() => setOpen(!open)}\n style={{ display: \"inline-flex\" }}\n >\n {inner}\n </span>\n );\n }, [trigger, open, setOpen]);\n\n const usePortal = !!trigger && typeof document !== \"undefined\";\n\n const position = useContextMenuPosition({\n triggerRef,\n panelRef,\n isOpen: open && usePortal,\n placement,\n });\n\n const cellsForNav = useMemo(\n () => cellsRef.current.map((c) => ({ id: c.id, meta: c.meta })),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [cellsVersion]\n );\n\n const { handleKeyDown } = useKeyboardNavigation({\n isOpen: open,\n cells: cellsForNav,\n activeIndex,\n setActiveIndex,\n onClose: closeMenu,\n triggerRef,\n });\n\n const sizingFn = (theme.sizing as { contextMenu?: (s: string) => unknown })\n .contextMenu;\n const sizing = sizingFn\n ? (sizingFn(size) as {\n panelWidth?: number;\n borderRadius?: number;\n paddingVertical?: number;\n })\n : {};\n const radiusObj = (theme as { radius?: { contextMenu?: number } }).radius;\n const radiusVal = sizing.borderRadius ?? radiusObj?.contextMenu ?? 8;\n const shadowObj = (theme as { shadow?: { contextMenu?: string } }).shadow;\n const shadowVal = shadowObj?.contextMenu ?? \"\";\n const panelPaddingVertical = sizing.paddingVertical ?? 8;\n\n const panelStyle: React.CSSProperties = {\n background: theme.colors.background.primary,\n borderRadius: radiusVal,\n boxShadow: shadowVal,\n width: width ?? sizing.panelWidth,\n maxHeight,\n overflow: \"hidden\",\n display: open ? \"flex\" : \"none\",\n flexDirection: \"column\",\n outline: \"none\",\n fontFamily: theme.fonts.body,\n paddingTop: panelPaddingVertical,\n paddingBottom: panelPaddingVertical,\n };\n\n if (usePortal) {\n panelStyle.position = \"fixed\";\n panelStyle.top = position?.top ?? 0;\n panelStyle.left = position?.left ?? 0;\n }\n\n // Filter preset items based on debounced query (only when searchable + items)\n const filteredItems = useMemo<PresetItem[] | undefined>(() => {\n if (!items) return undefined;\n if (!searchable || !debouncedQuery) return items.slice();\n const q = debouncedQuery.toLowerCase();\n const matchedFlags = items.map((item) => {\n if (item.type === \"option\") {\n return String(item.label ?? \"\")\n .toLowerCase()\n .includes(q);\n }\n return false; // headings/dividers themselves never match\n });\n // Now hide headings whose options are all filtered out and trailing dividers\n const result: PresetItem[] = [];\n let pendingHeading: {\n item: ContextMenuHeadingItemProps;\n idx: number;\n } | null = null;\n let lastEmittedWasContent = false; // whether last pushed was option (so divider can follow)\n let groupHasOption = false;\n for (let i = 0; i < items.length; i += 1) {\n const item = items[i];\n if (item.type === \"heading\") {\n // Starting a new group\n pendingHeading = { item, idx: i };\n groupHasOption = false;\n } else if (item.type === \"divider\") {\n // Emit divider only if the previously emitted thing was content\n if (lastEmittedWasContent) {\n result.push(item);\n lastEmittedWasContent = false;\n }\n // dividers also reset pending heading so a heading sits with its group\n pendingHeading = null;\n groupHasOption = false;\n } else if (item.type === \"option\") {\n if (matchedFlags[i]) {\n if (pendingHeading) {\n result.push(pendingHeading.item);\n pendingHeading = null;\n }\n result.push(item);\n lastEmittedWasContent = true;\n groupHasOption = true;\n }\n }\n }\n // Strip trailing divider if any\n while (result.length > 0 && result[result.length - 1].type === \"divider\") {\n result.pop();\n }\n // Suppress unused\n void groupHasOption;\n return result;\n }, [items, searchable, debouncedQuery]);\n\n const effectiveCloseOnSelect =\n closeOnSelect !== undefined ? closeOnSelect : type !== \"checkbox\";\n\n const renderPresetItem = (item: PresetItem, key: number) => {\n if (item.type === \"heading\") {\n return (\n <ContextMenuItem\n key={`h-${key}`}\n {...item}\n size={item.size ?? (size as ContextMenuSize)}\n />\n );\n }\n if (item.type === \"divider\") {\n return (\n <ContextMenuItem\n key={`d-${key}`}\n {...item}\n size={item.size ?? (size as ContextMenuSize)}\n />\n );\n }\n const composed = composeItemForPreset(type, item);\n const originalSelect = composed.onSelect;\n const wrappedSelect = () => {\n originalSelect?.();\n onSelect?.(item);\n if (effectiveCloseOnSelect) closeMenu();\n };\n return (\n <ContextMenuItem\n key={`o-${key}`}\n {...composed}\n size={composed.size ?? (size as ContextMenuSize)}\n onSelect={wrappedSelect}\n />\n );\n };\n\n // Determine what to render in the panel body\n const isLoadingState = loading;\n\n let bodyContent: React.ReactNode = null;\n let isBodyEmpty = false;\n let searchNode: React.ReactNode = null;\n\n if (isLoadingState) {\n bodyContent = (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n }}\n >\n <Spinner size={size === \"xl\" ? \"lg\" : size === \"lg\" ? \"md\" : \"sm\"} />\n </div>\n );\n } else if (children !== undefined && children !== null) {\n // Custom path. Empty when children is falsy/empty array.\n const childArr = React.Children.toArray(children);\n if (childArr.length === 0) {\n isBodyEmpty = true;\n } else {\n bodyContent = children;\n }\n } else if (type && items) {\n // Preset path\n if (searchable) {\n searchNode = (\n <ContextMenuItem\n type=\"search\"\n value={query}\n onValueChange={setQuery}\n size={size as ContextMenuSize}\n />\n );\n }\n const visible = filteredItems ?? [];\n const optionCount = visible.filter((i) => i.type === \"option\").length;\n if (optionCount === 0) {\n isBodyEmpty = true;\n } else {\n bodyContent = visible.map((it, idx) => renderPresetItem(it, idx));\n }\n } else {\n // No children, no items — empty\n isBodyEmpty = true;\n }\n\n if (isBodyEmpty) {\n bodyContent = empty ?? (\n <EmptyMessage color={theme.colors.content.tertiary}>\n {emptyMessage ?? \"No results\"}\n </EmptyMessage>\n );\n }\n\n const hasStickySearch = !!searchNode;\n\n // Focus management on open/close transitions\n const prevOpenRef = useRef(false);\n useEffect(() => {\n const wasOpen = prevOpenRef.current;\n prevOpenRef.current = open;\n if (!wasOpen && open) {\n // open transition: focus search if present, else first option\n // wait for cells to register\n const timer = setTimeout(() => {\n const panel = panelRef.current;\n if (!panel) return;\n const search =\n panel.querySelector<HTMLInputElement>(\"[role='searchbox']\");\n if (search) {\n search.focus();\n return;\n }\n const firstOption = panel.querySelector<HTMLElement>(\n \"[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']\"\n );\n if (firstOption) {\n firstOption.focus();\n // Reset activeIndex so keyboard nav starts fresh; onFocus will have\n // set it to the focused option's index, but tests expect ArrowDown\n // to move to the first option from -1.\n setActiveIndex(-1);\n } else {\n panel.focus();\n }\n }, 0);\n return () => clearTimeout(timer);\n }\n if (wasOpen && !open) {\n // close transition: focus the trigger\n triggerRef.current?.focus();\n }\n }, [open]);\n\n const resolvedPlacement = position?.placement ?? placement;\n\n const scrollContainerStyle: React.CSSProperties = {\n overflowY: \"auto\",\n flex: 1,\n minHeight: 0,\n };\n\n const stickyHeaderStyle: React.CSSProperties = {\n position: \"sticky\",\n top: 0,\n zIndex: 1,\n background: theme.colors.background.primary,\n };\n\n const panel = open ? (\n <ContextMenuContext.Provider value={ctx}>\n <div\n ref={panelRef}\n role=\"menu\"\n aria-label={ariaLabel}\n data-testid={testId || testID}\n data-placement={usePortal ? resolvedPlacement : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n onMouseLeave={() => setActiveIndex(-1)}\n style={panelStyle}\n >\n {hasStickySearch && (\n <div data-sticky=\"top\" style={stickyHeaderStyle}>\n {searchNode}\n </div>\n )}\n <div style={scrollContainerStyle}>{bodyContent}</div>\n </div>\n </ContextMenuContext.Provider>\n ) : null;\n\n return (\n <>\n {triggerNode}\n {usePortal ? panel && createPortal(panel, document.body) : panel}\n </>\n );\n};\n\nContextMenu.displayName = \"ContextMenu\";\n\nfunction composeItemForPreset(\n type: ContextMenuProps[\"type\"],\n item: ContextMenuOptionItemProps\n): ContextMenuOptionItemProps {\n switch (type) {\n case \"checkbox\":\n return { ...item, leadingControl: \"checkbox\" };\n case \"radio\":\n return { ...item, leadingControl: \"radio\" };\n case \"list\":\n case \"phone\":\n case \"status\":\n case \"brandLogo\":\n case \"avatar\":\n default:\n return { ...item };\n }\n}\n","import { createContext, useContext } from \"react\";\nimport type { ContextMenuContextValue } from \"./types\";\n\nexport const ContextMenuContext = createContext<\n ContextMenuContextValue | undefined\n>(undefined);\n\nexport const useContextMenu = () => {\n const context = useContext(ContextMenuContext);\n return context;\n};\n\nexport const useContextMenuRequired = () => {\n const context = useContext(ContextMenuContext);\n if (!context) {\n throw new Error(\n \"useContextMenuRequired must be used within a ContextMenu component\"\n );\n }\n return context;\n};\n","import React, { useEffect, useLayoutEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n useResolvedTheme,\n useId,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport { Checkbox } from \"@xsolla/xui-checkbox\";\nimport { Radio } from \"@xsolla/xui-radio\";\nimport { useContextMenu } from \"./ContextMenuContext\";\nimport type {\n ContextMenuDividerItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuItemProps,\n ContextMenuOptionItemProps,\n ContextMenuSearchItemProps,\n ContextMenuSize,\n} from \"./types\";\n\ntype BodyVariant = \"bodyLg\" | \"bodyMd\" | \"bodySm\" | \"bodyXs\";\ntype AccentVariant =\n | \"bodyLgAccent\"\n | \"bodyMdAccent\"\n | \"bodySmAccent\"\n | \"bodyXsAccent\";\n\nconst sizeToVariants: Record<\n ContextMenuSize,\n { label: BodyVariant; description: BodyVariant; headingAccent: AccentVariant }\n> = {\n xl: { label: \"bodyLg\", description: \"bodyLg\", headingAccent: \"bodyLgAccent\" },\n lg: { label: \"bodyLg\", description: \"bodyMd\", headingAccent: \"bodyMdAccent\" },\n md: { label: \"bodyMd\", description: \"bodySm\", headingAccent: \"bodySmAccent\" },\n sm: { label: \"bodySm\", description: \"bodyXs\", headingAccent: \"bodyXsAccent\" },\n};\n\nconst sizeLabelOverride: Partial<\n Record<ContextMenuSize, { fontSize: number; lineHeight: string }>\n> = {\n xl: { fontSize: 20, lineHeight: \"26px\" },\n};\n\ntype Props = ContextMenuItemProps & ThemeOverrideProps;\n\nexport const ContextMenuItem: React.FC<Props> = (props) => {\n if (props.type === \"option\") return <OptionCell {...props} />;\n if (props.type === \"heading\") return <HeadingCell {...props} />;\n if (props.type === \"divider\") return <DividerCell {...props} />;\n if (props.type === \"search\") return <SearchCell {...props} />;\n return null;\n};\n\nContextMenuItem.displayName = \"ContextMenuItem\";\n\nconst SubmenuChevron: React.FC<{ color: string; size: number }> = ({\n color,\n size,\n}) => (\n <span\n data-testid=\"ctxmenu-submenu-chevron\"\n aria-hidden=\"true\"\n style={{\n color,\n display: \"inline-flex\",\n alignItems: \"center\",\n width: size,\n height: size,\n }}\n >\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M17.0605 11.6464C17.2558 11.8417 17.2558 12.1583 17.0605 12.3536L9.70703 19.707L8.29297 18.293L14.5859 12L8.29297 5.70703L9.70703 4.29297L17.0605 11.6464Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n);\n\nconst OptionCell: React.FC<ContextMenuOptionItemProps & ThemeOverrideProps> = ({\n size: propSize,\n label,\n description,\n disabled,\n destructive,\n checked,\n leadingControl,\n leadingIcon,\n status,\n iconWrapper,\n slotContent,\n value,\n hint,\n trailingIcon,\n keyboardShortcut,\n hasSubmenu,\n submenu,\n onSelect,\n testID,\n themeMode,\n themeProductContext,\n \"data-testid\": testId,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n const variants = sizeToVariants[size];\n\n const id = useId();\n const [index, setIndex] = useState<number>(-1);\n const [isHovered, setIsHovered] = useState(false);\n const [submenuOpen, setSubmenuOpen] = useState(false);\n const [submenuPos, setSubmenuPos] = useState<{\n top: number;\n left: number;\n } | null>(null);\n const optionRef = React.useRef<HTMLDivElement | null>(null);\n const submenuWrapperRef = React.useRef<HTMLDivElement | null>(null);\n const closeTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n\n const cancelClose = () => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n };\n\n const scheduleClose = () => {\n cancelClose();\n closeTimerRef.current = setTimeout(() => setSubmenuOpen(false), 120);\n };\n\n useEffect(() => () => cancelClose(), []);\n\n useEffect(() => {\n if (!hasSubmenu || !submenuOpen) return;\n const onMouseDown = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n const inOption = optionRef.current?.contains(target);\n const inSubmenu = submenuWrapperRef.current?.contains(target);\n if (!inOption && !inSubmenu) {\n setSubmenuOpen(false);\n return;\n }\n if (\n inSubmenu &&\n target.closest(\n '[role=\"menuitem\"],[role=\"menuitemcheckbox\"],[role=\"menuitemradio\"]'\n )\n ) {\n setSubmenuOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n return () => document.removeEventListener(\"mousedown\", onMouseDown);\n }, [hasSubmenu, submenuOpen]);\n\n useLayoutEffect(() => {\n if (!hasSubmenu || !submenuOpen) {\n setSubmenuPos(null);\n return;\n }\n const update = () => {\n const node = optionRef.current;\n if (!node) return;\n const rect = node.getBoundingClientRect();\n setSubmenuPos({ top: rect.top, left: rect.right });\n };\n update();\n window.addEventListener(\"scroll\", update, true);\n window.addEventListener(\"resize\", update);\n return () => {\n window.removeEventListener(\"scroll\", update, true);\n window.removeEventListener(\"resize\", update);\n };\n }, [hasSubmenu, submenuOpen]);\n const onSelectRef = React.useRef(onSelect);\n onSelectRef.current = onSelect;\n useEffect(() => {\n if (!ctx) return;\n const idx = ctx.registerCell(id, {\n type: \"option\",\n disabled,\n onSelect: () => onSelectRef.current?.(),\n });\n setIndex(idx);\n return () => ctx.unregisterCell(id);\n }, [ctx, id, disabled]);\n\n const isActive = ctx ? index >= 0 && ctx.activeIndex === index : false;\n const inHoverState =\n isActive || (!ctx && isHovered) || (hasSubmenu && submenuOpen);\n\n const handleEnter = () => {\n if (disabled) return;\n if (ctx && index >= 0) ctx.setActiveIndex(index);\n if (!ctx) setIsHovered(true);\n if (hasSubmenu) setSubmenuOpen(true);\n };\n\n const handleLeave = () => {\n if (!ctx) setIsHovered(false);\n if (hasSubmenu) scheduleClose();\n };\n\n const labelColor = disabled\n ? theme.colors.control.input.textDisable\n : destructive\n ? theme.colors.content.alert.primary\n : theme.colors.content.primary;\n\n const bg = inHoverState ? theme.colors.control.input.bgHover : \"transparent\";\n\n const role =\n !hasSubmenu && checked !== undefined ? \"menuitemcheckbox\" : \"menuitem\";\n const ariaChecked =\n !hasSubmenu && checked !== undefined\n ? checked\n ? \"true\"\n : \"false\"\n : undefined;\n\n const handleClick = () => {\n if (disabled) return;\n if (hasSubmenu) {\n setSubmenuOpen(true);\n return;\n }\n onSelect?.();\n };\n\n const closeSubmenuAndFocus = () => {\n setSubmenuOpen(false);\n optionRef.current?.focus();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (disabled) {\n if (e.key === \"Enter\" || e.key === \" \") e.preventDefault();\n return;\n }\n if (hasSubmenu) {\n if (e.key === \"ArrowRight\" || e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n setSubmenuOpen(true);\n return;\n }\n if (e.key === \"ArrowLeft\" || e.key === \"Escape\") {\n if (submenuOpen) {\n e.preventDefault();\n e.stopPropagation();\n closeSubmenuAndFocus();\n return;\n }\n }\n }\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect?.();\n }\n };\n\n return (\n <div\n ref={optionRef}\n role={role}\n aria-checked={ariaChecked}\n aria-disabled={disabled ? \"true\" : undefined}\n aria-haspopup={hasSubmenu ? \"menu\" : undefined}\n aria-expanded={hasSubmenu ? (submenuOpen ? \"true\" : \"false\") : undefined}\n tabIndex={0}\n data-testid={testId || testID}\n data-state={inHoverState ? \"hover\" : undefined}\n data-destructive={destructive ? \"true\" : undefined}\n aria-keyshortcuts={keyboardShortcut}\n onMouseEnter={() => {\n cancelClose();\n handleEnter();\n }}\n onMouseLeave={handleLeave}\n onFocus={handleEnter}\n onBlur={() => {\n if (!ctx) setIsHovered(false);\n }}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: sizing.gap,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n backgroundColor: bg,\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n outline: \"none\",\n }}\n >\n {leadingControl === \"checkbox\" && (\n <span\n data-testid=\"ctxmenu-leading-checkbox\"\n aria-hidden=\"true\"\n style={{ pointerEvents: \"none\", display: \"inline-flex\" }}\n >\n <Checkbox\n size={size}\n checked={!!checked}\n disabled={!!disabled}\n themeMode={themeMode}\n themeProductContext={themeProductContext}\n />\n </span>\n )}\n {leadingControl === \"radio\" && (\n <span\n data-testid=\"ctxmenu-leading-radio\"\n aria-hidden=\"true\"\n style={{ pointerEvents: \"none\", display: \"inline-flex\" }}\n >\n <Radio\n size={size}\n checked={!!checked}\n disabled={!!disabled}\n themeMode={themeMode}\n themeProductContext={themeProductContext}\n />\n </span>\n )}\n {leadingIcon}\n {status}\n {iconWrapper}\n {slotContent}\n <span\n style={{\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n minWidth: 0,\n }}\n >\n <Typography\n variant={variants.label}\n color={labelColor}\n noWrap={description === undefined}\n style={{\n ...(description === undefined\n ? { display: \"block\", minWidth: 0 }\n : {}),\n ...(sizeLabelOverride[size] ?? {}),\n }}\n >\n {label}\n </Typography>\n {description !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {description}\n </Typography>\n )}\n </span>\n {(value !== undefined || hint !== undefined) && (\n <span\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"flex-end\",\n }}\n >\n {value !== undefined && (\n <Typography\n variant={variants.label}\n color={theme.colors.content.secondary}\n style={sizeLabelOverride[size]}\n >\n {value}\n </Typography>\n )}\n {hint !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {hint}\n </Typography>\n )}\n </span>\n )}\n {keyboardShortcut && (\n <Typography\n as=\"kbd\"\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {keyboardShortcut}\n </Typography>\n )}\n {hasSubmenu && (\n <SubmenuChevron\n color={theme.colors.content.tertiary}\n size={sizing.iconSize}\n />\n )}\n {trailingIcon}\n {hasSubmenu &&\n submenuOpen &&\n submenu &&\n submenuPos &&\n typeof document !== \"undefined\" &&\n createPortal(\n <div\n ref={submenuWrapperRef}\n onMouseEnter={cancelClose}\n onMouseLeave={scheduleClose}\n style={{\n position: \"fixed\",\n top: submenuPos.top,\n left: submenuPos.left,\n zIndex: 2000,\n }}\n >\n {submenu}\n </div>,\n document.body\n )}\n </div>\n );\n};\n\nconst HeadingCell: React.FC<\n ContextMenuHeadingItemProps & ThemeOverrideProps\n> = ({\n size: propSize,\n label,\n description,\n testID,\n themeMode,\n themeProductContext,\n \"data-testid\": testId,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n const variants = sizeToVariants[size];\n\n const id = useId();\n useEffect(() => {\n if (!ctx) return;\n ctx.registerCell(id, { type: \"heading\" });\n return () => ctx.unregisterCell(id);\n }, [ctx, id]);\n\n return (\n <div\n role=\"presentation\"\n data-testid={testId || testID}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n }}\n >\n <Typography\n variant={variants.headingAccent}\n color={theme.colors.content.secondary}\n style={{ textTransform: \"uppercase\", letterSpacing: 0.5 }}\n >\n {label}\n </Typography>\n {description !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {description}\n </Typography>\n )}\n </div>\n );\n};\n\nconst SearchCell: React.FC<ContextMenuSearchItemProps & ThemeOverrideProps> = ({\n size: propSize,\n value,\n onValueChange,\n placeholder = \"Search\",\n autoFocus,\n \"aria-label\": ariaLabel = \"Search options\",\n \"data-testid\": testId,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n\n const id = useId();\n useEffect(() => {\n if (!ctx) return;\n ctx.registerCell(id, { type: \"search\" });\n return () => ctx.unregisterCell(id);\n }, [ctx, id]);\n\n return (\n <input\n type=\"search\"\n role=\"searchbox\"\n aria-label={ariaLabel}\n placeholder={placeholder}\n value={value}\n autoFocus={autoFocus}\n onChange={(e) => onValueChange(e.target.value)}\n data-testid={testId || testID}\n style={{\n width: \"100%\",\n boxSizing: \"border-box\",\n border: \"none\",\n outline: \"none\",\n background: \"transparent\",\n color: theme.colors.content.primary,\n fontSize: sizing.fontSize,\n lineHeight: `${sizing.fontSize + 2}px`,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n }}\n />\n );\n};\n\nconst DividerCell: React.FC<\n ContextMenuDividerItemProps & ThemeOverrideProps\n> = ({ themeMode, themeProductContext, \"data-testid\": testId }) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const id = useId();\n useEffect(() => {\n if (!ctx) return;\n ctx.registerCell(id, { type: \"divider\" });\n return () => ctx.unregisterCell(id);\n }, [ctx, id]);\n return (\n <div\n role=\"separator\"\n data-testid={testId}\n style={{\n height: 1,\n backgroundColor: theme.colors.border.secondary,\n margin: \"4px 0\",\n }}\n />\n );\n};\n","import { useEffect, useState, type RefObject } from \"react\";\nimport type { ContextMenuPlacement } from \"../types\";\n\ninterface UseContextMenuPositionOptions {\n triggerRef: RefObject<HTMLElement | null>;\n panelRef: RefObject<HTMLElement | null>;\n isOpen: boolean;\n placement?: ContextMenuPlacement;\n offset?: number;\n}\n\ninterface ResolvedPosition {\n top: number;\n left: number;\n placement: ContextMenuPlacement;\n}\n\nconst splitPlacement = (\n placement: ContextMenuPlacement\n): { vertical: \"top\" | \"bottom\"; horizontal: \"start\" | \"end\" } => {\n const [vertical, horizontal] = placement.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n return { vertical, horizontal };\n};\n\nconst joinPlacement = (\n vertical: \"top\" | \"bottom\",\n horizontal: \"start\" | \"end\"\n): ContextMenuPlacement => `${vertical}-${horizontal}` as ContextMenuPlacement;\n\nexport const useContextMenuPosition = ({\n triggerRef,\n panelRef,\n isOpen,\n placement = \"bottom-start\",\n offset = 4,\n}: UseContextMenuPositionOptions): ResolvedPosition | undefined => {\n const [resolved, setResolved] = useState<ResolvedPosition | undefined>();\n\n useEffect(() => {\n if (!isOpen) {\n setResolved(undefined);\n return;\n }\n\n const compute = () => {\n const trigger = triggerRef.current;\n const panel = panelRef.current;\n if (!trigger || !panel) return;\n\n const triggerRect = trigger.getBoundingClientRect();\n const panelRect = panel.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n let { vertical, horizontal } = splitPlacement(placement);\n\n const computeTop = (v: \"top\" | \"bottom\") =>\n v === \"bottom\"\n ? triggerRect.bottom + offset\n : triggerRect.top - panelRect.height - offset;\n\n const computeLeft = (h: \"start\" | \"end\") =>\n h === \"start\" ? triggerRect.left : triggerRect.right - panelRect.width;\n\n let top = computeTop(vertical);\n const wantedBottom = top + panelRect.height;\n if (top < 0 || wantedBottom > viewportHeight) {\n const flipped = vertical === \"bottom\" ? \"top\" : \"bottom\";\n const flippedTop = computeTop(flipped);\n const flippedBottom = flippedTop + panelRect.height;\n if (flippedTop >= 0 && flippedBottom <= viewportHeight) {\n vertical = flipped;\n top = flippedTop;\n }\n }\n\n let left = computeLeft(horizontal);\n const wantedRight = left + panelRect.width;\n if (left < 0 || wantedRight > viewportWidth) {\n const flipped = horizontal === \"start\" ? \"end\" : \"start\";\n const flippedLeft = computeLeft(flipped);\n const flippedRight = flippedLeft + panelRect.width;\n if (flippedLeft >= 0 && flippedRight <= viewportWidth) {\n horizontal = flipped;\n left = flippedLeft;\n }\n }\n\n setResolved({\n top,\n left,\n placement: joinPlacement(vertical, horizontal),\n });\n };\n\n const rafId = window.requestAnimationFrame(compute);\n const onResize = () => compute();\n window.addEventListener(\"resize\", onResize);\n\n return () => {\n window.cancelAnimationFrame(rafId);\n window.removeEventListener(\"resize\", onResize);\n };\n }, [isOpen, placement, offset, triggerRef, panelRef]);\n\n return resolved;\n};\n","import { useCallback, type RefObject } from \"react\";\n\nexport type CellType = \"option\" | \"search\" | \"heading\" | \"divider\";\n\nexport interface CellMeta {\n type: CellType;\n onSelect?: () => void;\n disabled?: boolean;\n}\n\ninterface UseKeyboardNavigationOptions {\n isOpen: boolean;\n cells: Array<{ id: string; meta: CellMeta }>;\n activeIndex: number;\n setActiveIndex: (index: number) => void;\n onClose: () => void;\n triggerRef?: RefObject<HTMLElement | null>;\n}\n\nconst isNavigableOption = (meta: CellMeta) =>\n meta.type === \"option\" && !meta.disabled;\n\nconst isTextInputTarget = (target: EventTarget | null): boolean => {\n if (!(target instanceof HTMLElement)) return false;\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") return true;\n return target.getAttribute(\"role\") === \"searchbox\";\n};\n\nexport const useKeyboardNavigation = ({\n isOpen,\n cells,\n activeIndex,\n setActiveIndex,\n onClose,\n triggerRef,\n}: UseKeyboardNavigationOptions) => {\n const findFirstOption = useCallback(() => {\n for (let i = 0; i < cells.length; i += 1) {\n if (isNavigableOption(cells[i].meta)) return i;\n }\n return -1;\n }, [cells]);\n\n const findLastOption = useCallback(() => {\n for (let i = cells.length - 1; i >= 0; i -= 1) {\n if (isNavigableOption(cells[i].meta)) return i;\n }\n return -1;\n }, [cells]);\n\n const findNextOption = useCallback(\n (from: number) => {\n const len = cells.length;\n if (len === 0) return -1;\n for (let step = 1; step <= len; step += 1) {\n const idx = (from + step + len) % len;\n if (isNavigableOption(cells[idx].meta)) return idx;\n }\n return -1;\n },\n [cells]\n );\n\n const findPrevOption = useCallback(\n (from: number) => {\n const len = cells.length;\n if (len === 0) return -1;\n for (let step = 1; step <= len; step += 1) {\n const idx = (from - step + len * 2) % len;\n if (isNavigableOption(cells[idx].meta)) return idx;\n }\n return -1;\n },\n [cells]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (!isOpen) return;\n\n switch (event.key) {\n case \"ArrowDown\": {\n event.preventDefault();\n const next =\n activeIndex < 0 ? findFirstOption() : findNextOption(activeIndex);\n if (next >= 0) setActiveIndex(next);\n break;\n }\n case \"ArrowUp\": {\n event.preventDefault();\n const prev =\n activeIndex < 0 ? findLastOption() : findPrevOption(activeIndex);\n if (prev >= 0) setActiveIndex(prev);\n break;\n }\n case \"Home\": {\n event.preventDefault();\n const first = findFirstOption();\n if (first >= 0) setActiveIndex(first);\n break;\n }\n case \"End\": {\n event.preventDefault();\n const last = findLastOption();\n if (last >= 0) setActiveIndex(last);\n break;\n }\n case \"Enter\":\n case \" \": {\n if (event.defaultPrevented) break;\n if (event.key === \" \" && isTextInputTarget(event.target)) break;\n if (event.key === \" \") event.preventDefault();\n if (activeIndex >= 0 && activeIndex < cells.length) {\n const meta = cells[activeIndex].meta;\n if (isNavigableOption(meta)) meta.onSelect?.();\n }\n break;\n }\n case \"Escape\": {\n event.preventDefault();\n onClose();\n triggerRef?.current?.focus();\n break;\n }\n case \"Tab\": {\n onClose();\n break;\n }\n default:\n break;\n }\n },\n [\n isOpen,\n cells,\n activeIndex,\n setActiveIndex,\n onClose,\n triggerRef,\n findFirstOption,\n findLastOption,\n findNextOption,\n findPrevOption,\n ]\n );\n\n return { handleKeyDown };\n};\n"],"mappings":"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","names":["import_react","import_react_dom","import_xui_core","import_react","React","import_react","import_react","import_jsx_runtime","React","panel"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.tsx","../../src/ContextMenu.tsx","../../src/ContextMenuContext.tsx","../../src/ContextMenuItem.tsx","../../src/hooks/useContextMenuPosition.ts","../../src/hooks/useKeyboardNavigation.ts"],"sourcesContent":["export { ContextMenu } from \"./ContextMenu\";\nexport { ContextMenuItem } from \"./ContextMenuItem\";\n\nexport {\n useContextMenu,\n useContextMenuRequired,\n ContextMenuContext,\n} from \"./ContextMenuContext\";\n\nexport { useContextMenuPosition } from \"./hooks/useContextMenuPosition\";\nexport { useKeyboardNavigation } from \"./hooks/useKeyboardNavigation\";\n\nexport type {\n ContextMenuSize,\n ContextMenuItemType,\n ContextMenuItemLeadingControl,\n ContextMenuOptionItemProps,\n ContextMenuSearchItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuDividerItemProps,\n ContextMenuItemProps,\n ContextMenuPanelType,\n ContextMenuPlacement,\n ContextMenuPosition,\n ContextMenuProps,\n ContextMenuContextValue,\n ContextMenuSizing,\n} from \"./types\";\n","import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useResolvedTheme, type ThemeOverrideProps } from \"@xsolla/xui-core\";\nimport { Spinner } from \"@xsolla/xui-spinner\";\nimport { ContextMenuContext } from \"./ContextMenuContext\";\nimport { ContextMenuItem } from \"./ContextMenuItem\";\nimport { useContextMenuPosition } from \"./hooks/useContextMenuPosition\";\nimport { useKeyboardNavigation } from \"./hooks/useKeyboardNavigation\";\nimport type {\n ContextMenuCellMeta,\n ContextMenuContextValue,\n ContextMenuDividerItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuOptionItemProps,\n ContextMenuProps,\n ContextMenuSize,\n} from \"./types\";\n\ntype Props = ContextMenuProps & ThemeOverrideProps;\n\ninterface CellEntry {\n id: string;\n meta: ContextMenuCellMeta;\n}\n\ntype PresetItem =\n | ContextMenuOptionItemProps\n | ContextMenuHeadingItemProps\n | ContextMenuDividerItemProps;\n\nconst SEARCH_DEBOUNCE_MS = 200;\n\nconst EmptyMessage: React.FC<{ children: React.ReactNode; color: string }> = ({\n children,\n color,\n}) => (\n <div\n style={{\n padding: 12,\n color,\n fontSize: 14,\n textAlign: \"center\",\n }}\n >\n {children}\n </div>\n);\n\nexport const ContextMenu: React.FC<Props> = (props) => {\n const {\n type,\n items,\n children,\n size = \"md\",\n searchable,\n loading,\n emptyMessage,\n empty,\n trigger,\n isOpen,\n onOpenChange,\n closeOnSelect,\n width,\n maxHeight,\n placement = \"bottom-start\",\n onSelect,\n \"aria-label\": ariaLabel,\n \"data-testid\": testId,\n testID,\n themeMode,\n themeProductContext,\n } = props;\n\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(false);\n const open = isControlled ? !!isOpen : internalOpen;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange]\n );\n\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const cellsRef = useRef<CellEntry[]>([]);\n const [cellsVersion, setCellsVersion] = useState(0);\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const panelRef = useRef<HTMLDivElement | null>(null);\n\n // Search query state\n const [query, setQuery] = useState(\"\");\n const [debouncedQuery, setDebouncedQuery] = useState(\"\");\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = setTimeout(() => {\n setDebouncedQuery(query);\n }, SEARCH_DEBOUNCE_MS);\n return () => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);\n };\n }, [query]);\n\n const closeMenu = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen]);\n\n const registerCell = useCallback((id: string, meta: ContextMenuCellMeta) => {\n const existing = cellsRef.current.findIndex((c) => c.id === id);\n if (existing === -1) {\n cellsRef.current.push({ id, meta });\n setCellsVersion((v) => v + 1);\n return cellsRef.current.length - 1;\n }\n // Update metadata in place — keep registry order stable. Only bump the\n // version when nav-relevant metadata (disabled) actually changes.\n const prev = cellsRef.current[existing].meta;\n cellsRef.current[existing] = { id, meta };\n if (prev.disabled !== meta.disabled || prev.type !== meta.type) {\n setCellsVersion((v) => v + 1);\n }\n return existing;\n }, []);\n\n const unregisterCell = useCallback((id: string) => {\n const idx = cellsRef.current.findIndex((c) => c.id === id);\n if (idx !== -1) {\n // splice keeps survivors contiguous; consumers derive their index live\n // via getCellIndex so they reindex automatically.\n cellsRef.current.splice(idx, 1);\n setCellsVersion((v) => v + 1);\n }\n }, []);\n\n const getCellIndex = useCallback(\n (id: string) => cellsRef.current.findIndex((c) => c.id === id),\n []\n );\n\n const ctx: ContextMenuContextValue = useMemo(\n () => ({\n size: size as ContextMenuSize,\n closeMenu,\n registerCell,\n unregisterCell,\n getCellIndex,\n cellsVersion,\n activeIndex,\n setActiveIndex,\n query,\n setQuery,\n }),\n [\n size,\n closeMenu,\n registerCell,\n unregisterCell,\n getCellIndex,\n cellsVersion,\n activeIndex,\n query,\n ]\n );\n\n const triggerNode = useMemo(() => {\n if (!trigger) return null;\n const inner = isValidElement(trigger)\n ? cloneElement(\n trigger as ReactElement<{\n \"aria-haspopup\"?: string;\n \"aria-expanded\"?: string | boolean;\n }>,\n {\n \"aria-haspopup\": \"menu\",\n \"aria-expanded\": open ? \"true\" : \"false\",\n }\n )\n : trigger;\n return (\n <span\n ref={(node) => {\n if (!node) {\n triggerRef.current = null;\n return;\n }\n const focusable = node.querySelector<HTMLElement>(\n \"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])\"\n );\n triggerRef.current = focusable ?? node;\n }}\n onClick={() => setOpen(!open)}\n style={{ display: \"inline-flex\" }}\n >\n {inner}\n </span>\n );\n }, [trigger, open, setOpen]);\n\n const usePortal = !!trigger && typeof document !== \"undefined\";\n\n const position = useContextMenuPosition({\n triggerRef,\n panelRef,\n isOpen: open && usePortal,\n placement,\n });\n\n const cellsForNav = useMemo(\n () => cellsRef.current.map((c) => ({ id: c.id, meta: c.meta })),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [cellsVersion]\n );\n\n const { handleKeyDown } = useKeyboardNavigation({\n isOpen: open,\n cells: cellsForNav,\n activeIndex,\n setActiveIndex,\n onClose: closeMenu,\n triggerRef,\n });\n\n const sizingFn = (theme.sizing as { contextMenu?: (s: string) => unknown })\n .contextMenu;\n const sizing = sizingFn\n ? (sizingFn(size) as {\n panelWidth?: number;\n borderRadius?: number;\n paddingVertical?: number;\n })\n : {};\n const radiusObj = (theme as { radius?: { contextMenu?: number } }).radius;\n const radiusVal = sizing.borderRadius ?? radiusObj?.contextMenu ?? 8;\n const shadowObj = (theme as { shadow?: { contextMenu?: string } }).shadow;\n const shadowVal = shadowObj?.contextMenu ?? \"\";\n const panelPaddingVertical = sizing.paddingVertical ?? 8;\n\n const panelStyle: React.CSSProperties = {\n background: theme.colors.background.primary,\n borderRadius: radiusVal,\n boxShadow: shadowVal,\n width: width ?? sizing.panelWidth,\n maxHeight,\n overflow: \"hidden\",\n display: open ? \"flex\" : \"none\",\n flexDirection: \"column\",\n outline: \"none\",\n fontFamily: theme.fonts.body,\n paddingTop: panelPaddingVertical,\n paddingBottom: panelPaddingVertical,\n };\n\n if (usePortal) {\n panelStyle.position = \"fixed\";\n panelStyle.top = position?.top ?? 0;\n panelStyle.left = position?.left ?? 0;\n }\n\n // Filter preset items based on debounced query (only when searchable + items)\n const filteredItems = useMemo<PresetItem[] | undefined>(() => {\n if (!items) return undefined;\n if (!searchable || !debouncedQuery) return items.slice();\n const q = debouncedQuery.toLowerCase();\n const matchedFlags = items.map((item) => {\n if (item.type === \"option\") {\n return String(item.label ?? \"\")\n .toLowerCase()\n .includes(q);\n }\n return false; // headings/dividers themselves never match\n });\n // Now hide headings whose options are all filtered out and trailing dividers\n const result: PresetItem[] = [];\n let pendingHeading: {\n item: ContextMenuHeadingItemProps;\n idx: number;\n } | null = null;\n let lastEmittedWasContent = false; // whether last pushed was option (so divider can follow)\n let groupHasOption = false;\n for (let i = 0; i < items.length; i += 1) {\n const item = items[i];\n if (item.type === \"heading\") {\n // Starting a new group\n pendingHeading = { item, idx: i };\n groupHasOption = false;\n } else if (item.type === \"divider\") {\n // Emit divider only if the previously emitted thing was content\n if (lastEmittedWasContent) {\n result.push(item);\n lastEmittedWasContent = false;\n }\n // dividers also reset pending heading so a heading sits with its group\n pendingHeading = null;\n groupHasOption = false;\n } else if (item.type === \"option\") {\n if (matchedFlags[i]) {\n if (pendingHeading) {\n result.push(pendingHeading.item);\n pendingHeading = null;\n }\n result.push(item);\n lastEmittedWasContent = true;\n groupHasOption = true;\n }\n }\n }\n // Strip trailing divider if any\n while (result.length > 0 && result[result.length - 1].type === \"divider\") {\n result.pop();\n }\n // Suppress unused\n void groupHasOption;\n return result;\n }, [items, searchable, debouncedQuery]);\n\n const effectiveCloseOnSelect =\n closeOnSelect !== undefined ? closeOnSelect : type !== \"checkbox\";\n\n const renderPresetItem = (item: PresetItem, key: number) => {\n if (item.type === \"heading\") {\n return (\n <ContextMenuItem\n key={`h-${key}`}\n {...item}\n size={item.size ?? (size as ContextMenuSize)}\n />\n );\n }\n if (item.type === \"divider\") {\n return (\n <ContextMenuItem\n key={`d-${key}`}\n {...item}\n size={item.size ?? (size as ContextMenuSize)}\n />\n );\n }\n const composed = composeItemForPreset(type, item);\n const originalSelect = composed.onSelect;\n const wrappedSelect = () => {\n originalSelect?.();\n onSelect?.(item);\n if (effectiveCloseOnSelect) closeMenu();\n };\n return (\n <ContextMenuItem\n key={`o-${key}`}\n {...composed}\n size={composed.size ?? (size as ContextMenuSize)}\n onSelect={wrappedSelect}\n />\n );\n };\n\n // Determine what to render in the panel body\n const isLoadingState = loading;\n\n let bodyContent: React.ReactNode = null;\n let isBodyEmpty = false;\n let searchNode: React.ReactNode = null;\n\n if (isLoadingState) {\n bodyContent = (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 16,\n }}\n >\n <Spinner size={size === \"xl\" ? \"lg\" : size === \"lg\" ? \"md\" : \"sm\"} />\n </div>\n );\n } else if (children !== undefined && children !== null) {\n // Custom path. Empty when children is falsy/empty array.\n const childArr = React.Children.toArray(children);\n if (childArr.length === 0) {\n isBodyEmpty = true;\n } else {\n bodyContent = children;\n }\n } else if (type && items) {\n // Preset path\n if (searchable) {\n searchNode = (\n <ContextMenuItem\n type=\"search\"\n value={query}\n onValueChange={setQuery}\n size={size as ContextMenuSize}\n />\n );\n }\n const visible = filteredItems ?? [];\n const optionCount = visible.filter((i) => i.type === \"option\").length;\n if (optionCount === 0) {\n isBodyEmpty = true;\n } else {\n bodyContent = visible.map((it, idx) => renderPresetItem(it, idx));\n }\n } else {\n // No children, no items — empty\n isBodyEmpty = true;\n }\n\n if (isBodyEmpty) {\n bodyContent = empty ?? (\n <EmptyMessage color={theme.colors.content.tertiary}>\n {emptyMessage ?? \"No results\"}\n </EmptyMessage>\n );\n }\n\n const hasStickySearch = !!searchNode;\n\n // Focus management on open/close transitions\n const prevOpenRef = useRef(false);\n useEffect(() => {\n const wasOpen = prevOpenRef.current;\n prevOpenRef.current = open;\n if (!wasOpen && open) {\n // open transition: focus search if present, else first option\n // wait for cells to register\n const timer = setTimeout(() => {\n const panel = panelRef.current;\n if (!panel) return;\n const search =\n panel.querySelector<HTMLInputElement>(\"[role='searchbox']\");\n if (search) {\n search.focus();\n return;\n }\n const firstOption = panel.querySelector<HTMLElement>(\n \"[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']\"\n );\n if (firstOption) {\n firstOption.focus();\n // Reset activeIndex so keyboard nav starts fresh; onFocus will have\n // set it to the focused option's index, but tests expect ArrowDown\n // to move to the first option from -1.\n setActiveIndex(-1);\n } else {\n panel.focus();\n }\n }, 0);\n return () => clearTimeout(timer);\n }\n if (wasOpen && !open) {\n // close transition: focus the trigger\n triggerRef.current?.focus();\n }\n }, [open]);\n\n const resolvedPlacement = position?.placement ?? placement;\n\n const scrollContainerStyle: React.CSSProperties = {\n overflowY: \"auto\",\n flex: 1,\n minHeight: 0,\n };\n\n const stickyHeaderStyle: React.CSSProperties = {\n position: \"sticky\",\n top: 0,\n zIndex: 1,\n background: theme.colors.background.primary,\n };\n\n const panel = open ? (\n <ContextMenuContext.Provider value={ctx}>\n <div\n ref={panelRef}\n role=\"menu\"\n aria-label={ariaLabel}\n data-testid={testId || testID}\n data-placement={usePortal ? resolvedPlacement : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n onMouseLeave={() => setActiveIndex(-1)}\n style={panelStyle}\n >\n {hasStickySearch && (\n <div data-sticky=\"top\" style={stickyHeaderStyle}>\n {searchNode}\n </div>\n )}\n <div style={scrollContainerStyle}>{bodyContent}</div>\n </div>\n </ContextMenuContext.Provider>\n ) : null;\n\n return (\n <>\n {triggerNode}\n {usePortal ? panel && createPortal(panel, document.body) : panel}\n </>\n );\n};\n\nContextMenu.displayName = \"ContextMenu\";\n\nfunction composeItemForPreset(\n type: ContextMenuProps[\"type\"],\n item: ContextMenuOptionItemProps\n): ContextMenuOptionItemProps {\n switch (type) {\n case \"checkbox\":\n return { ...item, leadingControl: \"checkbox\" };\n case \"radio\":\n return { ...item, leadingControl: \"radio\" };\n case \"list\":\n case \"phone\":\n case \"status\":\n case \"brandLogo\":\n case \"avatar\":\n default:\n return { ...item };\n }\n}\n","import { createContext, useContext } from \"react\";\nimport type { ContextMenuContextValue } from \"./types\";\n\nexport const ContextMenuContext = createContext<\n ContextMenuContextValue | undefined\n>(undefined);\n\nexport const useContextMenu = () => {\n const context = useContext(ContextMenuContext);\n return context;\n};\n\nexport const useContextMenuRequired = () => {\n const context = useContext(ContextMenuContext);\n if (!context) {\n throw new Error(\n \"useContextMenuRequired must be used within a ContextMenu component\"\n );\n }\n return context;\n};\n","import React, { useEffect, useLayoutEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n useResolvedTheme,\n useId,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport { Checkbox } from \"@xsolla/xui-checkbox\";\nimport { Radio } from \"@xsolla/xui-radio\";\nimport { useContextMenu } from \"./ContextMenuContext\";\nimport type {\n ContextMenuDividerItemProps,\n ContextMenuHeadingItemProps,\n ContextMenuItemProps,\n ContextMenuOptionItemProps,\n ContextMenuSearchItemProps,\n ContextMenuSize,\n} from \"./types\";\n\ntype BodyVariant = \"bodyLg\" | \"bodyMd\" | \"bodySm\" | \"bodyXs\";\ntype AccentVariant =\n | \"bodyLgAccent\"\n | \"bodyMdAccent\"\n | \"bodySmAccent\"\n | \"bodyXsAccent\";\n\nconst sizeToVariants: Record<\n ContextMenuSize,\n { label: BodyVariant; description: BodyVariant; headingAccent: AccentVariant }\n> = {\n xl: { label: \"bodyLg\", description: \"bodyLg\", headingAccent: \"bodyLgAccent\" },\n lg: { label: \"bodyLg\", description: \"bodyMd\", headingAccent: \"bodyMdAccent\" },\n md: { label: \"bodyMd\", description: \"bodySm\", headingAccent: \"bodySmAccent\" },\n sm: { label: \"bodySm\", description: \"bodyXs\", headingAccent: \"bodyXsAccent\" },\n};\n\nconst sizeLabelOverride: Partial<\n Record<ContextMenuSize, { fontSize: number; lineHeight: string }>\n> = {\n xl: { fontSize: 20, lineHeight: \"26px\" },\n};\n\ntype Props = ContextMenuItemProps & ThemeOverrideProps;\n\nexport const ContextMenuItem: React.FC<Props> = (props) => {\n if (props.type === \"option\") return <OptionCell {...props} />;\n if (props.type === \"heading\") return <HeadingCell {...props} />;\n if (props.type === \"divider\") return <DividerCell {...props} />;\n if (props.type === \"search\") return <SearchCell {...props} />;\n return null;\n};\n\nContextMenuItem.displayName = \"ContextMenuItem\";\n\nconst SubmenuChevron: React.FC<{ color: string; size: number }> = ({\n color,\n size,\n}) => (\n <span\n data-testid=\"ctxmenu-submenu-chevron\"\n aria-hidden=\"true\"\n style={{\n color,\n display: \"inline-flex\",\n alignItems: \"center\",\n width: size,\n height: size,\n }}\n >\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M17.0605 11.6464C17.2558 11.8417 17.2558 12.1583 17.0605 12.3536L9.70703 19.707L8.29297 18.293L14.5859 12L8.29297 5.70703L9.70703 4.29297L17.0605 11.6464Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n);\n\nconst OptionCell: React.FC<ContextMenuOptionItemProps & ThemeOverrideProps> = ({\n size: propSize,\n label,\n description,\n disabled,\n destructive,\n checked,\n leadingControl,\n leadingIcon,\n status,\n iconWrapper,\n slotContent,\n value,\n hint,\n trailingIcon,\n keyboardShortcut,\n hasSubmenu,\n submenu,\n onSelect,\n testID,\n themeMode,\n themeProductContext,\n \"data-testid\": testId,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n const variants = sizeToVariants[size];\n\n const id = useId();\n const registerCell = ctx?.registerCell;\n const unregisterCell = ctx?.unregisterCell;\n const getCellIndex = ctx?.getCellIndex;\n const [isHovered, setIsHovered] = useState(false);\n const [submenuOpen, setSubmenuOpen] = useState(false);\n const [submenuPos, setSubmenuPos] = useState<{\n top: number;\n left: number;\n } | null>(null);\n const optionRef = React.useRef<HTMLDivElement | null>(null);\n const submenuWrapperRef = React.useRef<HTMLDivElement | null>(null);\n const closeTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n\n const cancelClose = () => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n };\n\n const scheduleClose = () => {\n cancelClose();\n closeTimerRef.current = setTimeout(() => setSubmenuOpen(false), 120);\n };\n\n useEffect(() => () => cancelClose(), []);\n\n useEffect(() => {\n if (!hasSubmenu || !submenuOpen) return;\n const onMouseDown = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target) return;\n const inOption = optionRef.current?.contains(target);\n const inSubmenu = submenuWrapperRef.current?.contains(target);\n if (!inOption && !inSubmenu) {\n setSubmenuOpen(false);\n return;\n }\n if (\n inSubmenu &&\n target.closest(\n '[role=\"menuitem\"],[role=\"menuitemcheckbox\"],[role=\"menuitemradio\"]'\n )\n ) {\n setSubmenuOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n return () => document.removeEventListener(\"mousedown\", onMouseDown);\n }, [hasSubmenu, submenuOpen]);\n\n useLayoutEffect(() => {\n if (!hasSubmenu || !submenuOpen) {\n setSubmenuPos(null);\n return;\n }\n const update = () => {\n const node = optionRef.current;\n if (!node) return;\n const rect = node.getBoundingClientRect();\n setSubmenuPos({ top: rect.top, left: rect.right });\n };\n update();\n window.addEventListener(\"scroll\", update, true);\n window.addEventListener(\"resize\", update);\n return () => {\n window.removeEventListener(\"scroll\", update, true);\n window.removeEventListener(\"resize\", update);\n };\n }, [hasSubmenu, submenuOpen]);\n const onSelectRef = React.useRef(onSelect);\n onSelectRef.current = onSelect;\n // Register on mount / unregister on unmount. Deps are the stable registry\n // callbacks + id only — NOT `ctx`, whose identity changes on every hover\n // (activeIndex). Depending on `ctx` here churned register/unregister and, on\n // React <18 (interleaved passive-effect flush), collapsed every row to the\n // same index so one hover highlighted all rows (FEP-764).\n useEffect(() => {\n if (!registerCell || !unregisterCell) return;\n registerCell(id, {\n type: \"option\",\n onSelect: () => onSelectRef.current?.(),\n });\n return () => unregisterCell(id);\n }, [registerCell, unregisterCell, id]);\n\n // Keep registry metadata (disabled) in sync without reordering the cell.\n useEffect(() => {\n if (!registerCell) return;\n registerCell(id, {\n type: \"option\",\n disabled,\n onSelect: () => onSelectRef.current?.(),\n });\n }, [registerCell, id, disabled]);\n\n // Derive index live from the registry instead of caching it in state, so it\n // is always correct after dynamic add/remove without per-item re-registration.\n const index = getCellIndex ? getCellIndex(id) : -1;\n const isActive = ctx ? index >= 0 && ctx.activeIndex === index : false;\n const inHoverState =\n isActive || (!ctx && isHovered) || (hasSubmenu && submenuOpen);\n\n const handleEnter = () => {\n if (disabled) return;\n if (ctx && index >= 0) ctx.setActiveIndex(index);\n if (!ctx) setIsHovered(true);\n if (hasSubmenu) setSubmenuOpen(true);\n };\n\n const handleLeave = () => {\n if (!ctx) setIsHovered(false);\n if (hasSubmenu) scheduleClose();\n };\n\n const labelColor = disabled\n ? theme.colors.control.input.textDisable\n : destructive\n ? theme.colors.content.alert.primary\n : theme.colors.content.primary;\n\n const bg = inHoverState ? theme.colors.control.input.bgHover : \"transparent\";\n\n const role =\n !hasSubmenu && checked !== undefined ? \"menuitemcheckbox\" : \"menuitem\";\n const ariaChecked =\n !hasSubmenu && checked !== undefined\n ? checked\n ? \"true\"\n : \"false\"\n : undefined;\n\n const handleClick = () => {\n if (disabled) return;\n if (hasSubmenu) {\n setSubmenuOpen(true);\n return;\n }\n onSelect?.();\n };\n\n const closeSubmenuAndFocus = () => {\n setSubmenuOpen(false);\n optionRef.current?.focus();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (disabled) {\n if (e.key === \"Enter\" || e.key === \" \") e.preventDefault();\n return;\n }\n if (hasSubmenu) {\n if (e.key === \"ArrowRight\" || e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n setSubmenuOpen(true);\n return;\n }\n if (e.key === \"ArrowLeft\" || e.key === \"Escape\") {\n if (submenuOpen) {\n e.preventDefault();\n e.stopPropagation();\n closeSubmenuAndFocus();\n return;\n }\n }\n }\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onSelect?.();\n }\n };\n\n return (\n <div\n ref={optionRef}\n role={role}\n aria-checked={ariaChecked}\n aria-disabled={disabled ? \"true\" : undefined}\n aria-haspopup={hasSubmenu ? \"menu\" : undefined}\n aria-expanded={hasSubmenu ? (submenuOpen ? \"true\" : \"false\") : undefined}\n tabIndex={0}\n data-testid={testId || testID}\n data-state={inHoverState ? \"hover\" : undefined}\n data-destructive={destructive ? \"true\" : undefined}\n aria-keyshortcuts={keyboardShortcut}\n onMouseEnter={() => {\n cancelClose();\n handleEnter();\n }}\n onMouseLeave={handleLeave}\n onFocus={handleEnter}\n onBlur={() => {\n if (!ctx) setIsHovered(false);\n }}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n style={{\n position: \"relative\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: sizing.gap,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n backgroundColor: bg,\n cursor: disabled ? \"not-allowed\" : \"pointer\",\n outline: \"none\",\n }}\n >\n {leadingControl === \"checkbox\" && (\n <span\n data-testid=\"ctxmenu-leading-checkbox\"\n aria-hidden=\"true\"\n style={{ pointerEvents: \"none\", display: \"inline-flex\" }}\n >\n <Checkbox\n size={size}\n checked={!!checked}\n disabled={!!disabled}\n themeMode={themeMode}\n themeProductContext={themeProductContext}\n />\n </span>\n )}\n {leadingControl === \"radio\" && (\n <span\n data-testid=\"ctxmenu-leading-radio\"\n aria-hidden=\"true\"\n style={{ pointerEvents: \"none\", display: \"inline-flex\" }}\n >\n <Radio\n size={size}\n checked={!!checked}\n disabled={!!disabled}\n themeMode={themeMode}\n themeProductContext={themeProductContext}\n />\n </span>\n )}\n {leadingIcon}\n {status}\n {iconWrapper}\n {slotContent}\n <span\n style={{\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n minWidth: 0,\n }}\n >\n <Typography\n variant={variants.label}\n color={labelColor}\n noWrap={description === undefined}\n style={{\n ...(description === undefined\n ? { display: \"block\", minWidth: 0 }\n : {}),\n ...(sizeLabelOverride[size] ?? {}),\n }}\n >\n {label}\n </Typography>\n {description !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {description}\n </Typography>\n )}\n </span>\n {(value !== undefined || hint !== undefined) && (\n <span\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"flex-end\",\n }}\n >\n {value !== undefined && (\n <Typography\n variant={variants.label}\n color={theme.colors.content.secondary}\n style={sizeLabelOverride[size]}\n >\n {value}\n </Typography>\n )}\n {hint !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {hint}\n </Typography>\n )}\n </span>\n )}\n {keyboardShortcut && (\n <Typography\n as=\"kbd\"\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {keyboardShortcut}\n </Typography>\n )}\n {hasSubmenu && (\n <SubmenuChevron\n color={theme.colors.content.tertiary}\n size={sizing.iconSize}\n />\n )}\n {trailingIcon}\n {hasSubmenu &&\n submenuOpen &&\n submenu &&\n submenuPos &&\n typeof document !== \"undefined\" &&\n createPortal(\n <div\n ref={submenuWrapperRef}\n onMouseEnter={cancelClose}\n onMouseLeave={scheduleClose}\n style={{\n position: \"fixed\",\n top: submenuPos.top,\n left: submenuPos.left,\n zIndex: 2000,\n }}\n >\n {submenu}\n </div>,\n document.body\n )}\n </div>\n );\n};\n\nconst HeadingCell: React.FC<\n ContextMenuHeadingItemProps & ThemeOverrideProps\n> = ({\n size: propSize,\n label,\n description,\n testID,\n themeMode,\n themeProductContext,\n \"data-testid\": testId,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n const variants = sizeToVariants[size];\n\n const id = useId();\n const registerCell = ctx?.registerCell;\n const unregisterCell = ctx?.unregisterCell;\n useEffect(() => {\n if (!registerCell || !unregisterCell) return;\n registerCell(id, { type: \"heading\" });\n return () => unregisterCell(id);\n }, [registerCell, unregisterCell, id]);\n\n return (\n <div\n role=\"presentation\"\n data-testid={testId || testID}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n }}\n >\n <Typography\n variant={variants.headingAccent}\n color={theme.colors.content.secondary}\n style={{ textTransform: \"uppercase\", letterSpacing: 0.5 }}\n >\n {label}\n </Typography>\n {description !== undefined && (\n <Typography\n variant={variants.description}\n color={theme.colors.content.tertiary}\n >\n {description}\n </Typography>\n )}\n </div>\n );\n};\n\nconst SearchCell: React.FC<ContextMenuSearchItemProps & ThemeOverrideProps> = ({\n size: propSize,\n value,\n onValueChange,\n placeholder = \"Search\",\n autoFocus,\n \"aria-label\": ariaLabel = \"Search options\",\n \"data-testid\": testId,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const size: ContextMenuSize = propSize ?? ctx?.size ?? \"md\";\n const sizing = theme.sizing.contextMenu(size);\n\n const id = useId();\n const registerCell = ctx?.registerCell;\n const unregisterCell = ctx?.unregisterCell;\n useEffect(() => {\n if (!registerCell || !unregisterCell) return;\n registerCell(id, { type: \"search\" });\n return () => unregisterCell(id);\n }, [registerCell, unregisterCell, id]);\n\n return (\n <input\n type=\"search\"\n role=\"searchbox\"\n aria-label={ariaLabel}\n placeholder={placeholder}\n value={value}\n autoFocus={autoFocus}\n onChange={(e) => onValueChange(e.target.value)}\n data-testid={testId || testID}\n style={{\n width: \"100%\",\n boxSizing: \"border-box\",\n border: \"none\",\n outline: \"none\",\n background: \"transparent\",\n color: theme.colors.content.primary,\n fontSize: sizing.fontSize,\n lineHeight: `${sizing.fontSize + 2}px`,\n paddingLeft: sizing.itemPaddingHorizontal,\n paddingRight: sizing.itemPaddingHorizontal,\n paddingTop: sizing.itemPaddingVertical,\n paddingBottom: sizing.itemPaddingVertical,\n }}\n />\n );\n};\n\nconst DividerCell: React.FC<\n ContextMenuDividerItemProps & ThemeOverrideProps\n> = ({ themeMode, themeProductContext, \"data-testid\": testId }) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const ctx = useContextMenu();\n const id = useId();\n const registerCell = ctx?.registerCell;\n const unregisterCell = ctx?.unregisterCell;\n useEffect(() => {\n if (!registerCell || !unregisterCell) return;\n registerCell(id, { type: \"divider\" });\n return () => unregisterCell(id);\n }, [registerCell, unregisterCell, id]);\n return (\n <div\n role=\"separator\"\n data-testid={testId}\n style={{\n height: 1,\n backgroundColor: theme.colors.border.secondary,\n margin: \"4px 0\",\n }}\n />\n );\n};\n","import { useEffect, useState, type RefObject } from \"react\";\nimport type { ContextMenuPlacement } from \"../types\";\n\ninterface UseContextMenuPositionOptions {\n triggerRef: RefObject<HTMLElement | null>;\n panelRef: RefObject<HTMLElement | null>;\n isOpen: boolean;\n placement?: ContextMenuPlacement;\n offset?: number;\n}\n\ninterface ResolvedPosition {\n top: number;\n left: number;\n placement: ContextMenuPlacement;\n}\n\nconst splitPlacement = (\n placement: ContextMenuPlacement\n): { vertical: \"top\" | \"bottom\"; horizontal: \"start\" | \"end\" } => {\n const [vertical, horizontal] = placement.split(\"-\") as [\n \"top\" | \"bottom\",\n \"start\" | \"end\",\n ];\n return { vertical, horizontal };\n};\n\nconst joinPlacement = (\n vertical: \"top\" | \"bottom\",\n horizontal: \"start\" | \"end\"\n): ContextMenuPlacement => `${vertical}-${horizontal}` as ContextMenuPlacement;\n\nexport const useContextMenuPosition = ({\n triggerRef,\n panelRef,\n isOpen,\n placement = \"bottom-start\",\n offset = 4,\n}: UseContextMenuPositionOptions): ResolvedPosition | undefined => {\n const [resolved, setResolved] = useState<ResolvedPosition | undefined>();\n\n useEffect(() => {\n if (!isOpen) {\n setResolved(undefined);\n return;\n }\n\n const compute = () => {\n const trigger = triggerRef.current;\n const panel = panelRef.current;\n if (!trigger || !panel) return;\n\n const triggerRect = trigger.getBoundingClientRect();\n const panelRect = panel.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n let { vertical, horizontal } = splitPlacement(placement);\n\n const computeTop = (v: \"top\" | \"bottom\") =>\n v === \"bottom\"\n ? triggerRect.bottom + offset\n : triggerRect.top - panelRect.height - offset;\n\n const computeLeft = (h: \"start\" | \"end\") =>\n h === \"start\" ? triggerRect.left : triggerRect.right - panelRect.width;\n\n let top = computeTop(vertical);\n const wantedBottom = top + panelRect.height;\n if (top < 0 || wantedBottom > viewportHeight) {\n const flipped = vertical === \"bottom\" ? \"top\" : \"bottom\";\n const flippedTop = computeTop(flipped);\n const flippedBottom = flippedTop + panelRect.height;\n if (flippedTop >= 0 && flippedBottom <= viewportHeight) {\n vertical = flipped;\n top = flippedTop;\n }\n }\n\n let left = computeLeft(horizontal);\n const wantedRight = left + panelRect.width;\n if (left < 0 || wantedRight > viewportWidth) {\n const flipped = horizontal === \"start\" ? \"end\" : \"start\";\n const flippedLeft = computeLeft(flipped);\n const flippedRight = flippedLeft + panelRect.width;\n if (flippedLeft >= 0 && flippedRight <= viewportWidth) {\n horizontal = flipped;\n left = flippedLeft;\n }\n }\n\n setResolved({\n top,\n left,\n placement: joinPlacement(vertical, horizontal),\n });\n };\n\n const rafId = window.requestAnimationFrame(compute);\n const onResize = () => compute();\n window.addEventListener(\"resize\", onResize);\n\n return () => {\n window.cancelAnimationFrame(rafId);\n window.removeEventListener(\"resize\", onResize);\n };\n }, [isOpen, placement, offset, triggerRef, panelRef]);\n\n return resolved;\n};\n","import { useCallback, type RefObject } from \"react\";\n\nexport type CellType = \"option\" | \"search\" | \"heading\" | \"divider\";\n\nexport interface CellMeta {\n type: CellType;\n onSelect?: () => void;\n disabled?: boolean;\n}\n\ninterface UseKeyboardNavigationOptions {\n isOpen: boolean;\n cells: Array<{ id: string; meta: CellMeta }>;\n activeIndex: number;\n setActiveIndex: (index: number) => void;\n onClose: () => void;\n triggerRef?: RefObject<HTMLElement | null>;\n}\n\nconst isNavigableOption = (meta: CellMeta) =>\n meta.type === \"option\" && !meta.disabled;\n\nconst isTextInputTarget = (target: EventTarget | null): boolean => {\n if (!(target instanceof HTMLElement)) return false;\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") return true;\n return target.getAttribute(\"role\") === \"searchbox\";\n};\n\nexport const useKeyboardNavigation = ({\n isOpen,\n cells,\n activeIndex,\n setActiveIndex,\n onClose,\n triggerRef,\n}: UseKeyboardNavigationOptions) => {\n const findFirstOption = useCallback(() => {\n for (let i = 0; i < cells.length; i += 1) {\n if (isNavigableOption(cells[i].meta)) return i;\n }\n return -1;\n }, [cells]);\n\n const findLastOption = useCallback(() => {\n for (let i = cells.length - 1; i >= 0; i -= 1) {\n if (isNavigableOption(cells[i].meta)) return i;\n }\n return -1;\n }, [cells]);\n\n const findNextOption = useCallback(\n (from: number) => {\n const len = cells.length;\n if (len === 0) return -1;\n for (let step = 1; step <= len; step += 1) {\n const idx = (from + step + len) % len;\n if (isNavigableOption(cells[idx].meta)) return idx;\n }\n return -1;\n },\n [cells]\n );\n\n const findPrevOption = useCallback(\n (from: number) => {\n const len = cells.length;\n if (len === 0) return -1;\n for (let step = 1; step <= len; step += 1) {\n const idx = (from - step + len * 2) % len;\n if (isNavigableOption(cells[idx].meta)) return idx;\n }\n return -1;\n },\n [cells]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (!isOpen) return;\n\n switch (event.key) {\n case \"ArrowDown\": {\n event.preventDefault();\n const next =\n activeIndex < 0 ? findFirstOption() : findNextOption(activeIndex);\n if (next >= 0) setActiveIndex(next);\n break;\n }\n case \"ArrowUp\": {\n event.preventDefault();\n const prev =\n activeIndex < 0 ? findLastOption() : findPrevOption(activeIndex);\n if (prev >= 0) setActiveIndex(prev);\n break;\n }\n case \"Home\": {\n event.preventDefault();\n const first = findFirstOption();\n if (first >= 0) setActiveIndex(first);\n break;\n }\n case \"End\": {\n event.preventDefault();\n const last = findLastOption();\n if (last >= 0) setActiveIndex(last);\n break;\n }\n case \"Enter\":\n case \" \": {\n if (event.defaultPrevented) break;\n if (event.key === \" \" && isTextInputTarget(event.target)) break;\n if (event.key === \" \") event.preventDefault();\n if (activeIndex >= 0 && activeIndex < cells.length) {\n const meta = cells[activeIndex].meta;\n if (isNavigableOption(meta)) meta.onSelect?.();\n }\n break;\n }\n case \"Escape\": {\n event.preventDefault();\n onClose();\n triggerRef?.current?.focus();\n break;\n }\n case \"Tab\": {\n onClose();\n break;\n }\n default:\n break;\n }\n },\n [\n isOpen,\n cells,\n activeIndex,\n setActiveIndex,\n onClose,\n triggerRef,\n findFirstOption,\n findLastOption,\n findNextOption,\n findPrevOption,\n ]\n );\n\n return { handleKeyDown };\n};\n"],"mappings":"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","names":["import_react","import_react_dom","import_xui_core","import_react","React","import_react","import_react","import_jsx_runtime","React","panel"]}
|
package/native/index.mjs
CHANGED
|
@@ -121,7 +121,9 @@ var OptionCell = ({
|
|
|
121
121
|
const sizing = theme.sizing.contextMenu(size);
|
|
122
122
|
const variants = sizeToVariants[size];
|
|
123
123
|
const id = useId();
|
|
124
|
-
const
|
|
124
|
+
const registerCell = ctx?.registerCell;
|
|
125
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
126
|
+
const getCellIndex = ctx?.getCellIndex;
|
|
125
127
|
const [isHovered, setIsHovered] = useState(false);
|
|
126
128
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
|
127
129
|
const [submenuPos, setSubmenuPos] = useState(null);
|
|
@@ -183,15 +185,22 @@ var OptionCell = ({
|
|
|
183
185
|
const onSelectRef = React.useRef(onSelect);
|
|
184
186
|
onSelectRef.current = onSelect;
|
|
185
187
|
useEffect(() => {
|
|
186
|
-
if (!
|
|
187
|
-
|
|
188
|
+
if (!registerCell || !unregisterCell) return;
|
|
189
|
+
registerCell(id, {
|
|
190
|
+
type: "option",
|
|
191
|
+
onSelect: () => onSelectRef.current?.()
|
|
192
|
+
});
|
|
193
|
+
return () => unregisterCell(id);
|
|
194
|
+
}, [registerCell, unregisterCell, id]);
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (!registerCell) return;
|
|
197
|
+
registerCell(id, {
|
|
188
198
|
type: "option",
|
|
189
199
|
disabled,
|
|
190
200
|
onSelect: () => onSelectRef.current?.()
|
|
191
201
|
});
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}, [ctx, id, disabled]);
|
|
202
|
+
}, [registerCell, id, disabled]);
|
|
203
|
+
const index = getCellIndex ? getCellIndex(id) : -1;
|
|
195
204
|
const isActive = ctx ? index >= 0 && ctx.activeIndex === index : false;
|
|
196
205
|
const inHoverState = isActive || !ctx && isHovered || hasSubmenu && submenuOpen;
|
|
197
206
|
const handleEnter = () => {
|
|
@@ -444,11 +453,13 @@ var HeadingCell = ({
|
|
|
444
453
|
const sizing = theme.sizing.contextMenu(size);
|
|
445
454
|
const variants = sizeToVariants[size];
|
|
446
455
|
const id = useId();
|
|
456
|
+
const registerCell = ctx?.registerCell;
|
|
457
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
447
458
|
useEffect(() => {
|
|
448
|
-
if (!
|
|
449
|
-
|
|
450
|
-
return () =>
|
|
451
|
-
}, [
|
|
459
|
+
if (!registerCell || !unregisterCell) return;
|
|
460
|
+
registerCell(id, { type: "heading" });
|
|
461
|
+
return () => unregisterCell(id);
|
|
462
|
+
}, [registerCell, unregisterCell, id]);
|
|
452
463
|
return /* @__PURE__ */ jsxs(
|
|
453
464
|
"div",
|
|
454
465
|
{
|
|
@@ -502,11 +513,13 @@ var SearchCell = ({
|
|
|
502
513
|
const size = propSize ?? ctx?.size ?? "md";
|
|
503
514
|
const sizing = theme.sizing.contextMenu(size);
|
|
504
515
|
const id = useId();
|
|
516
|
+
const registerCell = ctx?.registerCell;
|
|
517
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
505
518
|
useEffect(() => {
|
|
506
|
-
if (!
|
|
507
|
-
|
|
508
|
-
return () =>
|
|
509
|
-
}, [
|
|
519
|
+
if (!registerCell || !unregisterCell) return;
|
|
520
|
+
registerCell(id, { type: "search" });
|
|
521
|
+
return () => unregisterCell(id);
|
|
522
|
+
}, [registerCell, unregisterCell, id]);
|
|
510
523
|
return /* @__PURE__ */ jsx(
|
|
511
524
|
"input",
|
|
512
525
|
{
|
|
@@ -539,11 +552,13 @@ var DividerCell = ({ themeMode, themeProductContext, "data-testid": testId }) =>
|
|
|
539
552
|
const { theme } = useResolvedTheme({ themeMode, themeProductContext });
|
|
540
553
|
const ctx = useContextMenu();
|
|
541
554
|
const id = useId();
|
|
555
|
+
const registerCell = ctx?.registerCell;
|
|
556
|
+
const unregisterCell = ctx?.unregisterCell;
|
|
542
557
|
useEffect(() => {
|
|
543
|
-
if (!
|
|
544
|
-
|
|
545
|
-
return () =>
|
|
546
|
-
}, [
|
|
558
|
+
if (!registerCell || !unregisterCell) return;
|
|
559
|
+
registerCell(id, { type: "divider" });
|
|
560
|
+
return () => unregisterCell(id);
|
|
561
|
+
}, [registerCell, unregisterCell, id]);
|
|
547
562
|
return /* @__PURE__ */ jsx(
|
|
548
563
|
"div",
|
|
549
564
|
{
|
|
@@ -830,7 +845,11 @@ var ContextMenu = (props) => {
|
|
|
830
845
|
setCellsVersion((v) => v + 1);
|
|
831
846
|
return cellsRef.current.length - 1;
|
|
832
847
|
}
|
|
848
|
+
const prev = cellsRef.current[existing].meta;
|
|
833
849
|
cellsRef.current[existing] = { id, meta };
|
|
850
|
+
if (prev.disabled !== meta.disabled || prev.type !== meta.type) {
|
|
851
|
+
setCellsVersion((v) => v + 1);
|
|
852
|
+
}
|
|
834
853
|
return existing;
|
|
835
854
|
}, []);
|
|
836
855
|
const unregisterCell = useCallback2((id) => {
|
|
@@ -840,18 +859,33 @@ var ContextMenu = (props) => {
|
|
|
840
859
|
setCellsVersion((v) => v + 1);
|
|
841
860
|
}
|
|
842
861
|
}, []);
|
|
862
|
+
const getCellIndex = useCallback2(
|
|
863
|
+
(id) => cellsRef.current.findIndex((c) => c.id === id),
|
|
864
|
+
[]
|
|
865
|
+
);
|
|
843
866
|
const ctx = useMemo(
|
|
844
867
|
() => ({
|
|
845
868
|
size,
|
|
846
869
|
closeMenu,
|
|
847
870
|
registerCell,
|
|
848
871
|
unregisterCell,
|
|
872
|
+
getCellIndex,
|
|
873
|
+
cellsVersion,
|
|
849
874
|
activeIndex,
|
|
850
875
|
setActiveIndex,
|
|
851
876
|
query,
|
|
852
877
|
setQuery
|
|
853
878
|
}),
|
|
854
|
-
[
|
|
879
|
+
[
|
|
880
|
+
size,
|
|
881
|
+
closeMenu,
|
|
882
|
+
registerCell,
|
|
883
|
+
unregisterCell,
|
|
884
|
+
getCellIndex,
|
|
885
|
+
cellsVersion,
|
|
886
|
+
activeIndex,
|
|
887
|
+
query
|
|
888
|
+
]
|
|
855
889
|
);
|
|
856
890
|
const triggerNode = useMemo(() => {
|
|
857
891
|
if (!trigger) return null;
|