@bazza-ui/react 0.0.0 → 0.1.0-canary.2
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/dist/ListboxStore-BtcTXpzi.d.cts +351 -0
- package/dist/ListboxStore-DPqpLlAL.d.ts +351 -0
- package/dist/adapters/index.cjs +2 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +363 -0
- package/dist/adapters/index.d.ts +363 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/chunk-4C666HHU.js +2 -0
- package/dist/chunk-4C666HHU.js.map +1 -0
- package/dist/chunk-AVZ64JQ3.js +2 -0
- package/dist/chunk-AVZ64JQ3.js.map +1 -0
- package/dist/chunk-BGJJC6GX.cjs +2 -0
- package/dist/chunk-BGJJC6GX.cjs.map +1 -0
- package/dist/chunk-FWWOE2SW.cjs +2 -0
- package/dist/chunk-FWWOE2SW.cjs.map +1 -0
- package/dist/chunk-JSPKF52O.cjs +2 -0
- package/dist/chunk-JSPKF52O.cjs.map +1 -0
- package/dist/chunk-KWGD24VS.js +2 -0
- package/dist/chunk-KWGD24VS.js.map +1 -0
- package/dist/chunk-M4G6J7DP.cjs +2 -0
- package/dist/chunk-M4G6J7DP.cjs.map +1 -0
- package/dist/chunk-WKAPAKUL.js +2 -0
- package/dist/chunk-WKAPAKUL.js.map +1 -0
- package/dist/combobox/index.cjs +2 -0
- package/dist/combobox/index.cjs.map +1 -0
- package/dist/combobox/index.d.cts +1039 -0
- package/dist/combobox/index.d.ts +1039 -0
- package/dist/combobox/index.js +2 -0
- package/dist/combobox/index.js.map +1 -0
- package/dist/command-score-Dgo3ZS3Z.d.ts +36 -0
- package/dist/command-score-YjNr3ZWi.d.cts +36 -0
- package/dist/context-menu/index.cjs +2 -0
- package/dist/context-menu/index.cjs.map +1 -0
- package/dist/context-menu/index.d.cts +658 -0
- package/dist/context-menu/index.d.ts +658 -0
- package/dist/context-menu/index.js +2 -0
- package/dist/context-menu/index.js.map +1 -0
- package/dist/data-surface-B-eIGTBi.d.cts +678 -0
- package/dist/data-surface-D1OilMDu.d.ts +678 -0
- package/dist/dropdown-menu/index.cjs +2 -0
- package/dist/dropdown-menu/index.cjs.map +1 -0
- package/dist/dropdown-menu/index.d.cts +700 -0
- package/dist/dropdown-menu/index.d.ts +700 -0
- package/dist/dropdown-menu/index.js +2 -0
- package/dist/dropdown-menu/index.js.map +1 -0
- package/dist/events-BPr8sRGH.d.cts +166 -0
- package/dist/events-BPr8sRGH.d.ts +166 -0
- package/dist/input-BoIK003I.d.cts +41 -0
- package/dist/input-DF7D8YzW.d.ts +41 -0
- package/dist/internal/listbox/index.cjs +2 -0
- package/dist/internal/listbox/index.cjs.map +1 -0
- package/dist/internal/listbox/index.d.cts +269 -0
- package/dist/internal/listbox/index.d.ts +269 -0
- package/dist/internal/listbox/index.js +2 -0
- package/dist/internal/listbox/index.js.map +1 -0
- package/dist/internal/popup-menu/index.cjs +2 -0
- package/dist/internal/popup-menu/index.cjs.map +1 -0
- package/dist/internal/popup-menu/index.d.cts +846 -0
- package/dist/internal/popup-menu/index.d.ts +846 -0
- package/dist/internal/popup-menu/index.js +2 -0
- package/dist/internal/popup-menu/index.js.map +1 -0
- package/dist/item-equality-B6TbXlBT.d.cts +7 -0
- package/dist/item-equality-B6TbXlBT.d.ts +7 -0
- package/dist/loading-DphSt8MY.d.cts +27 -0
- package/dist/loading-TsgH6v92.d.ts +27 -0
- package/dist/select/index.cjs +2 -0
- package/dist/select/index.cjs.map +1 -0
- package/dist/select/index.d.cts +927 -0
- package/dist/select/index.d.ts +927 -0
- package/dist/select/index.js +2 -0
- package/dist/select/index.js.map +1 -0
- package/dist/separator-B4Ot84B0.d.ts +748 -0
- package/dist/separator-BmbUeeaT.d.cts +748 -0
- package/dist/types-9vS1uLIK.d.cts +1557 -0
- package/dist/types-lQCIvWW8.d.ts +1557 -0
- package/dist/use-listbox-item-BIi4eRPI.d.cts +182 -0
- package/dist/use-listbox-item-BIi4eRPI.d.ts +182 -0
- package/package.json +50 -12
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/listbox/contexts/group-context.ts","../src/internal/listbox/contexts/item-context.ts","../src/internal/listbox/contexts/listbox-context.ts","../src/internal/listbox/contexts/row-width-context.ts","../src/internal/listbox/contexts/surface-context.ts","../src/internal/listbox/hooks/use-listbox-item.ts","../src/internal/listbox/utils/normalize.ts","../src/internal/listbox/hooks/use-listbox-keyboard.ts","../src/internal/listbox/hooks/use-sticky-row-width.ts","../src/internal/listbox/store/ListboxStore.ts","../src/utils/events/reason-parts.ts","../src/utils/events/create-event-details.ts","../src/internal/listbox/utils/command-score.ts"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n// ============================================================================\n// Group Context (for items to know their parent group)\n// ============================================================================\n\nexport interface GroupContextValue {\n groupId: string\n}\n\nconst GroupContext = React.createContext<GroupContextValue | null>(null)\n\nexport function useGroupContext(): GroupContextValue | null {\n return React.useContext(GroupContext)\n}\n\nexport { GroupContext }\n","'use client'\n\nimport * as React from 'react'\n\n// ============================================================================\n// Item Context (for child components like indicators to access item state)\n// ============================================================================\n\nexport interface ItemContextValue {\n /** Unique ID for this item (for DOM id attribute) */\n id: string\n /** Whether the item is highlighted */\n highlighted: boolean\n /** Whether the item is disabled */\n disabled: boolean\n /** Keyboard shortcut for this item */\n shortcut?: string\n}\n\nconst ItemContext = React.createContext<ItemContextValue | null>(null)\n\nexport function useItemContext(): ItemContextValue {\n const context = React.useContext(ItemContext)\n if (!context) {\n throw new Error('Item child components must be used within an Item')\n }\n return context\n}\n\nexport function useMaybeItemContext(): ItemContextValue | null {\n return React.useContext(ItemContext)\n}\n\nexport { ItemContext }\n","'use client'\n\nimport * as React from 'react'\nimport type { HighlightChangeEventDetails } from '../../popup-menu/events.js'\nimport type { ListboxStore, VirtualItem } from '../store/ListboxStore.js'\n\n/**\n * Virtualization configuration passed from Root/Submenu to Surface.\n */\nexport interface VirtualizationConfig {\n /** Whether virtualization mode is enabled */\n virtualized: boolean\n /** Pre-registered items for virtualization */\n items: VirtualItem[]\n /**\n * Callback when highlighted item changes (for scroll sync).\n * The third parameter contains event details including the reason for the change.\n */\n onHighlightChange?: (\n id: string | null,\n index: number,\n eventDetails: HighlightChangeEventDetails,\n ) => void\n}\n\nexport interface ListboxContextValue {\n /** The Listbox store instance */\n store: ListboxStore\n /** Nesting depth: 0 = root menu, 1+ = submenu */\n depth: number\n /** Close the entire menu tree (deepest submenu to root, sequentially) */\n closeAll: () => void\n /** Register a surface (submenu) for closeAll tracking. Returns unregister function. */\n registerSurface: (\n depth: number,\n setOpen: (open: boolean) => void,\n ) => () => void\n /** Virtualization configuration (if enabled) */\n virtualization?: VirtualizationConfig\n}\n\nconst ListboxContext = React.createContext<ListboxContextValue | null>(null)\n\nexport function useListboxContext(): ListboxContextValue {\n const context = React.useContext(ListboxContext)\n if (!context) {\n throw new Error('Listbox components must be used within a Listbox provider')\n }\n return context\n}\n\nexport function useMaybeListboxContext(): ListboxContextValue | null {\n return React.useContext(ListboxContext)\n}\n\nexport { ListboxContext }\n","'use client'\n\nimport * as React from 'react'\n\n/**\n * Context value for row width measurement.\n * Provided by List when `measureRowWidth` is enabled.\n */\nexport interface RowWidthContextValue {\n /**\n * Queue a row element for width measurement.\n * Call this when a row mounts or becomes visible.\n *\n * @param element - The DOM element to measure\n * @param id - Unique identifier for this row (used to avoid re-measuring)\n */\n queueMeasurement: (element: HTMLElement, id: string) => void\n}\n\n/**\n * Context for row width measurement.\n * Items use this to self-register for measurement.\n */\nexport const RowWidthContext = React.createContext<RowWidthContextValue | null>(\n null,\n)\n\nif (process.env.NODE_ENV !== 'production') {\n RowWidthContext.displayName = 'RowWidthContext'\n}\n\n/**\n * Hook to get the row width context.\n * Throws an error if used outside of a List with `measureRowWidth` enabled.\n */\nexport function useRowWidthContext(): RowWidthContextValue {\n const context = React.useContext(RowWidthContext)\n if (!context) {\n throw new Error(\n 'useRowWidthContext must be used within a List with measureRowWidth enabled',\n )\n }\n return context\n}\n\n/**\n * Hook to optionally get the row width context.\n * Returns null if not within a List with `measureRowWidth` enabled.\n * This is the preferred hook for items that should work both with and without measurement.\n */\nexport function useMaybeRowWidthContext(): RowWidthContextValue | null {\n return React.useContext(RowWidthContext)\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { ListboxStore } from '../store/ListboxStore.js'\n\n// Re-export types from the store for convenience\nexport type {\n FilterFn,\n ItemRegistration,\n} from '../store/ListboxStore.js'\n\n// ============================================================================\n// Surface Context\n// ============================================================================\n\nexport interface SurfaceContextValue {\n /** The Listbox store instance */\n store: ListboxStore\n /** Unique identifier for this surface */\n surfaceId: string\n}\n\nconst SurfaceContext = React.createContext<SurfaceContextValue | null>(null)\n\nexport function useSurfaceContext(): SurfaceContextValue {\n const context = React.useContext(SurfaceContext)\n if (!context) {\n throw new Error('Listbox components must be used within a Surface provider')\n }\n return context\n}\n\nexport function useMaybeSurfaceContext(): SurfaceContextValue | null {\n return React.useContext(SurfaceContext)\n}\n\nexport { SurfaceContext }\n","'use client'\n\nimport * as React from 'react'\nimport { useGroupContext } from '../contexts/group-context.js'\nimport type { ItemContextValue } from '../contexts/item-context.js'\nimport { useListboxContext } from '../contexts/listbox-context.js'\nimport { useMaybeRowWidthContext } from '../contexts/row-width-context.js'\nimport { useSurfaceContext } from '../contexts/surface-context.js'\nimport { normalizeValue } from '../utils/normalize.js'\n\n/**\n * Aim guard refs for submenu navigation.\n * Optional - only needed for popup menus with submenus.\n */\nexport interface AimGuardRefs {\n /** Whether aim guard is currently active */\n aimGuardActiveRef: React.RefObject<boolean>\n /** The depth at which aim guard is active (null when not guarding) */\n guardedDepthRef: React.RefObject<number | null>\n}\n\n/**\n * Parameters for the useListboxItem hook.\n */\nexport interface UseListboxItemParams {\n /**\n * Explicit unique identifier for this item in the store.\n * Takes highest priority for item registration.\n * Used by data-first APIs to ensure consistent IDs.\n */\n id?: string\n\n /**\n * Unique value for this item used as identifier and for filtering.\n * If not provided, will be inferred from textContent.\n * Used for registration when `id` is not provided.\n */\n value?: string\n\n /**\n * Additional keywords to match against when filtering.\n * Useful for aliases or synonyms.\n */\n keywords?: string[]\n\n /**\n * Whether this item is disabled.\n * Disabled items are not selectable and are skipped during keyboard navigation.\n */\n disabled?: boolean\n\n /**\n * Whether to force render this item regardless of filter results.\n * @default false\n */\n forceMount?: boolean\n\n /**\n * Keyboard shortcut to trigger this item.\n * When the menu is focused and the user presses this key, the item will be selected.\n * Should be a single character (e.g., \"1\", \"a\", etc.).\n */\n shortcut?: string\n\n /**\n * Forces this row's relative order during score-based sorting.\n * Lower values appear earlier.\n * @default 0\n */\n forceOrder?: number\n\n /**\n * Overrides this row's computed fuzzy-match score.\n * Useful for pinning or manually ranking rows in score-based views.\n */\n forceScore?: number\n\n /**\n * Whether this item is a submenu trigger (for store registration).\n * @default false\n */\n isSubmenuTrigger?: boolean\n\n /**\n * Callback when this item is selected (via click or Enter key).\n * For simple items, pass this directly. For checkbox/radio items,\n * use registerSelect() to register a dynamic handler.\n */\n onSelect?: () => void\n\n /**\n * Whether selecting this item should close the menu.\n * @default true\n */\n closeOnClick?: boolean\n\n /**\n * Callback invoked after selection occurs (via click).\n * Called with the item's ID. The consumer handles any\n * post-selection behavior like closing menus.\n */\n onAfterSelect?: (itemId: string) => void\n\n /**\n * Children (used for text content inference when value is not provided).\n */\n children?: React.ReactNode\n\n /**\n * Optional aim guard refs for popup menu navigation.\n * When provided, pointer move events will respect the aim guard.\n */\n aimGuard?: AimGuardRefs\n}\n\n/**\n * Return value from the useListboxItem hook.\n */\nexport interface UseListboxItemReturn {\n /** Unique ID for this item (DOM ID for aria-activedescendant) */\n id: string\n\n /**\n * Store identifier for this item.\n * Use this when calling store methods like setHighlightedId, registerSubmenuOpen, etc.\n * This is the `value` prop (or inferred from textContent).\n */\n storeId: string\n\n /** Ref to attach to the item element */\n ref: React.RefObject<HTMLDivElement | null>\n\n /** Whether item is currently highlighted */\n isHighlighted: boolean\n\n /** Whether item should be rendered (passes filter) */\n isVisible: boolean\n\n /** Context value to provide to children via ItemContext.Provider */\n contextValue: ItemContextValue\n\n /**\n * Event handlers to spread on the element.\n * These handle click, pointer move, and pointer down events.\n */\n handlers: {\n onClick: React.MouseEventHandler<HTMLDivElement>\n onPointerMove: React.PointerEventHandler<HTMLDivElement>\n onPointerDown: React.PointerEventHandler<HTMLDivElement>\n }\n\n /**\n * Register a custom onSelect handler.\n * This is useful for checkbox/radio items where the select behavior\n * needs to be customized (e.g., toggle checked state).\n * Returns an unregister function.\n */\n registerSelect: (handler: (() => void) | undefined) => () => void\n}\n\n/**\n * Hook that provides all shared logic for navigatable/highlightable listbox items.\n *\n * This hook handles:\n * - Item registration with the listbox store\n * - Highlight state management (keyboard and pointer)\n * - Filter/search visibility\n * - Scroll into view on keyboard navigation\n * - Click, pointer move, and pointer down event handlers\n * - Optional aim guard integration (for submenu navigation)\n *\n * @example\n * ```tsx\n * function CustomItem(props) {\n * const item = useListboxItem({\n * value: props.value,\n * disabled: props.disabled,\n * onSelect: props.onSelect,\n * })\n *\n * if (!item.isVisible) return null\n *\n * return (\n * <ItemContext.Provider value={item.contextValue}>\n * <div\n * ref={item.ref}\n * {...item.handlers}\n * data-highlighted={item.isHighlighted || undefined}\n * >\n * {props.children}\n * </div>\n * </ItemContext.Provider>\n * )\n * }\n * ```\n */\nexport function useListboxItem(\n params: UseListboxItemParams,\n): UseListboxItemReturn {\n const {\n id: idProp,\n value: valueProp,\n keywords,\n disabled = false,\n forceMount = false,\n shortcut,\n forceOrder,\n forceScore,\n isSubmenuTrigger = false,\n onSelect,\n closeOnClick = true,\n onAfterSelect,\n children,\n aimGuard,\n } = params\n\n const { store } = useSurfaceContext()\n const groupContext = useGroupContext()\n const { depth } = useListboxContext()\n\n const ref = React.useRef<HTMLDivElement>(null)\n\n // Infer value from textContent if not provided\n const [inferredValue, setInferredValue] = React.useState<string>('')\n\n React.useLayoutEffect(() => {\n if (idProp === undefined && valueProp === undefined && ref.current) {\n const textContent = ref.current.textContent?.trim() ?? ''\n setInferredValue(textContent)\n }\n }, [idProp, valueProp, children])\n\n // Registration ID priority: id prop > value prop > textContent inference\n // This ensures data-first APIs can provide explicit IDs that match their internal tracking\n // Note: valueProp is normalized (trimmed) to match cmdk's behavior\n const registrationId = idProp ?? (normalizeValue(valueProp) || inferredValue)\n\n // Generate a stable ID for DOM id attribute (aria-activedescendant, etc.)\n // When id prop is provided (data-first APIs), use it directly for DOM ID\n // This ensures aria-activedescendant works correctly with composite IDs\n const generatedDomId = React.useId()\n const domId = idProp ?? `item-${generatedDomId}`\n\n // Stabilize keywords to prevent infinite loops when passed as inline arrays\n // We use JSON.stringify to compare by value rather than reference\n // Keywords are also normalized (trimmed) to match cmdk's behavior\n const normalizedKeywords = keywords\n ?.map((k) => normalizeValue(k))\n .filter(Boolean)\n const keywordsKey = normalizedKeywords\n ? JSON.stringify(normalizedKeywords)\n : undefined\n\n // Register item with store (using registrationId as the unique identifier)\n React.useEffect(() => {\n if (!registrationId && !forceMount) return\n\n const unregister = store.registerItem(registrationId, {\n value: registrationId,\n keywords: normalizedKeywords,\n groupId: groupContext?.groupId,\n disabled,\n isSubmenuTrigger,\n shortcut,\n forceOrder,\n forceScore,\n closeOnClick,\n })\n\n return unregister\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keywordsKey is a stable representation of keywords\n }, [\n registrationId,\n keywordsKey,\n groupContext?.groupId,\n disabled,\n isSubmenuTrigger,\n shortcut,\n forceOrder,\n forceScore,\n closeOnClick,\n store,\n forceMount,\n ])\n\n // Register DOM ref with store for scroll behavior\n React.useEffect(() => {\n if (!registrationId) return\n return store.registerItemRef(registrationId, ref)\n }, [registrationId, store])\n\n // Register for row width measurement if enabled\n const rowWidthContext = useMaybeRowWidthContext()\n\n React.useLayoutEffect(() => {\n if (rowWidthContext && ref.current && registrationId) {\n rowWidthContext.queueMeasurement(ref.current, registrationId)\n }\n }, [rowWidthContext, registrationId])\n\n // Register onSelect handler if provided directly\n React.useEffect(() => {\n if (!onSelect || !registrationId) return\n return store.registerItemSelect(registrationId, onSelect)\n }, [registrationId, onSelect, store])\n\n // Use selectors to get derived state (using registrationId as identifier)\n const normalizedSearch = store.useState('normalizedSearch')\n const isHighlighted = store.useState('isHighlighted', registrationId)\n const score = store.useState('getItemScore', registrationId)\n\n // Check if filtering is disabled (consumer handles filtering externally)\n const filterDisabled = store.isFilterDisabled()\n\n // Determine visibility based on filter score\n // When filterDisabled, consumer handles filtering so all items are visible\n const hasSearch = normalizedSearch.length > 0\n const isVisible = forceMount || filterDisabled || !hasSearch || score > 0\n\n // Note: Scroll behavior is now handled by the store's setHighlightedId method.\n // It uses the registered DOM refs to call scrollIntoView when the element exists,\n // or falls back to onHighlightChange for virtualizer sync.\n\n // Event handlers\n const handleClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return\n if (disabled) return\n if (!registrationId) return\n\n event.preventDefault()\n\n // Call the registered onSelect handler via store\n const registeredHandler = store.context.itemSelects.get(registrationId)\n registeredHandler?.()\n\n // Notify consumer that selection occurred (for closing menus, etc.)\n onAfterSelect?.(registrationId)\n },\n [disabled, store, registrationId, onAfterSelect],\n )\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n },\n [],\n )\n\n const handlePointerMove = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return\n if (disabled) return\n if (!registrationId) return\n\n // Don't highlight if aim guard is active at this depth (user is moving toward submenu)\n // Only block highlighting in the same menu where the trigger is located\n if (aimGuard) {\n const { aimGuardActiveRef, guardedDepthRef } = aimGuard\n if (aimGuardActiveRef.current && guardedDepthRef.current === depth)\n return\n }\n\n // Check if pointer has actually moved - prevents phantom highlights when\n // content shifts under a stationary pointer (e.g., search results changing)\n if (!store.shouldAllowPointerHighlight(event.clientX, event.clientY)) {\n return\n }\n\n // Highlight on hover (using registrationId as identifier)\n store.setHighlightedId(registrationId)\n },\n [disabled, aimGuard, depth, store, registrationId],\n )\n\n // Context value for child components\n const contextValue: ItemContextValue = React.useMemo(\n () => ({\n id: domId,\n highlighted: isHighlighted,\n disabled,\n shortcut,\n }),\n [domId, isHighlighted, disabled, shortcut],\n )\n\n // Register a custom select handler (for checkbox/radio items)\n const registerSelect = React.useCallback(\n (handler: (() => void) | undefined) => {\n if (!registrationId) return () => {}\n return store.registerItemSelect(registrationId, handler)\n },\n [store, registrationId],\n )\n\n const handlers = React.useMemo(\n () => ({\n onClick: handleClick,\n onPointerMove: handlePointerMove,\n onPointerDown: handlePointerDown,\n }),\n [handleClick, handlePointerMove, handlePointerDown],\n )\n\n return {\n id: domId,\n storeId: registrationId,\n ref,\n isHighlighted,\n isVisible,\n contextValue,\n handlers,\n registerSelect,\n }\n}\n","/**\n * Normalizes a value string for use in filtering and identification.\n * Trims leading/trailing whitespace to match cmdk's behavior.\n *\n * @param value - The value to normalize\n * @returns The trimmed value, or empty string if value is nullish\n */\nexport function normalizeValue(value: string | undefined | null): string {\n return value?.trim() ?? ''\n}\n\n/**\n * Converts a value string to a URL-safe slug for use in IDs.\n * - Trims whitespace\n * - Converts to lowercase\n * - Replaces spaces with hyphens\n * - Removes non-alphanumeric characters (except hyphens)\n * - Collapses multiple hyphens into one\n * - Removes leading/trailing hyphens\n *\n * @example\n * slugify(\"User Settings\") // \"user-settings\"\n * slugify(\" Hello World! \") // \"hello-world\"\n * slugify(\"Café & Co.\") // \"caf-co\"\n *\n * @param value - The value to slugify\n * @returns The slugified value, or empty string if value is nullish\n */\nexport function slugify(value: string | undefined | null): string {\n if (!value) return ''\n\n return (\n value\n .trim()\n .toLowerCase()\n // Replace spaces with hyphens\n .replace(/\\s+/g, '-')\n // Remove non-alphanumeric characters (except hyphens)\n .replace(/[^a-z0-9-]/g, '')\n // Collapse multiple hyphens into one\n .replace(/-+/g, '-')\n // Remove leading/trailing hyphens\n .replace(/^-|-$/g, '')\n )\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { ListboxStore } from '../store/ListboxStore.js'\n\n/**\n * Focus owner interface for keyboard handling.\n * Optional - only needed for popup menus with multiple surfaces.\n */\nexport interface FocusOwnerInterface {\n /** Check if a surface is the current owner */\n useState: (selector: 'isOwner', surfaceId: string) => boolean\n /** Set the owner surface ID */\n setOwnerId: (id: string | null) => void\n}\n\n/**\n * Submenu context interface for keyboard handling.\n * Optional - only needed for popup menus with submenus.\n */\nexport interface SubmenuInterface {\n /** Close this submenu */\n setOpen: (open: boolean) => void\n /** Parent surface ID for focus transfer */\n parentSurfaceId: string\n /** Whether Escape closes the root menu or just this submenu */\n closeRootOnEsc?: boolean\n}\n\n/**\n * Subpage context interface for keyboard handling.\n * Optional - only needed for popup menus with in-popup subpage navigation.\n */\nexport interface SubpageInterface {\n /** Pop one page from the current stack. */\n goBack: () => boolean\n /** Parent surface ID for focus transfer after back navigation. */\n parentSurfaceId: string | null\n /** Whether Escape closes the root menu instead of navigating back. */\n closeRootOnEsc?: boolean\n}\n\nexport interface UseListboxKeyboardParams {\n /** The Listbox store instance */\n store: ListboxStore\n /** Unique identifier for this surface */\n surfaceId: string\n /** Whether keyboard handling is enabled */\n enabled: boolean\n /** User's onKeyDown handler to compose with */\n onKeyDown?: React.KeyboardEventHandler\n /**\n * Callback when an item is selected via keyboard (Enter or shortcut).\n * Called with selection details. The consumer handles any\n * post-selection behavior like closing menus.\n */\n onSelect?: (details: { itemId: string | null; closeOnClick: boolean }) => void\n /**\n * Callback to close the entire menu tree from the root.\n * Used when Escape is pressed and closeRootOnEsc is true (default).\n */\n closeAll: () => void\n\n // Optional popup-menu features\n\n /** The FocusOwner store for managing focus ownership (optional) */\n focusOwner?: FocusOwnerInterface\n /** Menu depth (0 for root, >0 for submenus) - defaults to 0 */\n depth?: number\n /** Submenu context for ArrowLeft navigation back to parent (optional) */\n submenuContext?: SubmenuInterface | null\n /** Subpage context for ArrowLeft/Ctrl+H navigation back (optional) */\n subpageContext?: SubpageInterface | null\n /**\n * Whether to enable type-to-search behavior.\n * When true, printable characters will activate the input and set pending search.\n * Used by List when hideUntilActive is enabled and input is not yet active.\n * @default false\n */\n enableTypeToSearch?: boolean\n /**\n * Whether to skip the focus owner check.\n * When true, keyboard handling will work based on `enabled` prop alone,\n * ignoring focus ownership. Useful for Combobox where the input is outside\n * the Surface but should still handle keyboard navigation.\n * @default false\n */\n skipFocusOwnerCheck?: boolean\n}\n\nexport interface UseListboxKeyboardReturn {\n /** Keyboard event handler to attach to the element */\n handleKeyDown: React.KeyboardEventHandler\n}\n\n/**\n * Centralized keyboard navigation hook for listbox-like components.\n * Handles arrow navigation, vim bindings, submenu open/close, and selection.\n *\n * This hook can be used standalone (for simple listboxes like Select/Command)\n * or with focus owner and submenu support (for popup menus with nested menus).\n */\nexport function useListboxKeyboard(\n params: UseListboxKeyboardParams,\n): UseListboxKeyboardReturn {\n const {\n store,\n surfaceId,\n enabled,\n onKeyDown,\n onSelect,\n closeAll,\n focusOwner,\n depth = 0,\n submenuContext,\n subpageContext,\n enableTypeToSearch = false,\n skipFocusOwnerCheck = false,\n } = params\n\n // Subscribe to focus ownership if available\n const focusOwnerIsOwner = focusOwner?.useState('isOwner', surfaceId) ?? true\n // Skip this check for Combobox where input is outside Surface\n const isOwner = skipFocusOwnerCheck ? true : focusOwnerIsOwner\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n // Call user's handler first\n onKeyDown?.(event)\n\n if (event.defaultPrevented) return\n\n // Only handle keyboard if enabled and this surface owns focus\n if (!enabled) return\n if (!isOwner) return\n\n // Check for IME composition\n if (event.nativeEvent.isComposing || event.keyCode === 229) return\n\n // Type-to-search: detect printable characters when enabled\n // Note: Shortcuts take priority over type-to-search, so we check for shortcuts first\n if (enableTypeToSearch) {\n const hideUntilActive = store.context.hideUntilActive\n const inputActive = store.state.inputActive\n\n if (hideUntilActive && !inputActive) {\n const isPrintable =\n event.key.length === 1 &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey\n\n if (isPrintable) {\n // Check if this key is a registered shortcut first\n const shortcutItemId = store.context.shortcuts.get(\n event.key.toLowerCase(),\n )\n if (shortcutItemId && store.selectByShortcut(event.key)) {\n event.preventDefault()\n const item = store.context.items.get(shortcutItemId)\n onSelect?.({\n itemId: shortcutItemId,\n closeOnClick: item?.closeOnClick ?? true,\n })\n return\n }\n\n // No shortcut match, trigger type-to-search\n event.preventDefault()\n store.setPendingSearch(event.key)\n store.setInputActive(true)\n return\n }\n }\n }\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault()\n store.highlightNext()\n break\n }\n case 'ArrowUp': {\n event.preventDefault()\n store.highlightPrev()\n break\n }\n case 'n': {\n // Ctrl+N - next item (vim binding)\n if (event.ctrlKey) {\n event.preventDefault()\n store.highlightNext()\n }\n break\n }\n case 'p': {\n // Ctrl+P - previous item (vim binding)\n if (event.ctrlKey) {\n event.preventDefault()\n store.highlightPrev()\n }\n break\n }\n case 'ArrowRight': {\n // Open submenu if highlighted item is a submenu trigger\n // Note: Focus transfer is handled by SubmenuTrigger's registerSubmenuOpen callback\n if (store.isHighlightedSubmenuTrigger()) {\n event.preventDefault()\n store.openSubmenuForHighlighted()\n }\n break\n }\n case 'l': {\n // Ctrl+L - open submenu (vim binding)\n // Note: Focus transfer is handled by SubmenuTrigger's registerSubmenuOpen callback\n if (event.ctrlKey && store.isHighlightedSubmenuTrigger()) {\n event.preventDefault()\n store.openSubmenuForHighlighted()\n }\n break\n }\n case 'ArrowLeft': {\n // Back one subpage when in subpage navigation context.\n if (subpageContext?.goBack()) {\n event.preventDefault()\n if (focusOwner && subpageContext.parentSurfaceId) {\n focusOwner.setOwnerId(subpageContext.parentSurfaceId)\n }\n break\n }\n\n // Close submenu and return to parent (only if in a submenu)\n if (depth > 0 && submenuContext && focusOwner) {\n event.preventDefault()\n submenuContext.setOpen(false)\n // Transfer focus back to parent surface\n focusOwner.setOwnerId(submenuContext.parentSurfaceId)\n }\n break\n }\n case 'h': {\n // Ctrl+H - back one subpage (vim binding)\n if (event.ctrlKey && subpageContext?.goBack()) {\n event.preventDefault()\n if (focusOwner && subpageContext.parentSurfaceId) {\n focusOwner.setOwnerId(subpageContext.parentSurfaceId)\n }\n break\n }\n\n // Ctrl+H - close submenu (vim binding fallback)\n if (event.ctrlKey && depth > 0 && submenuContext && focusOwner) {\n event.preventDefault()\n submenuContext.setOpen(false)\n // Transfer focus back to parent surface\n focusOwner.setOwnerId(submenuContext.parentSurfaceId)\n }\n break\n }\n case 'Enter': {\n event.preventDefault()\n const selectedId = store.state.highlightedId\n const item = store.getHighlightedItem()\n store.selectHighlighted()\n onSelect?.({\n itemId: selectedId,\n closeOnClick: item?.closeOnClick ?? true,\n })\n break\n }\n case 'Home': {\n // Highlight first item\n event.preventDefault()\n store.setHighlightedId(null)\n store.highlightNext()\n break\n }\n case 'End': {\n // Highlight last item\n event.preventDefault()\n store.setHighlightedId(null)\n store.highlightPrev()\n break\n }\n case 'Escape': {\n // In a subpage: close root (optional) or navigate back one page.\n if (subpageContext) {\n event.preventDefault()\n event.stopPropagation()\n\n if (subpageContext.closeRootOnEsc) {\n closeAll()\n } else {\n const didGoBack = subpageContext.goBack()\n if (didGoBack && focusOwner && subpageContext.parentSurfaceId) {\n focusOwner.setOwnerId(subpageContext.parentSurfaceId)\n } else if (depth > 0 && submenuContext) {\n submenuContext.setOpen(false)\n if (focusOwner) {\n focusOwner.setOwnerId(submenuContext.parentSurfaceId)\n }\n }\n }\n break\n }\n\n // Handle Escape based on depth and closeRootOnEsc setting\n if (depth === 0) {\n // Root menu: let parent component handle it normally\n // (closes the menu and returns focus to trigger)\n break\n }\n\n // In a submenu: always prevent default to avoid parent's focus restoration\n event.preventDefault()\n event.stopPropagation()\n\n if (submenuContext?.closeRootOnEsc !== false) {\n // closeRootOnEsc is true (default): close entire menu tree\n closeAll()\n } else {\n // closeRootOnEsc is false: close only this submenu\n submenuContext.setOpen(false)\n // Transfer focus back to parent surface\n if (focusOwner) {\n focusOwner.setOwnerId(submenuContext.parentSurfaceId)\n }\n }\n break\n }\n default: {\n // Handle single-character shortcuts (only when not typing in search input)\n // Shortcuts are disabled when there's an active search to avoid conflicts\n const hasActiveSearch = store.state.normalizedSearch.length > 0\n const isPrintable =\n event.key.length === 1 &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey\n\n if (isPrintable && !hasActiveSearch) {\n const itemId = store.context.shortcuts.get(event.key.toLowerCase())\n if (itemId && store.selectByShortcut(event.key)) {\n event.preventDefault()\n const item = store.context.items.get(itemId)\n onSelect?.({\n itemId,\n closeOnClick: item?.closeOnClick ?? true,\n })\n }\n }\n break\n }\n }\n },\n [\n onKeyDown,\n enabled,\n isOwner,\n enableTypeToSearch,\n store,\n depth,\n submenuContext,\n subpageContext,\n focusOwner,\n onSelect,\n closeAll,\n ],\n )\n\n return { handleKeyDown }\n}\n","'use client'\n\nimport * as React from 'react'\n\n// Debug flag - set to true to enable console logging\nconst DEBUG_ROW_WIDTH = false\n\n// Debug flag - when true, keeps measurement styles applied (doesn't reset them)\n// This lets you visually inspect what elements look like during measurement\nconst DEBUG_FREEZE_MEASUREMENT = false\n\nfunction debugLog(...args: unknown[]) {\n if (DEBUG_ROW_WIDTH) {\n console.log('[useStickyRowWidth]', ...args)\n }\n}\n\nfunction px(n: number) {\n return `${Math.ceil(n)}px`\n}\n\nfunction parseCssPixelValue(value: string): number | null {\n const parsed = Number.parseFloat(value)\n return Number.isFinite(parsed) ? parsed : null\n}\n\ninterface MeasurementEntry {\n element: HTMLElement\n id: string\n}\n\nexport interface UseStickyRowWidthOptions {\n /**\n * Ref to the list container element.\n * Used to measure row widths and as a fallback target for the CSS variable.\n */\n listRef: React.RefObject<HTMLElement | null>\n /**\n * Optional ref to the element where `--row-width` CSS variable should be applied.\n * If not provided, uses the listRef element.\n * Useful for applying the variable to a parent Popup element.\n */\n targetRef?: React.RefObject<HTMLElement | null>\n /**\n * Optional maximum width cap in pixels.\n * The measured width will never exceed this value.\n */\n maxWidth?: number\n /**\n * Whether measurement is enabled.\n * @default true\n */\n enabled?: boolean\n}\n\nexport interface UseStickyRowWidthReturn {\n /**\n * Queue a row element for measurement.\n * The element's natural width will be measured and tracked.\n * Call this when a row mounts or becomes visible.\n */\n queueMeasurement: (element: HTMLElement, id: string) => void\n /**\n * Reset all measurements.\n * Call this when the menu closes to start fresh on next open.\n */\n resetMeasurements: () => void\n}\n\n/**\n * Hook that measures row widths and maintains a sticky maximum width.\n *\n * This is useful for virtualized lists where the popup width should adapt\n * to the content but never shrink as the user scrolls through items.\n *\n * The hook:\n * 1. Measures each row's natural width (using `max-content`)\n * 2. Tracks the maximum width seen across all rows\n * 3. Applies `--row-width` CSS variable to the target element\n * 4. Only grows the width, never shrinks (until reset)\n *\n * @example\n * ```tsx\n * const { queueMeasurement, resetMeasurements } = useStickyRowWidth({\n * listRef: myListRef,\n * maxWidth: 500,\n * })\n *\n * // Queue items for measurement\n * useLayoutEffect(() => {\n * if (itemRef.current) {\n * queueMeasurement(itemRef.current, itemId)\n * }\n * }, [itemId])\n *\n * // Reset on menu close\n * useEffect(() => {\n * if (!isOpen) {\n * resetMeasurements()\n * }\n * }, [isOpen])\n * ```\n */\nexport function useStickyRowWidth(\n options: UseStickyRowWidthOptions,\n): UseStickyRowWidthReturn {\n const { listRef, targetRef, maxWidth, enabled = true } = options\n\n // The element where CSS variable is applied (targetRef if provided, else listRef)\n const getTargetElement = React.useCallback(() => {\n return targetRef?.current ?? listRef.current\n }, [targetRef, listRef])\n\n // Track the maximum width seen so far\n const maxSeenRef = React.useRef(0)\n\n // Hydrate max width from an existing popup-level CSS variable.\n // This keeps row width sticky when List remounts during subpage navigation.\n React.useLayoutEffect(() => {\n if (!enabled) return\n\n const el = getTargetElement()\n if (!el) return\n\n const existingWidth = parseCssPixelValue(\n el.style.getPropertyValue('--row-width').trim(),\n )\n\n if (!existingWidth || existingWidth <= maxSeenRef.current) {\n return\n }\n\n maxSeenRef.current = existingWidth\n debugLog('Hydrated max width from existing --row-width:', {\n hydratedWidth: existingWidth,\n })\n }, [enabled, getTargetElement])\n\n // RAF scheduler state\n const readQueue = React.useRef<MeasurementEntry[]>([])\n const writeQueue = React.useRef<Array<() => void>>([])\n const scheduled = React.useRef(false)\n const measuredIds = React.useRef<Set<string>>(new Set())\n\n /**\n * Apply the `--row-width` CSS variable to the target element.\n */\n const applyVar = React.useCallback(\n (width: number) => {\n const el = getTargetElement()\n if (!el) return\n\n // Apply hard cap if specified\n const capped = maxWidth !== undefined ? Math.min(width, maxWidth) : width\n\n debugLog('--row-width updated:', {\n rawWidth: width,\n cappedWidth: capped,\n cssValue: px(capped),\n maxWidthCap: maxWidth,\n targetElement:\n el.tagName +\n (el.className ? `.${el.className.split(' ').join('.')}` : ''),\n })\n\n el.style.setProperty('--row-width', px(capped))\n },\n [getTargetElement, maxWidth],\n )\n\n /**\n * RAF scheduler: batch reads, then batch writes.\n * This avoids layout thrashing by separating measurement from application.\n */\n const schedule = React.useCallback(() => {\n if (scheduled.current) return\n scheduled.current = true\n\n requestAnimationFrame(() => {\n // === READ PHASE: Measure all queued rows at once ===\n const measurements = readQueue.current\n let maxWidth = maxSeenRef.current\n let foundNewMax = false\n\n if (measurements.length > 0) {\n debugLog('Measuring batch of', measurements.length, 'rows')\n\n for (const { element, id } of measurements) {\n // Skip if already measured\n if (measuredIds.current.has(id)) continue\n\n // Skip if element is no longer in the DOM (unmounted before RAF fired)\n if (!element.isConnected) {\n debugLog('Row skipped (unmounted):', { id })\n continue\n }\n\n // Read natural width without interleaving writes\n const prevWidth = element.style.width\n const prevMaxWidth = element.style.maxWidth\n\n // Add data-measuring attribute so CSS can remove constraints on descendants\n element.setAttribute('data-measuring', '')\n\n // Temporarily remove max-width constraint to get true natural width\n element.style.maxWidth = 'none'\n element.style.width = 'max-content'\n\n const w = Math.max(element.scrollWidth, element.offsetWidth) + 1\n\n // Log computed styles to debug constraint issues\n const computed = getComputedStyle(element)\n debugLog('Row measurement details:', {\n id,\n measuredWidth: w,\n scrollWidth: element.scrollWidth,\n offsetWidth: element.offsetWidth,\n computedWidth: computed.width,\n computedMaxWidth: computed.maxWidth,\n inlineMaxWidth: prevMaxWidth,\n textContent: element.textContent?.slice(0, 50),\n })\n\n // Reset styles unless DEBUG_FREEZE_MEASUREMENT is enabled\n if (!DEBUG_FREEZE_MEASUREMENT) {\n element.style.width = prevWidth\n element.style.maxWidth = prevMaxWidth\n element.removeAttribute('data-measuring')\n }\n\n // Debug: if width is suspiciously small, log more info\n if (w <= 1) {\n const rect = element.getBoundingClientRect()\n const computed = getComputedStyle(element)\n debugLog('Row skipped (0-width, will retry):', {\n id,\n width: w,\n scrollWidth: element.scrollWidth,\n offsetWidth: element.offsetWidth,\n boundingRect: { width: rect.width, height: rect.height },\n display: computed.display,\n visibility: computed.visibility,\n innerHTML: element.innerHTML.slice(0, 100),\n isConnected: element.isConnected,\n parentElement: element.parentElement?.tagName,\n })\n // Don't mark as measured - element was likely unmounted/replaced\n // before we could measure it. It will be re-queued when it re-mounts.\n continue\n }\n\n debugLog('Row measured:', {\n id,\n width: w,\n currentMax: maxWidth,\n isNewMax: w > maxWidth,\n })\n\n if (w > maxWidth) {\n maxWidth = w\n foundNewMax = true\n }\n\n measuredIds.current.add(id)\n }\n\n readQueue.current = []\n }\n\n // === WRITE PHASE: Apply styles after all reads complete ===\n if (foundNewMax) {\n debugLog('New max width found:', {\n previousMax: maxSeenRef.current,\n newMax: maxWidth,\n totalMeasured: measuredIds.current.size,\n })\n maxSeenRef.current = maxWidth\n writeQueue.current.push(() => applyVar(maxWidth))\n }\n\n for (const write of writeQueue.current) {\n write()\n }\n writeQueue.current = []\n scheduled.current = false\n })\n }, [applyVar])\n\n /**\n * Queue a row element for measurement.\n */\n const queueMeasurement = React.useCallback(\n (element: HTMLElement, id: string) => {\n if (!enabled) {\n debugLog('queueMeasurement skipped (disabled):', id)\n return\n }\n\n // Skip if already measured\n if (measuredIds.current.has(id)) {\n debugLog('queueMeasurement skipped (already measured):', id)\n return\n }\n\n debugLog('queueMeasurement:', {\n id,\n queueSize: readQueue.current.length + 1,\n element: element.tagName,\n })\n\n // Add to read queue\n readQueue.current.push({ element, id })\n schedule()\n },\n [enabled, schedule],\n )\n\n /**\n * Reset all measurements.\n * This clears the tracked IDs and resets the max width.\n */\n const resetMeasurements = React.useCallback(() => {\n const prevMax = maxSeenRef.current\n const prevCount = measuredIds.current.size\n\n measuredIds.current.clear()\n maxSeenRef.current = 0\n\n // Clear the CSS variable\n const el = getTargetElement()\n if (el) {\n el.style.removeProperty('--row-width')\n }\n\n debugLog('Measurements reset:', {\n previousMaxWidth: prevMax,\n previousMeasuredCount: prevCount,\n targetElement: el?.tagName,\n })\n }, [getTargetElement])\n\n // Re-apply the CSS variable when the target element resizes (e.g., viewport changes)\n // Use useEffect (not useLayoutEffect) to avoid conflicts with React's commit phase\n React.useEffect(() => {\n if (!enabled) return\n\n const container = getTargetElement()\n if (!container) return\n\n // ResizeObserver may not be available in some environments (SSR, older browsers, tests)\n if (typeof ResizeObserver === 'undefined') return\n\n let rafId: number | null = null\n\n const ro = new ResizeObserver((entries) => {\n // Defer to next frame to avoid flushSync conflicts during React's commit phase\n // This prevents errors when virtualizers or other components use flushSync\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n }\n rafId = requestAnimationFrame(() => {\n rafId = null\n if (maxSeenRef.current > 0) {\n debugLog('ResizeObserver triggered re-apply:', {\n currentMax: maxSeenRef.current,\n containerSize: entries[0]?.contentRect,\n })\n applyVar(maxSeenRef.current)\n }\n })\n })\n ro.observe(container)\n\n return () => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n }\n ro.disconnect()\n }\n }, [getTargetElement, enabled, applyVar])\n\n return { queueMeasurement, resetMeasurements }\n}\n","import { createSelector, ReactStore } from '@base-ui/utils/store'\nimport { useRefWithInit } from '@base-ui/utils/useRefWithInit'\nimport {\n type ChangeEventDetails,\n createChangeEventDetails,\n createGenericEventDetails,\n type GenericEventDetails,\n REASONS,\n} from '../../../utils/events/index.js'\nimport type {\n HighlightChangeEventDetails,\n HighlightChangeReason,\n PopupMenuOpenChangeEventDetails,\n PopupMenuOpenChangeReason,\n} from '../../popup-menu/events.js'\nimport { commandScore } from '../utils/command-score.js'\nimport { normalizeValue } from '../utils/normalize.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type FilterFn = (\n value: string,\n search: string,\n keywords?: string[],\n) => number\n\nexport type SearchNormalizer = (search: string) => string\n\nexport interface ItemRegistration {\n value: string\n keywords?: string[]\n groupId?: string\n disabled?: boolean\n /** Lower values are sorted earlier in score-based lists. */\n forceOrder?: number\n /** Overrides computed fuzzy score in score-based lists. */\n forceScore?: number\n /** Whether this item is a submenu trigger */\n isSubmenuTrigger?: boolean\n /** Single character keyboard shortcut to trigger this item */\n shortcut?: string\n /** Whether selecting this item should close the menu (default: true) */\n closeOnClick?: boolean\n}\n\n/**\n * Pre-registered item for virtualization.\n * This allows the store to know about all items even when they're not mounted.\n * The `value` field serves as both the unique identifier and the filtering value.\n */\nexport interface VirtualItem {\n /** Value used as unique identifier and for filtering/matching */\n value: string\n /** Additional keywords for filtering */\n keywords?: string[]\n /** Whether the item is disabled */\n disabled?: boolean\n}\n\nexport type HighlightSource = 'keyboard' | 'pointer' | 'auto' | null\n\n/**\n * Describes why the consumer updated ordered items when filter={false}.\n * - `replace`: list was re-ordered/replaced (default)\n * - `append`: new items were appended to the end while preserving existing order\n */\nexport type OrderedItemsUpdateReason = 'replace' | 'append'\n\nexport interface SetOrderedItemsOptions {\n /** Why ordered items were updated. @default 'replace' */\n reason?: OrderedItemsUpdateReason\n}\n\n/**\n * Refs for DOM elements used for scroll behavior.\n * These are stored outside of reactive state to avoid unnecessary re-renders.\n */\nexport interface DOMRefs {\n /** Ref to the list/scroll container element */\n listRef: React.RefObject<HTMLElement | null>\n /** Map of item ID to ref for the item's DOM element */\n itemRefs: Map<string, React.RefObject<HTMLElement | null>>\n}\n\nexport interface ListboxState {\n /** Whether the listbox is open */\n open: boolean\n /** Current search query */\n search: string\n /** Normalized search query used for filtering and visibility checks */\n normalizedSearch: string\n /** Currently highlighted item ID */\n highlightedId: string | null\n /** Source of the current highlight (keyboard or pointer) */\n highlightSource: HighlightSource\n /** Whether an Input is present in the Surface */\n hasInput: boolean\n /** Whether the input is currently active (rendered) when hideUntilActive mode is used */\n inputActive: boolean\n /** Pending search character typed before input was active */\n pendingSearch: string\n /** Filtered results: item ID to score */\n filteredItems: Map<string, number>\n /** Groups that have at least one visible item */\n visibleGroups: Set<string>\n /** Count of visible items */\n filteredCount: number\n /** Counter to trigger re-filtering when items change */\n filterTrigger: number\n /** Whether virtualization mode is enabled */\n virtualized: boolean\n /** Count of virtual items (from items prop) */\n virtualItemsCount: number\n}\n\nexport interface ListboxContext {\n /** Filter function or false to disable filtering */\n filter: FilterFn | false\n /** Function used to normalize search before filtering. */\n normalizeSearch: SearchNormalizer\n /** Whether to loop navigation */\n loop: boolean\n /**\n * Controls auto-highlighting behavior when the menu opens.\n * - `true`: highlight the first item (default)\n * - `false`: don't auto-highlight any item\n * - `string`: highlight the item with this specific value\n */\n autoHighlightFirst: boolean | string\n /**\n * Whether to clear search on close.\n * - `true`: clear immediately when menu closes (default)\n * - `false`: preserve search when menu closes\n * - `'after-exit'`: clear after exit animation completes (requires Surface to call clearSearch)\n */\n clearSearchOnClose: boolean | 'after-exit'\n /** Whether hideUntilActive mode is enabled */\n hideUntilActive: boolean\n /** ID for the list element (for aria-activedescendant) */\n listId: string\n /** ID for the input element */\n inputId: string\n /** Map of item ID to registration data */\n readonly items: Map<string, ItemRegistration>\n /** Map of group ID to set of item IDs */\n readonly groups: Map<string, Set<string>>\n /** Map of item ID to onSelect callback */\n readonly itemSelects: Map<string, () => void>\n /** Map of submenu trigger ID to open callback */\n readonly submenuOpens: Map<string, () => void>\n /** Map of submenu trigger ID to close callback */\n readonly submenuCloses: Map<string, () => void>\n /** Map of shortcut key to item ID */\n readonly shortcuts: Map<string, string>\n /**\n * Callback when open state changes.\n * The second parameter contains event details including the reason for the change.\n */\n onOpenChange: (\n open: boolean,\n eventDetails: PopupMenuOpenChangeEventDetails,\n ) => void\n /** Callback when search state changes */\n onSearchChange: ((search: string) => void) | undefined\n /**\n * Pre-registered items for virtualization.\n * When provided, navigation uses this array order instead of DOM registration order.\n */\n virtualItems: VirtualItem[]\n /**\n * Consumer-provided ordered list of item values when filter={false}.\n * Used to determine correct navigation/highlight order when consumer handles filtering externally.\n * Must always be provided when filter={false}.\n */\n orderedItems: string[]\n /**\n * Callback when highlighted item changes.\n * Useful for synchronizing with virtualizers (scrollToIndex) and other UI state.\n * The third parameter contains event details including the reason for the change.\n */\n onHighlightChange:\n | ((\n id: string | null,\n index: number,\n eventDetails: HighlightChangeEventDetails,\n ) => void)\n | undefined\n /**\n * DOM refs for scroll behavior.\n * Stored in context (not state) to avoid re-renders.\n */\n refs: DOMRefs\n /**\n * Callback when menu close animation completes.\n * Used for resetting row width measurements.\n */\n onCloseComplete?: () => void\n /**\n * Callback when popup close transition completes.\n * Used by popup-layer features that should reset only after exit animations.\n */\n onPopupCloseComplete?: () => void\n /**\n * Last known pointer position for detecting actual pointer movement.\n * Used to prevent \"phantom\" highlights when content shifts under a stationary pointer.\n */\n lastPointerPosition: { x: number; y: number } | null\n}\n\n/**\n * Options for the validateHighlight method.\n */\ninterface ValidateHighlightOptions {\n /**\n * Force highlighting the first item, even if current highlight is valid.\n * Used when the list order changes (e.g., after filtering).\n */\n forceFirst?: boolean\n /**\n * The newly computed filteredItems map. If not provided, uses state.filteredItems.\n * This is needed when calling from recomputeFilteredItems before state is updated.\n */\n filteredItems?: Map<string, number>\n /**\n * The new search query that corresponds to filteredItems.\n * Used together with prevSearch to detect when search is cleared.\n */\n newSearch?: string\n /**\n * The previous search query before the change.\n * Used together with newSearch to detect when search is cleared.\n */\n prevSearch?: string\n}\n\n// ============================================================================\n// Selectors\n// ============================================================================\n\nconst selectors = {\n open: createSelector((state: ListboxState) => state.open),\n search: createSelector((state: ListboxState) => state.search),\n normalizedSearch: createSelector(\n (state: ListboxState) => state.normalizedSearch,\n ),\n highlightedId: createSelector((state: ListboxState) => state.highlightedId),\n highlightSource: createSelector(\n (state: ListboxState) => state.highlightSource,\n ),\n hasInput: createSelector((state: ListboxState) => state.hasInput),\n inputActive: createSelector((state: ListboxState) => state.inputActive),\n pendingSearch: createSelector((state: ListboxState) => state.pendingSearch),\n filteredCount: createSelector((state: ListboxState) => state.filteredCount),\n filteredItems: createSelector((state: ListboxState) => state.filteredItems),\n visibleGroups: createSelector((state: ListboxState) => state.visibleGroups),\n virtualized: createSelector((state: ListboxState) => state.virtualized),\n\n isHighlighted: createSelector(\n (state: ListboxState, itemId: string) => state.highlightedId === itemId,\n ),\n\n isGroupVisible: createSelector(\n (state: ListboxState, groupId: string) =>\n state.normalizedSearch.length === 0 || state.visibleGroups.has(groupId),\n ),\n\n getItemScore: createSelector((state: ListboxState, itemId: string) => {\n if (state.normalizedSearch.length === 0) {\n return 1 // All items visible when no search\n }\n return state.filteredItems.get(itemId) ?? 0\n }),\n\n hasSearchWithNoResults: createSelector((state: ListboxState) => {\n // Must have an active search\n if (state.normalizedSearch.length === 0) return false\n\n // In virtualized mode with items prop, check virtualItemsCount\n // (filteredCount won't be accurate since items aren't registered in DOM)\n if (state.virtualized && state.virtualItemsCount >= 0) {\n return state.virtualItemsCount === 0\n }\n\n // Non-virtualized mode: check filteredCount from registered items\n return state.filteredCount === 0\n }),\n}\n\nconst defaultSearchNormalizer: SearchNormalizer = (search) => {\n return normalizeValue(search)\n}\n\n// ============================================================================\n// Store\n// ============================================================================\n\n/**\n * Core store for listbox-like components.\n * Handles item registration, filtering, navigation, and highlight state.\n *\n * Used by: DropdownMenu, ContextMenu, Select, CommandMenu\n */\nexport class ListboxStore extends ReactStore<\n ListboxState,\n ListboxContext,\n typeof selectors\n> {\n constructor(\n initialState?: Partial<ListboxState>,\n context?: Partial<ListboxContext>,\n ) {\n const defaultContext: ListboxContext = {\n filter: commandScore,\n normalizeSearch: defaultSearchNormalizer,\n loop: true,\n autoHighlightFirst: true,\n clearSearchOnClose: true,\n hideUntilActive: false,\n listId: '',\n inputId: '',\n items: new Map(),\n groups: new Map(),\n itemSelects: new Map(),\n submenuOpens: new Map(),\n submenuCloses: new Map(),\n shortcuts: new Map(),\n onOpenChange: () => {},\n onSearchChange: undefined,\n virtualItems: [],\n orderedItems: [],\n onHighlightChange: undefined,\n refs: {\n listRef: { current: null },\n itemRefs: new Map(),\n },\n onCloseComplete: undefined,\n onPopupCloseComplete: undefined,\n lastPointerPosition: null,\n }\n\n const mergedContext = { ...defaultContext, ...context }\n const mergedState = { ...createInitialState(), ...initialState }\n mergedState.normalizedSearch = mergedContext.normalizeSearch(\n mergedState.search,\n )\n\n super(mergedState, mergedContext, selectors)\n\n // Handle open/close\n this.observe('open', (open) => {\n if (open) {\n // Reset pointer position tracking on open to prevent phantom highlights\n this.resetPointerPosition()\n\n // Auto-highlight is now handled by applyAutoHighlight() called from Surface\n // after it has set the context. This ensures the correct value is used.\n // We only handle the simple boolean true case here for backwards compatibility\n // with components that don't use Surface (if any).\n const autoHighlight = this.context.autoHighlightFirst\n if (autoHighlight === true) {\n // Default: highlight first item\n this.highlightFirstItem()\n }\n // String values are handled by applyAutoHighlight() from Surface\n } else {\n // Clear search and highlight on close\n // When 'after-exit', search is cleared by Root after animation completes\n if (this.context.clearSearchOnClose === true) {\n this.setSearch('')\n }\n\n // When 'after-exit', also defer hiding the input until animation completes\n // This prevents the input from disappearing before the popup animates out\n const deferInputHide = this.context.clearSearchOnClose === 'after-exit'\n\n this.update({\n highlightedId: null,\n highlightSource: null,\n inputActive: deferInputHide ? this.state.inputActive : false,\n pendingSearch: '',\n })\n }\n })\n\n // Keep normalizedSearch in sync when search changes (including controlled props).\n this.observe('search', (search, prevSearch) => {\n if (search === prevSearch) {\n return\n }\n\n const normalizedSearch = this.context.normalizeSearch(search)\n if (normalizedSearch !== this.state.normalizedSearch) {\n this.set('normalizedSearch', normalizedSearch)\n }\n })\n\n // Recompute filtered items when normalized search changes.\n this.observe('normalizedSearch', (search, prevSearch) => {\n if (search !== prevSearch) {\n // Reset pointer position when search changes - content is about to shift\n // and we don't want stationary pointers to trigger phantom highlights\n this.resetPointerPosition()\n this.recomputeFilteredItems(prevSearch)\n }\n })\n }\n\n // ============================================================================\n // Actions\n // ============================================================================\n\n /**\n * Set the open state with event details.\n *\n * @param open - The new open state\n * @param reason - The reason for the state change (default: 'none')\n * @param event - The native DOM event that triggered the change (optional)\n */\n setOpen(\n open: boolean,\n reason: PopupMenuOpenChangeReason = REASONS.none,\n event?: Event,\n ) {\n const eventDetails = createChangeEventDetails(reason, event)\n\n // Call the user's callback first\n this.context.onOpenChange(open, eventDetails)\n\n // If the user canceled, don't update internal state\n if (eventDetails.isCanceled) {\n return\n }\n\n this.set('open', open)\n }\n\n setSearch(search: string) {\n this.update({\n search,\n normalizedSearch: this.context.normalizeSearch(search),\n })\n this.context.onSearchChange?.(search)\n // Note: recomputeFilteredItems is called by the 'normalizedSearch' observer\n }\n\n setSearchNormalizer(normalizeSearch: SearchNormalizer) {\n this.context.normalizeSearch = normalizeSearch\n\n const normalizedSearch = normalizeSearch(this.state.search)\n if (normalizedSearch !== this.state.normalizedSearch) {\n this.set('normalizedSearch', normalizedSearch)\n }\n }\n\n setHighlightedId(id: string | null, cause: HighlightSource = 'pointer') {\n const prevId = this.state.highlightedId\n if (prevId === id) return\n\n // Close any open submenus that are not the newly highlighted item\n // This ensures only one submenu is open at a time in this menu\n this.closeSiblingSubmenus(id)\n\n this.update({ highlightedId: id, highlightSource: cause })\n\n // Always notify about highlight changes (for virtualization, analytics, etc.)\n this.notifyHighlightChange(id, cause)\n\n // Handle scroll behavior for keyboard navigation\n if (cause === 'keyboard' && id !== null) {\n this.scrollItemIntoView(id)\n }\n }\n\n /**\n * Notify listeners about highlight changes.\n * Called whenever highlightedId changes, regardless of virtualization or DOM state.\n * Useful for virtualization scroll sync, analytics, or any other tracking needs.\n *\n * @param id - The newly highlighted item ID (or null if cleared)\n * @param cause - What caused the highlight change\n */\n private notifyHighlightChange(id: string | null, cause: HighlightSource) {\n const { onHighlightChange } = this.context\n if (!onHighlightChange) return\n\n // Map cause to reason\n const reason: HighlightChangeReason =\n cause === 'keyboard'\n ? REASONS.keyboard\n : cause === 'pointer'\n ? REASONS.pointer\n : REASONS.auto\n\n // Get the index of the highlighted item\n const index =\n id === null\n ? -1\n : this.state.virtualized\n ? this.getVirtualItemIndex(id)\n : this.getVisibleItemIndex(id)\n\n const eventDetails = createGenericEventDetails(reason, undefined, { index })\n onHighlightChange(id, index, eventDetails)\n }\n\n /**\n * Scroll the highlighted item into view.\n * Uses native scrollIntoView if the element is in the DOM.\n * For virtualized lists, the onHighlightChange callback (called from setHighlightedId)\n * should handle scrolling via the virtualizer.\n *\n * @param id - The item ID to scroll into view\n */\n private scrollItemIntoView(id: string) {\n const { refs } = this.context\n const listEl = refs.listRef.current\n const itemRef = refs.itemRefs.get(id)\n const itemEl = itemRef?.current\n\n // If the item element exists and is inside the list, use native scrollIntoView\n if (itemEl && listEl) {\n try {\n const isInList = listEl.contains(itemEl)\n if (isInList) {\n itemEl.scrollIntoView({ block: 'nearest' })\n }\n } catch {\n // Ignore errors from scrollIntoView\n }\n }\n // For virtualized lists where the item is not in the DOM,\n // the onHighlightChange callback handles scroll via virtualizer\n }\n\n setHasInput(hasInput: boolean) {\n this.set('hasInput', hasInput)\n }\n\n setInputActive(active: boolean) {\n this.set('inputActive', active)\n }\n\n setPendingSearch(search: string) {\n this.set('pendingSearch', search)\n }\n\n setHideUntilActive(enabled: boolean) {\n this.context.hideUntilActive = enabled\n // If enabling and there's already search content, activate immediately\n if (enabled && this.state.normalizedSearch.length > 0) {\n this.setInputActive(true)\n }\n }\n\n setVirtualized(virtualized: boolean) {\n this.set('virtualized', virtualized)\n }\n\n setVirtualItems(items: VirtualItem[]) {\n const prevItems = this.context.virtualItems\n this.context.virtualItems = items\n\n // Update count in state for selectors to use\n this.set('virtualItemsCount', items.length)\n\n // Pre-register all virtual items so filtering works for unmounted items\n this.preRegisterVirtualItems()\n\n // Skip if items reference didn't change (same array)\n if (items === prevItems) {\n return\n }\n\n // Skip if the navigation-relevant data hasn't changed.\n // Virtual items are often recreated (new array reference) when parent content\n // re-renders due to non-navigation changes (e.g. checkbox checked state).\n // Only trigger highlight validation when values or disabled states actually differ.\n if (prevItems.length === items.length) {\n let same = true\n for (let i = 0; i < prevItems.length; i++) {\n const prev = prevItems[i]\n const next = items[i]\n if (\n !prev ||\n !next ||\n prev.value !== next.value ||\n prev.disabled !== next.disabled\n ) {\n same = false\n break\n }\n }\n if (same) {\n return\n }\n }\n\n // Skip highlight validation if not in the right state\n if (!this.state.virtualized || !this.state.open || items.length === 0) {\n return\n }\n\n // Determine if we need to force highlight the first item\n const prevFirstItem = prevItems.find((item) => !item.disabled)\n const newFirstItem = items.find((item) => !item.disabled)\n // Only force \"first item\" behavior when there was a previous first item.\n // This avoids transient resets caused by virtualization effect cleanup\n // temporarily clearing virtual items between renders.\n const firstItemChanged =\n prevFirstItem !== undefined && newFirstItem?.value !== prevFirstItem.value\n\n // Validate and potentially update the highlight\n this.validateHighlight({ forceFirst: firstItemChanged })\n }\n\n /**\n * Set the consumer-provided ordered items.\n * Used when filter={false} and consumer controls item order/visibility.\n * Must always be provided when filter={false}.\n *\n * @param items - Array of item IDs in display order\n */\n setOrderedItems(items: string[], options?: SetOrderedItemsOptions) {\n const reason = options?.reason ?? 'replace'\n const prevItems = this.context.orderedItems\n this.context.orderedItems = items\n\n // Skip if items reference didn't change (same array)\n if (items === prevItems) {\n return\n }\n\n // Skip highlight update if not open\n if (!this.state.open) {\n return\n }\n\n // For append-only updates, preserve current highlight when it remains valid.\n if (reason === 'append' && this.state.highlightedId !== null) {\n const highlightedId = this.state.highlightedId\n const highlightedRegistration = this.context.items.get(highlightedId)\n const isStillInOrderedItems = items.includes(highlightedId)\n const isStillEnabled = highlightedRegistration\n ? !highlightedRegistration.disabled\n : false\n\n if (isStillInOrderedItems && isStillEnabled) {\n return\n }\n }\n\n // When ordered items change, highlight the first registered item\n // Use 'auto' source to indicate this is automatic (not user-initiated)\n // This prevents submenus from auto-opening\n if (items.length > 0) {\n const firstRegisteredItem = items.find((id) => this.context.items.has(id))\n if (firstRegisteredItem !== undefined) {\n this.setHighlightedId(firstRegisteredItem, 'auto')\n } else {\n this.setHighlightedId(null)\n }\n } else {\n this.setHighlightedId(null)\n }\n }\n\n /**\n * Try to auto-highlight when an item registers.\n * This handles the case where orderedItems was set before items mounted.\n * Only highlights if:\n * - filter={false} (using orderedItems)\n * - Menu is open\n * - No item is currently highlighted\n * - autoHighlightFirst is enabled\n * - The registering item is the first in orderedItems\n */\n private maybeAutoHighlightOnRegister(id: string) {\n if (this.context.filter !== false) {\n return\n }\n if (!this.state.open) {\n return\n }\n if (this.state.highlightedId !== null) {\n return\n }\n if (!this.context.autoHighlightFirst) {\n return\n }\n\n const orderedItems = this.context.orderedItems\n if (orderedItems.length === 0) {\n return\n }\n\n // Find the first item in orderedItems that is registered\n const firstRegisteredItem = orderedItems.find((itemId) =>\n this.context.items.has(itemId),\n )\n\n // Only highlight if this is the first registered item\n // Use 'auto' source to indicate this is automatic (not user-initiated)\n if (firstRegisteredItem === id) {\n this.setHighlightedId(id, 'auto')\n }\n }\n\n setOnHighlightChange(\n callback:\n | ((\n id: string | null,\n index: number,\n eventDetails: HighlightChangeEventDetails,\n ) => void)\n | undefined,\n ) {\n this.context.onHighlightChange = callback\n }\n\n // ============================================================================\n // DOM Refs Management\n // ============================================================================\n\n /**\n * Set the list element ref for scroll container detection.\n */\n setListRef(ref: React.RefObject<HTMLElement | null>) {\n this.context.refs.listRef = ref\n }\n\n /**\n * Register an item's DOM ref for scrollIntoView behavior.\n * Returns a cleanup function.\n */\n registerItemRef(\n id: string,\n ref: React.RefObject<HTMLElement | null>,\n ): () => void {\n this.context.refs.itemRefs.set(id, ref)\n return () => {\n this.context.refs.itemRefs.delete(id)\n }\n }\n\n // ============================================================================\n // Pointer Position Tracking\n // ============================================================================\n\n /**\n * Check if pointer has moved and should allow highlight.\n * This prevents \"phantom\" highlights when content shifts under a stationary pointer\n * (e.g., when search results change or menu items reorder).\n *\n * @param x - Current pointer X position\n * @param y - Current pointer Y position\n * @returns true if pointer has actually moved and highlight should be allowed\n */\n shouldAllowPointerHighlight(x: number, y: number): boolean {\n const last = this.context.lastPointerPosition\n if (last === null) {\n // First pointer event - record position and allow highlight\n this.context.lastPointerPosition = { x, y }\n return true\n }\n\n // Check if pointer has actually moved (with small tolerance for sub-pixel movements)\n const dx = Math.abs(x - last.x)\n const dy = Math.abs(y - last.y)\n const hasMoved = dx > 1 || dy > 1\n\n if (hasMoved) {\n // Update position and allow highlight\n this.context.lastPointerPosition = { x, y }\n return true\n }\n\n // Pointer hasn't moved - don't allow highlight\n return false\n }\n\n /**\n * Reset pointer position tracking.\n * Call this when the menu opens or content changes significantly.\n */\n resetPointerPosition() {\n this.context.lastPointerPosition = null\n }\n\n /**\n * Pre-register virtual items so they appear in filteredItems.\n * This allows filtering to work for items that aren't mounted yet.\n */\n private preRegisterVirtualItems() {\n const virtualItems = this.context.virtualItems\n if (virtualItems.length === 0) return\n\n // Register each virtual item (using value as the unique identifier)\n for (const item of virtualItems) {\n if (!this.context.items.has(item.value)) {\n this.context.items.set(item.value, {\n value: item.value,\n keywords: item.keywords,\n disabled: item.disabled,\n })\n }\n }\n\n // Recompute filtered items to include virtual items\n this.recomputeFilteredItems()\n }\n\n // ============================================================================\n // Item Registration\n // ============================================================================\n\n registerItem(id: string, registration: ItemRegistration): () => void {\n // Check if this item is already registered with the same properties\n // This optimization reduces unnecessary recomputation in virtualized mode\n const existing = this.context.items.get(id)\n const isSameRegistration =\n existing &&\n existing.value === registration.value &&\n existing.disabled === registration.disabled &&\n existing.forceOrder === registration.forceOrder &&\n existing.forceScore === registration.forceScore &&\n existing.groupId === registration.groupId &&\n existing.shortcut === registration.shortcut\n\n if (isSameRegistration) {\n // Item already registered with same properties, skip recompute\n return () => {\n // Only clean up if this item is still in the map\n // (another registration might have replaced it)\n if (this.context.items.get(id) === existing) {\n this.context.items.delete(id)\n this.context.itemSelects.delete(id)\n if (registration.groupId) {\n this.context.groups.get(registration.groupId)?.delete(id)\n }\n if (registration.shortcut) {\n this.context.shortcuts.delete(registration.shortcut.toLowerCase())\n }\n this.recomputeFilteredItems()\n }\n }\n }\n\n this.context.items.set(id, registration)\n\n // Add to group if specified\n if (registration.groupId) {\n const groupItems = this.context.groups.get(registration.groupId)\n if (groupItems) {\n groupItems.add(id)\n }\n }\n\n // Register shortcut if specified\n if (registration.shortcut) {\n const key = registration.shortcut.toLowerCase()\n this.context.shortcuts.set(key, id)\n }\n\n // Trigger recompute\n this.recomputeFilteredItems()\n\n // When filter={false} and we're open with no highlight, try to highlight\n // This handles the case where orderedItems was set before items mounted\n this.maybeAutoHighlightOnRegister(id)\n\n return () => {\n this.context.items.delete(id)\n this.context.itemSelects.delete(id)\n\n if (registration.groupId) {\n const groupItems = this.context.groups.get(registration.groupId)\n if (groupItems) {\n groupItems.delete(id)\n }\n }\n\n // Unregister shortcut\n if (registration.shortcut) {\n const key = registration.shortcut.toLowerCase()\n this.context.shortcuts.delete(key)\n }\n\n this.recomputeFilteredItems()\n }\n }\n\n registerGroup(id: string): () => void {\n this.context.groups.set(id, new Set())\n\n return () => {\n this.context.groups.delete(id)\n }\n }\n\n registerItemSelect(\n id: string,\n onSelect: (() => void) | undefined,\n ): () => void {\n if (onSelect) {\n this.context.itemSelects.set(id, onSelect)\n }\n return () => {\n this.context.itemSelects.delete(id)\n }\n }\n\n registerSubmenuOpen(\n id: string,\n onOpen: (() => void) | undefined,\n ): () => void {\n if (onOpen) {\n this.context.submenuOpens.set(id, onOpen)\n }\n return () => {\n this.context.submenuOpens.delete(id)\n }\n }\n\n registerSubmenuClose(\n id: string,\n onClose: (() => void) | undefined,\n ): () => void {\n if (onClose) {\n this.context.submenuCloses.set(id, onClose)\n }\n return () => {\n this.context.submenuCloses.delete(id)\n }\n }\n\n /**\n * Close all submenus except the one with the given ID.\n * Used when hovering over a new submenu trigger to close sibling submenus.\n */\n closeSiblingSubmenus(exceptId: string | null) {\n for (const [id, onClose] of this.context.submenuCloses) {\n if (id !== exceptId) {\n try {\n onClose()\n } catch {\n // Ignore errors from closing submenus\n }\n }\n }\n }\n\n // ============================================================================\n // Navigation\n // ============================================================================\n\n highlightNext() {\n const visibleIds = this.getVisibleItemIds()\n\n if (visibleIds.length === 0) return\n\n const currentIndex = this.state.highlightedId\n ? visibleIds.indexOf(this.state.highlightedId)\n : -1\n let nextIndex = currentIndex + 1\n\n if (nextIndex >= visibleIds.length) {\n nextIndex = this.context.loop ? 0 : visibleIds.length - 1\n }\n\n const nextId = visibleIds[nextIndex]\n\n if (nextId) {\n this.setHighlightedId(nextId, 'keyboard')\n }\n }\n\n highlightPrev() {\n const visibleIds = this.getVisibleItemIds()\n\n if (visibleIds.length === 0) return\n\n const currentIndex = this.state.highlightedId\n ? visibleIds.indexOf(this.state.highlightedId)\n : visibleIds.length\n let prevIndex = currentIndex - 1\n\n if (prevIndex < 0) {\n prevIndex = this.context.loop ? visibleIds.length - 1 : 0\n }\n\n const prevId = visibleIds[prevIndex]\n\n if (prevId) {\n this.setHighlightedId(prevId, 'keyboard')\n }\n }\n\n selectHighlighted() {\n if (this.state.highlightedId) {\n const onSelect = this.context.itemSelects.get(this.state.highlightedId)\n onSelect?.()\n }\n }\n\n /**\n * Select an item by its keyboard shortcut.\n * Returns true if an item was found and selected, false otherwise.\n */\n selectByShortcut(key: string): boolean {\n const itemId = this.context.shortcuts.get(key.toLowerCase())\n if (!itemId) return false\n\n const registration = this.context.items.get(itemId)\n if (!registration || registration.disabled) return false\n\n // Check if item is visible (passes filter)\n const score = this.state.filteredItems.get(itemId) ?? 0\n const isVisible = this.state.normalizedSearch.length === 0 || score > 0\n if (!isVisible) return false\n\n const onSelect = this.context.itemSelects.get(itemId)\n onSelect?.()\n return true\n }\n\n openSubmenuForHighlighted() {\n if (this.state.highlightedId) {\n const onOpen = this.context.submenuOpens.get(this.state.highlightedId)\n onOpen?.()\n }\n }\n\n isHighlightedSubmenuTrigger(): boolean {\n if (!this.state.highlightedId) return false\n return (\n this.context.items.get(this.state.highlightedId)?.isSubmenuTrigger ??\n false\n )\n }\n\n /**\n * Get the item registration for the highlighted item.\n * Returns undefined if no item is highlighted.\n */\n getHighlightedItem(): ItemRegistration | undefined {\n if (!this.state.highlightedId) return undefined\n return this.context.items.get(this.state.highlightedId)\n }\n\n clearSearch() {\n this.setSearch('')\n }\n\n highlightFirstItem() {\n const visibleIds = this.getVisibleItemIds()\n if (visibleIds.length > 0 && visibleIds[0]) {\n // Don't set a cause - auto-highlight shouldn't trigger scroll\n this.update({ highlightedId: visibleIds[0], highlightSource: null })\n } else {\n this.update({ highlightedId: null, highlightSource: null })\n }\n }\n\n /**\n * Apply auto-highlight based on the current context.autoHighlightFirst value.\n * Called by Surface after updating the context to ensure correct value is used.\n */\n applyAutoHighlight() {\n if (!this.state.open) return\n\n const autoHighlight = this.context.autoHighlightFirst\n if (autoHighlight === true) {\n this.highlightFirstItem()\n } else if (typeof autoHighlight === 'string') {\n this.highlightItemByValue(autoHighlight)\n }\n // If false, don't highlight anything\n }\n\n /**\n * Highlight a specific item by its value.\n * If the item is not visible or doesn't exist, falls back to highlighting the first item.\n * Scrolls the highlighted item into view.\n */\n highlightItemByValue(value: string) {\n const visibleIds = this.getVisibleItemIds()\n let highlightedId: string | null = null\n\n if (visibleIds.includes(value)) {\n // Item exists and is visible - highlight it\n highlightedId = value\n } else if (visibleIds.length > 0 && visibleIds[0]) {\n // Fall back to first item if specified value not found\n highlightedId = visibleIds[0]\n }\n\n this.update({ highlightedId, highlightSource: null })\n\n // Scroll the highlighted item into view\n // This is important when opening a combobox with a pre-selected value\n // that may be far down the list\n if (highlightedId) {\n // Use requestAnimationFrame to ensure the DOM has updated\n requestAnimationFrame(() => {\n this.scrollItemIntoView(highlightedId)\n })\n }\n }\n\n // ============================================================================\n // Internal Helpers\n // ============================================================================\n\n /**\n * Returns whether filtering is disabled (consumer handles filtering externally).\n */\n isFilterDisabled(): boolean {\n return this.context.filter === false\n }\n\n getVisibleItemIds(): string[] {\n const result: string[] = []\n const search = this.state.normalizedSearch\n const filteredItems = this.state.filteredItems\n const virtualItems = this.context.virtualItems\n const orderedItems = this.context.orderedItems\n\n // When virtualized with items, use the virtualItems order\n // This ensures navigation order matches the data array order\n if (this.state.virtualized && virtualItems.length > 0) {\n for (const item of virtualItems) {\n const score = filteredItems.get(item.value) ?? 0\n const isVisible = search.length === 0 || score > 0\n if (isVisible && !item.disabled) {\n result.push(item.value)\n }\n }\n return result\n }\n\n // When consumer provides ordered items (filter={false}), use that order\n // This ensures navigation matches the consumer's intended display order\n if (this.context.filter === false && orderedItems.length > 0) {\n // Track unregistered items for warning\n const unregisteredItems: string[] = []\n\n for (const itemId of orderedItems) {\n const registration = this.context.items.get(itemId)\n // Only include if registered (mounted) and not disabled\n if (registration && !registration.disabled) {\n result.push(itemId)\n } else if (!registration) {\n unregisteredItems.push(itemId)\n }\n }\n\n // Only warn about unregistered items if SOME items are registered\n // If no items are registered, we're likely in the initial mount phase\n // and items will register shortly via maybeAutoHighlightOnRegister\n if (\n process.env.NODE_ENV !== 'production' &&\n unregisteredItems.length > 0 &&\n result.length > 0\n ) {\n for (const itemId of unregisteredItems) {\n console.warn(\n `[ListboxStore] Item \"${itemId}\" is in orderedItems but not registered. ` +\n 'This may cause keyboard navigation to skip this item. ' +\n 'Make sure the render function passes the `id` prop: <Item {...props}>...</Item>',\n )\n }\n }\n\n return result\n }\n\n // Non-virtualized: use mounted items order\n this.context.items.forEach((registration, id) => {\n const score = filteredItems.get(id) ?? 0\n const isVisible = search.length === 0 || score > 0\n if (isVisible && !registration.disabled) {\n result.push(id)\n }\n })\n\n return result\n }\n\n /**\n * Get the index of an item in the visible items list.\n * Returns -1 if the item is not found or not visible.\n */\n getVisibleItemIndex(id: string): number {\n return this.getVisibleItemIds().indexOf(id)\n }\n\n /**\n * Get the index of an item in the virtualItems array.\n * This is used for virtualizer scrollToIndex which needs the raw array index,\n * not the filtered/visible index.\n * Returns -1 if not found or not in virtualized mode.\n */\n getVirtualItemIndex(value: string): number {\n if (!this.state.virtualized) return -1\n return this.context.virtualItems.findIndex((item) => item.value === value)\n }\n\n /**\n * Validates and updates the highlighted item.\n * This is the single source of truth for highlight management.\n *\n * @param options.forceFirst - Force highlight first item even if current is valid\n * @param options.filteredItems - Use this map instead of state (for mid-update calls)\n * @param options.newSearch - The search query for filteredItems (to detect search cleared)\n */\n private validateHighlight(\n options: ValidateHighlightOptions = {},\n ): string | null {\n const {\n forceFirst = false,\n filteredItems = this.state.filteredItems,\n newSearch,\n prevSearch: optionsPrevSearch,\n } = options\n\n // Determine if search changed (requires reset to first item)\n // Use prevSearch from options if provided (from observer), otherwise fall back to state\n const prevSearch =\n optionsPrevSearch !== undefined\n ? optionsPrevSearch\n : this.state.normalizedSearch\n const effectiveSearch = newSearch !== undefined ? newSearch : prevSearch\n const searchChanged = newSearch !== undefined && newSearch !== prevSearch\n\n // If not open or autoHighlightFirst disabled, don't change anything\n if (!this.state.open || !this.context.autoHighlightFirst) {\n return this.state.highlightedId\n }\n\n const currentHighlight = this.state.highlightedId\n const { filter } = this.context\n\n // If search changed, force reset to first item\n const shouldForceFirst = forceFirst || searchChanged\n\n // Check if current highlight is valid\n let isCurrentValid = false\n if (currentHighlight && !shouldForceFirst) {\n // Check if item exists and passes filter\n const score = filteredItems.get(currentHighlight) ?? 0\n const isVisible =\n effectiveSearch.length === 0 || filter === false || score > 0\n\n // Check if item is disabled\n const registration = this.context.items.get(currentHighlight)\n const virtualItem = this.context.virtualItems.find(\n (v) => v.value === currentHighlight,\n )\n const isDisabled =\n registration?.disabled ?? virtualItem?.disabled ?? false\n\n // Item must be registered (in items map) for non-virtualized mode\n const isRegistered = this.state.virtualized || registration !== undefined\n\n // In virtualized mode, item must also be in virtualItems array\n const inVirtualItems =\n !this.state.virtualized ||\n this.context.virtualItems.length === 0 ||\n virtualItem !== undefined\n\n isCurrentValid =\n isVisible && !isDisabled && isRegistered && inVirtualItems\n }\n\n // If current highlight is valid and we're not forcing first, keep it\n if (isCurrentValid) {\n return currentHighlight\n }\n\n // Find the first valid item to highlight\n let newHighlightId: string | null = null\n\n if (this.state.virtualized && this.context.virtualItems.length > 0) {\n // Virtualized mode: use virtualItems order\n for (const item of this.context.virtualItems) {\n const score = filteredItems.get(item.value) ?? 0\n const isVisible =\n effectiveSearch.length === 0 || filter === false || score > 0\n if (isVisible && !item.disabled) {\n newHighlightId = item.value\n break\n }\n }\n } else if (filter === false && this.context.orderedItems.length > 0) {\n // Consumer-controlled filtering: use orderedItems order\n for (const itemId of this.context.orderedItems) {\n const registration = this.context.items.get(itemId)\n // Only include if registered (mounted) and not disabled\n if (registration && !registration.disabled) {\n newHighlightId = itemId\n break\n }\n }\n } else {\n // Non-virtualized mode: use mounted items\n for (const [id, registration] of this.context.items) {\n const score = filteredItems.get(id) ?? 0\n const isVisible = effectiveSearch.length === 0 || score > 0\n if (isVisible && !registration.disabled) {\n newHighlightId = id\n break\n }\n }\n }\n\n // Only update if highlight actually changed\n if (newHighlightId !== currentHighlight) {\n this.update({\n highlightedId: newHighlightId,\n highlightSource: null, // Auto-highlight shouldn't trigger scroll\n })\n }\n\n return newHighlightId\n }\n\n private recomputeFilteredItems(prevSearch?: string) {\n const { filter } = this.context\n const normalizedSearch = this.state.normalizedSearch\n const items = this.context.items\n const groups = this.context.groups\n\n const filteredItems = new Map<string, number>()\n const visibleGroups = new Set<string>()\n let filteredCount = 0\n\n // If no search or filtering disabled, all items are visible\n if (!normalizedSearch || filter === false) {\n // When virtualized with consumer-side filtering (filter === false),\n // use virtualItems as the source of truth for what's visible.\n // This ensures the scores match the consumer's filtered array,\n // not just what's currently mounted.\n if (this.state.virtualized && this.context.virtualItems.length > 0) {\n for (const item of this.context.virtualItems) {\n filteredItems.set(item.value, 1)\n filteredCount++\n }\n } else {\n items.forEach((_, id) => {\n filteredItems.set(id, 1)\n filteredCount++\n })\n }\n groups.forEach((_, groupId) => {\n visibleGroups.add(groupId)\n })\n } else {\n // Apply filter function\n const filterFn = filter || commandScore\n items.forEach((registration, id) => {\n const fuzzyScore = filterFn(\n registration.value,\n normalizedSearch,\n registration.keywords,\n )\n const score = registration.forceScore ?? fuzzyScore\n filteredItems.set(id, score)\n if (score > 0) {\n filteredCount++\n if (registration.groupId) {\n visibleGroups.add(registration.groupId)\n }\n }\n })\n }\n\n // When filter={false}, consumer controls highlighting via setConsumerFilteredItems.\n // Don't call validateHighlight here because consumerFilteredItems hasn't been updated yet.\n // The highlight will be set when setConsumerFilteredItems is called from the Surface effect.\n if (filter === false) {\n this.update({\n filteredItems,\n visibleGroups,\n filteredCount,\n filterTrigger: this.state.filterTrigger + 1,\n // Don't change highlight - let setConsumerFilteredItems handle it\n })\n return\n }\n\n // Validate highlight using the newly computed filteredItems\n // We pass filteredItems, newSearch, and prevSearch here because we need to detect search cleared\n const highlightedId = this.validateHighlight({\n filteredItems,\n newSearch: normalizedSearch,\n prevSearch,\n })\n\n this.update({\n filteredItems,\n visibleGroups,\n filteredCount,\n filterTrigger: this.state.filterTrigger + 1,\n highlightedId,\n // Auto-highlight shouldn't trigger scroll\n highlightSource: null,\n })\n }\n\n // ============================================================================\n // Static Factory\n // ============================================================================\n\n static useStore(\n externalStore: ListboxStore | undefined,\n initialState?: Partial<ListboxState>,\n context?: Partial<ListboxContext>,\n ): ListboxStore {\n const store = useRefWithInit(() => {\n return externalStore ?? new ListboxStore(initialState, context)\n }).current\n\n return store\n }\n}\n\n// ============================================================================\n// Initial State Factory\n// ============================================================================\n\nfunction createInitialState(): ListboxState {\n return {\n open: false,\n search: '',\n normalizedSearch: '',\n highlightedId: null,\n highlightSource: null,\n hasInput: false,\n inputActive: false,\n pendingSearch: '',\n filteredItems: new Map(),\n visibleGroups: new Set(),\n filteredCount: 0,\n filterTrigger: 0,\n virtualized: false,\n virtualItemsCount: 0,\n }\n}\n\n// ============================================================================\n// Re-export types for convenience\n// ============================================================================\n\nexport type { ListboxState as State, ListboxContext as Context }\n","// ============================================================================\n// Event Reason Constants\n// ============================================================================\n// Centralized reason constants for all UI event handlers.\n// These provide type-safe reasons for WHY a state change occurred.\n\n// Trigger interactions\nexport const triggerPress = 'trigger-press' as const\nexport const triggerHover = 'trigger-hover' as const\nexport const triggerFocus = 'trigger-focus' as const\nexport const triggerContextMenu = 'trigger-context-menu' as const\n\n// Dismissal reasons\nexport const escapeKey = 'escape-key' as const\nexport const outsidePress = 'outside-press' as const\nexport const focusOut = 'focus-out' as const\n\n// Item interactions\nexport const itemPress = 'item-press' as const\nexport const itemKeyboardSelect = 'item-keyboard-select' as const\nexport const closePress = 'close-press' as const\n\n// Input interactions (Combobox)\nexport const inputChange = 'input-change' as const\nexport const inputClear = 'input-clear' as const\nexport const clearPress = 'clear-press' as const\n\n// Navigation\nexport const listNavigation = 'list-navigation' as const\n\n// Submenu/menu management\nexport const submenuTrigger = 'submenu-trigger' as const\nexport const siblingOpen = 'sibling-open' as const\n\n// Highlight changes\nexport const pointer = 'pointer' as const\nexport const keyboard = 'keyboard' as const\nexport const auto = 'auto' as const\n\n// Programmatic/other\nexport const imperativeAction = 'imperative-action' as const\nexport const none = 'none' as const\n","import { REASONS } from './reasons.js'\n\n// ============================================================================\n// Reason to Native Event Type Mapping\n// ============================================================================\n\n/**\n * Maps event reason strings to their corresponding native DOM event types.\n * This provides type-safe access to the native event based on the reason.\n */\ninterface ReasonToEventMap {\n [REASONS.none]: Event\n\n // Trigger interactions\n [REASONS.triggerPress]: MouseEvent | PointerEvent | TouchEvent | KeyboardEvent\n [REASONS.triggerHover]: MouseEvent | PointerEvent\n [REASONS.triggerFocus]: FocusEvent\n [REASONS.triggerContextMenu]: MouseEvent\n\n // Dismissal reasons\n [REASONS.escapeKey]: KeyboardEvent\n [REASONS.outsidePress]: MouseEvent | PointerEvent | TouchEvent\n [REASONS.focusOut]: FocusEvent | KeyboardEvent\n\n // Item interactions\n [REASONS.itemPress]: MouseEvent | PointerEvent | KeyboardEvent\n [REASONS.itemKeyboardSelect]: KeyboardEvent\n [REASONS.closePress]: MouseEvent | PointerEvent | KeyboardEvent\n\n // Input interactions\n [REASONS.inputChange]: InputEvent | Event\n [REASONS.inputClear]: InputEvent | FocusEvent | Event\n [REASONS.clearPress]: MouseEvent | PointerEvent | KeyboardEvent\n\n // Navigation\n [REASONS.listNavigation]: KeyboardEvent\n\n // Submenu/menu management\n [REASONS.submenuTrigger]: MouseEvent | PointerEvent | KeyboardEvent\n [REASONS.siblingOpen]: Event\n\n // Highlight changes\n [REASONS.pointer]: MouseEvent | PointerEvent\n [REASONS.keyboard]: KeyboardEvent\n\n // Programmatic\n [REASONS.imperativeAction]: Event\n}\n\n/**\n * Maps a reason string to the corresponding native event type.\n * Falls back to `Event` for unknown reasons.\n */\nexport type ReasonToEvent<Reason extends string> =\n Reason extends keyof ReasonToEventMap ? ReasonToEventMap[Reason] : Event\n\n// ============================================================================\n// Change Event Details\n// ============================================================================\n\n/**\n * Details object passed to change event handlers (onOpenChange, onValueChange, etc.)\n *\n * @template Reason - The union of allowed reason strings for this event\n * @template CustomProperties - Additional component-specific properties\n */\nexport interface ChangeEventDetails<\n Reason extends string,\n CustomProperties extends object = {},\n> {\n /**\n * The reason why this state change occurred.\n * Use this to conditionally handle different scenarios.\n *\n * @example\n * ```tsx\n * onOpenChange={(open, details) => {\n * if (details.reason === 'escape-key') {\n * // Handle escape key differently\n * }\n * }}\n * ```\n */\n reason: Reason\n\n /**\n * The native DOM event that triggered this change.\n * May be a synthetic event in some cases.\n */\n event: ReasonToEvent<Reason>\n\n /**\n * Cancels the state change.\n * When called, the component's internal state will not update.\n *\n * @example\n * ```tsx\n * onOpenChange={(open, details) => {\n * if (!open && details.reason === 'outside-press') {\n * details.cancel() // Prevent closing on outside press\n * }\n * }}\n * ```\n */\n cancel: () => void\n\n /**\n * Allows the native event to propagate.\n * By default, some events (like Escape key) stop propagation\n * to prevent parent popups from closing simultaneously.\n */\n allowPropagation: () => void\n\n /**\n * Whether `cancel()` has been called.\n */\n readonly isCanceled: boolean\n\n /**\n * Whether `allowPropagation()` has been called.\n */\n readonly isPropagationAllowed: boolean\n\n /**\n * The element that triggered this event, if applicable.\n */\n trigger: Element | undefined\n}\n\n/**\n * Creates a change event details object.\n *\n * @param reason - The reason for the state change\n * @param event - The native DOM event (optional)\n * @param trigger - The element that triggered the event (optional)\n * @param customProperties - Additional component-specific properties (optional)\n */\nexport function createChangeEventDetails<\n Reason extends string,\n CustomProperties extends object = {},\n>(\n reason: Reason,\n event?: ReasonToEvent<Reason> | Event,\n trigger?: Element,\n customProperties?: CustomProperties,\n): ChangeEventDetails<Reason, CustomProperties> {\n let canceled = false\n let propagationAllowed = false\n\n const details: ChangeEventDetails<Reason, CustomProperties> = {\n reason,\n event: (event ?? new Event('ui-event')) as ReasonToEvent<Reason>,\n cancel() {\n canceled = true\n },\n allowPropagation() {\n propagationAllowed = true\n },\n get isCanceled() {\n return canceled\n },\n get isPropagationAllowed() {\n return propagationAllowed\n },\n trigger,\n ...(customProperties as CustomProperties),\n }\n\n return details\n}\n\n// ============================================================================\n// Generic Event Details (for non-change events)\n// ============================================================================\n\n/**\n * Details object for generic events that don't support cancellation.\n * Used for events like onHighlightChange where cancellation doesn't make sense.\n *\n * @template Reason - The union of allowed reason strings for this event\n * @template CustomProperties - Additional component-specific properties\n */\nexport interface GenericEventDetails<\n Reason extends string,\n CustomProperties extends object = {},\n> {\n /**\n * The reason why this event occurred.\n */\n reason: Reason\n\n /**\n * The native DOM event that triggered this event.\n */\n event: ReasonToEvent<Reason>\n}\n\n/**\n * Creates a generic event details object (without cancel/propagation methods).\n */\nexport function createGenericEventDetails<\n Reason extends string,\n CustomProperties extends object = {},\n>(\n reason: Reason,\n event?: ReasonToEvent<Reason> | Event,\n customProperties?: CustomProperties,\n): GenericEventDetails<Reason, CustomProperties> {\n return {\n reason,\n event: (event ?? new Event('ui-event')) as ReasonToEvent<Reason>,\n ...(customProperties as CustomProperties),\n }\n}\n","/**\n * Fuzzy matching algorithm for command palette-style filtering.\n * Vendored from cmdk (https://github.com/pacocoursey/cmdk)\n *\n * The scores are arranged so that a continuous match of characters will\n * result in a total score of 1.\n */\n\n// The best case: this character is a match, and either this is the start\n// of the string, or the previous character was also a match.\nconst SCORE_CONTINUE_MATCH = 1\n\n// A new match at the start of a word scores better than a new match\n// elsewhere as it's more likely that the user will type the starts\n// of fragments.\n// NOTE: We score word jumps between spaces slightly higher than slashes, brackets, hyphens, etc.\nconst SCORE_SPACE_WORD_JUMP = 0.9\nconst SCORE_NON_SPACE_WORD_JUMP = 0.8\n\n// Any other match isn't ideal, but we include it for completeness.\nconst SCORE_CHARACTER_JUMP = 0.17\n\n// If the user transposed two letters, it should be significantly penalized.\n// i.e. \"ouch\" is more likely than \"curtain\" when \"uc\" is typed.\nconst SCORE_TRANSPOSITION = 0.1\n\n// The goodness of a match should decay slightly with each missing character.\n// i.e. \"bad\" is more likely than \"bard\" when \"bd\" is typed.\n// This will not change the order of suggestions based on SCORE_* until\n// 100 characters are inserted between matches.\nconst PENALTY_SKIPPED = 0.999\n\n// The goodness of an exact-case match should be higher than a\n// case-insensitive match by a small amount.\n// i.e. \"HTML\" is more likely than \"haml\" when \"HM\" is typed.\n// This will not change the order of suggestions based on SCORE_* until\n// 1000 characters are inserted between matches.\nconst PENALTY_CASE_MISMATCH = 0.9999\n\n// Match higher for letters closer to the beginning of the word\nconst PENALTY_DISTANCE_FROM_START = 0.9\n\n// If the word has more characters than the user typed, it should\n// be penalised slightly.\n// i.e. \"html\" is more likely than \"html5\" if I type \"html\".\n// However, it may well be the case that there's a sensible secondary\n// ordering (like alphabetical) that it makes sense to rely on when\n// there are many prefix matches, so we don't make the penalty increase\n// with the number of tokens.\nconst PENALTY_NOT_COMPLETE = 0.99\n\nconst IS_GAP_REGEXP = /[\\\\/_+.#\"@[({&]/\nconst COUNT_GAPS_REGEXP = /[\\\\/_+.#\"@[({&]/g\nconst IS_SPACE_REGEXP = /[\\s-]/\nconst COUNT_SPACE_REGEXP = /[\\s-]/g\n\ntype MemoizedResults = Record<string, number>\n\nfunction commandScoreInner(\n string: string,\n abbreviation: string,\n lowerString: string,\n lowerAbbreviation: string,\n stringIndex: number,\n abbreviationIndex: number,\n memoizedResults: MemoizedResults,\n): number {\n if (abbreviationIndex === abbreviation.length) {\n if (stringIndex === string.length) {\n return SCORE_CONTINUE_MATCH\n }\n return PENALTY_NOT_COMPLETE\n }\n\n const memoizeKey = `${stringIndex},${abbreviationIndex}`\n if (memoizedResults[memoizeKey] !== undefined) {\n return memoizedResults[memoizeKey]\n }\n\n const abbreviationChar = lowerAbbreviation.charAt(abbreviationIndex)\n let index = lowerString.indexOf(abbreviationChar, stringIndex)\n let highScore = 0\n\n let score: number\n let transposedScore: number\n let wordBreaks: RegExpMatchArray | null\n let spaceBreaks: RegExpMatchArray | null\n\n while (index >= 0) {\n score = commandScoreInner(\n string,\n abbreviation,\n lowerString,\n lowerAbbreviation,\n index + 1,\n abbreviationIndex + 1,\n memoizedResults,\n )\n\n if (score > highScore) {\n if (index === stringIndex) {\n score *= SCORE_CONTINUE_MATCH\n } else if (IS_GAP_REGEXP.test(string.charAt(index - 1))) {\n score *= SCORE_NON_SPACE_WORD_JUMP\n wordBreaks = string\n .slice(stringIndex, index - 1)\n .match(COUNT_GAPS_REGEXP)\n if (wordBreaks && stringIndex > 0) {\n score *= PENALTY_SKIPPED ** wordBreaks.length\n }\n } else if (IS_SPACE_REGEXP.test(string.charAt(index - 1))) {\n score *= SCORE_SPACE_WORD_JUMP\n spaceBreaks = string\n .slice(stringIndex, index - 1)\n .match(COUNT_SPACE_REGEXP)\n if (spaceBreaks && stringIndex > 0) {\n score *= PENALTY_SKIPPED ** spaceBreaks.length\n }\n } else {\n score *= SCORE_CHARACTER_JUMP\n if (stringIndex > 0) {\n score *= PENALTY_SKIPPED ** (index - stringIndex)\n }\n }\n\n if (string.charAt(index) !== abbreviation.charAt(abbreviationIndex)) {\n score *= PENALTY_CASE_MISMATCH\n }\n }\n\n if (\n (score < SCORE_TRANSPOSITION &&\n lowerString.charAt(index - 1) ===\n lowerAbbreviation.charAt(abbreviationIndex + 1)) ||\n (lowerAbbreviation.charAt(abbreviationIndex + 1) ===\n lowerAbbreviation.charAt(abbreviationIndex) &&\n lowerString.charAt(index - 1) !==\n lowerAbbreviation.charAt(abbreviationIndex))\n ) {\n transposedScore = commandScoreInner(\n string,\n abbreviation,\n lowerString,\n lowerAbbreviation,\n index + 1,\n abbreviationIndex + 2,\n memoizedResults,\n )\n\n if (transposedScore * SCORE_TRANSPOSITION > score) {\n score = transposedScore * SCORE_TRANSPOSITION\n }\n }\n\n if (score > highScore) {\n highScore = score\n }\n\n index = lowerString.indexOf(abbreviationChar, index + 1)\n }\n\n memoizedResults[memoizeKey] = highScore\n return highScore\n}\n\nfunction formatInput(string: string): string {\n // Convert all valid space characters to space so they match each other\n return string.toLowerCase().replace(COUNT_SPACE_REGEXP, ' ')\n}\n\n/**\n * Calculates a fuzzy match score between a string and an abbreviation.\n *\n * @param string - The string to match against\n * @param abbreviation - The search query\n * @param keywords - Optional additional keywords to include in matching\n * @returns A score between 0 and 1, where 1 is a perfect match and 0 is no match\n */\nexport function commandScore(\n string: string,\n abbreviation: string,\n keywords?: string[],\n): number {\n if (!abbreviation) return 1\n if (!string) return 0\n\n // Combine string with keywords for matching\n const fullString =\n keywords && keywords.length > 0 ? `${string} ${keywords.join(' ')}` : string\n\n return commandScoreInner(\n fullString,\n abbreviation,\n formatInput(fullString),\n formatInput(abbreviation),\n 0,\n 0,\n {},\n )\n}\n\n/**\n * Default filter function for listbox items.\n * Returns a score > 0 for matches, 0 for non-matches.\n */\nexport const defaultFilter = commandScore\n"],"mappings":"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","names":["React","GroupContext","useGroupContext","React","ItemContext","useItemContext","context","useMaybeItemContext","React","ListboxContext","useListboxContext","context","useMaybeListboxContext","React","RowWidthContext","useRowWidthContext","context","useMaybeRowWidthContext","React","SurfaceContext","useSurfaceContext","context","useMaybeSurfaceContext","React","normalizeValue","value","slugify","useListboxItem","params","idProp","valueProp","keywords","disabled","forceMount","shortcut","forceOrder","forceScore","isSubmenuTrigger","onSelect","closeOnClick","onAfterSelect","children","aimGuard","store","useSurfaceContext","groupContext","useGroupContext","depth","useListboxContext","ref","inferredValue","setInferredValue","textContent","registrationId","normalizeValue","generatedDomId","domId","normalizedKeywords","k","keywordsKey","rowWidthContext","useMaybeRowWidthContext","normalizedSearch","isHighlighted","score","filterDisabled","hasSearch","isVisible","handleClick","event","handlePointerDown","handlePointerMove","aimGuardActiveRef","guardedDepthRef","contextValue","registerSelect","handler","handlers","React","useListboxKeyboard","params","store","surfaceId","enabled","onKeyDown","onSelect","closeAll","focusOwner","depth","submenuContext","subpageContext","enableTypeToSearch","skipFocusOwnerCheck","focusOwnerIsOwner","isOwner","event","hideUntilActive","inputActive","shortcutItemId","item","selectedId","hasActiveSearch","itemId","React","DEBUG_ROW_WIDTH","DEBUG_FREEZE_MEASUREMENT","debugLog","args","px","parseCssPixelValue","value","parsed","useStickyRowWidth","options","listRef","targetRef","maxWidth","enabled","getTargetElement","maxSeenRef","el","existingWidth","readQueue","writeQueue","scheduled","measuredIds","applyVar","width","capped","schedule","measurements","foundNewMax","element","id","prevWidth","prevMaxWidth","computed","rect","write","queueMeasurement","resetMeasurements","prevMax","prevCount","container","rafId","ro","entries","createSelector","ReactStore","useRefWithInit","reason_parts_exports","__export","auto","clearPress","closePress","escapeKey","focusOut","imperativeAction","inputChange","inputClear","itemKeyboardSelect","itemPress","keyboard","listNavigation","none","outsidePress","pointer","siblingOpen","submenuTrigger","triggerContextMenu","triggerFocus","triggerHover","triggerPress","createChangeEventDetails","reason","event","trigger","customProperties","canceled","propagationAllowed","createGenericEventDetails","IS_GAP_REGEXP","COUNT_GAPS_REGEXP","IS_SPACE_REGEXP","COUNT_SPACE_REGEXP","commandScoreInner","string","abbreviation","lowerString","lowerAbbreviation","stringIndex","abbreviationIndex","memoizedResults","memoizeKey","abbreviationChar","index","highScore","score","transposedScore","wordBreaks","spaceBreaks","formatInput","commandScore","keywords","fullString","defaultFilter","selectors","createSelector","state","itemId","groupId","defaultSearchNormalizer","search","normalizeValue","ListboxStore","_ListboxStore","ReactStore","initialState","context","mergedContext","commandScore","mergedState","createInitialState","open","deferInputHide","prevSearch","normalizedSearch","reason","reason_parts_exports","event","eventDetails","createChangeEventDetails","normalizeSearch","id","cause","onHighlightChange","index","createGenericEventDetails","refs","listEl","itemEl","hasInput","active","enabled","virtualized","items","prevItems","same","i","prev","next","prevFirstItem","item","newFirstItem","firstItemChanged","options","highlightedId","highlightedRegistration","isStillInOrderedItems","isStillEnabled","firstRegisteredItem","orderedItems","callback","ref","x","y","last","dx","dy","virtualItems","registration","existing","groupItems","key","onSelect","onOpen","onClose","exceptId","visibleIds","nextIndex","nextId","prevIndex","prevId","score","autoHighlight","value","result","filteredItems","unregisteredItems","forceFirst","newSearch","optionsPrevSearch","effectiveSearch","searchChanged","currentHighlight","filter","shouldForceFirst","isCurrentValid","isVisible","virtualItem","v","isDisabled","isRegistered","inVirtualItems","newHighlightId","groups","visibleGroups","filteredCount","_","filterFn","fuzzyScore","externalStore","useRefWithInit"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/ui/ui/packages/react/dist/chunk-M4G6J7DP.cjs"],"names":[],"mappings":"AAAA,6EAAI,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc","file":"/home/runner/work/ui/ui/packages/react/dist/chunk-M4G6J7DP.cjs"}
|