@xsolla/xui-context-menu 0.170.5 → 0.170.6-pr334.1781172208
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.js +89 -28
- package/native/index.js.map +1 -1
- package/native/index.mjs +89 -28
- package/native/index.mjs.map +1 -1
- package/package.json +10 -9
- package/web/index.js +89 -28
- package/web/index.js.map +1 -1
- package/web/index.mjs +89 -28
- package/web/index.mjs.map +1 -1
package/native/index.js
CHANGED
|
@@ -70,6 +70,7 @@ var import_xui_core = require("@xsolla/xui-core");
|
|
|
70
70
|
var import_xui_typography = require("@xsolla/xui-typography");
|
|
71
71
|
var import_xui_checkbox = require("@xsolla/xui-checkbox");
|
|
72
72
|
var import_xui_radio = require("@xsolla/xui-radio");
|
|
73
|
+
var import_xui_icons_base = require("@xsolla/xui-icons-base");
|
|
73
74
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
74
75
|
var sizeToVariants = {
|
|
75
76
|
xl: { label: "bodyLg", description: "bodyLg", headingAccent: "bodyLgAccent" },
|
|
@@ -553,30 +554,63 @@ var SearchCell = ({
|
|
|
553
554
|
return () => unregisterCell(id);
|
|
554
555
|
}, [registerCell, unregisterCell, id]);
|
|
555
556
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
556
|
-
"
|
|
557
|
+
"div",
|
|
557
558
|
{
|
|
558
|
-
type: "search",
|
|
559
|
-
role: "searchbox",
|
|
560
|
-
"aria-label": ariaLabel,
|
|
561
|
-
placeholder,
|
|
562
|
-
value,
|
|
563
|
-
autoFocus,
|
|
564
|
-
onChange: (e) => onValueChange(e.target.value),
|
|
565
|
-
"data-testid": testId || testID,
|
|
566
559
|
style: {
|
|
567
|
-
width: "100%",
|
|
568
560
|
boxSizing: "border-box",
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
561
|
+
paddingLeft: sizing.searchPaddingHorizontal,
|
|
562
|
+
paddingRight: sizing.searchPaddingHorizontal,
|
|
563
|
+
paddingTop: sizing.searchPaddingVertical,
|
|
564
|
+
paddingBottom: sizing.searchPaddingVertical
|
|
565
|
+
},
|
|
566
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
567
|
+
"div",
|
|
568
|
+
{
|
|
569
|
+
style: {
|
|
570
|
+
display: "flex",
|
|
571
|
+
alignItems: "center",
|
|
572
|
+
gap: 8,
|
|
573
|
+
paddingBottom: 6,
|
|
574
|
+
borderBottom: `1px solid ${theme.colors.border.secondary}`
|
|
575
|
+
},
|
|
576
|
+
children: [
|
|
577
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
578
|
+
import_xui_icons_base.Search,
|
|
579
|
+
{
|
|
580
|
+
variant: "line",
|
|
581
|
+
size: sizing.searchIconSize,
|
|
582
|
+
color: theme.colors.control.input.placeholder,
|
|
583
|
+
"aria-hidden": true
|
|
584
|
+
}
|
|
585
|
+
),
|
|
586
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
587
|
+
"input",
|
|
588
|
+
{
|
|
589
|
+
type: "search",
|
|
590
|
+
role: "searchbox",
|
|
591
|
+
"aria-label": ariaLabel,
|
|
592
|
+
placeholder,
|
|
593
|
+
value,
|
|
594
|
+
autoFocus,
|
|
595
|
+
onChange: (e) => onValueChange(e.target.value),
|
|
596
|
+
"data-testid": testId || testID,
|
|
597
|
+
style: {
|
|
598
|
+
flex: 1,
|
|
599
|
+
minWidth: 0,
|
|
600
|
+
boxSizing: "border-box",
|
|
601
|
+
border: "none",
|
|
602
|
+
outline: "none",
|
|
603
|
+
background: "transparent",
|
|
604
|
+
padding: 0,
|
|
605
|
+
color: theme.colors.content.primary,
|
|
606
|
+
fontSize: sizing.searchFontSize,
|
|
607
|
+
lineHeight: `${sizing.searchLineHeight}px`
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
)
|
|
611
|
+
]
|
|
612
|
+
}
|
|
613
|
+
)
|
|
580
614
|
}
|
|
581
615
|
);
|
|
582
616
|
};
|
|
@@ -658,18 +692,32 @@ var useContextMenuPosition = ({
|
|
|
658
692
|
left = flippedLeft;
|
|
659
693
|
}
|
|
660
694
|
}
|
|
661
|
-
|
|
695
|
+
const next = {
|
|
662
696
|
top,
|
|
663
697
|
left,
|
|
664
698
|
placement: joinPlacement(vertical, horizontal)
|
|
665
|
-
}
|
|
699
|
+
};
|
|
700
|
+
setResolved(
|
|
701
|
+
(prev) => prev && prev.top === next.top && prev.left === next.left && prev.placement === next.placement ? prev : next
|
|
702
|
+
);
|
|
666
703
|
};
|
|
667
|
-
|
|
704
|
+
let rafId = window.requestAnimationFrame(compute);
|
|
668
705
|
const onResize = () => compute();
|
|
706
|
+
let scrollRafPending = false;
|
|
707
|
+
const onScroll = () => {
|
|
708
|
+
if (scrollRafPending) return;
|
|
709
|
+
scrollRafPending = true;
|
|
710
|
+
rafId = window.requestAnimationFrame(() => {
|
|
711
|
+
scrollRafPending = false;
|
|
712
|
+
compute();
|
|
713
|
+
});
|
|
714
|
+
};
|
|
669
715
|
window.addEventListener("resize", onResize);
|
|
716
|
+
window.addEventListener("scroll", onScroll, true);
|
|
670
717
|
return () => {
|
|
671
718
|
window.cancelAnimationFrame(rafId);
|
|
672
719
|
window.removeEventListener("resize", onResize);
|
|
720
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
673
721
|
};
|
|
674
722
|
}, [isOpen, placement, offset, triggerRef, panelRef]);
|
|
675
723
|
return resolved;
|
|
@@ -975,10 +1023,14 @@ var ContextMenu = (props) => {
|
|
|
975
1023
|
const radiusObj = theme.radius;
|
|
976
1024
|
const radiusVal = sizing.borderRadius ?? radiusObj?.contextMenu ?? 8;
|
|
977
1025
|
const shadowObj = theme.shadow;
|
|
978
|
-
const shadowVal = shadowObj?.
|
|
1026
|
+
const shadowVal = shadowObj?.popover ?? "";
|
|
979
1027
|
const panelPaddingVertical = sizing.paddingVertical ?? 8;
|
|
1028
|
+
const glassBackground = theme.colors.layer?.float ?? theme.colors.background.primary;
|
|
980
1029
|
const panelStyle = {
|
|
981
|
-
background:
|
|
1030
|
+
background: glassBackground,
|
|
1031
|
+
backdropFilter: "blur(12px)",
|
|
1032
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
1033
|
+
border: `1px solid ${theme.colors.border.secondary}`,
|
|
982
1034
|
borderRadius: radiusVal,
|
|
983
1035
|
boxShadow: shadowVal,
|
|
984
1036
|
width: width ?? sizing.panelWidth,
|
|
@@ -1130,6 +1182,7 @@ var ContextMenu = (props) => {
|
|
|
1130
1182
|
}
|
|
1131
1183
|
const hasStickySearch = !!searchNode;
|
|
1132
1184
|
const prevOpenRef = (0, import_react5.useRef)(false);
|
|
1185
|
+
const skipFocusRestoreRef = (0, import_react5.useRef)(false);
|
|
1133
1186
|
(0, import_react5.useEffect)(() => {
|
|
1134
1187
|
const wasOpen = prevOpenRef.current;
|
|
1135
1188
|
prevOpenRef.current = open;
|
|
@@ -1155,7 +1208,10 @@ var ContextMenu = (props) => {
|
|
|
1155
1208
|
return () => clearTimeout(timer);
|
|
1156
1209
|
}
|
|
1157
1210
|
if (wasOpen && !open) {
|
|
1158
|
-
|
|
1211
|
+
if (!skipFocusRestoreRef.current) {
|
|
1212
|
+
triggerRef.current?.focus();
|
|
1213
|
+
}
|
|
1214
|
+
skipFocusRestoreRef.current = false;
|
|
1159
1215
|
}
|
|
1160
1216
|
}, [open]);
|
|
1161
1217
|
(0, import_react5.useEffect)(() => {
|
|
@@ -1176,6 +1232,7 @@ var ContextMenu = (props) => {
|
|
|
1176
1232
|
}
|
|
1177
1233
|
}
|
|
1178
1234
|
}
|
|
1235
|
+
skipFocusRestoreRef.current = true;
|
|
1179
1236
|
closeMenu();
|
|
1180
1237
|
};
|
|
1181
1238
|
document.addEventListener("mousedown", handlePointerDown);
|
|
@@ -1191,7 +1248,11 @@ var ContextMenu = (props) => {
|
|
|
1191
1248
|
position: "sticky",
|
|
1192
1249
|
top: 0,
|
|
1193
1250
|
zIndex: 1,
|
|
1194
|
-
|
|
1251
|
+
// Match the glass panel so options scrolling underneath blur instead of
|
|
1252
|
+
// showing through the translucent header.
|
|
1253
|
+
background: glassBackground,
|
|
1254
|
+
backdropFilter: "blur(12px)",
|
|
1255
|
+
WebkitBackdropFilter: "blur(12px)"
|
|
1195
1256
|
};
|
|
1196
1257
|
const panel = open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ContextMenuContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1197
1258
|
"div",
|
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 {\n useResolvedTheme,\n useId,\n type ThemeOverrideProps,\n} 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 const menuId = useId();\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 menuId,\n closeMenu,\n registerCell,\n unregisterCell,\n getCellIndex,\n cellsVersion,\n activeIndex,\n setActiveIndex,\n query,\n setQuery,\n }),\n [\n size,\n menuId,\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 // Dismiss on outside press. Per-instance + DOM-guarded so multiple open\n // menus each close only themselves, native builds no-op, and React <18 is\n // unaffected (plain document listener, no concurrent APIs). Only for\n // portal-anchored (trigger) menus; inline/controlled menus stay\n // consumer-managed.\n useEffect(() => {\n if (!open || !usePortal || typeof document === \"undefined\") return;\n const handlePointerDown = (event: MouseEvent) => {\n const target = event.target as Node | null;\n if (!target) return;\n if (panelRef.current?.contains(target)) return;\n if (triggerRef.current?.contains(target)) return;\n // This menu's submenus render in a separate DOM subtree (document.body);\n // treat clicks inside them as inside this menu.\n if (target instanceof Element) {\n const portals = document.querySelectorAll(\n \"[data-xui-context-menu-portal]\"\n );\n for (let i = 0; i < portals.length; i += 1) {\n const portal = portals[i];\n if (\n portal.getAttribute(\"data-xui-context-menu-portal\") === menuId &&\n portal.contains(target)\n ) {\n return;\n }\n }\n }\n closeMenu();\n };\n document.addEventListener(\"mousedown\", handlePointerDown);\n return () => document.removeEventListener(\"mousedown\", handlePointerDown);\n }, [open, usePortal, closeMenu, menuId]);\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 data-xui-context-menu-portal={ctx?.menuId}\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBASO;AACP,IAAAC,oBAA6B;AAC7B,IAAAC,mBAIO;AACP,yBAAwB;;;AChBxB,mBAA0C;AAGnC,IAAM,yBAAqB,4BAEhC,MAAS;AAEJ,IAAM,iBAAiB,MAAM;AAClC,QAAM,cAAU,yBAAW,kBAAkB;AAC7C,SAAO;AACT;AAEO,IAAM,yBAAyB,MAAM;AAC1C,QAAM,cAAU,yBAAW,kBAAkB;AAC7C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACpBA,IAAAC,gBAA4D;AAC5D,uBAA6B;AAC7B,sBAIO;AACP,4BAA2B;AAC3B,0BAAyB;AACzB,uBAAsB;AAqCgB;AAnBtC,IAAM,iBAGF;AAAA,EACF,IAAI,EAAE,OAAO,UAAU,aAAa,UAAU,eAAe,eAAe;AAAA,EAC5E,IAAI,EAAE,OAAO,UAAU,aAAa,UAAU,eAAe,eAAe;AAAA,EAC5E,IAAI,EAAE,OAAO,UAAU,aAAa,UAAU,eAAe,eAAe;AAAA,EAC5E,IAAI,EAAE,OAAO,UAAU,aAAa,UAAU,eAAe,eAAe;AAC9E;AAEA,IAAM,oBAEF;AAAA,EACF,IAAI,EAAE,UAAU,IAAI,YAAY,OAAO;AACzC;AAIO,IAAM,kBAAmC,CAAC,UAAU;AACzD,MAAI,MAAM,SAAS,SAAU,QAAO,4CAAC,cAAY,GAAG,OAAO;AAC3D,MAAI,MAAM,SAAS,UAAW,QAAO,4CAAC,eAAa,GAAG,OAAO;AAC7D,MAAI,MAAM,SAAS,UAAW,QAAO,4CAAC,eAAa,GAAG,OAAO;AAC7D,MAAI,MAAM,SAAS,SAAU,QAAO,4CAAC,cAAY,GAAG,OAAO;AAC3D,SAAO;AACT;AAEA,gBAAgB,cAAc;AAE9B,IAAM,iBAA4D,CAAC;AAAA,EACjE;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA;AAAA,IACC,eAAY;AAAA,IACZ,eAAY;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,OAAM;AAAA,QAEN;AAAA,UAAC;AAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA;AAAA,QACP;AAAA;AAAA,IACF;AAAA;AACF;AAGF,IAAM,aAAwE,CAAC;AAAA,EAC7E,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,MAAM,eAAe;AAC3B,QAAM,OAAwB,YAAY,KAAK,QAAQ;AACvD,QAAM,SAAS,MAAM,OAAO,YAAY,IAAI;AAC5C,QAAM,WAAW,eAAe,IAAI;AAEpC,QAAM,SAAK,uBAAM;AACjB,QAAM,eAAe,KAAK;AAC1B,QAAM,iBAAiB,KAAK;AAC5B,QAAM,eAAe,KAAK;AAC1B,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AACpD,QAAM,CAAC,YAAY,aAAa,QAAI,wBAG1B,IAAI;AACd,QAAM,YAAY,cAAAC,QAAM,OAA8B,IAAI;AAC1D,QAAM,oBAAoB,cAAAA,QAAM,OAA8B,IAAI;AAClE,QAAM,gBAAgB,cAAAA,QAAM;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,cAAc,MAAM;AACxB,QAAI,cAAc,SAAS;AACzB,mBAAa,cAAc,OAAO;AAClC,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,gBAAgB,MAAM;AAC1B,gBAAY;AACZ,kBAAc,UAAU,WAAW,MAAM,eAAe,KAAK,GAAG,GAAG;AAAA,EACrE;AAEA,+BAAU,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvC,+BAAU,MAAM;AACd,QAAI,CAAC,cAAc,CAAC,YAAa;AACjC,UAAM,cAAc,CAAC,MAAkB;AACrC,YAAM,SAAS,EAAE;AACjB,UAAI,CAAC,OAAQ;AACb,YAAM,WAAW,UAAU,SAAS,SAAS,MAAM;AACnD,YAAM,YAAY,kBAAkB,SAAS,SAAS,MAAM;AAC5D,UAAI,CAAC,YAAY,CAAC,WAAW;AAC3B,uBAAe,KAAK;AACpB;AAAA,MACF;AACA,UACE,aACA,OAAO;AAAA,QACL;AAAA,MACF,GACA;AACA,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM,SAAS,oBAAoB,aAAa,WAAW;AAAA,EACpE,GAAG,CAAC,YAAY,WAAW,CAAC;AAE5B,qCAAgB,MAAM;AACpB,QAAI,CAAC,cAAc,CAAC,aAAa;AAC/B,oBAAc,IAAI;AAClB;AAAA,IACF;AACA,UAAM,SAAS,MAAM;AACnB,YAAM,OAAO,UAAU;AACvB,UAAI,CAAC,KAAM;AACX,YAAM,OAAO,KAAK,sBAAsB;AACxC,oBAAc,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,IACnD;AACA,WAAO;AACP,WAAO,iBAAiB,UAAU,QAAQ,IAAI;AAC9C,WAAO,iBAAiB,UAAU,MAAM;AACxC,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,QAAQ,IAAI;AACjD,aAAO,oBAAoB,UAAU,MAAM;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,CAAC;AAC5B,QAAM,cAAc,cAAAA,QAAM,OAAO,QAAQ;AACzC,cAAY,UAAU;AAMtB,+BAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,eAAgB;AACtC,iBAAa,IAAI;AAAA,MACf,MAAM;AAAA,MACN,UAAU,MAAM,YAAY,UAAU;AAAA,IACxC,CAAC;AACD,WAAO,MAAM,eAAe,EAAE;AAAA,EAChC,GAAG,CAAC,cAAc,gBAAgB,EAAE,CAAC;AAGrC,+BAAU,MAAM;AACd,QAAI,CAAC,aAAc;AACnB,iBAAa,IAAI;AAAA,MACf,MAAM;AAAA,MACN;AAAA,MACA,UAAU,MAAM,YAAY,UAAU;AAAA,IACxC,CAAC;AAAA,EACH,GAAG,CAAC,cAAc,IAAI,QAAQ,CAAC;AAI/B,QAAM,QAAQ,eAAe,aAAa,EAAE,IAAI;AAChD,QAAM,WAAW,MAAM,SAAS,KAAK,IAAI,gBAAgB,QAAQ;AACjE,QAAM,eACJ,YAAa,CAAC,OAAO,aAAe,cAAc;AAEpD,QAAM,cAAc,MAAM;AACxB,QAAI,SAAU;AACd,QAAI,OAAO,SAAS,EAAG,KAAI,eAAe,KAAK;AAC/C,QAAI,CAAC,IAAK,cAAa,IAAI;AAC3B,QAAI,WAAY,gBAAe,IAAI;AAAA,EACrC;AAEA,QAAM,cAAc,MAAM;AACxB,QAAI,CAAC,IAAK,cAAa,KAAK;AAC5B,QAAI,WAAY,eAAc;AAAA,EAChC;AAEA,QAAM,aAAa,WACf,MAAM,OAAO,QAAQ,MAAM,cAC3B,cACE,MAAM,OAAO,QAAQ,MAAM,UAC3B,MAAM,OAAO,QAAQ;AAE3B,QAAM,KAAK,eAAe,MAAM,OAAO,QAAQ,MAAM,UAAU;AAE/D,QAAM,OACJ,CAAC,cAAc,YAAY,SAAY,qBAAqB;AAC9D,QAAM,cACJ,CAAC,cAAc,YAAY,SACvB,UACE,SACA,UACF;AAEN,QAAM,cAAc,MAAM;AACxB,QAAI,SAAU;AACd,QAAI,YAAY;AACd,qBAAe,IAAI;AACnB;AAAA,IACF;AACA,eAAW;AAAA,EACb;AAEA,QAAM,uBAAuB,MAAM;AACjC,mBAAe,KAAK;AACpB,cAAU,SAAS,MAAM;AAAA,EAC3B;AAEA,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,UAAU;AACZ,UAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK,GAAE,eAAe;AACzD;AAAA,IACF;AACA,QAAI,YAAY;AACd,UAAI,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,SAAS;AAC/C,UAAE,eAAe;AACjB,UAAE,gBAAgB;AAClB,uBAAe,IAAI;AACnB;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,UAAU;AAC/C,YAAI,aAAa;AACf,YAAE,eAAe;AACjB,YAAE,gBAAgB;AAClB,+BAAqB;AACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,QAAE,eAAe;AACjB,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,gBAAc;AAAA,MACd,iBAAe,WAAW,SAAS;AAAA,MACnC,iBAAe,aAAa,SAAS;AAAA,MACrC,iBAAe,aAAc,cAAc,SAAS,UAAW;AAAA,MAC/D,UAAU;AAAA,MACV,eAAa,UAAU;AAAA,MACvB,cAAY,eAAe,UAAU;AAAA,MACrC,oBAAkB,cAAc,SAAS;AAAA,MACzC,qBAAmB;AAAA,MACnB,cAAc,MAAM;AAClB,oBAAY;AACZ,oBAAY;AAAA,MACd;AAAA,MACA,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ,MAAM;AACZ,YAAI,CAAC,IAAK,cAAa,KAAK;AAAA,MAC9B;AAAA,MACA,SAAS;AAAA,MACT,WAAW;AAAA,MACX,OAAO;AAAA,QACL,UAAU;AAAA,QACV,SAAS;AAAA,QACT,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,YAAY,OAAO;AAAA,QACnB,eAAe,OAAO;AAAA,QACtB,iBAAiB;AAAA,QACjB,QAAQ,WAAW,gBAAgB;AAAA,QACnC,SAAS;AAAA,MACX;AAAA,MAEC;AAAA,2BAAmB,cAClB;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,eAAY;AAAA,YACZ,OAAO,EAAE,eAAe,QAAQ,SAAS,cAAc;AAAA,YAEvD;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA,SAAS,CAAC,CAAC;AAAA,gBACX,UAAU,CAAC,CAAC;AAAA,gBACZ;AAAA,gBACA;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QAED,mBAAmB,WAClB;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,eAAY;AAAA,YACZ,OAAO,EAAE,eAAe,QAAQ,SAAS,cAAc;AAAA,YAEvD;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA,SAAS,CAAC,CAAC;AAAA,gBACX,UAAU,CAAC,CAAC;AAAA,gBACZ;AAAA,gBACA;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA,QAED;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,cACT,eAAe;AAAA,cACf,KAAK;AAAA,cACL,UAAU;AAAA,YACZ;AAAA,YAEA;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,SAAS;AAAA,kBAClB,OAAO;AAAA,kBACP,QAAQ,gBAAgB;AAAA,kBACxB,OAAO;AAAA,oBACL,GAAI,gBAAgB,SAChB,EAAE,SAAS,SAAS,UAAU,EAAE,IAChC,CAAC;AAAA,oBACL,GAAI,kBAAkB,IAAI,KAAK,CAAC;AAAA,kBAClC;AAAA,kBAEC;AAAA;AAAA,cACH;AAAA,cACC,gBAAgB,UACf;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,SAAS;AAAA,kBAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,kBAE3B;AAAA;AAAA,cACH;AAAA;AAAA;AAAA,QAEJ;AAAA,SACE,UAAU,UAAa,SAAS,WAChC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,SAAS;AAAA,cACT,eAAe;AAAA,cACf,YAAY;AAAA,YACd;AAAA,YAEC;AAAA,wBAAU,UACT;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,SAAS;AAAA,kBAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,kBAC5B,OAAO,kBAAkB,IAAI;AAAA,kBAE5B;AAAA;AAAA,cACH;AAAA,cAED,SAAS,UACR;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,SAAS;AAAA,kBAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,kBAE3B;AAAA;AAAA,cACH;AAAA;AAAA;AAAA,QAEJ;AAAA,QAED,oBACC;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,SAAS,SAAS;AAAA,YAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,YAE3B;AAAA;AAAA,QACH;AAAA,QAED,cACC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,MAAM,OAAO,QAAQ;AAAA,YAC5B,MAAM,OAAO;AAAA;AAAA,QACf;AAAA,QAED;AAAA,QACA,cACC,eACA,WACA,cACA,OAAO,aAAa,mBACpB;AAAA,UACE;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,gCAA8B,KAAK;AAAA,cACnC,cAAc;AAAA,cACd,cAAc;AAAA,cACd,OAAO;AAAA,gBACL,UAAU;AAAA,gBACV,KAAK,WAAW;AAAA,gBAChB,MAAM,WAAW;AAAA,gBACjB,QAAQ;AAAA,cACV;AAAA,cAEC;AAAA;AAAA,UACH;AAAA,UACA,SAAS;AAAA,QACX;AAAA;AAAA;AAAA,EACJ;AAEJ;AAEA,IAAM,cAEF,CAAC;AAAA,EACH,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,MAAM,eAAe;AAC3B,QAAM,OAAwB,YAAY,KAAK,QAAQ;AACvD,QAAM,SAAS,MAAM,OAAO,YAAY,IAAI;AAC5C,QAAM,WAAW,eAAe,IAAI;AAEpC,QAAM,SAAK,uBAAM;AACjB,QAAM,eAAe,KAAK;AAC1B,QAAM,iBAAiB,KAAK;AAC5B,+BAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,eAAgB;AACtC,iBAAa,IAAI,EAAE,MAAM,UAAU,CAAC;AACpC,WAAO,MAAM,eAAe,EAAE;AAAA,EAChC,GAAG,CAAC,cAAc,gBAAgB,EAAE,CAAC;AAErC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,eAAa,UAAU;AAAA,MACvB,OAAO;AAAA,QACL,SAAS;AAAA,QACT,eAAe;AAAA,QACf,KAAK;AAAA,QACL,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,YAAY,OAAO;AAAA,QACnB,eAAe,OAAO;AAAA,MACxB;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,SAAS;AAAA,YAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,YAC5B,OAAO,EAAE,eAAe,aAAa,eAAe,IAAI;AAAA,YAEvD;AAAA;AAAA,QACH;AAAA,QACC,gBAAgB,UACf;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,SAAS;AAAA,YAClB,OAAO,MAAM,OAAO,QAAQ;AAAA,YAE3B;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,IAAM,aAAwE,CAAC;AAAA,EAC7E,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,cAAc,YAAY;AAAA,EAC1B,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,MAAM,eAAe;AAC3B,QAAM,OAAwB,YAAY,KAAK,QAAQ;AACvD,QAAM,SAAS,MAAM,OAAO,YAAY,IAAI;AAE5C,QAAM,SAAK,uBAAM;AACjB,QAAM,eAAe,KAAK;AAC1B,QAAM,iBAAiB,KAAK;AAC5B,+BAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,eAAgB;AACtC,iBAAa,IAAI,EAAE,MAAM,SAAS,CAAC;AACnC,WAAO,MAAM,eAAe,EAAE;AAAA,EAChC,GAAG,CAAC,cAAc,gBAAgB,EAAE,CAAC;AAErC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA,MAC7C,eAAa,UAAU;AAAA,MACvB,OAAO;AAAA,QACL,OAAO;AAAA,QACP,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,OAAO,MAAM,OAAO,QAAQ;AAAA,QAC5B,UAAU,OAAO;AAAA,QACjB,YAAY,GAAG,OAAO,WAAW,CAAC;AAAA,QAClC,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,YAAY,OAAO;AAAA,QACnB,eAAe,OAAO;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;AAEA,IAAM,cAEF,CAAC,EAAE,WAAW,qBAAqB,eAAe,OAAO,MAAM;AACjE,QAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,MAAM,eAAe;AAC3B,QAAM,SAAK,uBAAM;AACjB,QAAM,eAAe,KAAK;AAC1B,QAAM,iBAAiB,KAAK;AAC5B,+BAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,eAAgB;AACtC,iBAAa,IAAI,EAAE,MAAM,UAAU,CAAC;AACpC,WAAO,MAAM,eAAe,EAAE;AAAA,EAChC,GAAG,CAAC,cAAc,gBAAgB,EAAE,CAAC;AACrC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,eAAa;AAAA,MACb,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,iBAAiB,MAAM,OAAO,OAAO;AAAA,QACrC,QAAQ;AAAA,MACV;AAAA;AAAA,EACF;AAEJ;;;ACzlBA,IAAAC,gBAAoD;AAiBpD,IAAM,iBAAiB,CACrB,cACgE;AAChE,QAAM,CAAC,UAAU,UAAU,IAAI,UAAU,MAAM,GAAG;AAIlD,SAAO,EAAE,UAAU,WAAW;AAChC;AAEA,IAAM,gBAAgB,CACpB,UACA,eACyB,GAAG,QAAQ,IAAI,UAAU;AAE7C,IAAM,yBAAyB,CAAC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,SAAS;AACX,MAAmE;AACjE,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAuC;AAEvE,+BAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,kBAAY,MAAS;AACrB;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,YAAM,UAAU,WAAW;AAC3B,YAAM,QAAQ,SAAS;AACvB,UAAI,CAAC,WAAW,CAAC,MAAO;AAExB,YAAM,cAAc,QAAQ,sBAAsB;AAClD,YAAM,YAAY,MAAM,sBAAsB;AAC9C,YAAM,gBAAgB,OAAO;AAC7B,YAAM,iBAAiB,OAAO;AAE9B,UAAI,EAAE,UAAU,WAAW,IAAI,eAAe,SAAS;AAEvD,YAAM,aAAa,CAAC,MAClB,MAAM,WACF,YAAY,SAAS,SACrB,YAAY,MAAM,UAAU,SAAS;AAE3C,YAAM,cAAc,CAAC,MACnB,MAAM,UAAU,YAAY,OAAO,YAAY,QAAQ,UAAU;AAEnE,UAAI,MAAM,WAAW,QAAQ;AAC7B,YAAM,eAAe,MAAM,UAAU;AACrC,UAAI,MAAM,KAAK,eAAe,gBAAgB;AAC5C,cAAM,UAAU,aAAa,WAAW,QAAQ;AAChD,cAAM,aAAa,WAAW,OAAO;AACrC,cAAM,gBAAgB,aAAa,UAAU;AAC7C,YAAI,cAAc,KAAK,iBAAiB,gBAAgB;AACtD,qBAAW;AACX,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,OAAO,YAAY,UAAU;AACjC,YAAM,cAAc,OAAO,UAAU;AACrC,UAAI,OAAO,KAAK,cAAc,eAAe;AAC3C,cAAM,UAAU,eAAe,UAAU,QAAQ;AACjD,cAAM,cAAc,YAAY,OAAO;AACvC,cAAM,eAAe,cAAc,UAAU;AAC7C,YAAI,eAAe,KAAK,gBAAgB,eAAe;AACrD,uBAAa;AACb,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,kBAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA,WAAW,cAAc,UAAU,UAAU;AAAA,MAC/C,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,OAAO,sBAAsB,OAAO;AAClD,UAAM,WAAW,MAAM,QAAQ;AAC/B,WAAO,iBAAiB,UAAU,QAAQ;AAE1C,WAAO,MAAM;AACX,aAAO,qBAAqB,KAAK;AACjC,aAAO,oBAAoB,UAAU,QAAQ;AAAA,IAC/C;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,QAAQ,YAAY,QAAQ,CAAC;AAEpD,SAAO;AACT;;;AC7GA,IAAAC,gBAA4C;AAmB5C,IAAM,oBAAoB,CAAC,SACzB,KAAK,SAAS,YAAY,CAAC,KAAK;AAElC,IAAM,oBAAoB,CAAC,WAAwC;AACjE,MAAI,EAAE,kBAAkB,aAAc,QAAO;AAC7C,MAAI,OAAO,YAAY,WAAW,OAAO,YAAY,WAAY,QAAO;AACxE,SAAO,OAAO,aAAa,MAAM,MAAM;AACzC;AAEO,IAAM,wBAAwB,CAAC;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAoC;AAClC,QAAM,sBAAkB,2BAAY,MAAM;AACxC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,UAAI,kBAAkB,MAAM,CAAC,EAAE,IAAI,EAAG,QAAO;AAAA,IAC/C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,qBAAiB,2BAAY,MAAM;AACvC,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC7C,UAAI,kBAAkB,MAAM,CAAC,EAAE,IAAI,EAAG,QAAO;AAAA,IAC/C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,qBAAiB;AAAA,IACrB,CAAC,SAAiB;AAChB,YAAM,MAAM,MAAM;AAClB,UAAI,QAAQ,EAAG,QAAO;AACtB,eAAS,OAAO,GAAG,QAAQ,KAAK,QAAQ,GAAG;AACzC,cAAM,OAAO,OAAO,OAAO,OAAO;AAClC,YAAI,kBAAkB,MAAM,GAAG,EAAE,IAAI,EAAG,QAAO;AAAA,MACjD;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,qBAAiB;AAAA,IACrB,CAAC,SAAiB;AAChB,YAAM,MAAM,MAAM;AAClB,UAAI,QAAQ,EAAG,QAAO;AACtB,eAAS,OAAO,GAAG,QAAQ,KAAK,QAAQ,GAAG;AACzC,cAAM,OAAO,OAAO,OAAO,MAAM,KAAK;AACtC,YAAI,kBAAkB,MAAM,GAAG,EAAE,IAAI,EAAG,QAAO;AAAA,MACjD;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,oBAAgB;AAAA,IACpB,CAAC,UAA+B;AAC9B,UAAI,CAAC,OAAQ;AAEb,cAAQ,MAAM,KAAK;AAAA,QACjB,KAAK,aAAa;AAChB,gBAAM,eAAe;AACrB,gBAAM,OACJ,cAAc,IAAI,gBAAgB,IAAI,eAAe,WAAW;AAClE,cAAI,QAAQ,EAAG,gBAAe,IAAI;AAClC;AAAA,QACF;AAAA,QACA,KAAK,WAAW;AACd,gBAAM,eAAe;AACrB,gBAAM,OACJ,cAAc,IAAI,eAAe,IAAI,eAAe,WAAW;AACjE,cAAI,QAAQ,EAAG,gBAAe,IAAI;AAClC;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,gBAAM,eAAe;AACrB,gBAAM,QAAQ,gBAAgB;AAC9B,cAAI,SAAS,EAAG,gBAAe,KAAK;AACpC;AAAA,QACF;AAAA,QACA,KAAK,OAAO;AACV,gBAAM,eAAe;AACrB,gBAAM,OAAO,eAAe;AAC5B,cAAI,QAAQ,EAAG,gBAAe,IAAI;AAClC;AAAA,QACF;AAAA,QACA,KAAK;AAAA,QACL,KAAK,KAAK;AACR,cAAI,MAAM,iBAAkB;AAC5B,cAAI,MAAM,QAAQ,OAAO,kBAAkB,MAAM,MAAM,EAAG;AAC1D,cAAI,MAAM,QAAQ,IAAK,OAAM,eAAe;AAC5C,cAAI,eAAe,KAAK,cAAc,MAAM,QAAQ;AAClD,kBAAM,OAAO,MAAM,WAAW,EAAE;AAChC,gBAAI,kBAAkB,IAAI,EAAG,MAAK,WAAW;AAAA,UAC/C;AACA;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,eAAe;AACrB,kBAAQ;AACR,sBAAY,SAAS,MAAM;AAC3B;AAAA,QACF;AAAA,QACA,KAAK,OAAO;AACV,kBAAQ;AACR;AAAA,QACF;AAAA,QACA;AACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,cAAc;AACzB;;;AJlGE,IAAAC,sBAAA;AANF,IAAM,qBAAqB;AAE3B,IAAM,eAAuE,CAAC;AAAA,EAC5E;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA;AAAA,IACC,OAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA,IAEC;AAAA;AACH;AAGK,IAAM,cAA+B,CAAC,UAAU;AACrD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,cAAc;AAAA,IACd,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,EAAE,MAAM,QAAI,mCAAiB,EAAE,WAAW,oBAAoB,CAAC;AAErE,QAAM,eAAe,WAAW;AAChC,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,KAAK;AACtD,QAAM,OAAO,eAAe,CAAC,CAAC,SAAS;AAEvC,QAAM,cAAU;AAAA,IACd,CAAC,SAAkB;AACjB,UAAI,CAAC,aAAc,iBAAgB,IAAI;AACvC,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,cAAc,YAAY;AAAA,EAC7B;AAEA,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAiB,EAAE;AACzD,QAAM,eAAW,sBAAoB,CAAC,CAAC;AACvC,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,CAAC;AAElD,QAAM,iBAAa,sBAA2B,IAAI;AAClD,QAAM,eAAW,sBAA8B,IAAI;AACnD,QAAM,aAAS,wBAAM;AAGrB,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,wBAAS,EAAE;AACvD,QAAM,uBAAmB,sBAA6C,IAAI;AAE1E,+BAAU,MAAM;AACd,QAAI,iBAAiB,QAAS,cAAa,iBAAiB,OAAO;AACnE,qBAAiB,UAAU,WAAW,MAAM;AAC1C,wBAAkB,KAAK;AAAA,IACzB,GAAG,kBAAkB;AACrB,WAAO,MAAM;AACX,UAAI,iBAAiB,QAAS,cAAa,iBAAiB,OAAO;AAAA,IACrE;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,gBAAY,2BAAY,MAAM;AAClC,YAAQ,KAAK;AACb,mBAAe,EAAE;AAAA,EACnB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,mBAAe,2BAAY,CAAC,IAAY,SAA8B;AAC1E,UAAM,WAAW,SAAS,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAC9D,QAAI,aAAa,IAAI;AACnB,eAAS,QAAQ,KAAK,EAAE,IAAI,KAAK,CAAC;AAClC,sBAAgB,CAAC,MAAM,IAAI,CAAC;AAC5B,aAAO,SAAS,QAAQ,SAAS;AAAA,IACnC;AAGA,UAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE;AACxC,aAAS,QAAQ,QAAQ,IAAI,EAAE,IAAI,KAAK;AACxC,QAAI,KAAK,aAAa,KAAK,YAAY,KAAK,SAAS,KAAK,MAAM;AAC9D,sBAAgB,CAAC,MAAM,IAAI,CAAC;AAAA,IAC9B;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,qBAAiB,2BAAY,CAAC,OAAe;AACjD,UAAM,MAAM,SAAS,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACzD,QAAI,QAAQ,IAAI;AAGd,eAAS,QAAQ,OAAO,KAAK,CAAC;AAC9B,sBAAgB,CAAC,MAAM,IAAI,CAAC;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe;AAAA,IACnB,CAAC,OAAe,SAAS,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,IAC7D,CAAC;AAAA,EACH;AAEA,QAAM,UAA+B;AAAA,IACnC,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAc,uBAAQ,MAAM;AAChC,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,YAAQ,8BAAe,OAAO,QAChC;AAAA,MACE;AAAA,MAIA;AAAA,QACE,iBAAiB;AAAA,QACjB,iBAAiB,OAAO,SAAS;AAAA,MACnC;AAAA,IACF,IACA;AACJ,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,CAAC,SAAS;AACb,cAAI,CAAC,MAAM;AACT,uBAAW,UAAU;AACrB;AAAA,UACF;AACA,gBAAM,YAAY,KAAK;AAAA,YACrB;AAAA,UACF;AACA,qBAAW,UAAU,aAAa;AAAA,QACpC;AAAA,QACA,SAAS,MAAM,QAAQ,CAAC,IAAI;AAAA,QAC5B,OAAO,EAAE,SAAS,cAAc;AAAA,QAE/B;AAAA;AAAA,IACH;AAAA,EAEJ,GAAG,CAAC,SAAS,MAAM,OAAO,CAAC;AAE3B,QAAM,YAAY,CAAC,CAAC,WAAW,OAAO,aAAa;AAEnD,QAAM,WAAW,uBAAuB;AAAA,IACtC;AAAA,IACA;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB;AAAA,EACF,CAAC;AAED,QAAM,kBAAc;AAAA,IAClB,MAAM,SAAS,QAAQ,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,KAAK,EAAE;AAAA;AAAA,IAE9D,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,EAAE,cAAc,IAAI,sBAAsB;AAAA,IAC9C,QAAQ;AAAA,IACR,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF,CAAC;AAED,QAAM,WAAY,MAAM,OACrB;AACH,QAAM,SAAS,WACV,SAAS,IAAI,IAKd,CAAC;AACL,QAAM,YAAa,MAAgD;AACnE,QAAM,YAAY,OAAO,gBAAgB,WAAW,eAAe;AACnE,QAAM,YAAa,MAAgD;AACnE,QAAM,YAAY,WAAW,eAAe;AAC5C,QAAM,uBAAuB,OAAO,mBAAmB;AAEvD,QAAM,aAAkC;AAAA,IACtC,YAAY,MAAM,OAAO,WAAW;AAAA,IACpC,cAAc;AAAA,IACd,WAAW;AAAA,IACX,OAAO,SAAS,OAAO;AAAA,IACvB;AAAA,IACA,UAAU;AAAA,IACV,SAAS,OAAO,SAAS;AAAA,IACzB,eAAe;AAAA,IACf,SAAS;AAAA,IACT,YAAY,MAAM,MAAM;AAAA,IACxB,YAAY;AAAA,IACZ,eAAe;AAAA,EACjB;AAEA,MAAI,WAAW;AACb,eAAW,WAAW;AACtB,eAAW,MAAM,UAAU,OAAO;AAClC,eAAW,OAAO,UAAU,QAAQ;AAAA,EACtC;AAGA,QAAM,oBAAgB,uBAAkC,MAAM;AAC5D,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,CAAC,cAAc,CAAC,eAAgB,QAAO,MAAM,MAAM;AACvD,UAAM,IAAI,eAAe,YAAY;AACrC,UAAM,eAAe,MAAM,IAAI,CAAC,SAAS;AACvC,UAAI,KAAK,SAAS,UAAU;AAC1B,eAAO,OAAO,KAAK,SAAS,EAAE,EAC3B,YAAY,EACZ,SAAS,CAAC;AAAA,MACf;AACA,aAAO;AAAA,IACT,CAAC;AAED,UAAM,SAAuB,CAAC;AAC9B,QAAI,iBAGO;AACX,QAAI,wBAAwB;AAC5B,QAAI,iBAAiB;AACrB,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,KAAK,SAAS,WAAW;AAE3B,yBAAiB,EAAE,MAAM,KAAK,EAAE;AAChC,yBAAiB;AAAA,MACnB,WAAW,KAAK,SAAS,WAAW;AAElC,YAAI,uBAAuB;AACzB,iBAAO,KAAK,IAAI;AAChB,kCAAwB;AAAA,QAC1B;AAEA,yBAAiB;AACjB,yBAAiB;AAAA,MACnB,WAAW,KAAK,SAAS,UAAU;AACjC,YAAI,aAAa,CAAC,GAAG;AACnB,cAAI,gBAAgB;AAClB,mBAAO,KAAK,eAAe,IAAI;AAC/B,6BAAiB;AAAA,UACnB;AACA,iBAAO,KAAK,IAAI;AAChB,kCAAwB;AACxB,2BAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,OAAO,SAAS,KAAK,OAAO,OAAO,SAAS,CAAC,EAAE,SAAS,WAAW;AACxE,aAAO,IAAI;AAAA,IACb;AAEA,SAAK;AACL,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,YAAY,cAAc,CAAC;AAEtC,QAAM,yBACJ,kBAAkB,SAAY,gBAAgB,SAAS;AAEzD,QAAM,mBAAmB,CAAC,MAAkB,QAAgB;AAC1D,QAAI,KAAK,SAAS,WAAW;AAC3B,aACE;AAAA,QAAC;AAAA;AAAA,UAEE,GAAG;AAAA,UACJ,MAAM,KAAK,QAAS;AAAA;AAAA,QAFf,KAAK,GAAG;AAAA,MAGf;AAAA,IAEJ;AACA,QAAI,KAAK,SAAS,WAAW;AAC3B,aACE;AAAA,QAAC;AAAA;AAAA,UAEE,GAAG;AAAA,UACJ,MAAM,KAAK,QAAS;AAAA;AAAA,QAFf,KAAK,GAAG;AAAA,MAGf;AAAA,IAEJ;AACA,UAAM,WAAW,qBAAqB,MAAM,IAAI;AAChD,UAAM,iBAAiB,SAAS;AAChC,UAAM,gBAAgB,MAAM;AAC1B,uBAAiB;AACjB,iBAAW,IAAI;AACf,UAAI,uBAAwB,WAAU;AAAA,IACxC;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QAEE,GAAG;AAAA,QACJ,MAAM,SAAS,QAAS;AAAA,QACxB,UAAU;AAAA;AAAA,MAHL,KAAK,GAAG;AAAA,IAIf;AAAA,EAEJ;AAGA,QAAM,iBAAiB;AAEvB,MAAI,cAA+B;AACnC,MAAI,cAAc;AAClB,MAAI,aAA8B;AAElC,MAAI,gBAAgB;AAClB,kBACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,gBAAgB;AAAA,UAChB,SAAS;AAAA,QACX;AAAA,QAEA,uDAAC,8BAAQ,MAAM,SAAS,OAAO,OAAO,SAAS,OAAO,OAAO,MAAM;AAAA;AAAA,IACrE;AAAA,EAEJ,WAAW,aAAa,UAAa,aAAa,MAAM;AAEtD,UAAM,WAAW,cAAAC,QAAM,SAAS,QAAQ,QAAQ;AAChD,QAAI,SAAS,WAAW,GAAG;AACzB,oBAAc;AAAA,IAChB,OAAO;AACL,oBAAc;AAAA,IAChB;AAAA,EACF,WAAW,QAAQ,OAAO;AAExB,QAAI,YAAY;AACd,mBACE;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,eAAe;AAAA,UACf;AAAA;AAAA,MACF;AAAA,IAEJ;AACA,UAAM,UAAU,iBAAiB,CAAC;AAClC,UAAM,cAAc,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE;AAC/D,QAAI,gBAAgB,GAAG;AACrB,oBAAc;AAAA,IAChB,OAAO;AACL,oBAAc,QAAQ,IAAI,CAAC,IAAI,QAAQ,iBAAiB,IAAI,GAAG,CAAC;AAAA,IAClE;AAAA,EACF,OAAO;AAEL,kBAAc;AAAA,EAChB;AAEA,MAAI,aAAa;AACf,kBAAc,SACZ,6CAAC,gBAAa,OAAO,MAAM,OAAO,QAAQ,UACvC,0BAAgB,cACnB;AAAA,EAEJ;AAEA,QAAM,kBAAkB,CAAC,CAAC;AAG1B,QAAM,kBAAc,sBAAO,KAAK;AAChC,+BAAU,MAAM;AACd,UAAM,UAAU,YAAY;AAC5B,gBAAY,UAAU;AACtB,QAAI,CAAC,WAAW,MAAM;AAGpB,YAAM,QAAQ,WAAW,MAAM;AAC7B,cAAMC,SAAQ,SAAS;AACvB,YAAI,CAACA,OAAO;AACZ,cAAM,SACJA,OAAM,cAAgC,oBAAoB;AAC5D,YAAI,QAAQ;AACV,iBAAO,MAAM;AACb;AAAA,QACF;AACA,cAAM,cAAcA,OAAM;AAAA,UACxB;AAAA,QACF;AACA,YAAI,aAAa;AACf,sBAAY,MAAM;AAIlB,yBAAe,EAAE;AAAA,QACnB,OAAO;AACL,UAAAA,OAAM,MAAM;AAAA,QACd;AAAA,MACF,GAAG,CAAC;AACJ,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC;AACA,QAAI,WAAW,CAAC,MAAM;AAEpB,iBAAW,SAAS,MAAM;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAOT,+BAAU,MAAM;AACd,QAAI,CAAC,QAAQ,CAAC,aAAa,OAAO,aAAa,YAAa;AAC5D,UAAM,oBAAoB,CAAC,UAAsB;AAC/C,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AACb,UAAI,SAAS,SAAS,SAAS,MAAM,EAAG;AACxC,UAAI,WAAW,SAAS,SAAS,MAAM,EAAG;AAG1C,UAAI,kBAAkB,SAAS;AAC7B,cAAM,UAAU,SAAS;AAAA,UACvB;AAAA,QACF;AACA,iBAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,gBAAM,SAAS,QAAQ,CAAC;AACxB,cACE,OAAO,aAAa,8BAA8B,MAAM,UACxD,OAAO,SAAS,MAAM,GACtB;AACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,gBAAU;AAAA,IACZ;AACA,aAAS,iBAAiB,aAAa,iBAAiB;AACxD,WAAO,MAAM,SAAS,oBAAoB,aAAa,iBAAiB;AAAA,EAC1E,GAAG,CAAC,MAAM,WAAW,WAAW,MAAM,CAAC;AAEvC,QAAM,oBAAoB,UAAU,aAAa;AAEjD,QAAM,uBAA4C;AAAA,IAChD,WAAW;AAAA,IACX,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAEA,QAAM,oBAAyC;AAAA,IAC7C,UAAU;AAAA,IACV,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,YAAY,MAAM,OAAO,WAAW;AAAA,EACtC;AAEA,QAAM,QAAQ,OACZ,6CAAC,mBAAmB,UAAnB,EAA4B,OAAO,KAClC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,eAAa,UAAU;AAAA,MACvB,kBAAgB,YAAY,oBAAoB;AAAA,MAChD,UAAU;AAAA,MACV,WAAW;AAAA,MACX,cAAc,MAAM,eAAe,EAAE;AAAA,MACrC,OAAO;AAAA,MAEN;AAAA,2BACC,6CAAC,SAAI,eAAY,OAAM,OAAO,mBAC3B,sBACH;AAAA,QAEF,6CAAC,SAAI,OAAO,sBAAuB,uBAAY;AAAA;AAAA;AAAA,EACjD,GACF,IACE;AAEJ,SACE,8EACG;AAAA;AAAA,IACA,YAAY,aAAS,gCAAa,OAAO,SAAS,IAAI,IAAI;AAAA,KAC7D;AAEJ;AAEA,YAAY,cAAc;AAE1B,SAAS,qBACP,MACA,MAC4B;AAC5B,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,gBAAgB,WAAW;AAAA,IAC/C,KAAK;AACH,aAAO,EAAE,GAAG,MAAM,gBAAgB,QAAQ;AAAA,IAC5C,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AACE,aAAO,EAAE,GAAG,KAAK;AAAA,EACrB;AACF;","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 {\n useResolvedTheme,\n useId,\n type ThemeOverrideProps,\n} 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 const menuId = useId();\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 menuId,\n closeMenu,\n registerCell,\n unregisterCell,\n getCellIndex,\n cellsVersion,\n activeIndex,\n setActiveIndex,\n query,\n setQuery,\n }),\n [\n size,\n menuId,\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?: { popover?: string } }).shadow;\n const shadowVal = shadowObj?.popover ?? \"\";\n const panelPaddingVertical = sizing.paddingVertical ?? 8;\n\n // Figma ContextMenu panel is \"glass\": semi-transparent layer/float fill\n // with a backdrop blur (Figma BACKGROUND_BLUR 24 = CSS blur(12px)).\n const glassBackground =\n theme.colors.layer?.float ?? theme.colors.background.primary;\n\n const panelStyle: React.CSSProperties = {\n background: glassBackground,\n backdropFilter: \"blur(12px)\",\n WebkitBackdropFilter: \"blur(12px)\",\n border: `1px solid ${theme.colors.border.secondary}`,\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 const skipFocusRestoreRef = 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 — unless the menu was dismissed\n // by clicking outside. Stealing focus back then would scroll the\n // trigger into view while the user is interacting elsewhere.\n if (!skipFocusRestoreRef.current) {\n triggerRef.current?.focus();\n }\n skipFocusRestoreRef.current = false;\n }\n }, [open]);\n\n // Dismiss on outside press. Per-instance + DOM-guarded so multiple open\n // menus each close only themselves, native builds no-op, and React <18 is\n // unaffected (plain document listener, no concurrent APIs). Only for\n // portal-anchored (trigger) menus; inline/controlled menus stay\n // consumer-managed.\n useEffect(() => {\n if (!open || !usePortal || typeof document === \"undefined\") return;\n const handlePointerDown = (event: MouseEvent) => {\n const target = event.target as Node | null;\n if (!target) return;\n if (panelRef.current?.contains(target)) return;\n if (triggerRef.current?.contains(target)) return;\n // This menu's submenus render in a separate DOM subtree (document.body);\n // treat clicks inside them as inside this menu.\n if (target instanceof Element) {\n const portals = document.querySelectorAll(\n \"[data-xui-context-menu-portal]\"\n );\n for (let i = 0; i < portals.length; i += 1) {\n const portal = portals[i];\n if (\n portal.getAttribute(\"data-xui-context-menu-portal\") === menuId &&\n portal.contains(target)\n ) {\n return;\n }\n }\n }\n skipFocusRestoreRef.current = true;\n closeMenu();\n };\n document.addEventListener(\"mousedown\", handlePointerDown);\n return () => document.removeEventListener(\"mousedown\", handlePointerDown);\n }, [open, usePortal, closeMenu, menuId]);\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 // Match the glass panel so options scrolling underneath blur instead of\n // showing through the translucent header.\n background: glassBackground,\n backdropFilter: \"blur(12px)\",\n WebkitBackdropFilter: \"blur(12px)\",\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 { Search } from \"@xsolla/xui-icons-base\";\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 data-xui-context-menu-portal={ctx?.menuId}\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 <div\n style={{\n boxSizing: \"border-box\",\n paddingLeft: sizing.searchPaddingHorizontal,\n paddingRight: sizing.searchPaddingHorizontal,\n paddingTop: sizing.searchPaddingVertical,\n paddingBottom: sizing.searchPaddingVertical,\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n paddingBottom: 6,\n borderBottom: `1px solid ${theme.colors.border.secondary}`,\n }}\n >\n <Search\n variant=\"line\"\n size={sizing.searchIconSize}\n color={theme.colors.control.input.placeholder}\n aria-hidden\n />\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 flex: 1,\n minWidth: 0,\n boxSizing: \"border-box\",\n border: \"none\",\n outline: \"none\",\n background: \"transparent\",\n padding: 0,\n color: theme.colors.content.primary,\n fontSize: sizing.searchFontSize,\n lineHeight: `${sizing.searchLineHeight}px`,\n }}\n />\n </div>\n </div>\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 const next = {\n top,\n left,\n placement: joinPlacement(vertical, horizontal),\n };\n setResolved((prev) =>\n prev &&\n prev.top === next.top &&\n prev.left === next.left &&\n prev.placement === next.placement\n ? prev\n : next\n );\n };\n\n let rafId = window.requestAnimationFrame(compute);\n const onResize = () => compute();\n // Recompute on scroll so the panel follows its trigger. Capture phase\n // catches scrolling inside nested containers, not just the window.\n // rAF-coalesced: at most one compute per frame.\n let scrollRafPending = false;\n const onScroll = () => {\n if (scrollRafPending) return;\n scrollRafPending = true;\n rafId = window.requestAnimationFrame(() => {\n scrollRafPending = false;\n compute();\n });\n };\n window.addEventListener(\"resize\", onResize);\n window.addEventListener(\"scroll\", onScroll, true);\n\n return () => {\n window.cancelAnimationFrame(rafId);\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"scroll\", onScroll, true);\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"]}
|