@mortenbrudvik/waveui 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +413 -53
- package/dist/components/data-display/InfoButton.cjs +1 -1
- package/dist/components/data-display/InfoButton.cjs.map +1 -1
- package/dist/components/data-display/InfoButton.mjs +90 -82
- package/dist/components/data-display/InfoButton.mjs.map +1 -1
- package/dist/components/data-display/InfoLabel.cjs +1 -1
- package/dist/components/data-display/InfoLabel.cjs.map +1 -1
- package/dist/components/data-display/InfoLabel.mjs +19 -18
- package/dist/components/data-display/InfoLabel.mjs.map +1 -1
- package/dist/components/data-display/List.cjs +1 -1
- package/dist/components/data-display/List.cjs.map +1 -1
- package/dist/components/data-display/List.mjs +405 -219
- package/dist/components/data-display/List.mjs.map +1 -1
- package/dist/components/data-display/List.registry.cjs +1 -1
- package/dist/components/data-display/List.registry.cjs.map +1 -1
- package/dist/components/data-display/List.registry.mjs +12 -6
- package/dist/components/data-display/List.registry.mjs.map +1 -1
- package/dist/components/feedback/MessageBarGroup.cjs +2 -0
- package/dist/components/feedback/MessageBarGroup.cjs.map +1 -0
- package/dist/components/feedback/MessageBarGroup.mjs +63 -0
- package/dist/components/feedback/MessageBarGroup.mjs.map +1 -0
- package/dist/components/feedback/ProgressBar.cjs +1 -1
- package/dist/components/feedback/ProgressBar.cjs.map +1 -1
- package/dist/components/feedback/ProgressBar.mjs +41 -39
- package/dist/components/feedback/ProgressBar.mjs.map +1 -1
- package/dist/components/feedback/Toaster.cjs +1 -1
- package/dist/components/feedback/Toaster.cjs.map +1 -1
- package/dist/components/feedback/Toaster.mjs +222 -216
- package/dist/components/feedback/Toaster.mjs.map +1 -1
- package/dist/components/input/Combobox.cjs +1 -1
- package/dist/components/input/Combobox.cjs.map +1 -1
- package/dist/components/input/Combobox.mjs +163 -148
- package/dist/components/input/Combobox.mjs.map +1 -1
- package/dist/components/input/Dropdown.cjs +1 -1
- package/dist/components/input/Dropdown.cjs.map +1 -1
- package/dist/components/input/Dropdown.mjs +132 -117
- package/dist/components/input/Dropdown.mjs.map +1 -1
- package/dist/components/input/Listbox.cjs +1 -1
- package/dist/components/input/Listbox.cjs.map +1 -1
- package/dist/components/input/Listbox.mjs +90 -78
- package/dist/components/input/Listbox.mjs.map +1 -1
- package/dist/components/input/Option.cjs +1 -1
- package/dist/components/input/Option.cjs.map +1 -1
- package/dist/components/input/Option.data.cjs +2 -0
- package/dist/components/input/Option.data.cjs.map +1 -0
- package/dist/components/input/Option.data.mjs +114 -0
- package/dist/components/input/Option.data.mjs.map +1 -0
- package/dist/components/input/Option.mjs +21 -17
- package/dist/components/input/Option.mjs.map +1 -1
- package/dist/components/layout/Accordion.cjs +1 -1
- package/dist/components/layout/Accordion.cjs.map +1 -1
- package/dist/components/layout/Accordion.mjs +124 -109
- package/dist/components/layout/Accordion.mjs.map +1 -1
- package/dist/components/layout/Carousel.cjs +1 -1
- package/dist/components/layout/Carousel.cjs.map +1 -1
- package/dist/components/layout/Carousel.mjs +207 -193
- package/dist/components/layout/Carousel.mjs.map +1 -1
- package/dist/components/layout/FlatTree.cjs +2 -0
- package/dist/components/layout/FlatTree.cjs.map +1 -0
- package/dist/components/layout/FlatTree.context.cjs +2 -0
- package/dist/components/layout/FlatTree.context.cjs.map +1 -0
- package/dist/components/layout/FlatTree.context.mjs +20 -0
- package/dist/components/layout/FlatTree.context.mjs.map +1 -0
- package/dist/components/layout/FlatTree.item.cjs +2 -0
- package/dist/components/layout/FlatTree.item.cjs.map +1 -0
- package/dist/components/layout/FlatTree.item.mjs +90 -0
- package/dist/components/layout/FlatTree.item.mjs.map +1 -0
- package/dist/components/layout/FlatTree.mjs +276 -0
- package/dist/components/layout/FlatTree.mjs.map +1 -0
- package/dist/components/layout/TabList.cjs +1 -1
- package/dist/components/layout/TabList.cjs.map +1 -1
- package/dist/components/layout/TabList.mjs +149 -108
- package/dist/components/layout/TabList.mjs.map +1 -1
- package/dist/components/layout/Tree.cjs.map +1 -1
- package/dist/components/layout/Tree.context.cjs +1 -1
- package/dist/components/layout/Tree.context.cjs.map +1 -1
- package/dist/components/layout/Tree.context.mjs +2 -1
- package/dist/components/layout/Tree.context.mjs.map +1 -1
- package/dist/components/layout/Tree.core.cjs +1 -1
- package/dist/components/layout/Tree.core.cjs.map +1 -1
- package/dist/components/layout/Tree.core.mjs +162 -148
- package/dist/components/layout/Tree.core.mjs.map +1 -1
- package/dist/components/layout/Tree.mjs.map +1 -1
- package/dist/components/layout/Tree.selection.cjs +1 -1
- package/dist/components/layout/Tree.selection.cjs.map +1 -1
- package/dist/components/layout/Tree.selection.mjs +11 -57
- package/dist/components/layout/Tree.selection.mjs.map +1 -1
- package/dist/components/layout/useFlatTree.cjs +2 -0
- package/dist/components/layout/useFlatTree.cjs.map +1 -0
- package/dist/components/layout/useFlatTree.mjs +83 -0
- package/dist/components/layout/useFlatTree.mjs.map +1 -0
- package/dist/components/navigation/Menu.popover.cjs +1 -1
- package/dist/components/navigation/Menu.popover.cjs.map +1 -1
- package/dist/components/navigation/Menu.popover.mjs +154 -168
- package/dist/components/navigation/Menu.popover.mjs.map +1 -1
- package/dist/components/navigation/Nav.cjs +1 -1
- package/dist/components/navigation/Nav.cjs.map +1 -1
- package/dist/components/navigation/Nav.mjs +156 -136
- package/dist/components/navigation/Nav.mjs.map +1 -1
- package/dist/components/navigation/NavDrawer.cjs +1 -1
- package/dist/components/navigation/NavDrawer.cjs.map +1 -1
- package/dist/components/navigation/NavDrawer.mjs +48 -45
- package/dist/components/navigation/NavDrawer.mjs.map +1 -1
- package/dist/components/overlays/Dialog.cjs +1 -1
- package/dist/components/overlays/Dialog.cjs.map +1 -1
- package/dist/components/overlays/Dialog.mjs +128 -122
- package/dist/components/overlays/Dialog.mjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.cjs +1 -1
- package/dist/components/overlays/Dialog.slots.cjs.map +1 -1
- package/dist/components/overlays/Dialog.slots.mjs +12 -12
- package/dist/components/overlays/Dialog.slots.mjs.map +1 -1
- package/dist/components/overlays/Drawer.cjs.map +1 -1
- package/dist/components/overlays/Drawer.mjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.cjs +1 -1
- package/dist/components/overlays/Drawer.panel.cjs.map +1 -1
- package/dist/components/overlays/Drawer.panel.mjs +97 -81
- package/dist/components/overlays/Drawer.panel.mjs.map +1 -1
- package/dist/components/overlays/Drawer.root.cjs +1 -1
- package/dist/components/overlays/Drawer.root.cjs.map +1 -1
- package/dist/components/overlays/Drawer.root.mjs +34 -31
- package/dist/components/overlays/Drawer.root.mjs.map +1 -1
- package/dist/components/overlays/Popover.cjs +1 -1
- package/dist/components/overlays/Popover.cjs.map +1 -1
- package/dist/components/overlays/Popover.mjs +231 -216
- package/dist/components/overlays/Popover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.parts.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.parts.mjs +199 -177
- package/dist/components/overlays/TeachingPopover.parts.mjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs +1 -1
- package/dist/components/overlays/TeachingPopover.root.cjs.map +1 -1
- package/dist/components/overlays/TeachingPopover.root.mjs +93 -92
- package/dist/components/overlays/TeachingPopover.root.mjs.map +1 -1
- package/dist/components/overlays/Tooltip.cjs +1 -1
- package/dist/components/overlays/Tooltip.cjs.map +1 -1
- package/dist/components/overlays/Tooltip.mjs +66 -61
- package/dist/components/overlays/Tooltip.mjs.map +1 -1
- package/dist/components/provider/WaveProvider.cjs +1 -1
- package/dist/components/provider/WaveProvider.cjs.map +1 -1
- package/dist/components/provider/WaveProvider.mjs +26 -23
- package/dist/components/provider/WaveProvider.mjs.map +1 -1
- package/dist/components/table/DataGrid.cjs +1 -1
- package/dist/components/table/DataGrid.cjs.map +1 -1
- package/dist/components/table/DataGrid.context.cjs +2 -0
- package/dist/components/table/DataGrid.context.cjs.map +1 -0
- package/dist/components/table/DataGrid.context.mjs +74 -0
- package/dist/components/table/DataGrid.context.mjs.map +1 -0
- package/dist/components/table/DataGrid.header.cjs +2 -0
- package/dist/components/table/DataGrid.header.cjs.map +1 -0
- package/dist/components/table/DataGrid.header.mjs +168 -0
- package/dist/components/table/DataGrid.header.mjs.map +1 -0
- package/dist/components/table/DataGrid.mjs +488 -591
- package/dist/components/table/DataGrid.mjs.map +1 -1
- package/dist/components/table/DataGrid.sizing.cjs +2 -0
- package/dist/components/table/DataGrid.sizing.cjs.map +1 -0
- package/dist/components/table/DataGrid.sizing.mjs +187 -0
- package/dist/components/table/DataGrid.sizing.mjs.map +1 -0
- package/dist/components/table/DataGrid.store.cjs +2 -0
- package/dist/components/table/DataGrid.store.cjs.map +1 -0
- package/dist/components/table/DataGrid.store.mjs +108 -0
- package/dist/components/table/DataGrid.store.mjs.map +1 -0
- package/dist/components/table/DataGrid.window.cjs +2 -0
- package/dist/components/table/DataGrid.window.cjs.map +1 -0
- package/dist/components/table/DataGrid.window.mjs +165 -0
- package/dist/components/table/DataGrid.window.mjs.map +1 -0
- package/dist/components/table/Table.cjs +1 -1
- package/dist/components/table/Table.cjs.map +1 -1
- package/dist/components/table/Table.mjs +74 -72
- package/dist/components/table/Table.mjs.map +1 -1
- package/dist/components/table/Table.resize.cjs +2 -0
- package/dist/components/table/Table.resize.cjs.map +1 -0
- package/dist/components/table/Table.resize.mjs +98 -0
- package/dist/components/table/Table.resize.mjs.map +1 -0
- package/dist/components/table/useGridNavigation.cjs +1 -1
- package/dist/components/table/useGridNavigation.cjs.map +1 -1
- package/dist/components/table/useGridNavigation.mjs +343 -140
- package/dist/components/table/useGridNavigation.mjs.map +1 -1
- package/dist/components/table/useTableColumnSizing.cjs +2 -0
- package/dist/components/table/useTableColumnSizing.cjs.map +1 -0
- package/dist/components/table/useTableColumnSizing.mjs +76 -0
- package/dist/components/table/useTableColumnSizing.mjs.map +1 -0
- package/dist/components/table/useTableFeatures.cjs +2 -0
- package/dist/components/table/useTableFeatures.cjs.map +1 -0
- package/dist/components/table/useTableFeatures.mjs +110 -0
- package/dist/components/table/useTableFeatures.mjs.map +1 -0
- package/dist/components/table/useTableSelection.cjs +2 -0
- package/dist/components/table/useTableSelection.cjs.map +1 -0
- package/dist/components/table/useTableSelection.mjs +39 -0
- package/dist/components/table/useTableSelection.mjs.map +1 -0
- package/dist/components/table/useTableSort.cjs +2 -0
- package/dist/components/table/useTableSort.cjs.map +1 -0
- package/dist/components/table/useTableSort.mjs +58 -0
- package/dist/components/table/useTableSort.mjs.map +1 -0
- package/dist/hooks/useActiveDescendant.cjs +1 -1
- package/dist/hooks/useActiveDescendant.cjs.map +1 -1
- package/dist/hooks/useActiveDescendant.mjs +33 -25
- package/dist/hooks/useActiveDescendant.mjs.map +1 -1
- package/dist/hooks/useCollapsePresence.cjs +2 -0
- package/dist/hooks/useCollapsePresence.cjs.map +1 -0
- package/dist/hooks/useCollapsePresence.mjs +21 -0
- package/dist/hooks/useCollapsePresence.mjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.cjs +2 -0
- package/dist/hooks/useFocusBeforeClose.cjs.map +1 -0
- package/dist/hooks/useFocusBeforeClose.mjs +33 -0
- package/dist/hooks/useFocusBeforeClose.mjs.map +1 -0
- package/dist/hooks/useListbox.cjs +1 -1
- package/dist/hooks/useListbox.cjs.map +1 -1
- package/dist/hooks/useListbox.mjs +140 -130
- package/dist/hooks/useListbox.mjs.map +1 -1
- package/dist/hooks/useMotion.cjs +2 -0
- package/dist/hooks/useMotion.cjs.map +1 -0
- package/dist/hooks/useMotion.mjs +14 -0
- package/dist/hooks/useMotion.mjs.map +1 -0
- package/dist/hooks/usePresenceGroup.cjs +2 -0
- package/dist/hooks/usePresenceGroup.cjs.map +1 -0
- package/dist/hooks/usePresenceGroup.mjs +68 -0
- package/dist/hooks/usePresenceGroup.mjs.map +1 -0
- package/dist/hooks/useRovingTabIndex.cjs +1 -1
- package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
- package/dist/hooks/useRovingTabIndex.mjs +1 -1
- package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
- package/dist/hooks/useSelection.cjs +2 -0
- package/dist/hooks/useSelection.cjs.map +1 -0
- package/dist/hooks/useSelection.mjs +57 -0
- package/dist/hooks/useSelection.mjs.map +1 -0
- package/dist/hooks/useVirtualWindow.cjs +2 -0
- package/dist/hooks/useVirtualWindow.cjs.map +1 -0
- package/dist/hooks/useVirtualWindow.mjs +102 -0
- package/dist/hooks/useVirtualWindow.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2249 -179
- package/dist/index.d.ts +2249 -179
- package/dist/index.mjs +147 -135
- package/dist/lib/classNames.cjs +1 -1
- package/dist/lib/classNames.cjs.map +1 -1
- package/dist/lib/classNames.mjs +125 -95
- package/dist/lib/classNames.mjs.map +1 -1
- package/dist/lib/columnSizing.cjs +2 -0
- package/dist/lib/columnSizing.cjs.map +1 -0
- package/dist/lib/columnSizing.mjs +27 -0
- package/dist/lib/columnSizing.mjs.map +1 -0
- package/dist/lib/flatTree.cjs +2 -0
- package/dist/lib/flatTree.cjs.map +1 -0
- package/dist/lib/flatTree.mjs +118 -0
- package/dist/lib/flatTree.mjs.map +1 -0
- package/dist/lib/motion.cjs +2 -0
- package/dist/lib/motion.cjs.map +1 -0
- package/dist/lib/motion.mjs +43 -0
- package/dist/lib/motion.mjs.map +1 -0
- package/dist/lib/selection.cjs +2 -0
- package/dist/lib/selection.cjs.map +1 -0
- package/dist/lib/selection.mjs +28 -0
- package/dist/lib/selection.mjs.map +1 -0
- package/dist/lib/treeCascade.cjs +2 -0
- package/dist/lib/treeCascade.cjs.map +1 -0
- package/dist/lib/treeCascade.mjs +52 -0
- package/dist/lib/treeCascade.mjs.map +1 -0
- package/dist/lib/virtual.cjs +2 -0
- package/dist/lib/virtual.cjs.map +1 -0
- package/dist/lib/virtual.mjs +54 -0
- package/dist/lib/virtual.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListbox.mjs","names":["useId","useId$1","React","EMPTY_ITEMS","EMPTY_SET","getEmptyItems","sameSet","a","b","value","sameItem","sameItems","item","index","compareRegistrations","ea","eb","position","ListboxStoreImpl","selectedValues","listener","registration","sorted","seen","next","el","element","enabled","i","moved","recorded","changed","active","selected","hidden","target","isDev","warnIfPageWide","ancestor","count","warnDuplicateValue","doc","warnOnce","ListboxContext","inertContext","getInertContext","useListboxContext","useContext","reportMissingContext","componentName","LISTBOX_ELEMENT_KIND","markListboxElement","component","kind","kindOf","type","textOf","node","text","child","optionLabel","props","walkOptions","children","getElementType","labels","label","collectOptionLabels","hasText","TEXT_SHORTCUTS","editsText","event","key","isAltGraphCharacter","preventMouseDown","useListbox","onOpenChangeProp","dataItems","mode","options","standalone","onOpenChange","listElement","setListElement","useState","listboxId","idPrefix","store","dataMode","registered","useSyncExternalStore","allItems","navigable","useMemo","filter","navigationValues","disabledOptionsFocusable","itemByValue","map","hiddenSet","visible","selectedSet","firstSelected","fallback","autoHighlight","getOptionId","useCallback","getItem","ad","useActiveDescendant","open","highlightOnFilter","commit","useEventCallback","fallbackItem","onSelect","multiselect","reason","select","press","keyEventRef","useRef","useTypeahead","onKeyDown","altGraph","altKey","selectOnly","editable","first","last","openWith","commitOrClose","activeValue","onClearDraft","typeahead","onTypeahead","onKeyUp","useLayoutEffect","useEffect","context","activeDescendantId","FLAG_ACTIVE","FLAG_SELECTED","FLAG_HIDDEN","optionFlags","useListboxOption","hiddenByConsumer","id","flags","elementRef","setElement","mergedRef","useMergedRefs","ref","registrationRef","textValue","disabled","current","onClick","onPointerMove","highlight","onMouseDown","optionProps"],"sources":["../../src/hooks/useListbox.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { getElementType } from '../lib/children';\nimport { isDev, reportMissingContext, warnOnce } from '../lib/dev';\nimport type { OpenChangeDetails } from '../lib/types';\nimport { useActiveDescendant } from './useActiveDescendant';\nimport { useEventCallback } from './useEventCallback';\nimport { useId } from './useId';\nimport { useMergedRefs } from './useMergedRefs';\nimport { isAltGraphCharacter, useTypeahead } from './useTypeahead';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\n/**\n * One option of a listbox: an entry of {@link UseListboxOptions.items} (data mode), or what an\n * option registers (registration mode, {@link useListboxOption}).\n */\nexport interface ListboxItem {\n /** Unique value within the listbox. */\n value: string;\n /** Display label (Combobox input text, Dropdown trigger text). */\n label: string;\n /** Text matched by typeahead and filters instead of `label`. */\n textValue?: string;\n /**\n * Disabled options are rendered but skipped by navigation, unless\n * {@link UseListboxOptions.disabledOptionsFocusable}, and never committed.\n * @default false\n */\n disabled?: boolean;\n /**\n * Hidden options are left out of navigation and rendering like filtered-out ones: never\n * highlighted, reached by typeahead or committed with the keyboard (registered options render\n * `hidden`). Their label stays known ({@link UseListboxResult.getItem}). In data mode (`items`)\n * it is the item's own flag; a registered option is hidden by its own `hidden` attribute or by a\n * hidden group around it.\n * @default false\n */\n hidden?: boolean;\n}\n\n/**\n * Why {@link UseListboxOptions.onOpenChange} was called: `'keyboard'` (a key opens the listbox, or\n * closes it without a commit), `'select'` (a commit closes it), `'escape'` or `'tab'` (Tab closes\n * it, after committing the active option in single-select select-only mode).\n */\nexport type ListboxOpenChangeReason = 'keyboard' | 'select' | 'escape' | 'tab';\n\n/**\n * Second argument of {@link UseListboxOptions.onSelect}: the committed option and the event behind\n * the commit.\n */\nexport interface ListboxSelectDetails {\n /** The committed option. */\n item: ListboxItem;\n /** The key or click event behind the commit. */\n event: Event;\n}\n\n/** Options of {@link useListbox}. */\nexport interface UseListboxOptions {\n /**\n * Whether the listbox is shown. You own the open state: `onOpenChange` asks you to change it.\n * Standalone mode: whether the list has focus — an option is active only while it does.\n */\n open: boolean;\n /**\n * Called when a key (or a commit) wants to open or close the listbox. `details.reason` says why\n * (`'keyboard'`, `'select'` — a commit that closes the list —, `'escape'` or `'tab'`) and\n * `details.event` is the key or click event behind it. WaveUI always passes `details`; it is\n * typed optional until 1.0 so that code which calls this prop itself keeps compiling. Never\n * called in standalone mode.\n */\n onOpenChange?: (open: boolean, details?: OpenChangeDetails<ListboxOpenChangeReason>) => void;\n /**\n * `'editable'`: a text input combobox (Combobox, TagPicker, TimePicker).\n * `'select-only'`: a button/div combobox without text entry (Dropdown).\n * `'standalone'`: a listbox that holds focus itself (Listbox), with nothing to open or close:\n * `open` is its focus state, the keys move and commit (a commit keeps the committed option\n * active), Tab, Escape and Alt+Arrow keys are left to the page, `onOpenChange` is never called,\n * and a pointer press on an option focuses the list and activates the option without scrolling\n * (see {@link UseListboxResult.getListboxProps}).\n */\n mode: 'editable' | 'select-only' | 'standalone';\n /**\n * Several values can be selected: the list gets `aria-multiselectable`, and a commit keeps the\n * listbox open on the committed option (your `onSelect` adds or removes the value).\n * @default false\n */\n multiselect?: boolean;\n /** The selected values (`[]` when nothing is selected). */\n selectedValues: readonly string[];\n /**\n * Called when an option is committed: click, Enter/Space, and in single-select select-only mode\n * also Tab and Alt+ArrowUp (with `multiselect` they close without committing). `details.item` is\n * the committed option and `details.event` the key or click event behind the commit. WaveUI\n * always passes `details`; it is typed optional until 1.0 so that code which calls this prop\n * itself keeps compiling.\n */\n onSelect: (value: string, details?: ListboxSelectDetails) => void;\n /**\n * Data mode: the options, in list order. Omit it for registration mode, where the options\n * register themselves in DOM order: `Option`s, or option components of your own on\n * {@link useListboxOption}, rendered inside the listbox's context\n * ({@link UseListboxResult.context}, which `ListboxSurface` and `ListboxProvider` provide).\n */\n items?: readonly ListboxItem[];\n /**\n * The navigability predicate: an item it returns `false` for is filtered out — left out of the\n * keys, typeahead and {@link UseListboxResult.items}, never active, and rendered `hidden` as a\n * registered option (its label stays known). It is never called for a hidden item, which stays\n * out anyway. A picker builds it from its query, the text typed so far: by default, Combobox\n * keeps the options whose `textValue ?? label` contains it, ignoring case, and a component with\n * a `filter(option, query)` prop builds this predicate by binding its current query to it.\n * Without it, every item that is not hidden is navigable.\n */\n filter?: (item: ListboxItem) => boolean;\n /** Arrow keys wrap around at the ends. @default false */\n loop?: boolean;\n /**\n * Printable characters move to the matching option (on by default in select-only and standalone\n * mode).\n * @default mode !== 'editable'\n */\n typeahead?: boolean;\n /**\n * Keeps disabled options in the arrow-key, Home/End, PageUp/PageDown and typeahead order: they\n * still cannot be committed (Enter, Space and a click do nothing and the list stays open), and\n * while one is active, Tab and Alt+ArrowUp in single-select select-only mode close the listbox\n * without a commit. The `autoHighlight` fallback may then be a disabled option too.\n * @default false\n */\n disabledOptionsFocusable?: boolean;\n /**\n * The option that is active while no option is highlighted: `'selected'` — the first selected\n * navigable option in list order (not in `selectedValues` order), else the first option;\n * `'first'` — the first option; `false` — none. A highlighted option that leaves the navigable\n * set is dropped (the fallback takes over, also when the option returns); in editable mode a\n * text-editing key clears the highlight.\n * @default 'selected'\n */\n autoHighlight?: 'selected' | 'first' | false;\n /**\n * Whenever the options the keys reach (the navigable ones, the disabled ones only with\n * `disabledOptionsFocusable`) change while open, compared by content — as when typing filters\n * them — the first of them becomes active, also when the update that opens the listbox changes\n * them (compared with those before opening, as the keystroke that opens a filtered list does).\n * Opening with unchanged options keeps the `autoHighlight` start. It sets\n * {@link useActiveDescendant}'s `activateFirstOnChange`. In editable mode every text-editing key\n * while open makes the first option active as well, also when the options stay the same.\n * @default false\n */\n highlightOnFilter?: boolean;\n /**\n * Called after the active (highlighted) option changes — arrow keys, typeahead, the pointer, a\n * filter change, opening — and with `null` when the listbox closes. Passed straight through to\n * {@link useActiveDescendant}'s `onActiveValueChange`.\n */\n onActiveValueChange?: (value: string | null) => void;\n /** Prefix of the generated listbox id. @default 'listbox' */\n idPrefix?: string;\n /**\n * Editable: Escape with the listbox closed and text in the input calls this (and prevents the\n * default), so you can clear the typed text. Not handled when omitted.\n */\n onClearDraft?: () => void;\n}\n\n/** Props for the combobox element (spread them; see {@link UseListboxResult.getComboboxProps}). */\nexport interface ListboxComboboxProps {\n role: 'combobox';\n /** {@link UseListboxOptions.open}. */\n 'aria-expanded': boolean;\n /** The listbox's id ({@link UseListboxResult.listboxId}). */\n 'aria-controls': string;\n /** The active option's id, while the listbox is open and an option is active. */\n 'aria-activedescendant'?: string;\n 'aria-haspopup': 'listbox';\n /** Editable mode: typing filters the options. */\n 'aria-autocomplete'?: 'list';\n}\n\n/** Props for the listbox element (see {@link UseListboxResult.getListboxProps}). */\nexport interface ListboxListProps {\n /** {@link UseListboxResult.listboxId}. */\n id: string;\n role: 'listbox';\n /** With {@link UseListboxOptions.multiselect}. */\n 'aria-multiselectable'?: true;\n /** `-1` in the combobox modes (focus stays on the combobox); `0` in standalone mode. */\n tabIndex: 0 | -1;\n /** The combobox modes: keeps focus on the combobox while options are clicked. */\n onMouseDown?(event: React.MouseEvent): void;\n /** Standalone mode: the active option's id while the list has focus and an option is active. */\n 'aria-activedescendant'?: string;\n /**\n * Standalone mode: the list element, which a pointer press on an option focuses. Merge it with\n * your own ref (a press warns in development when it was dropped).\n */\n ref?: React.RefCallback<HTMLElement>;\n}\n\n/** Result of {@link useListbox}. */\nexport interface UseListboxResult {\n /** Id of the listbox element. */\n listboxId: string;\n /**\n * The active (highlighted) option, derived during render; `null` while closed (in standalone\n * mode, while the list does not have focus).\n */\n activeValue: string | null;\n /** `getOptionId(activeValue)` while open and an option is active. */\n activeDescendantId: string | undefined;\n /**\n * The navigable items in DOM/data order: without filtered-out and hidden items; disabled ones\n * are included (navigation skips them, unless\n * {@link UseListboxOptions.disabledOptionsFocusable}).\n */\n items: ListboxItem[];\n /** Unfiltered lookup (registered options or `items`), e.g. for the selected option's label. */\n getItem(value: string): ListboxItem | undefined;\n /** `${listboxId}-opt-${n}`; `n` is assigned the first time a value is seen and never changes. */\n getOptionId(value: string): string;\n /**\n * Highlights `value` while open, scrolled into view like a keyboard highlight; `null` returns the\n * highlight to the `autoHighlight` option. The value is checked in the next render: if it is not\n * navigable then (disabled options count as not navigable unless\n * {@link UseListboxOptions.disabledOptionsFocusable}), it is dropped like `null`, and the\n * highlight returns to the `autoHighlight` option, or to none. A kept value is dropped when it\n * leaves the navigable set later. A value set while closed survives only when the same update\n * opens the listbox.\n */\n setActiveValue(value: string | null): void;\n /**\n * Attach to the combobox element (to the list in standalone mode). Ignores key events a handler\n * of yours already prevented (compose yours first).\n */\n onKeyDown(event: React.KeyboardEvent): void;\n /**\n * Attach to the combobox element next to `onKeyDown`. Required for a `<button>` combobox\n * (select-only): it prevents the Space keyup, so the native click does not toggle the listbox\n * again after the keydown opened or committed.\n */\n onKeyUp(event: React.KeyboardEvent): void;\n /**\n * Props for the combobox element (spread them): its role, `aria-expanded`, `aria-controls`,\n * `aria-haspopup`, `aria-activedescendant` while an option is active and, in editable mode,\n * `aria-autocomplete`. Not used in standalone mode, where the list itself has focus.\n */\n getComboboxProps(): ListboxComboboxProps;\n /**\n * Props for the listbox element (spread them). The combobox modes: `tabIndex: -1` and an\n * `onMouseDown` that keeps focus on the combobox while options are clicked. Standalone mode:\n * `tabIndex: 0`, `aria-activedescendant` while an option is active, and a `ref` (merge it with\n * yours: a pointer press on an option focuses the list through it), without `onMouseDown`.\n * Both: `aria-multiselectable` with `multiselect`.\n */\n getListboxProps(): ListboxListProps;\n /**\n * The context of the listbox's options: `ListboxSurface` provides it; for a list you render\n * yourself, pass it to `ListboxProvider` around the options.\n */\n context: ListboxContextValue;\n}\n\n/**\n * The per-option state of a listbox ({@link ListboxContextValue.store}) and the contract of option\n * components: {@link useListboxOption} is built on it, and so can an option component of your own\n * be. `register` adds an option, `getIndex` gives its stable id, and the flags say how to draw it:\n * subscribe with `useSyncExternalStore` and read one value (`() => store.isActive(value)`), so only\n * the options whose answer changed re-render.\n */\nexport interface ListboxStore {\n /** Calls `listener` after a flag of any option changed. Returns the unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** Whether `value` is the active option, the one `aria-activedescendant` points at. */\n isActive(value: string): boolean;\n /** Whether `value` is selected. */\n isSelected(value: string): boolean;\n /** Whether `value` is filtered out or hidden: render its option `hidden`. */\n isHidden(value: string): boolean;\n /**\n * The stable per-listbox index of `value`, assigned the first time the value is seen: the\n * option's id is `${listboxId}-opt-${getIndex(value)}` ({@link UseListboxResult.getOptionId}),\n * the same across filtering and remounts.\n */\n getIndex(value: string): number;\n /**\n * Registers an option and its element (registration mode): call it from a layout effect, as\n * {@link useListboxOption} does, and call the returned function to unregister, when the option\n * unmounts or before it registers a changed item. The options are ordered by their elements'\n * DOM position. The registrations of a commit are published once from the listbox's layout\n * effect (later ones once per microtask).\n */\n register(item: ListboxItem, element: React.RefObject<HTMLElement | null>): () => void;\n}\n\n/**\n * The context a listbox provides to its options (`useListbox(...).context`; pass it to\n * `ListboxProvider` for a list rendered without `ListboxSurface`).\n */\nexport interface ListboxContextValue {\n /** The listbox element's id, which the option ids start with. */\n listboxId: string;\n /** The per-option state and the registration of the options. */\n store: ListboxStore;\n /** Whether the listbox is {@link UseListboxOptions.multiselect}. */\n multiselect: boolean;\n /** The listbox's {@link UseListboxOptions.mode}: standalone options add the pointer press. */\n mode: 'editable' | 'select-only' | 'standalone';\n /**\n * Commits `value` (an option click): `item` stands for the option while it has not registered\n * yet, and `event` is the click behind the commit. A disabled option is never committed.\n */\n select(value: string, item: ListboxItem, event: Event): void;\n /** Highlights `value` (pointer movement); not scrolled into view, so the list stays put. */\n highlight(value: string): void;\n /**\n * Standalone mode: a pointer press on option `value` (its `mousedown`, whose default the option\n * prevents). Focuses the list without scrolling and makes the option active without scrolling\n * it into view, in one update, so the click that follows commits the pressed option even when\n * focus would have scrolled another option into view. A press on an option that cannot be\n * active (a disabled one, unless {@link UseListboxOptions.disabledOptionsFocusable}) keeps a\n * focused list's active option, and an unfocused list starts on its `autoHighlight` option,\n * not scrolled into view either. Development warns once when the list element is unknown\n * (the `ref` of {@link UseListboxResult.getListboxProps} was not passed on).\n */\n press(value: string): void;\n}\n\n/**\n * Props of {@link useListboxOption}. The option's position in the listbox follows its element's\n * DOM position, also when the option is memoized and only moved (see {@link useListbox}).\n */\nexport interface UseListboxOptionProps {\n /** The option's value, unique within the listbox. */\n value: string;\n /**\n * Display label; falls back to `textValue`, then the element's text content, then `value`. The\n * text content is re-read after every render of the option. Text that a child component changes\n * from its own state (without the option re-rendering) is not seen; pass `label` or `textValue`\n * for such options.\n */\n label?: string;\n /** Text matched by typeahead and filters instead of the label. */\n textValue?: string;\n /**\n * The option can never be committed; the keys skip it unless the listbox has\n * {@link UseListboxOptions.disabledOptionsFocusable}.\n * @default false\n */\n disabled?: boolean;\n /**\n * The option is hidden (its own `hidden` attribute, or a hidden group around it): it registers\n * as a hidden item ({@link ListboxItem.hidden}), so it is not navigable, and\n * `optionProps.hidden` is set from the first render (server included).\n * @default false\n */\n hidden?: boolean;\n}\n\n/**\n * Props for an option element (spread them onto the `<li>`; compose `onClick`, and `onMouseDown`\n * in standalone mode, with yours).\n */\nexport interface ListboxOptionElementProps<E extends HTMLElement = HTMLElement> {\n /**\n * The option's stable id ({@link UseListboxResult.getOptionId}), which `aria-activedescendant`\n * points at while the option is active.\n */\n id: string;\n role: 'option';\n 'aria-selected': boolean;\n /** While disabled. */\n 'aria-disabled'?: true;\n /** While filtered out or hidden. */\n hidden?: boolean;\n /** While the option is active, for styling. */\n 'data-active'?: '';\n /** While selected, for styling. */\n 'data-selected'?: '';\n /** While disabled, for styling. */\n 'data-disabled'?: '';\n /** Commits the option (a disabled one does nothing). */\n onClick(event: React.MouseEvent<E>): void;\n /** Makes the option under the pointer active (not a disabled one), without scrolling the list. */\n onPointerMove(event: React.PointerEvent<E>): void;\n /** Standalone mode only: the pointer press ({@link ListboxContextValue.press}). */\n onMouseDown?(event: React.MouseEvent<E>): void;\n /** Registers the element; it also sets the `ref` passed to {@link useListboxOption}. */\n ref: React.RefCallback<E>;\n}\n\n/** Result of {@link useListboxOption}. */\nexport interface UseListboxOptionResult<E extends HTMLElement = HTMLElement> {\n /** The option's stable id. */\n id: string;\n /** Whether the option is selected. */\n selected: boolean;\n /** Whether the option is active (keyboard or pointer highlight). */\n active: boolean;\n /** Whether the option is disabled. */\n disabled: boolean;\n /** Filtered out, or hidden by its own props ({@link UseListboxOptionProps.hidden}). */\n hidden: boolean;\n /** Whether the surrounding listbox is {@link UseListboxOptions.multiselect}. */\n multiselect: boolean;\n /** Spread onto the option element (see {@link ListboxOptionElementProps}). */\n optionProps: ListboxOptionElementProps<E>;\n}\n\n/* ------------------------------------------------------------------ */\n/* Store */\n/* ------------------------------------------------------------------ */\n\ninterface Registration {\n item: ListboxItem;\n element: React.RefObject<HTMLElement | null>;\n seq: number;\n}\n\nconst EMPTY_ITEMS: ListboxItem[] = [];\nconst EMPTY_SET: ReadonlySet<string> = new Set<string>();\n\nfunction getEmptyItems(): ListboxItem[] {\n return EMPTY_ITEMS;\n}\n\nfunction sameSet(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a === b) return true;\n if (a.size !== b.size) return false;\n for (const value of a) if (!b.has(value)) return false;\n return true;\n}\n\nfunction sameItem(a: ListboxItem, b: ListboxItem): boolean {\n return (\n a.value === b.value &&\n a.label === b.label &&\n a.textValue === b.textValue &&\n !!a.disabled === !!b.disabled &&\n !!a.hidden === !!b.hidden\n );\n}\n\nfunction sameItems(a: readonly ListboxItem[], b: readonly ListboxItem[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n return a.every((item, index) => sameItem(item, b[index]));\n}\n\nfunction compareRegistrations(a: Registration, b: Registration): number {\n const ea = a.element.current;\n const eb = b.element.current;\n if (ea && eb && ea !== eb && ea.isConnected && eb.isConnected) {\n const position = ea.compareDocumentPosition(eb);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n }\n return a.seq - b.seq;\n}\n\nclass ListboxStoreImpl implements ListboxStore {\n private readonly indexByValue = new Map<string, number>();\n private readonly registrations = new Set<Registration>();\n private readonly stateListeners = new Set<() => void>();\n private readonly itemListeners = new Set<() => void>();\n private nextIndex = 0;\n private nextSeq = 0;\n private dirty = false;\n private flushScheduled = false;\n private itemsStale = false;\n private itemsSnapshot: ListboxItem[] = EMPTY_ITEMS;\n /** The registrations in the order of the last sort (the order {@link checkOrder} verifies). */\n private sorted: Registration[] = [];\n /** Watches the common ancestor of the option elements for moves ({@link syncObserver}). */\n private observer: MutationObserver | null = null;\n private observed: Node | null = null;\n /**\n * The observer has recorded every option move since the order was last verified, so a root\n * commit without `childList` records needs no {@link checkOrder} ({@link checkOrderAfterCommit}).\n * Cleared whenever the observed node changes: moves before `observe()` were not recorded.\n */\n private watching = false;\n /** The listbox root is mounted (between {@link connect} and {@link disconnect}). */\n private connected = false;\n private registrationMode = true;\n private active: string | null = null;\n private selected: ReadonlySet<string>;\n private hidden: ReadonlySet<string> = EMPTY_SET;\n\n constructor(selectedValues: readonly string[]) {\n this.selected = new Set(selectedValues);\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n };\n\n subscribeItems = (listener: () => void): (() => void) => {\n this.itemListeners.add(listener);\n return () => {\n this.itemListeners.delete(listener);\n };\n };\n\n isActive = (value: string): boolean => this.active === value;\n isSelected = (value: string): boolean => this.selected.has(value);\n isHidden = (value: string): boolean => this.hidden.has(value);\n\n getIndex = (value: string): number => {\n let index = this.indexByValue.get(value);\n if (index === undefined) {\n index = this.nextIndex++;\n this.indexByValue.set(value, index);\n }\n return index;\n };\n\n register = (item: ListboxItem, element: React.RefObject<HTMLElement | null>): (() => void) => {\n const registration: Registration = { item, element, seq: this.nextSeq++ };\n this.registrations.add(registration);\n this.markDirty();\n return () => {\n this.registrations.delete(registration);\n this.markDirty();\n };\n };\n\n /** Registration-mode snapshot: registered options in DOM order, first of each value. */\n getItems = (): ListboxItem[] => {\n if (this.itemsStale) {\n this.itemsStale = false;\n const sorted = Array.from(this.registrations).sort(compareRegistrations);\n this.sorted = sorted;\n const seen = new Set<string>();\n const next: ListboxItem[] = [];\n for (const { item } of sorted) {\n if (seen.has(item.value)) continue;\n seen.add(item.value);\n next.push(item);\n }\n if (!sameItems(next, this.itemsSnapshot)) this.itemsSnapshot = next;\n }\n return this.itemsSnapshot;\n };\n\n /** The mounted element of an option (for scrolling); passed on detached, hence an arrow. */\n getElement = (value: string): HTMLElement | null => {\n for (const { item, element } of this.registrations) {\n const el = element.current;\n if (item.value === value && el && el.isConnected) return el;\n }\n return null;\n };\n\n setRegistrationMode(enabled: boolean): void {\n if (enabled === this.registrationMode) return;\n this.registrationMode = enabled;\n this.syncObserver();\n }\n\n /** The listbox root mounted (layout effect): start watching the option elements for moves. */\n connect(): void {\n this.connected = true;\n this.syncObserver();\n }\n\n /** The listbox root unmounted: stop watching. */\n disconnect(): void {\n this.connected = false;\n this.syncObserver();\n }\n\n /** Publishes pending registrations: one notification for everything registered since the last. */\n flush(): void {\n if (!this.dirty) return;\n this.dirty = false;\n this.itemsStale = true;\n if (this.registrationMode) this.warnDuplicates();\n this.syncObserver();\n this.notifyItems();\n }\n\n /**\n * Re-sorts (and publishes) when the registered elements are no longer in document order. A keyed\n * reorder (a sort toggle, re-ranked results) moves option elements without registering them\n * again, so the order is verified after a commit of the listbox root that moved option elements\n * ({@link checkOrderAfterCommit}) and whenever the option elements move ({@link syncObserver}):\n * neighbours of the last sort are compared (n - 1 `compareDocumentPosition` calls), and only an\n * inversion publishes. Pending registrations are left to {@link flush}, which sorts anyway.\n */\n checkOrder(): void {\n if (!this.registrationMode || this.dirty || this.itemsStale) return;\n const sorted = this.sorted;\n for (let i = 1; i < sorted.length; i++) {\n const a = sorted[i - 1].element.current;\n const b = sorted[i].element.current;\n if (!a || !b || a === b || !a.isConnected || !b.isConnected) continue;\n if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING) {\n this.itemsStale = true;\n this.notifyItems();\n return;\n }\n }\n }\n\n /**\n * The listbox root committed (layout effect): verifies the order when the commit may have moved\n * option elements. While the observer watches them, the moves of this commit are its pending\n * `childList` records (the callback runs only in a later microtask): they are taken here, so a\n * keyed reorder re-sorts in the same commit and is not checked again by the callback, and a\n * commit without records (a highlight move) costs O(1). Without an observer (fewer than two\n * options, no `MutationObserver`) or right after it started watching, every commit checks.\n */\n checkOrderAfterCommit(): void {\n // Only childList is observed, so every pending record is an insertion or removal.\n const moved = this.observer !== null && this.observer.takeRecords().length > 0;\n const recorded = this.watching;\n this.watching = this.observed !== null;\n if (recorded && !moved) return;\n this.checkOrder();\n }\n\n /** Syncs the root's derived render state for the option selectors (layout effect). */\n setRenderState(\n active: string | null,\n selected: ReadonlySet<string>,\n hidden: ReadonlySet<string>,\n ): void {\n let changed = false;\n if (active !== this.active) {\n this.active = active;\n changed = true;\n }\n if (!sameSet(selected, this.selected)) {\n this.selected = selected;\n changed = true;\n }\n if (!sameSet(hidden, this.hidden)) {\n this.hidden = hidden;\n changed = true;\n }\n if (changed) for (const listener of Array.from(this.stateListeners)) listener();\n }\n\n private notifyItems(): void {\n for (const listener of Array.from(this.itemListeners)) listener();\n }\n\n private readonly onMutation = (): void => {\n this.checkOrder();\n };\n\n /**\n * Observes (`childList`, `subtree`) the closest common ancestor of the connected option elements,\n * so a move that renders neither the listbox root nor the options — memoized options or hoisted\n * elements reordered by a wrapper component inside the listbox — still triggers\n * {@link checkOrder}. Recomputed when the registrations change (a remounted list registers\n * again); nothing is observed with fewer than two options, in data mode, before the root mounted\n * or without `MutationObserver` (server). Attribute and text changes are not observed.\n *\n * All options must live in one container at a time (consumer contract): options split over an\n * inline and a portaled list have `<body>` as their common ancestor, so every DOM mutation on\n * the page would re-check the order — development warns once.\n */\n private syncObserver(): void {\n const target =\n this.connected && this.registrationMode && typeof MutationObserver !== 'undefined'\n ? this.commonAncestor()\n : null;\n if (target === this.observed) return;\n this.observer?.disconnect();\n this.observed = target;\n this.watching = false;\n if (!target) return;\n if (isDev) warnIfPageWide(target);\n this.observer ??= new MutationObserver(this.onMutation);\n this.observer.observe(target, { childList: true, subtree: true });\n }\n\n private commonAncestor(): Node | null {\n let ancestor: Node | null = null;\n let count = 0;\n for (const { element } of this.registrations) {\n const el = element.current;\n if (!el || !el.isConnected) continue;\n count++;\n if (ancestor === null) {\n ancestor = el.parentNode;\n continue;\n }\n while (ancestor && !ancestor.contains(el)) ancestor = ancestor.parentNode;\n if (!ancestor) return null;\n }\n return count > 1 ? ancestor : null;\n }\n\n private markDirty(): void {\n this.dirty = true;\n if (this.flushScheduled) return;\n this.flushScheduled = true;\n queueMicrotask(() => {\n this.flushScheduled = false;\n this.flush();\n });\n }\n\n private warnDuplicates(): void {\n const seen = new Set<string>();\n for (const { item } of this.registrations) {\n if (seen.has(item.value)) warnDuplicateValue(item.value);\n seen.add(item.value);\n }\n }\n}\n\nfunction warnDuplicateValue(value: string): void {\n warnOnce(\n `useListbox:duplicate:${value}`,\n `Listbox: several options share the value \"${value}\". Option values must be unique within ` +\n 'a listbox; only the first one can be highlighted and selected.',\n );\n}\n\nfunction warnIfPageWide(ancestor: Node): void {\n const doc = ancestor.ownerDocument;\n if (!doc || (ancestor !== doc.body && ancestor !== doc.documentElement)) return;\n warnOnce(\n 'useListbox:single-container',\n 'Listbox: the options of one listbox are rendered in more than one container (for example an ' +\n 'inline list kept mounted next to a portaled one). All options must live in a single ' +\n 'container at a time: render the list inline only while closed and in the portal only ' +\n 'while open.',\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Context */\n/* ------------------------------------------------------------------ */\n\n/**\n * The listbox context of the options, provided by `ListboxProvider` (and `ListboxSurface`, which\n * renders one). Not public: a context object would need flat names for its `Provider` and\n * `Consumer` members.\n */\nexport const ListboxContext: React.Context<ListboxContextValue | null> =\n React.createContext<ListboxContextValue | null>(null);\nListboxContext.displayName = 'ListboxContext';\n\nlet inertContext: ListboxContextValue | null = null;\n\nfunction getInertContext(): ListboxContextValue {\n inertContext ??= {\n listboxId: 'wave-listbox-inert',\n store: new ListboxStoreImpl([]),\n multiselect: false,\n mode: 'select-only',\n select: () => {},\n highlight: () => {},\n press: () => {},\n };\n return inertContext;\n}\n\nfunction useListboxContext(componentName: string): ListboxContextValue {\n const context = useContext(ListboxContext);\n if (context) return context;\n reportMissingContext(\n componentName,\n 'a listbox (Listbox, Combobox, Dropdown or a ListboxProvider)',\n );\n return getInertContext();\n}\n\n/* ------------------------------------------------------------------ */\n/* collectOptionLabels */\n/* ------------------------------------------------------------------ */\n\nconst LISTBOX_ELEMENT_KIND = Symbol.for('@mortenbrudvik/waveui/listbox-element-kind');\n\n/**\n * What {@link collectOptionLabels} does with the elements of a component marked by\n * {@link markListboxElement}: `'option'` reads a value and its label, `'group'` walks the children.\n */\nexport type ListboxElementKind = 'option' | 'group';\n\n/**\n * Marks a component so {@link collectOptionLabels} recognises its elements: `'option'` (reads its\n * `value` prop and its label: the `label` prop, else `textValue`, else its text children, as\n * `Option`'s) or `'group'` (walks its `children`). Returns the component (function, `memo` or\n * `forwardRef` components). Mark every option or group component of your own that is rendered in\n * a listbox (`Option` and `OptionGroup` are marked): unmarked components are opaque to\n * {@link collectOptionLabels}, so their labels are unknown on the server and in the first client\n * render.\n *\n * @example\n * // Option components of your own, built on useListboxOption:\n * export const ColorOption = markListboxElement(ColorOptionImpl, 'option');\n * export const ColorGroup = markListboxElement(ColorGroupImpl, 'group');\n */\nexport function markListboxElement<C extends object>(component: C, kind: ListboxElementKind): C {\n Object.defineProperty(component, LISTBOX_ELEMENT_KIND, { value: kind, configurable: true });\n return component;\n}\n\nfunction kindOf(type: unknown): ListboxElementKind | undefined {\n if ((typeof type !== 'function' && typeof type !== 'object') || type === null) return undefined;\n const kind = (type as { [LISTBOX_ELEMENT_KIND]?: unknown })[LISTBOX_ELEMENT_KIND];\n return kind === 'option' || kind === 'group' ? kind : undefined;\n}\n\ninterface OptionElementProps {\n value?: unknown;\n label?: unknown;\n textValue?: unknown;\n children?: React.ReactNode;\n}\n\nfunction textOf(node: React.ReactNode): string {\n if (typeof node === 'string') return node;\n if (typeof node === 'number' || typeof node === 'bigint') return String(node);\n if (node === null || node === undefined || typeof node === 'boolean') return '';\n if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n // Host elements and Fragments render their children; custom components are opaque.\n if (typeof node.type === 'string' || node.type === React.Fragment) {\n return textOf(node.props.children);\n }\n return '';\n }\n if (typeof node === 'object' && Symbol.iterator in node) {\n let text = '';\n for (const child of node as Iterable<React.ReactNode>) text += textOf(child);\n return text;\n }\n return '';\n}\n\nfunction optionLabel(props: OptionElementProps, value: string): string | undefined {\n if (typeof props.label === 'string') return props.label;\n if (typeof props.textValue === 'string') return props.textValue;\n if (props.children === null || props.children === undefined) return value;\n const text = textOf(props.children).trim();\n return text || undefined;\n}\n\nfunction walkOptions(children: React.ReactNode, labels: Map<string, string>): void {\n React.Children.forEach(children, (child) => {\n if (!React.isValidElement<OptionElementProps>(child)) return;\n // An Option or OptionGroup written in a Server Component arrives as a lazy type: unwrap it.\n const type = getElementType(child);\n if (type === React.Fragment) {\n walkOptions(child.props.children, labels);\n return;\n }\n const kind = kindOf(type);\n if (kind === 'group') {\n walkOptions(child.props.children, labels);\n return;\n }\n if (kind !== 'option') return;\n const { value } = child.props;\n if (typeof value !== 'string' || labels.has(value)) return;\n const label = optionLabel(child.props, value);\n if (label !== undefined) labels.set(value, label);\n });\n}\n\n/**\n * Reads option labels from `children` during render (read-only), for display text before the\n * options have registered: on the server, in the first client render and during hydration\n * (layout effects have not run yet). Walks elements of components marked with\n * {@link markListboxElement}: `'option'` elements give `value → label` (`label` prop →\n * `textValue` → the text of their string/number children and host elements → `value` when they\n * have no children); `'group'` elements and Fragments are walked into. Other components are\n * opaque (their options resolve after registration). The first option of a value wins. A lazy\n * element type (an option or group written in a React Server Component) is unwrapped first.\n *\n * Display text = `listbox.getItem(value)?.label ?? collectOptionLabels(children).get(value)`\n * (`?? value` for freeform input only).\n */\nexport function collectOptionLabels(children: React.ReactNode): Map<string, string> {\n const labels = new Map<string, string>();\n walkOptions(children, labels);\n return labels;\n}\n\n/* ------------------------------------------------------------------ */\n/* useListbox */\n/* ------------------------------------------------------------------ */\n\nfunction hasText(element: EventTarget): boolean {\n return (\n (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) &&\n element.value !== ''\n );\n}\n\n/** Ctrl/Cmd shortcuts that change the text: cut, paste, undo, redo. */\nconst TEXT_SHORTCUTS: ReadonlySet<string> = new Set(['x', 'v', 'z', 'y']);\n\n/**\n * Editable: whether a keydown edits the text of the combobox input — printable characters (AltGr,\n * i.e. Ctrl+Alt, included), Backspace/Delete, the cut/paste/undo/redo shortcuts and the\n * `Process`/`Unidentified` keys of IMEs and virtual keyboards. Nothing edits a read-only or\n * disabled input.\n */\nfunction editsText(event: React.KeyboardEvent): boolean {\n const target = event.currentTarget;\n if (\n (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) &&\n (target.readOnly || target.disabled)\n ) {\n return false;\n }\n const { key } = event;\n if (key === 'Backspace' || key === 'Delete' || key === 'Process' || key === 'Unidentified') {\n return true;\n }\n if (key.length !== 1) return false;\n const shortcut = event.metaKey || (event.ctrlKey && !isAltGraphCharacter(event));\n return !shortcut || TEXT_SHORTCUTS.has(key.toLowerCase());\n}\n\nfunction preventMouseDown(event: React.MouseEvent): void {\n event.preventDefault();\n}\n\n/**\n * The listbox behaviour of Listbox, Combobox, Dropdown, TagPicker and TimePicker, and the base of a\n * custom picker: one navigable option list drives the highlight, `aria-activedescendant` and the\n * commit, so the highlighted option is always the one Enter selects. The highlight is\n * {@link useActiveDescendant}'s.\n *\n * - **Items.** Data mode (`items`) or registration mode: `Option`s, or option components of your\n * own on {@link useListboxOption}, register inside the listbox's context (`context`, which\n * `ListboxSurface` and `ListboxProvider` provide) in DOM order, `OptionGroup`s included. The\n * registrations of one commit are published once from this hook's layout effect (which runs\n * after the options'); later additions/removals are published once per microtask. A keyed\n * reorder of the same options re-sorts them: the DOM order is re-checked after a commit of this\n * hook's component that moved option elements (a highlight move moves none, so it costs O(1))\n * and, while it is mounted, whenever option elements move (a `MutationObserver` on their\n * common ancestor) — so memoized options or hoisted elements that a wrapper component inside\n * the listbox reorders are re-sorted too, although neither the options nor this component\n * render. Options register even when filtered out or hidden (they render `hidden` and are not\n * navigable), so labels are always known — use {@link collectOptionLabels} for the display text\n * before registration (SSR/first render). `filter` is the navigability predicate, which a\n * picker builds from its query.\n * - **Active option** is derived during render: the highlighted value, else the `autoHighlight`\n * fallback. The highlight is reset on close and after a single-select commit that closes the\n * listbox, and dropped once it is not navigable any more (filtered out, hidden, removed by an\n * update, or disabled without `disabledOptionsFocusable`), so it does not come back without a\n * user action when the option returns. With\n * `highlightOnFilter` the first option becomes active whenever the options change while open\n * (`useActiveDescendant`'s `activateFirstOnChange`). Editable: a text-editing key (printable\n * characters, Backspace/Delete, cut/paste/undo/redo) clears the highlight — visual focus\n * returns to the textbox (APG) — so Enter after typing never commits an option highlighted\n * before the edit; with `highlightOnFilter` the key makes the first option active instead.\n * - **Ids** `${listboxId}-opt-${n}` are stable per value (across filtering and remounts between\n * an inline closed list and a portaled open list).\n * - **Store.** Options read their active/selected/hidden flags through a store, so moving the\n * highlight re-renders only the old and the new option.\n * - **Keys (APG).** Editable: ArrowDown/ArrowUp open (selected, else first/last) and move;\n * Alt+ArrowDown opens without moving; Alt+ArrowUp closes; Enter commits the active option (with\n * the listbox closed Enter is not prevented, so forms submit); Escape closes (closed + text:\n * `onClearDraft`); Tab closes; Home/End/printable keys stay with the input (text-editing keys\n * clear the highlight, see above). Select-only:\n * ArrowDown/ArrowUp/Home/End/typeahead open and position (a closed typeahead starts from the\n * selected option; characters typed with AltGr, i.e. Ctrl+Alt on Windows, count); PageUp/PageDown\n * jump 10; Enter/Space open or commit (always prevented on keydown, Space also on keyup, so a\n * `<button>` combobox is not clicked again; a Space typed within 500 ms of a typeahead character\n * continues the search instead); Alt+ArrowUp and Tab commit and close in single-select mode\n * (with `multiselect` they only close; Tab is not prevented); Escape closes. Standalone (the\n * list has focus; nothing opens or closes): ArrowDown/ArrowUp move (from no active option to\n * the first/last), Home/End, PageUp/PageDown (10) and typeahead move; Enter and Space commit\n * and keep the committed option active (a Space typed within 500 ms of a typeahead character\n * continues the search instead); Tab, Escape and Alt+Arrow keys are not handled and not\n * prevented, so the page and enclosing layers get them.\n * \"Selected\" means the first selected navigable option in list order. Disabled options are\n * skipped unless `disabledOptionsFocusable`, and never committed; hidden options are not\n * navigable at all.\n * - The active option is scrolled into view (`{ block: 'nearest' }`) in a layout effect, except\n * after a pointer highlight or a pointer press (the list would scroll under the pointer).\n *\n * **Consumer contract** (Listbox, Combobox, Dropdown, TagPicker, TimePicker and custom pickers):\n * - All options of a listbox live in a single container at a time (inline only while closed,\n * portaled only while open — never both, not even for an exit animation; `ListboxSurface` does\n * this). Options split over two containers are watched from `<body>` and registered twice\n * (development warns).\n * - Spread `getComboboxProps()` onto the combobox element and attach **both** `onKeyDown` and\n * `onKeyUp` to it (compose them with your own handlers). `onKeyUp` is required for a\n * `<button>` combobox (select-only): without it the button's native click on Space keyup\n * toggles the listbox again after a keydown commit.\n * - Registration mode: mark option and group components of your own with\n * {@link markListboxElement} (`Option` and `OptionGroup` are marked; memo components can be\n * marked too). {@link collectOptionLabels} walks only marked components, so without the marks\n * the server render and the first client render have no display text.\n * - Display text: `getItem(value)?.label ?? collectOptionLabels(children).get(value)` (`?? value`\n * for freeform input only). Editable pickers pass `onClearDraft` for Escape on a closed list.\n * - A popup list renders in `ListboxSurface`, placed and dismissed by `useListboxPopup`; a list\n * rendered without it gets the options' context from `ListboxProvider`.\n * - Standalone mode (a listbox that holds focus): spread `getListboxProps()` onto the list and\n * merge its `ref` with yours (a pointer press on an option focuses the list through it), attach\n * `onKeyDown` to the list and pass the list's focus state as `open`. `getComboboxProps()` is not\n * used.\n */\nexport function useListbox(options: UseListboxOptions): UseListboxResult {\n const {\n open,\n onOpenChange: onOpenChangeProp,\n mode,\n multiselect = false,\n selectedValues,\n onSelect,\n items: dataItems,\n filter,\n loop = false,\n typeahead = mode !== 'editable',\n disabledOptionsFocusable = false,\n autoHighlight = 'selected',\n highlightOnFilter = false,\n onActiveValueChange,\n idPrefix,\n onClearDraft,\n } = options;\n\n const standalone = mode === 'standalone';\n // Standalone mode has nothing to open or close (`open` is the list's focus state), so every open\n // change requested below — a key, a commit, a typeahead match — reaches no callback there.\n const onOpenChange = standalone ? undefined : onOpenChangeProp;\n // Standalone mode: the list element (from getListboxProps().ref), which a pointer press focuses.\n const [listElement, setListElement] = useState<HTMLElement | null>(null);\n\n const listboxId = useId(idPrefix ?? 'listbox');\n const [store] = useState(() => new ListboxStoreImpl(selectedValues));\n const dataMode = dataItems !== undefined;\n const registered = useSyncExternalStore(\n store.subscribeItems,\n dataMode ? getEmptyItems : store.getItems,\n getEmptyItems,\n );\n const allItems: readonly ListboxItem[] = dataItems ?? registered;\n\n const navigable = useMemo(\n () => allItems.filter((item) => !item.hidden && (!filter || filter(item))),\n [allItems, filter],\n );\n // The values useActiveDescendant navigates: enabled navigable items, or every navigable item\n // (disabled included) with disabledOptionsFocusable.\n const navigationValues = useMemo(\n () =>\n disabledOptionsFocusable\n ? navigable.map((item) => item.value)\n : navigable.filter((item) => !item.disabled).map((item) => item.value),\n [navigable, disabledOptionsFocusable],\n );\n const itemByValue = useMemo(() => {\n const map = new Map<string, ListboxItem>();\n for (const item of allItems) if (!map.has(item.value)) map.set(item.value, item);\n return map;\n }, [allItems]);\n // Filtered-out and hidden items: registered options read it to render `hidden`.\n const hiddenSet = useMemo(() => {\n if (navigable.length === allItems.length) return EMPTY_SET;\n const visible = new Set(navigable.map((item) => item.value));\n const hidden = new Set<string>();\n for (const item of allItems) if (!visible.has(item.value)) hidden.add(item.value);\n return hidden;\n }, [allItems, navigable]);\n const selectedSet = new Set(selectedValues);\n\n // In list order (APG), not in the order the values were selected.\n const firstSelected = navigationValues.find((value) => selectedSet.has(value)) ?? null;\n let fallback: string | null = null;\n if (autoHighlight === 'selected') fallback = firstSelected ?? navigationValues[0] ?? null;\n else if (autoHighlight === 'first') fallback = navigationValues[0] ?? null;\n\n const getOptionId = useCallback(\n (value: string) => `${listboxId}-opt-${store.getIndex(value)}`,\n [listboxId, store],\n );\n const getItem = useCallback((value: string) => itemByValue.get(value), [itemByValue]);\n\n // The active option, derived during render (C-HOOKS: no effect): the highlighted value (keyboard,\n // pointer, typeahead, setActiveValue), else the autoHighlight fallback. The highlight is forgotten\n // on close (one set while closed survives only when the same update opens the listbox) and\n // dropped once it is not navigable any more (filtered out, hidden, removed by an update, or\n // disabled without disabledOptionsFocusable), so it does not come back without a user action\n // when the option returns. highlightOnFilter: the first option whenever the navigable options\n // change while open, compared by content (an inline `filter` yields new arrays on every render)\n // and with the options before opening, so the keystroke that opens the listbox and filters it in\n // one update counts; opening with unchanged options (ArrowDown, a click) keeps the autoHighlight\n // start. Every highlight but the pointer's (a hover, a standalone press) is scrolled into view\n // (the list would scroll under the pointer).\n const ad = useActiveDescendant({\n items: navigationValues,\n getId: getOptionId,\n enabled: open,\n fallback,\n loop,\n activateFirstOnChange: highlightOnFilter,\n getElement: store.getElement,\n onActiveValueChange,\n });\n const { activeValue, activeDescendantId } = ad;\n\n const commit = useEventCallback(\n (value: string, reason: 'select' | 'tab', event: Event, fallbackItem?: ListboxItem): void => {\n const item = itemByValue.get(value) ?? fallbackItem;\n if (!item || item.disabled) return;\n onSelect(value, { item, event });\n // Standalone and multi-select commits keep the list as it is, on the committed option.\n if (standalone || (multiselect && reason === 'select')) {\n ad.setActiveValue(value);\n } else {\n ad.setActiveValue(null);\n onOpenChange?.(false, { reason, event });\n }\n },\n );\n\n const select = useEventCallback((value: string, item: ListboxItem, event: Event) => {\n commit(value, 'select', event, item);\n });\n\n // Standalone: an option's mousedown (its default prevented, so the browser neither focuses nor\n // scrolls). Focusing the list and moving to the option land in one update, so the render that\n // enables the active option (the consumer's focus state) has the pressed one, not the fallback\n // that the scroll effect would scroll under the pointer before the click. An option that\n // cannot be active is not moved to (the next render would drop it together with the highlight):\n // a focused list keeps its active option, and an unfocused one (`open` is its focus state)\n // starts on its fallback, moved to without scrolling for the same reason.\n const press = useEventCallback((value: string) => {\n if (listElement) {\n listElement.focus({ preventScroll: true });\n } else if (standalone) {\n warnOnce(\n 'useListbox:list-ref',\n 'useListbox: the list element is unknown, so a pointer press cannot focus the list. Pass ' +\n 'the `ref` of `getListboxProps()` to the list element, merged with your own ref.',\n );\n }\n if (navigationValues.includes(value)) ad.setActiveValue(value, { scroll: false });\n else if (!open && fallback !== null) ad.setActiveValue(fallback, { scroll: false });\n });\n\n // The native keydown event behind a typeahead match, so onMatch (called from inside\n // onTypeahead(), itself only ever called from onKeyDown below) can report it: onMatch has no\n // event parameter of its own (useTypeahead is unchanged by this hook).\n const keyEventRef = useRef<Event | null>(null);\n\n const { onTypeahead } = useTypeahead({\n getItems: () =>\n navigable.map((item) => ({\n value: item.value,\n text: item.textValue ?? item.label,\n disabled: disabledOptionsFocusable ? false : item.disabled,\n })),\n onMatch: (value) => {\n ad.setActiveValue(value);\n const event = keyEventRef.current;\n if (!open && event) onOpenChange?.(true, { reason: 'keyboard', event });\n },\n });\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent) => {\n if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n // Every open change and commit below comes from this key, so its native event is always\n // available; kept in a ref too, for the typeahead match handled outside this closure.\n keyEventRef.current = event.nativeEvent;\n // Editable: a key that edits the text returns visual focus to the textbox (APG) and is left to\n // the input: the highlight is cleared (highlightOnFilter: the first option), so Enter after\n // typing never commits an option highlighted before the edit.\n if (mode === 'editable' && open && editsText(event)) {\n ad.setActiveValue(highlightOnFilter ? (navigationValues[0] ?? null) : null);\n }\n const altGraph = isAltGraphCharacter(event);\n if ((event.ctrlKey && !altGraph) || event.metaKey) return;\n const { key, altKey } = event;\n // Standalone: nothing opens or closes, so Tab, Escape and Alt+Arrow keys are left to the page\n // and to enclosing layers (a Dialog's Escape), returned before anything is prevented.\n if (standalone && (key === 'Tab' || key === 'Escape' || (altKey && key.startsWith('Arrow')))) {\n return;\n }\n const selectOnly = mode === 'select-only';\n // Select-only and standalone: Home/End, PageUp/PageDown, Enter and Space act on the list.\n const editable = mode === 'editable';\n const first = navigationValues[0] ?? null;\n const last = navigationValues[navigationValues.length - 1] ?? null;\n\n const openWith = (value: string | null) => {\n ad.setActiveValue(value);\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n };\n const commitOrClose = () => {\n if (activeValue !== null) commit(activeValue, 'select', event.nativeEvent);\n else if (!multiselect) {\n onOpenChange?.(false, { reason: 'keyboard', event: event.nativeEvent });\n }\n };\n\n switch (key) {\n case 'ArrowDown':\n event.preventDefault();\n if (altKey) {\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n } else if (!open) {\n openWith(firstSelected ?? first);\n } else {\n ad.next();\n }\n return;\n case 'ArrowUp':\n event.preventDefault();\n if (altKey) {\n if (!open) return;\n // A disabled active option (disabledOptionsFocusable) closes instead of committing:\n // commit()'s own guard would otherwise leave the key unhandled.\n if (\n selectOnly &&\n !multiselect &&\n activeValue !== null &&\n !itemByValue.get(activeValue)?.disabled\n ) {\n commit(activeValue, 'select', event.nativeEvent);\n } else {\n onOpenChange?.(false, { reason: 'keyboard', event: event.nativeEvent });\n }\n } else if (!open) {\n openWith(firstSelected ?? last);\n } else {\n ad.prev();\n }\n return;\n case 'Home':\n case 'End':\n if (editable) return;\n event.preventDefault();\n openWith(key === 'Home' ? first : last);\n return;\n case 'PageUp':\n case 'PageDown':\n if (editable || !open) return;\n event.preventDefault();\n // Clamped at the ends, never wrapping (also with loop).\n ad.move(key === 'PageDown' ? 10 : -10);\n return;\n case 'Enter':\n if (editable) {\n // Closed: not prevented, so the surrounding form submits (APG).\n if (open && activeValue !== null) {\n event.preventDefault();\n commit(activeValue, 'select', event.nativeEvent);\n }\n return;\n }\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n return;\n case 'Escape':\n if (open) {\n event.preventDefault();\n onOpenChange?.(false, { reason: 'escape', event: event.nativeEvent });\n } else if (editable && onClearDraft && hasText(event.currentTarget)) {\n event.preventDefault();\n onClearDraft();\n }\n return;\n case 'Tab':\n if (!open) return;\n // A disabled active option (disabledOptionsFocusable) closes instead of committing: see\n // the Alt+ArrowUp comment above.\n if (\n selectOnly &&\n !multiselect &&\n activeValue !== null &&\n !itemByValue.get(activeValue)?.disabled\n ) {\n commit(activeValue, 'tab', event.nativeEvent);\n } else {\n onOpenChange?.(false, { reason: 'tab', event: event.nativeEvent });\n }\n return;\n default:\n break;\n }\n\n if (key === ' ' && editable) return;\n if (!typeahead || (altKey && !altGraph) || key.length !== 1) {\n if (key === ' ' && !editable) {\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n }\n return;\n }\n const current = open ? activeValue : firstSelected;\n if (onTypeahead(event, current)) {\n event.preventDefault();\n return;\n }\n if (key === ' ') {\n // Not part of a typeahead search: Space opens or commits (select-only; standalone: commits).\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n return;\n }\n if (selectOnly && !open) {\n event.preventDefault();\n onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n }\n });\n\n const onKeyUp = useEventCallback((event: React.KeyboardEvent) => {\n // A <button> clicks on Space keyup: the keydown already opened or committed.\n if (mode === 'select-only' && event.key === ' ') event.preventDefault();\n });\n\n // Publish the registrations of this commit once, re-check the DOM order when this commit moved\n // options (a keyed reorder moves them without registering them again; a highlight move does not\n // move any), then sync the option selectors.\n useLayoutEffect(() => {\n store.setRegistrationMode(!dataMode);\n store.flush();\n store.checkOrderAfterCommit();\n store.setRenderState(activeValue, selectedSet, hiddenSet);\n });\n\n // While mounted, option moves that render neither this root nor the options (memoized options\n // reordered by a wrapper component) re-check the order through a MutationObserver.\n useLayoutEffect(() => {\n store.connect();\n return () => store.disconnect();\n }, [store]);\n\n useEffect(() => {\n if (!dataItems) return;\n const seen = new Set<string>();\n for (const item of dataItems) {\n if (seen.has(item.value)) warnDuplicateValue(item.value);\n seen.add(item.value);\n }\n }, [dataItems]);\n\n const context = useMemo<ListboxContextValue>(\n () => ({ listboxId, store, multiselect, mode, select, highlight: ad.highlight, press }),\n [listboxId, store, multiselect, mode, select, ad.highlight, press],\n );\n\n return {\n listboxId,\n activeValue,\n activeDescendantId,\n items: navigable,\n getItem,\n getOptionId,\n setActiveValue: ad.setActiveValue,\n onKeyDown,\n onKeyUp,\n getComboboxProps: () => {\n const props: ListboxComboboxProps = {\n role: 'combobox',\n 'aria-expanded': open,\n 'aria-controls': listboxId,\n 'aria-haspopup': 'listbox',\n };\n if (activeDescendantId !== undefined) props['aria-activedescendant'] = activeDescendantId;\n if (mode === 'editable') props['aria-autocomplete'] = 'list';\n return props;\n },\n getListboxProps: () => {\n // Standalone: the list takes focus (its ref, for the pointer press) and carries the active\n // descendant; the combobox modes keep focus on the combobox.\n const props: ListboxListProps = standalone\n ? { id: listboxId, role: 'listbox', tabIndex: 0, ref: setListElement }\n : { id: listboxId, role: 'listbox', tabIndex: -1, onMouseDown: preventMouseDown };\n if (standalone && activeDescendantId !== undefined) {\n props['aria-activedescendant'] = activeDescendantId;\n }\n if (multiselect) props['aria-multiselectable'] = true;\n return props;\n },\n context,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* useListboxOption */\n/* ------------------------------------------------------------------ */\n\nconst FLAG_ACTIVE = 1;\nconst FLAG_SELECTED = 2;\nconst FLAG_HIDDEN = 4;\n\n/** What an option registered last (compared after each commit, see {@link useListboxOption}). */\ninterface OptionRegistration {\n store: ListboxStore;\n item: ListboxItem;\n unregister: () => void;\n}\n\nfunction optionFlags(store: ListboxStore, value: string): number {\n return (\n (store.isActive(value) ? FLAG_ACTIVE : 0) |\n (store.isSelected(value) ? FLAG_SELECTED : 0) |\n (store.isHidden(value) ? FLAG_HIDDEN : 0)\n );\n}\n\n/**\n * An option of the surrounding listbox, for option components of your own: the listbox's context\n * comes from `ListboxSurface` or `ListboxProvider` ({@link UseListboxResult.context}). Registers\n * the option (registration mode) and its element, and reads its flags from the listbox store, so\n * it re-renders only when its own active/selected/hidden state changes. Spread `optionProps` onto\n * the `<li>` (`id`, `role=\"option\"`, `aria-selected`, `aria-disabled`, `hidden`, and\n * `data-active`/`data-selected`/`data-disabled` for styling) and compose its `onClick` — and in\n * standalone mode its `onMouseDown`, the pointer press — with your own handlers. Mark the\n * component with {@link markListboxElement}, so its label is known before it registers.\n *\n * Throws in development when used outside a listbox; in production it logs the error once and\n * renders an inert option.\n *\n * @typeParam E The option element type (`HTMLLIElement` for an `<li>`), so `ref` needs no cast.\n */\nexport function useListboxOption<E extends HTMLElement = HTMLElement>(\n props: UseListboxOptionProps,\n ref?: React.Ref<E>,\n): UseListboxOptionResult<E> {\n const context = useListboxContext('Option');\n const { store, listboxId, multiselect, mode, select, highlight, press } = context;\n const { value, label, textValue, disabled = false, hidden: hiddenByConsumer = false } = props;\n\n const id = `${listboxId}-opt-${store.getIndex(value)}`;\n const flags = useSyncExternalStore(\n store.subscribe,\n () => optionFlags(store, value),\n () => optionFlags(store, value),\n );\n const active = (flags & FLAG_ACTIVE) !== 0;\n const selected = (flags & FLAG_SELECTED) !== 0;\n const hidden = hiddenByConsumer || (flags & FLAG_HIDDEN) !== 0;\n\n const elementRef = useRef<E | null>(null);\n const setElement = useCallback((element: E | null) => {\n elementRef.current = element;\n }, []);\n const mergedRef = useMergedRefs<E>(ref, setElement);\n const registrationRef = useRef<OptionRegistration | null>(null);\n\n // After every commit of this option: (re-)register when the item changed — including a label\n // read from the element's text, which any re-render can change. A move (keyed reorder) does not\n // register again; the listbox root sees it through its root commit or its MutationObserver.\n useLayoutEffect(() => {\n const text =\n label === undefined && textValue === undefined\n ? elementRef.current?.textContent?.trim()\n : undefined;\n const item: ListboxItem = { value, label: label ?? textValue ?? (text || value) };\n if (textValue !== undefined) item.textValue = textValue;\n if (disabled) item.disabled = true;\n if (hiddenByConsumer) item.hidden = true;\n const current = registrationRef.current;\n if (current && current.store === store && sameItem(current.item, item)) return;\n current?.unregister();\n registrationRef.current = { store, item, unregister: store.register(item, elementRef) };\n });\n useLayoutEffect(\n () => () => {\n registrationRef.current?.unregister();\n registrationRef.current = null;\n },\n [],\n );\n\n const onClick = useCallback(\n (event: React.MouseEvent<E>) => {\n if (disabled) return;\n const item: ListboxItem = { value, label: label ?? textValue ?? value };\n if (textValue !== undefined) item.textValue = textValue;\n select(value, item, event.nativeEvent);\n },\n [select, value, label, textValue, disabled],\n );\n\n const onPointerMove = useCallback(() => {\n if (!disabled && !store.isActive(value)) highlight(value);\n }, [highlight, store, value, disabled]);\n\n // Standalone: the browser would focus the list itself, and focus would scroll the list's\n // active option into view under the pointer before the click; the press focuses the list\n // without scrolling and makes this option active instead. Only the primary button presses: a\n // right or middle press is left to the browser (its context menu, autoscroll), as on a native\n // `<select size>`, and moves no active option.\n const onMouseDown = useCallback(\n (event: React.MouseEvent<E>) => {\n if (event.button !== 0) return;\n event.preventDefault();\n press(value);\n },\n [press, value],\n );\n\n const optionProps: ListboxOptionElementProps<E> = {\n id,\n role: 'option',\n 'aria-selected': selected,\n onClick,\n onPointerMove,\n ref: mergedRef,\n };\n if (mode === 'standalone') optionProps.onMouseDown = onMouseDown;\n if (disabled) {\n optionProps['aria-disabled'] = true;\n optionProps['data-disabled'] = '';\n }\n if (hidden) optionProps.hidden = true;\n if (active) optionProps['data-active'] = '';\n if (selected) optionProps['data-selected'] = '';\n\n return { id, selected, active, disabled, hidden, multiselect, optionProps };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useListbox.mjs","names":["useId","useId$1","React","EMPTY_ITEMS","EMPTY_SET","getEmptyItems","sameSet","a","b","value","sameItem","sameItems","item","index","compareRegistrations","ea","eb","position","ListboxStoreImpl","selectedValues","listener","registration","sorted","seen","next","el","element","enabled","i","moved","recorded","changed","active","selected","hidden","target","isDev","warnIfPageWide","ancestor","count","warnDuplicateValue","doc","warnOnce","ListboxContext","ListboxOptionPositionContext","inertContext","getInertContext","useListboxContext","useContext","reportMissingContext","componentName","LISTBOX_ELEMENT_KIND","markListboxElement","component","kind","kindOf","type","textOf","node","text","child","optionLabel","props","walkOptions","children","getElementType","labels","label","collectOptionLabels","hasText","TEXT_SHORTCUTS","editsText","event","key","isAltGraphCharacter","preventMouseDown","useListbox","onOpenChangeProp","dataItems","mode","options","standalone","onOpenChange","listElement","setListElement","useState","listboxId","idPrefix","store","dataMode","registered","useSyncExternalStore","allItems","navigable","useMemo","filter","navigationValues","disabledOptionsFocusable","itemByValue","map","hiddenSet","visible","selectedSet","firstSelected","fallback","autoHighlight","getOptionId","useCallback","getItem","scrollToItem","useEventCallback","navigationIndex","onScrollToItem","ad","useActiveDescendant","open","highlightOnFilter","commit","fallbackItem","onSelect","multiselect","reason","select","press","keyEventRef","useRef","useTypeahead","onKeyDown","altGraph","altKey","selectOnly","editable","first","last","openWith","commitOrClose","activeValue","onClearDraft","typeahead","onTypeahead","onKeyUp","useLayoutEffect","useEffect","context","activeDescendantId","FLAG_ACTIVE","FLAG_SELECTED","FLAG_HIDDEN","optionFlags","useListboxOption","hiddenByConsumer","id","flags","elementRef","setElement","mergedRef","useMergedRefs","ref","registrationRef","textValue","disabled","current","onClick","onPointerMove","highlight","onMouseDown","optionProps"],"sources":["../../src/hooks/useListbox.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { getElementType } from '../lib/children';\nimport { isDev, reportMissingContext, warnOnce } from '../lib/dev';\nimport type { OpenChangeDetails } from '../lib/types';\nimport { useActiveDescendant } from './useActiveDescendant';\nimport { useEventCallback } from './useEventCallback';\nimport { useId } from './useId';\nimport { useMergedRefs } from './useMergedRefs';\nimport { isAltGraphCharacter, useTypeahead } from './useTypeahead';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\n/**\n * One option of a listbox: an entry of {@link UseListboxOptions.items} (data mode), or what an\n * option registers (registration mode, {@link useListboxOption}).\n */\nexport interface ListboxItem {\n /** Unique value within the listbox. */\n value: string;\n /** Display label (Combobox input text, Dropdown trigger text). */\n label: string;\n /** Text matched by typeahead and filters instead of `label`. */\n textValue?: string;\n /**\n * Disabled options are rendered but skipped by navigation, unless\n * {@link UseListboxOptions.disabledOptionsFocusable}, and never committed.\n * @default false\n */\n disabled?: boolean;\n /**\n * Hidden options are left out of navigation and rendering like filtered-out ones: never\n * highlighted, reached by typeahead or committed with the keyboard (registered options render\n * `hidden`). Their label stays known ({@link UseListboxResult.getItem}). In data mode (`items`)\n * it is the item's own flag; a registered option is hidden by its own `hidden` attribute or by a\n * hidden group around it.\n * @default false\n */\n hidden?: boolean;\n}\n\n/**\n * Why {@link UseListboxOptions.onOpenChange} was called: `'keyboard'` (a key opens the listbox, or\n * closes it without a commit), `'select'` (a commit closes it), `'escape'` or `'tab'` (Tab closes\n * it, after committing the active option in single-select select-only mode).\n */\nexport type ListboxOpenChangeReason = 'keyboard' | 'select' | 'escape' | 'tab';\n\n/**\n * Second argument of {@link UseListboxOptions.onSelect}: the committed option and the event behind\n * the commit.\n */\nexport interface ListboxSelectDetails {\n /** The committed option. */\n item: ListboxItem;\n /** The key or click event behind the commit. */\n event: Event;\n}\n\n/** Options of {@link useListbox}. */\nexport interface UseListboxOptions {\n /**\n * Whether the listbox is shown. You own the open state: `onOpenChange` asks you to change it.\n * Standalone mode: whether the list has focus — an option is active only while it does.\n */\n open: boolean;\n /**\n * Called when a key (or a commit) wants to open or close the listbox. `details.reason` says why\n * (`'keyboard'`, `'select'` — a commit that closes the list —, `'escape'` or `'tab'`) and\n * `details.event` is the key or click event behind it. WaveUI always passes `details`; it is\n * typed optional until 1.0 so that code which calls this prop itself keeps compiling. Never\n * called in standalone mode.\n */\n onOpenChange?: (open: boolean, details?: OpenChangeDetails<ListboxOpenChangeReason>) => void;\n /**\n * `'editable'`: a text input combobox (Combobox, TagPicker, TimePicker).\n * `'select-only'`: a button/div combobox without text entry (Dropdown).\n * `'standalone'`: a listbox that holds focus itself (Listbox), with nothing to open or close:\n * `open` is its focus state, the keys move and commit (a commit keeps the committed option\n * active), Tab, Escape and Alt+Arrow keys are left to the page, `onOpenChange` is never called,\n * and a pointer press on an option focuses the list and activates the option without scrolling\n * (see {@link UseListboxResult.getListboxProps}).\n */\n mode: 'editable' | 'select-only' | 'standalone';\n /**\n * Several values can be selected: the list gets `aria-multiselectable`, and a commit keeps the\n * listbox open on the committed option (your `onSelect` adds or removes the value).\n * @default false\n */\n multiselect?: boolean;\n /** The selected values (`[]` when nothing is selected). */\n selectedValues: readonly string[];\n /**\n * Called when an option is committed: click, Enter/Space, and in single-select select-only mode\n * also Tab and Alt+ArrowUp (with `multiselect` they close without committing). `details.item` is\n * the committed option and `details.event` the key or click event behind the commit. WaveUI\n * always passes `details`; it is typed optional until 1.0 so that code which calls this prop\n * itself keeps compiling.\n */\n onSelect: (value: string, details?: ListboxSelectDetails) => void;\n /**\n * Data mode: the options, in list order. Omit it for registration mode, where the options\n * register themselves in DOM order: `Option`s, or option components of your own on\n * {@link useListboxOption}, rendered inside the listbox's context\n * ({@link UseListboxResult.context}, which `ListboxSurface` and `ListboxProvider` provide).\n */\n items?: readonly ListboxItem[];\n /**\n * The navigability predicate: an item it returns `false` for is filtered out — left out of the\n * keys, typeahead and {@link UseListboxResult.items}, never active, and rendered `hidden` as a\n * registered option (its label stays known). It is never called for a hidden item, which stays\n * out anyway. A picker builds it from its query, the text typed so far: by default, Combobox\n * keeps the options whose `textValue ?? label` contains it, ignoring case, and a component with\n * a `filter(option, query)` prop builds this predicate by binding its current query to it.\n * Without it, every item that is not hidden is navigable.\n */\n filter?: (item: ListboxItem) => boolean;\n /** Arrow keys wrap around at the ends. @default false */\n loop?: boolean;\n /**\n * Printable characters move to the matching option (on by default in select-only and standalone\n * mode).\n * @default mode !== 'editable'\n */\n typeahead?: boolean;\n /**\n * Keeps disabled options in the arrow-key, Home/End, PageUp/PageDown and typeahead order: they\n * still cannot be committed (Enter, Space and a click do nothing and the list stays open), and\n * while one is active, Tab and Alt+ArrowUp in single-select select-only mode close the listbox\n * without a commit. The `autoHighlight` fallback may then be a disabled option too.\n * @default false\n */\n disabledOptionsFocusable?: boolean;\n /**\n * The option that is active while no option is highlighted: `'selected'` — the first selected\n * navigable option in list order (not in `selectedValues` order), else the first option;\n * `'first'` — the first option; `false` — none. A highlighted option that leaves the navigable\n * set is dropped (the fallback takes over, also when the option returns); in editable mode a\n * text-editing key clears the highlight.\n * @default 'selected'\n */\n autoHighlight?: 'selected' | 'first' | false;\n /**\n * Whenever the options the keys reach (the navigable ones, the disabled ones only with\n * `disabledOptionsFocusable`) change while open, compared by content — as when typing filters\n * them — the first of them becomes active, also when the update that opens the listbox changes\n * them (compared with those before opening, as the keystroke that opens a filtered list does).\n * Opening with unchanged options keeps the `autoHighlight` start. It sets\n * {@link useActiveDescendant}'s `activateFirstOnChange`. In editable mode every text-editing key\n * while open makes the first option active as well, also when the options stay the same.\n * @default false\n */\n highlightOnFilter?: boolean;\n /**\n * Called after the active (highlighted) option changes — arrow keys, typeahead, the pointer, a\n * filter change, opening — and with `null` when the listbox closes. Passed straight through to\n * {@link useActiveDescendant}'s `onActiveValueChange`.\n */\n onActiveValueChange?: (value: string | null) => void;\n /**\n * Called for every move that scrolls the active option into view, for a list that renders only\n * part of its options (a virtual window): `index` is the option's index in\n * {@link UseListboxResult.items}, the shown options (filtered-out and hidden items are not\n * counted, disabled ones are). Scroll the list so that it shows and renders the option (a\n * window's `scrollToIndex(index)`, which moves the window in the same update); an element, when\n * there is one, is then scrolled into view as well. `aria-activedescendant` names the option\n * meanwhile. Passed to {@link useActiveDescendant}'s `onScrollToItem`, mapped to that index.\n */\n onScrollToItem?: (index: number) => void;\n /** Prefix of the generated listbox id. @default 'listbox' */\n idPrefix?: string;\n /**\n * Editable: Escape with the listbox closed and text in the input calls this (and prevents the\n * default), so you can clear the typed text. Not handled when omitted.\n */\n onClearDraft?: () => void;\n}\n\n/** Props for the combobox element (spread them; see {@link UseListboxResult.getComboboxProps}). */\nexport interface ListboxComboboxProps {\n role: 'combobox';\n /** {@link UseListboxOptions.open}. */\n 'aria-expanded': boolean;\n /** The listbox's id ({@link UseListboxResult.listboxId}). */\n 'aria-controls': string;\n /** The active option's id, while the listbox is open and an option is active. */\n 'aria-activedescendant'?: string;\n 'aria-haspopup': 'listbox';\n /** Editable mode: typing filters the options. */\n 'aria-autocomplete'?: 'list';\n}\n\n/** Props for the listbox element (see {@link UseListboxResult.getListboxProps}). */\nexport interface ListboxListProps {\n /** {@link UseListboxResult.listboxId}. */\n id: string;\n role: 'listbox';\n /** With {@link UseListboxOptions.multiselect}. */\n 'aria-multiselectable'?: true;\n /** `-1` in the combobox modes (focus stays on the combobox); `0` in standalone mode. */\n tabIndex: 0 | -1;\n /** The combobox modes: keeps focus on the combobox while options are clicked. */\n onMouseDown?(event: React.MouseEvent): void;\n /** Standalone mode: the active option's id while the list has focus and an option is active. */\n 'aria-activedescendant'?: string;\n /**\n * Standalone mode: the list element, which a pointer press on an option focuses. Merge it with\n * your own ref (a press warns in development when it was dropped).\n */\n ref?: React.RefCallback<HTMLElement>;\n}\n\n/** Result of {@link useListbox}. */\nexport interface UseListboxResult {\n /** Id of the listbox element. */\n listboxId: string;\n /**\n * The active (highlighted) option, derived during render; `null` while closed (in standalone\n * mode, while the list does not have focus).\n */\n activeValue: string | null;\n /** `getOptionId(activeValue)` while open and an option is active. */\n activeDescendantId: string | undefined;\n /**\n * The navigable items in DOM/data order: without filtered-out and hidden items; disabled ones\n * are included (navigation skips them, unless\n * {@link UseListboxOptions.disabledOptionsFocusable}).\n */\n items: ListboxItem[];\n /** Unfiltered lookup (registered options or `items`), e.g. for the selected option's label. */\n getItem(value: string): ListboxItem | undefined;\n /** `${listboxId}-opt-${n}`; `n` is assigned the first time a value is seen and never changes. */\n getOptionId(value: string): string;\n /**\n * Highlights `value` while open, scrolled into view like a keyboard highlight; `null` returns the\n * highlight to the `autoHighlight` option. The value is checked in the next render: if it is not\n * navigable then (disabled options count as not navigable unless\n * {@link UseListboxOptions.disabledOptionsFocusable}), it is dropped like `null`, and the\n * highlight returns to the `autoHighlight` option, or to none. A kept value is dropped when it\n * leaves the navigable set later. A value set while closed survives only when the same update\n * opens the listbox.\n */\n setActiveValue(value: string | null): void;\n /**\n * Attach to the combobox element (to the list in standalone mode). Ignores key events a handler\n * of yours already prevented (compose yours first).\n */\n onKeyDown(event: React.KeyboardEvent): void;\n /**\n * Attach to the combobox element next to `onKeyDown`. Required for a `<button>` combobox\n * (select-only): it prevents the Space keyup, so the native click does not toggle the listbox\n * again after the keydown opened or committed.\n */\n onKeyUp(event: React.KeyboardEvent): void;\n /**\n * Props for the combobox element (spread them): its role, `aria-expanded`, `aria-controls`,\n * `aria-haspopup`, `aria-activedescendant` while an option is active and, in editable mode,\n * `aria-autocomplete`. Not used in standalone mode, where the list itself has focus.\n */\n getComboboxProps(): ListboxComboboxProps;\n /**\n * Props for the listbox element (spread them). The combobox modes: `tabIndex: -1` and an\n * `onMouseDown` that keeps focus on the combobox while options are clicked. Standalone mode:\n * `tabIndex: 0`, `aria-activedescendant` while an option is active, and a `ref` (merge it with\n * yours: a pointer press on an option focuses the list through it), without `onMouseDown`.\n * Both: `aria-multiselectable` with `multiselect`.\n */\n getListboxProps(): ListboxListProps;\n /**\n * The context of the listbox's options: `ListboxSurface` provides it; for a list you render\n * yourself, pass it to `ListboxProvider` around the options.\n */\n context: ListboxContextValue;\n}\n\n/**\n * The per-option state of a listbox ({@link ListboxContextValue.store}) and the contract of option\n * components: {@link useListboxOption} is built on it, and so can an option component of your own\n * be. `register` adds an option, `getIndex` gives its stable id, and the flags say how to draw it:\n * subscribe with `useSyncExternalStore` and read one value (`() => store.isActive(value)`), so only\n * the options whose answer changed re-render.\n */\nexport interface ListboxStore {\n /** Calls `listener` after a flag of any option changed. Returns the unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** Whether `value` is the active option, the one `aria-activedescendant` points at. */\n isActive(value: string): boolean;\n /** Whether `value` is selected. */\n isSelected(value: string): boolean;\n /** Whether `value` is filtered out or hidden: render its option `hidden`. */\n isHidden(value: string): boolean;\n /**\n * The stable per-listbox index of `value`, assigned the first time the value is seen: the\n * option's id is `${listboxId}-opt-${getIndex(value)}` ({@link UseListboxResult.getOptionId}),\n * the same across filtering and remounts.\n */\n getIndex(value: string): number;\n /**\n * Registers an option and its element (registration mode): call it from a layout effect, as\n * {@link useListboxOption} does, and call the returned function to unregister, when the option\n * unmounts or before it registers a changed item. The options are ordered by their elements'\n * DOM position. The registrations of a commit are published once from the listbox's layout\n * effect (later ones once per microtask).\n */\n register(item: ListboxItem, element: React.RefObject<HTMLElement | null>): () => void;\n}\n\n/**\n * The context a listbox provides to its options (`useListbox(...).context`; pass it to\n * `ListboxProvider` for a list rendered without `ListboxSurface`).\n */\nexport interface ListboxContextValue {\n /** The listbox element's id, which the option ids start with. */\n listboxId: string;\n /** The per-option state and the registration of the options. */\n store: ListboxStore;\n /** Whether the listbox is {@link UseListboxOptions.multiselect}. */\n multiselect: boolean;\n /** The listbox's {@link UseListboxOptions.mode}: standalone options add the pointer press. */\n mode: 'editable' | 'select-only' | 'standalone';\n /**\n * Commits `value` (an option click): `item` stands for the option while it has not registered\n * yet, and `event` is the click behind the commit. A disabled option is never committed.\n */\n select(value: string, item: ListboxItem, event: Event): void;\n /** Highlights `value` (pointer movement); not scrolled into view, so the list stays put. */\n highlight(value: string): void;\n /**\n * Standalone mode: a pointer press on option `value` (its `mousedown`, whose default the option\n * prevents). Focuses the list without scrolling and makes the option active without scrolling\n * it into view, in one update, so the click that follows commits the pressed option even when\n * focus would have scrolled another option into view. A press on an option that cannot be\n * active (a disabled one, unless {@link UseListboxOptions.disabledOptionsFocusable}) keeps a\n * focused list's active option, and an unfocused list starts on its `autoHighlight` option,\n * not scrolled into view either. Development warns once when the list element is unknown\n * (the `ref` of {@link UseListboxResult.getListboxProps} was not passed on).\n */\n press(value: string): void;\n}\n\n/**\n * Props of {@link useListboxOption}. The option's position in the listbox follows its element's\n * DOM position, also when the option is memoized and only moved (see {@link useListbox}).\n */\nexport interface UseListboxOptionProps {\n /** The option's value, unique within the listbox. */\n value: string;\n /**\n * Display label; falls back to `textValue`, then the element's text content, then `value`. The\n * text content is re-read after every render of the option. Text that a child component changes\n * from its own state (without the option re-rendering) is not seen; pass `label` or `textValue`\n * for such options.\n */\n label?: string;\n /** Text matched by typeahead and filters instead of the label. */\n textValue?: string;\n /**\n * The option can never be committed; the keys skip it unless the listbox has\n * {@link UseListboxOptions.disabledOptionsFocusable}.\n * @default false\n */\n disabled?: boolean;\n /**\n * The option is hidden (its own `hidden` attribute, or a hidden group around it): it registers\n * as a hidden item ({@link ListboxItem.hidden}), so it is not navigable, and\n * `optionProps.hidden` is set from the first render (server included).\n * @default false\n */\n hidden?: boolean;\n}\n\n/**\n * Props for an option element (spread them onto the `<li>`; compose `onClick`, and `onMouseDown`\n * in standalone mode, with yours).\n */\nexport interface ListboxOptionElementProps<E extends HTMLElement = HTMLElement> {\n /**\n * The option's stable id ({@link UseListboxResult.getOptionId}), which `aria-activedescendant`\n * points at while the option is active.\n */\n id: string;\n role: 'option';\n 'aria-selected': boolean;\n /** While disabled. */\n 'aria-disabled'?: true;\n /** While filtered out or hidden. */\n hidden?: boolean;\n /**\n * The number of shown options, for an option of a data-mode list rendered by Listbox, Combobox\n * or Dropdown (`items`), which tell each option its position. Absent otherwise.\n */\n 'aria-setsize'?: number;\n /** The option's position among the shown options (from 1), with `aria-setsize`. */\n 'aria-posinset'?: number;\n /**\n * The option's box in a windowed list (a `virtualWindow` of Listbox, Combobox or Dropdown): the\n * window's `itemSize` as its height, padding and border included, and no shrinking in a flex\n * list. Absent otherwise. Merge it after a style of your own: every option of a window must be\n * exactly that tall.\n */\n style?: React.CSSProperties;\n /** While the option is active, for styling. */\n 'data-active'?: '';\n /** While selected, for styling. */\n 'data-selected'?: '';\n /** While disabled, for styling. */\n 'data-disabled'?: '';\n /** Commits the option (a disabled one does nothing). */\n onClick(event: React.MouseEvent<E>): void;\n /** Makes the option under the pointer active (not a disabled one), without scrolling the list. */\n onPointerMove(event: React.PointerEvent<E>): void;\n /** Standalone mode only: the pointer press ({@link ListboxContextValue.press}). */\n onMouseDown?(event: React.MouseEvent<E>): void;\n /** Registers the element; it also sets the `ref` passed to {@link useListboxOption}. */\n ref: React.RefCallback<E>;\n}\n\n/** Result of {@link useListboxOption}. */\nexport interface UseListboxOptionResult<E extends HTMLElement = HTMLElement> {\n /** The option's stable id. */\n id: string;\n /** Whether the option is selected. */\n selected: boolean;\n /** Whether the option is active (keyboard or pointer highlight). */\n active: boolean;\n /** Whether the option is disabled. */\n disabled: boolean;\n /** Filtered out, or hidden by its own props ({@link UseListboxOptionProps.hidden}). */\n hidden: boolean;\n /** Whether the surrounding listbox is {@link UseListboxOptions.multiselect}. */\n multiselect: boolean;\n /** Spread onto the option element (see {@link ListboxOptionElementProps}). */\n optionProps: ListboxOptionElementProps<E>;\n}\n\n/* ------------------------------------------------------------------ */\n/* Store */\n/* ------------------------------------------------------------------ */\n\ninterface Registration {\n item: ListboxItem;\n element: React.RefObject<HTMLElement | null>;\n seq: number;\n}\n\nconst EMPTY_ITEMS: ListboxItem[] = [];\nconst EMPTY_SET: ReadonlySet<string> = new Set<string>();\n\nfunction getEmptyItems(): ListboxItem[] {\n return EMPTY_ITEMS;\n}\n\nfunction sameSet(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n if (a === b) return true;\n if (a.size !== b.size) return false;\n for (const value of a) if (!b.has(value)) return false;\n return true;\n}\n\nfunction sameItem(a: ListboxItem, b: ListboxItem): boolean {\n return (\n a.value === b.value &&\n a.label === b.label &&\n a.textValue === b.textValue &&\n !!a.disabled === !!b.disabled &&\n !!a.hidden === !!b.hidden\n );\n}\n\nfunction sameItems(a: readonly ListboxItem[], b: readonly ListboxItem[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n return a.every((item, index) => sameItem(item, b[index]));\n}\n\nfunction compareRegistrations(a: Registration, b: Registration): number {\n const ea = a.element.current;\n const eb = b.element.current;\n if (ea && eb && ea !== eb && ea.isConnected && eb.isConnected) {\n const position = ea.compareDocumentPosition(eb);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n }\n return a.seq - b.seq;\n}\n\nclass ListboxStoreImpl implements ListboxStore {\n private readonly indexByValue = new Map<string, number>();\n private readonly registrations = new Set<Registration>();\n private readonly stateListeners = new Set<() => void>();\n private readonly itemListeners = new Set<() => void>();\n private nextIndex = 0;\n private nextSeq = 0;\n private dirty = false;\n private flushScheduled = false;\n private itemsStale = false;\n private itemsSnapshot: ListboxItem[] = EMPTY_ITEMS;\n /** The registrations in the order of the last sort (the order {@link checkOrder} verifies). */\n private sorted: Registration[] = [];\n /** Watches the common ancestor of the option elements for moves ({@link syncObserver}). */\n private observer: MutationObserver | null = null;\n private observed: Node | null = null;\n /**\n * The observer has recorded every option move since the order was last verified, so a root\n * commit without `childList` records needs no {@link checkOrder} ({@link checkOrderAfterCommit}).\n * Cleared whenever the observed node changes: moves before `observe()` were not recorded.\n */\n private watching = false;\n /** The listbox root is mounted (between {@link connect} and {@link disconnect}). */\n private connected = false;\n private registrationMode = true;\n private active: string | null = null;\n private selected: ReadonlySet<string>;\n private hidden: ReadonlySet<string> = EMPTY_SET;\n\n constructor(selectedValues: readonly string[]) {\n this.selected = new Set(selectedValues);\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n };\n\n subscribeItems = (listener: () => void): (() => void) => {\n this.itemListeners.add(listener);\n return () => {\n this.itemListeners.delete(listener);\n };\n };\n\n isActive = (value: string): boolean => this.active === value;\n isSelected = (value: string): boolean => this.selected.has(value);\n isHidden = (value: string): boolean => this.hidden.has(value);\n\n getIndex = (value: string): number => {\n let index = this.indexByValue.get(value);\n if (index === undefined) {\n index = this.nextIndex++;\n this.indexByValue.set(value, index);\n }\n return index;\n };\n\n register = (item: ListboxItem, element: React.RefObject<HTMLElement | null>): (() => void) => {\n const registration: Registration = { item, element, seq: this.nextSeq++ };\n this.registrations.add(registration);\n this.markDirty();\n return () => {\n this.registrations.delete(registration);\n this.markDirty();\n };\n };\n\n /** Registration-mode snapshot: registered options in DOM order, first of each value. */\n getItems = (): ListboxItem[] => {\n if (this.itemsStale) {\n this.itemsStale = false;\n const sorted = Array.from(this.registrations).sort(compareRegistrations);\n this.sorted = sorted;\n const seen = new Set<string>();\n const next: ListboxItem[] = [];\n for (const { item } of sorted) {\n if (seen.has(item.value)) continue;\n seen.add(item.value);\n next.push(item);\n }\n if (!sameItems(next, this.itemsSnapshot)) this.itemsSnapshot = next;\n }\n return this.itemsSnapshot;\n };\n\n /** The mounted element of an option (for scrolling); passed on detached, hence an arrow. */\n getElement = (value: string): HTMLElement | null => {\n for (const { item, element } of this.registrations) {\n const el = element.current;\n if (item.value === value && el && el.isConnected) return el;\n }\n return null;\n };\n\n setRegistrationMode(enabled: boolean): void {\n if (enabled === this.registrationMode) return;\n this.registrationMode = enabled;\n this.syncObserver();\n }\n\n /** The listbox root mounted (layout effect): start watching the option elements for moves. */\n connect(): void {\n this.connected = true;\n this.syncObserver();\n }\n\n /** The listbox root unmounted: stop watching. */\n disconnect(): void {\n this.connected = false;\n this.syncObserver();\n }\n\n /** Publishes pending registrations: one notification for everything registered since the last. */\n flush(): void {\n if (!this.dirty) return;\n this.dirty = false;\n this.itemsStale = true;\n if (this.registrationMode) this.warnDuplicates();\n this.syncObserver();\n this.notifyItems();\n }\n\n /**\n * Re-sorts (and publishes) when the registered elements are no longer in document order. A keyed\n * reorder (a sort toggle, re-ranked results) moves option elements without registering them\n * again, so the order is verified after a commit of the listbox root that moved option elements\n * ({@link checkOrderAfterCommit}) and whenever the option elements move ({@link syncObserver}):\n * neighbours of the last sort are compared (n - 1 `compareDocumentPosition` calls), and only an\n * inversion publishes. Pending registrations are left to {@link flush}, which sorts anyway.\n */\n checkOrder(): void {\n if (!this.registrationMode || this.dirty || this.itemsStale) return;\n const sorted = this.sorted;\n for (let i = 1; i < sorted.length; i++) {\n const a = sorted[i - 1].element.current;\n const b = sorted[i].element.current;\n if (!a || !b || a === b || !a.isConnected || !b.isConnected) continue;\n if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING) {\n this.itemsStale = true;\n this.notifyItems();\n return;\n }\n }\n }\n\n /**\n * The listbox root committed (layout effect): verifies the order when the commit may have moved\n * option elements. While the observer watches them, the moves of this commit are its pending\n * `childList` records (the callback runs only in a later microtask): they are taken here, so a\n * keyed reorder re-sorts in the same commit and is not checked again by the callback, and a\n * commit without records (a highlight move) costs O(1). Without an observer (fewer than two\n * options, no `MutationObserver`) or right after it started watching, every commit checks.\n */\n checkOrderAfterCommit(): void {\n // Only childList is observed, so every pending record is an insertion or removal.\n const moved = this.observer !== null && this.observer.takeRecords().length > 0;\n const recorded = this.watching;\n this.watching = this.observed !== null;\n if (recorded && !moved) return;\n this.checkOrder();\n }\n\n /** Syncs the root's derived render state for the option selectors (layout effect). */\n setRenderState(\n active: string | null,\n selected: ReadonlySet<string>,\n hidden: ReadonlySet<string>,\n ): void {\n let changed = false;\n if (active !== this.active) {\n this.active = active;\n changed = true;\n }\n if (!sameSet(selected, this.selected)) {\n this.selected = selected;\n changed = true;\n }\n if (!sameSet(hidden, this.hidden)) {\n this.hidden = hidden;\n changed = true;\n }\n if (changed) for (const listener of Array.from(this.stateListeners)) listener();\n }\n\n private notifyItems(): void {\n for (const listener of Array.from(this.itemListeners)) listener();\n }\n\n private readonly onMutation = (): void => {\n this.checkOrder();\n };\n\n /**\n * Observes (`childList`, `subtree`) the closest common ancestor of the connected option elements,\n * so a move that renders neither the listbox root nor the options — memoized options or hoisted\n * elements reordered by a wrapper component inside the listbox — still triggers\n * {@link checkOrder}. Recomputed when the registrations change (a remounted list registers\n * again); nothing is observed with fewer than two options, in data mode, before the root mounted\n * or without `MutationObserver` (server). Attribute and text changes are not observed.\n *\n * All options must live in one container at a time (consumer contract): options split over an\n * inline and a portaled list have `<body>` as their common ancestor, so every DOM mutation on\n * the page would re-check the order — development warns once.\n */\n private syncObserver(): void {\n const target =\n this.connected && this.registrationMode && typeof MutationObserver !== 'undefined'\n ? this.commonAncestor()\n : null;\n if (target === this.observed) return;\n this.observer?.disconnect();\n this.observed = target;\n this.watching = false;\n if (!target) return;\n if (isDev) warnIfPageWide(target);\n this.observer ??= new MutationObserver(this.onMutation);\n this.observer.observe(target, { childList: true, subtree: true });\n }\n\n private commonAncestor(): Node | null {\n let ancestor: Node | null = null;\n let count = 0;\n for (const { element } of this.registrations) {\n const el = element.current;\n if (!el || !el.isConnected) continue;\n count++;\n if (ancestor === null) {\n ancestor = el.parentNode;\n continue;\n }\n while (ancestor && !ancestor.contains(el)) ancestor = ancestor.parentNode;\n if (!ancestor) return null;\n }\n return count > 1 ? ancestor : null;\n }\n\n private markDirty(): void {\n this.dirty = true;\n if (this.flushScheduled) return;\n this.flushScheduled = true;\n queueMicrotask(() => {\n this.flushScheduled = false;\n this.flush();\n });\n }\n\n private warnDuplicates(): void {\n const seen = new Set<string>();\n for (const { item } of this.registrations) {\n if (seen.has(item.value)) warnDuplicateValue(item.value);\n seen.add(item.value);\n }\n }\n}\n\nfunction warnDuplicateValue(value: string): void {\n warnOnce(\n `useListbox:duplicate:${value}`,\n `Listbox: several options share the value \"${value}\". Option values must be unique within ` +\n 'a listbox; only the first one can be highlighted and selected.',\n );\n}\n\nfunction warnIfPageWide(ancestor: Node): void {\n const doc = ancestor.ownerDocument;\n if (!doc || (ancestor !== doc.body && ancestor !== doc.documentElement)) return;\n warnOnce(\n 'useListbox:single-container',\n 'Listbox: the options of one listbox are rendered in more than one container (for example an ' +\n 'inline list kept mounted next to a portaled one). All options must live in a single ' +\n 'container at a time: render the list inline only while closed and in the portal only ' +\n 'while open.',\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Context */\n/* ------------------------------------------------------------------ */\n\n/**\n * The listbox context of the options, provided by `ListboxProvider` (and `ListboxSurface`, which\n * renders one). Not public: a context object would need flat names for its `Provider` and\n * `Consumer` members.\n */\nexport const ListboxContext: React.Context<ListboxContextValue | null> =\n React.createContext<ListboxContextValue | null>(null);\nListboxContext.displayName = 'ListboxContext';\n\n/**\n * Where an option of a data-mode list stands among the shown options: `index` from 0 and the\n * number of shown options, and in a windowed list the window's `itemSize`. Read by\n * {@link useListboxOption}, which renders `aria-posinset` and `aria-setsize` from it, and the\n * option's height from `itemSize`.\n */\nexport interface ListboxOptionPosition {\n index: number;\n setSize: number;\n itemSize?: number;\n}\n\n/**\n * The position of the option rendered inside it, provided around each option of a data-mode list\n * (Listbox, Combobox and Dropdown with `items`), so an option a consumer's `renderOption` returns\n * gets its position too. Not public: `null` (no position) everywhere else.\n */\nexport const ListboxOptionPositionContext: React.Context<ListboxOptionPosition | null> =\n React.createContext<ListboxOptionPosition | null>(null);\nListboxOptionPositionContext.displayName = 'ListboxOptionPositionContext';\n\nlet inertContext: ListboxContextValue | null = null;\n\nfunction getInertContext(): ListboxContextValue {\n inertContext ??= {\n listboxId: 'wave-listbox-inert',\n store: new ListboxStoreImpl([]),\n multiselect: false,\n mode: 'select-only',\n select: () => {},\n highlight: () => {},\n press: () => {},\n };\n return inertContext;\n}\n\nfunction useListboxContext(componentName: string): ListboxContextValue {\n const context = useContext(ListboxContext);\n if (context) return context;\n reportMissingContext(\n componentName,\n 'a listbox (Listbox, Combobox, Dropdown or a ListboxProvider)',\n );\n return getInertContext();\n}\n\n/* ------------------------------------------------------------------ */\n/* collectOptionLabels */\n/* ------------------------------------------------------------------ */\n\nconst LISTBOX_ELEMENT_KIND = Symbol.for('@mortenbrudvik/waveui/listbox-element-kind');\n\n/**\n * What {@link collectOptionLabels} does with the elements of a component marked by\n * {@link markListboxElement}: `'option'` reads a value and its label, `'group'` walks the children.\n */\nexport type ListboxElementKind = 'option' | 'group';\n\n/**\n * Marks a component so {@link collectOptionLabels} recognises its elements: `'option'` (reads its\n * `value` prop and its label: the `label` prop, else `textValue`, else its text children, as\n * `Option`'s) or `'group'` (walks its `children`). Returns the component (function, `memo` or\n * `forwardRef` components). Mark every option or group component of your own that is rendered in\n * a listbox (`Option` and `OptionGroup` are marked): unmarked components are opaque to\n * {@link collectOptionLabels}, so their labels are unknown on the server and in the first client\n * render.\n *\n * @example\n * // Option components of your own, built on useListboxOption:\n * export const ColorOption = markListboxElement(ColorOptionImpl, 'option');\n * export const ColorGroup = markListboxElement(ColorGroupImpl, 'group');\n */\nexport function markListboxElement<C extends object>(component: C, kind: ListboxElementKind): C {\n Object.defineProperty(component, LISTBOX_ELEMENT_KIND, { value: kind, configurable: true });\n return component;\n}\n\nfunction kindOf(type: unknown): ListboxElementKind | undefined {\n if ((typeof type !== 'function' && typeof type !== 'object') || type === null) return undefined;\n const kind = (type as { [LISTBOX_ELEMENT_KIND]?: unknown })[LISTBOX_ELEMENT_KIND];\n return kind === 'option' || kind === 'group' ? kind : undefined;\n}\n\ninterface OptionElementProps {\n value?: unknown;\n label?: unknown;\n textValue?: unknown;\n children?: React.ReactNode;\n}\n\nfunction textOf(node: React.ReactNode): string {\n if (typeof node === 'string') return node;\n if (typeof node === 'number' || typeof node === 'bigint') return String(node);\n if (node === null || node === undefined || typeof node === 'boolean') return '';\n if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n // Host elements and Fragments render their children; custom components are opaque.\n if (typeof node.type === 'string' || node.type === React.Fragment) {\n return textOf(node.props.children);\n }\n return '';\n }\n if (typeof node === 'object' && Symbol.iterator in node) {\n let text = '';\n for (const child of node as Iterable<React.ReactNode>) text += textOf(child);\n return text;\n }\n return '';\n}\n\nfunction optionLabel(props: OptionElementProps, value: string): string | undefined {\n if (typeof props.label === 'string') return props.label;\n if (typeof props.textValue === 'string') return props.textValue;\n if (props.children === null || props.children === undefined) return value;\n const text = textOf(props.children).trim();\n return text || undefined;\n}\n\nfunction walkOptions(children: React.ReactNode, labels: Map<string, string>): void {\n React.Children.forEach(children, (child) => {\n if (!React.isValidElement<OptionElementProps>(child)) return;\n // An Option or OptionGroup written in a Server Component arrives as a lazy type: unwrap it.\n const type = getElementType(child);\n if (type === React.Fragment) {\n walkOptions(child.props.children, labels);\n return;\n }\n const kind = kindOf(type);\n if (kind === 'group') {\n walkOptions(child.props.children, labels);\n return;\n }\n if (kind !== 'option') return;\n const { value } = child.props;\n if (typeof value !== 'string' || labels.has(value)) return;\n const label = optionLabel(child.props, value);\n if (label !== undefined) labels.set(value, label);\n });\n}\n\n/**\n * Reads option labels from `children` during render (read-only), for display text before the\n * options have registered: on the server, in the first client render and during hydration\n * (layout effects have not run yet). Walks elements of components marked with\n * {@link markListboxElement}: `'option'` elements give `value → label` (`label` prop →\n * `textValue` → the text of their string/number children and host elements → `value` when they\n * have no children); `'group'` elements and Fragments are walked into. Other components are\n * opaque (their options resolve after registration). The first option of a value wins. A lazy\n * element type (an option or group written in a React Server Component) is unwrapped first.\n *\n * Display text = `listbox.getItem(value)?.label ?? collectOptionLabels(children).get(value)`\n * (`?? value` for freeform input only).\n */\nexport function collectOptionLabels(children: React.ReactNode): Map<string, string> {\n const labels = new Map<string, string>();\n walkOptions(children, labels);\n return labels;\n}\n\n/* ------------------------------------------------------------------ */\n/* useListbox */\n/* ------------------------------------------------------------------ */\n\nfunction hasText(element: EventTarget): boolean {\n return (\n (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) &&\n element.value !== ''\n );\n}\n\n/** Ctrl/Cmd shortcuts that change the text: cut, paste, undo, redo. */\nconst TEXT_SHORTCUTS: ReadonlySet<string> = new Set(['x', 'v', 'z', 'y']);\n\n/**\n * Editable: whether a keydown edits the text of the combobox input — printable characters (AltGr,\n * i.e. Ctrl+Alt, included), Backspace/Delete, the cut/paste/undo/redo shortcuts and the\n * `Process`/`Unidentified` keys of IMEs and virtual keyboards. Nothing edits a read-only or\n * disabled input.\n */\nfunction editsText(event: React.KeyboardEvent): boolean {\n const target = event.currentTarget;\n if (\n (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) &&\n (target.readOnly || target.disabled)\n ) {\n return false;\n }\n const { key } = event;\n if (key === 'Backspace' || key === 'Delete' || key === 'Process' || key === 'Unidentified') {\n return true;\n }\n if (key.length !== 1) return false;\n const shortcut = event.metaKey || (event.ctrlKey && !isAltGraphCharacter(event));\n return !shortcut || TEXT_SHORTCUTS.has(key.toLowerCase());\n}\n\nfunction preventMouseDown(event: React.MouseEvent): void {\n event.preventDefault();\n}\n\n/**\n * The listbox behaviour of Listbox, Combobox, Dropdown, TagPicker and TimePicker, and the base of a\n * custom picker: one navigable option list drives the highlight, `aria-activedescendant` and the\n * commit, so the highlighted option is always the one Enter selects. The highlight is\n * {@link useActiveDescendant}'s.\n *\n * - **Items.** Data mode (`items`) or registration mode: `Option`s, or option components of your\n * own on {@link useListboxOption}, register inside the listbox's context (`context`, which\n * `ListboxSurface` and `ListboxProvider` provide) in DOM order, `OptionGroup`s included. The\n * registrations of one commit are published once from this hook's layout effect (which runs\n * after the options'); later additions/removals are published once per microtask. A keyed\n * reorder of the same options re-sorts them: the DOM order is re-checked after a commit of this\n * hook's component that moved option elements (a highlight move moves none, so it costs O(1))\n * and, while it is mounted, whenever option elements move (a `MutationObserver` on their\n * common ancestor) — so memoized options or hoisted elements that a wrapper component inside\n * the listbox reorders are re-sorted too, although neither the options nor this component\n * render. Options register even when filtered out or hidden (they render `hidden` and are not\n * navigable), so labels are always known — use {@link collectOptionLabels} for the display text\n * before registration (SSR/first render). `filter` is the navigability predicate, which a\n * picker builds from its query. In data mode the keys, typeahead, the labels and the filter\n * read `items` only, so a picker may render just the shown options (`items` of the result), or\n * part of them: `onScrollToItem` receives every active option that scrolls into view, rendered\n * or not.\n * - **Active option** is derived during render: the highlighted value, else the `autoHighlight`\n * fallback. The highlight is reset on close and after a single-select commit that closes the\n * listbox, and dropped once it is not navigable any more (filtered out, hidden, removed by an\n * update, or disabled without `disabledOptionsFocusable`), so it does not come back without a\n * user action when the option returns. With\n * `highlightOnFilter` the first option becomes active whenever the options change while open\n * (`useActiveDescendant`'s `activateFirstOnChange`). Editable: a text-editing key (printable\n * characters, Backspace/Delete, cut/paste/undo/redo) clears the highlight — visual focus\n * returns to the textbox (APG) — so Enter after typing never commits an option highlighted\n * before the edit; with `highlightOnFilter` the key makes the first option active instead.\n * - **Ids** `${listboxId}-opt-${n}` are stable per value (across filtering and remounts between\n * an inline closed list and a portaled open list).\n * - **Store.** Options read their active/selected/hidden flags through a store, so moving the\n * highlight re-renders only the old and the new option.\n * - **Keys (APG).** Editable: ArrowDown/ArrowUp open (selected, else first/last) and move;\n * Alt+ArrowDown opens without moving; Alt+ArrowUp closes; Enter commits the active option (with\n * the listbox closed Enter is not prevented, so forms submit); Escape closes (closed + text:\n * `onClearDraft`); Tab closes; Home/End/printable keys stay with the input (text-editing keys\n * clear the highlight, see above). Select-only:\n * ArrowDown/ArrowUp/Home/End/typeahead open and position (a closed typeahead starts from the\n * selected option; characters typed with AltGr, i.e. Ctrl+Alt on Windows, count); PageUp/PageDown\n * jump 10; Enter/Space open or commit (always prevented on keydown, Space also on keyup, so a\n * `<button>` combobox is not clicked again; a Space typed within 500 ms of a typeahead character\n * continues the search instead); Alt+ArrowUp and Tab commit and close in single-select mode\n * (with `multiselect` they only close; Tab is not prevented); Escape closes. Standalone (the\n * list has focus; nothing opens or closes): ArrowDown/ArrowUp move (from no active option to\n * the first/last), Home/End, PageUp/PageDown (10) and typeahead move; Enter and Space commit\n * and keep the committed option active (a Space typed within 500 ms of a typeahead character\n * continues the search instead); Tab, Escape and Alt+Arrow keys are not handled and not\n * prevented, so the page and enclosing layers get them.\n * \"Selected\" means the first selected navigable option in list order. Disabled options are\n * skipped unless `disabledOptionsFocusable`, and never committed; hidden options are not\n * navigable at all.\n * - The active option is scrolled into view (`{ block: 'nearest' }`) in a layout effect, except\n * after a pointer highlight or a pointer press (the list would scroll under the pointer).\n *\n * **Consumer contract** (Listbox, Combobox, Dropdown, TagPicker, TimePicker and custom pickers):\n * - All options of a listbox live in a single container at a time (inline only while closed,\n * portaled only while open — never both, not even for an exit animation; `ListboxSurface` does\n * this). Options split over two containers are watched from `<body>` and registered twice\n * (development warns).\n * - Spread `getComboboxProps()` onto the combobox element and attach **both** `onKeyDown` and\n * `onKeyUp` to it (compose them with your own handlers). `onKeyUp` is required for a\n * `<button>` combobox (select-only): without it the button's native click on Space keyup\n * toggles the listbox again after a keydown commit.\n * - Registration mode: mark option and group components of your own with\n * {@link markListboxElement} (`Option` and `OptionGroup` are marked; memo components can be\n * marked too). {@link collectOptionLabels} walks only marked components, so without the marks\n * the server render and the first client render have no display text.\n * - Display text: `getItem(value)?.label ?? collectOptionLabels(children).get(value)` (`?? value`\n * for freeform input only). Editable pickers pass `onClearDraft` for Escape on a closed list.\n * - A popup list renders in `ListboxSurface`, placed and dismissed by `useListboxPopup`; a list\n * rendered without it gets the options' context from `ListboxProvider`.\n * - Standalone mode (a listbox that holds focus): spread `getListboxProps()` onto the list and\n * merge its `ref` with yours (a pointer press on an option focuses the list through it), attach\n * `onKeyDown` to the list and pass the list's focus state as `open`. `getComboboxProps()` is not\n * used.\n */\nexport function useListbox(options: UseListboxOptions): UseListboxResult {\n const {\n open,\n onOpenChange: onOpenChangeProp,\n mode,\n multiselect = false,\n selectedValues,\n onSelect,\n items: dataItems,\n filter,\n loop = false,\n typeahead = mode !== 'editable',\n disabledOptionsFocusable = false,\n autoHighlight = 'selected',\n highlightOnFilter = false,\n onActiveValueChange,\n onScrollToItem,\n idPrefix,\n onClearDraft,\n } = options;\n\n const standalone = mode === 'standalone';\n // Standalone mode has nothing to open or close (`open` is the list's focus state), so every open\n // change requested below — a key, a commit, a typeahead match — reaches no callback there.\n const onOpenChange = standalone ? undefined : onOpenChangeProp;\n // Standalone mode: the list element (from getListboxProps().ref), which a pointer press focuses.\n const [listElement, setListElement] = useState<HTMLElement | null>(null);\n\n const listboxId = useId(idPrefix ?? 'listbox');\n const [store] = useState(() => new ListboxStoreImpl(selectedValues));\n const dataMode = dataItems !== undefined;\n const registered = useSyncExternalStore(\n store.subscribeItems,\n dataMode ? getEmptyItems : store.getItems,\n getEmptyItems,\n );\n const allItems: readonly ListboxItem[] = dataItems ?? registered;\n\n const navigable = useMemo(\n () => allItems.filter((item) => !item.hidden && (!filter || filter(item))),\n [allItems, filter],\n );\n // The values useActiveDescendant navigates: enabled navigable items, or every navigable item\n // (disabled included) with disabledOptionsFocusable.\n const navigationValues = useMemo(\n () =>\n disabledOptionsFocusable\n ? navigable.map((item) => item.value)\n : navigable.filter((item) => !item.disabled).map((item) => item.value),\n [navigable, disabledOptionsFocusable],\n );\n const itemByValue = useMemo(() => {\n const map = new Map<string, ListboxItem>();\n for (const item of allItems) if (!map.has(item.value)) map.set(item.value, item);\n return map;\n }, [allItems]);\n // Filtered-out and hidden items: registered options read it to render `hidden`.\n const hiddenSet = useMemo(() => {\n if (navigable.length === allItems.length) return EMPTY_SET;\n const visible = new Set(navigable.map((item) => item.value));\n const hidden = new Set<string>();\n for (const item of allItems) if (!visible.has(item.value)) hidden.add(item.value);\n return hidden;\n }, [allItems, navigable]);\n const selectedSet = new Set(selectedValues);\n\n // In list order (APG), not in the order the values were selected.\n const firstSelected = navigationValues.find((value) => selectedSet.has(value)) ?? null;\n let fallback: string | null = null;\n if (autoHighlight === 'selected') fallback = firstSelected ?? navigationValues[0] ?? null;\n else if (autoHighlight === 'first') fallback = navigationValues[0] ?? null;\n\n const getOptionId = useCallback(\n (value: string) => `${listboxId}-opt-${store.getIndex(value)}`,\n [listboxId, store],\n );\n const getItem = useCallback((value: string) => itemByValue.get(value), [itemByValue]);\n\n // The active option, derived during render (C-HOOKS: no effect): the highlighted value (keyboard,\n // pointer, typeahead, setActiveValue), else the autoHighlight fallback. The highlight is forgotten\n // on close (one set while closed survives only when the same update opens the listbox) and\n // dropped once it is not navigable any more (filtered out, hidden, removed by an update, or\n // disabled without disabledOptionsFocusable), so it does not come back without a user action\n // when the option returns. highlightOnFilter: the first option whenever the navigable options\n // change while open, compared by content (an inline `filter` yields new arrays on every render)\n // and with the options before opening, so the keystroke that opens the listbox and filters it in\n // one update counts; opening with unchanged options (ArrowDown, a click) keeps the autoHighlight\n // start. Every highlight but the pointer's (a hover, a standalone press) is scrolled into view\n // (the list would scroll under the pointer). With onScrollToItem (a windowed list), every such\n // highlight goes to it first by its index among the shown options, which differs from its index\n // among the navigation values when disabled options are skipped.\n const scrollToItem = useEventCallback((navigationIndex: number) => {\n const value = navigationValues[navigationIndex];\n const index = navigable.findIndex((item) => item.value === value);\n if (index !== -1) onScrollToItem?.(index);\n });\n const ad = useActiveDescendant({\n items: navigationValues,\n getId: getOptionId,\n enabled: open,\n fallback,\n loop,\n activateFirstOnChange: highlightOnFilter,\n getElement: store.getElement,\n onScrollToItem: onScrollToItem ? scrollToItem : undefined,\n onActiveValueChange,\n });\n const { activeValue, activeDescendantId } = ad;\n\n const commit = useEventCallback(\n (value: string, reason: 'select' | 'tab', event: Event, fallbackItem?: ListboxItem): void => {\n const item = itemByValue.get(value) ?? fallbackItem;\n if (!item || item.disabled) return;\n onSelect(value, { item, event });\n // Standalone and multi-select commits keep the list as it is, on the committed option.\n if (standalone || (multiselect && reason === 'select')) {\n ad.setActiveValue(value);\n } else {\n ad.setActiveValue(null);\n onOpenChange?.(false, { reason, event });\n }\n },\n );\n\n const select = useEventCallback((value: string, item: ListboxItem, event: Event) => {\n commit(value, 'select', event, item);\n });\n\n // Standalone: an option's mousedown (its default prevented, so the browser neither focuses nor\n // scrolls). Focusing the list and moving to the option land in one update, so the render that\n // enables the active option (the consumer's focus state) has the pressed one, not the fallback\n // that the scroll effect would scroll under the pointer before the click. An option that\n // cannot be active is not moved to (the next render would drop it together with the highlight):\n // a focused list keeps its active option, and an unfocused one (`open` is its focus state)\n // starts on its fallback, moved to without scrolling for the same reason.\n const press = useEventCallback((value: string) => {\n if (listElement) {\n listElement.focus({ preventScroll: true });\n } else if (standalone) {\n warnOnce(\n 'useListbox:list-ref',\n 'useListbox: the list element is unknown, so a pointer press cannot focus the list. Pass ' +\n 'the `ref` of `getListboxProps()` to the list element, merged with your own ref.',\n );\n }\n if (navigationValues.includes(value)) ad.setActiveValue(value, { scroll: false });\n else if (!open && fallback !== null) ad.setActiveValue(fallback, { scroll: false });\n });\n\n // The native keydown event behind a typeahead match, so onMatch (called from inside\n // onTypeahead(), itself only ever called from onKeyDown below) can report it: onMatch has no\n // event parameter of its own (useTypeahead is unchanged by this hook).\n const keyEventRef = useRef<Event | null>(null);\n\n const { onTypeahead } = useTypeahead({\n getItems: () =>\n navigable.map((item) => ({\n value: item.value,\n text: item.textValue ?? item.label,\n disabled: disabledOptionsFocusable ? false : item.disabled,\n })),\n onMatch: (value) => {\n ad.setActiveValue(value);\n const event = keyEventRef.current;\n if (!open && event) onOpenChange?.(true, { reason: 'keyboard', event });\n },\n });\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent) => {\n if (event.defaultPrevented || event.nativeEvent.isComposing) return;\n // Every open change and commit below comes from this key, so its native event is always\n // available; kept in a ref too, for the typeahead match handled outside this closure.\n keyEventRef.current = event.nativeEvent;\n // Editable: a key that edits the text returns visual focus to the textbox (APG) and is left to\n // the input: the highlight is cleared (highlightOnFilter: the first option), so Enter after\n // typing never commits an option highlighted before the edit.\n if (mode === 'editable' && open && editsText(event)) {\n ad.setActiveValue(highlightOnFilter ? (navigationValues[0] ?? null) : null);\n }\n const altGraph = isAltGraphCharacter(event);\n if ((event.ctrlKey && !altGraph) || event.metaKey) return;\n const { key, altKey } = event;\n // Standalone: nothing opens or closes, so Tab, Escape and Alt+Arrow keys are left to the page\n // and to enclosing layers (a Dialog's Escape), returned before anything is prevented.\n if (standalone && (key === 'Tab' || key === 'Escape' || (altKey && key.startsWith('Arrow')))) {\n return;\n }\n const selectOnly = mode === 'select-only';\n // Select-only and standalone: Home/End, PageUp/PageDown, Enter and Space act on the list.\n const editable = mode === 'editable';\n const first = navigationValues[0] ?? null;\n const last = navigationValues[navigationValues.length - 1] ?? null;\n\n const openWith = (value: string | null) => {\n ad.setActiveValue(value);\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n };\n const commitOrClose = () => {\n if (activeValue !== null) commit(activeValue, 'select', event.nativeEvent);\n else if (!multiselect) {\n onOpenChange?.(false, { reason: 'keyboard', event: event.nativeEvent });\n }\n };\n\n switch (key) {\n case 'ArrowDown':\n event.preventDefault();\n if (altKey) {\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n } else if (!open) {\n openWith(firstSelected ?? first);\n } else {\n ad.next();\n }\n return;\n case 'ArrowUp':\n event.preventDefault();\n if (altKey) {\n if (!open) return;\n // A disabled active option (disabledOptionsFocusable) closes instead of committing:\n // commit()'s own guard would otherwise leave the key unhandled.\n if (\n selectOnly &&\n !multiselect &&\n activeValue !== null &&\n !itemByValue.get(activeValue)?.disabled\n ) {\n commit(activeValue, 'select', event.nativeEvent);\n } else {\n onOpenChange?.(false, { reason: 'keyboard', event: event.nativeEvent });\n }\n } else if (!open) {\n openWith(firstSelected ?? last);\n } else {\n ad.prev();\n }\n return;\n case 'Home':\n case 'End':\n if (editable) return;\n event.preventDefault();\n openWith(key === 'Home' ? first : last);\n return;\n case 'PageUp':\n case 'PageDown':\n if (editable || !open) return;\n event.preventDefault();\n // Clamped at the ends, never wrapping (also with loop).\n ad.move(key === 'PageDown' ? 10 : -10);\n return;\n case 'Enter':\n if (editable) {\n // Closed: not prevented, so the surrounding form submits (APG).\n if (open && activeValue !== null) {\n event.preventDefault();\n commit(activeValue, 'select', event.nativeEvent);\n }\n return;\n }\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n return;\n case 'Escape':\n if (open) {\n event.preventDefault();\n onOpenChange?.(false, { reason: 'escape', event: event.nativeEvent });\n } else if (editable && onClearDraft && hasText(event.currentTarget)) {\n event.preventDefault();\n onClearDraft();\n }\n return;\n case 'Tab':\n if (!open) return;\n // A disabled active option (disabledOptionsFocusable) closes instead of committing: see\n // the Alt+ArrowUp comment above.\n if (\n selectOnly &&\n !multiselect &&\n activeValue !== null &&\n !itemByValue.get(activeValue)?.disabled\n ) {\n commit(activeValue, 'tab', event.nativeEvent);\n } else {\n onOpenChange?.(false, { reason: 'tab', event: event.nativeEvent });\n }\n return;\n default:\n break;\n }\n\n if (key === ' ' && editable) return;\n if (!typeahead || (altKey && !altGraph) || key.length !== 1) {\n if (key === ' ' && !editable) {\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n }\n return;\n }\n const current = open ? activeValue : firstSelected;\n if (onTypeahead(event, current)) {\n event.preventDefault();\n return;\n }\n if (key === ' ') {\n // Not part of a typeahead search: Space opens or commits (select-only; standalone: commits).\n event.preventDefault();\n if (!open) onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n else commitOrClose();\n return;\n }\n if (selectOnly && !open) {\n event.preventDefault();\n onOpenChange?.(true, { reason: 'keyboard', event: event.nativeEvent });\n }\n });\n\n const onKeyUp = useEventCallback((event: React.KeyboardEvent) => {\n // A <button> clicks on Space keyup: the keydown already opened or committed.\n if (mode === 'select-only' && event.key === ' ') event.preventDefault();\n });\n\n // Publish the registrations of this commit once, re-check the DOM order when this commit moved\n // options (a keyed reorder moves them without registering them again; a highlight move does not\n // move any), then sync the option selectors.\n useLayoutEffect(() => {\n store.setRegistrationMode(!dataMode);\n store.flush();\n store.checkOrderAfterCommit();\n store.setRenderState(activeValue, selectedSet, hiddenSet);\n });\n\n // While mounted, option moves that render neither this root nor the options (memoized options\n // reordered by a wrapper component) re-check the order through a MutationObserver.\n useLayoutEffect(() => {\n store.connect();\n return () => store.disconnect();\n }, [store]);\n\n useEffect(() => {\n if (!dataItems) return;\n const seen = new Set<string>();\n for (const item of dataItems) {\n if (seen.has(item.value)) warnDuplicateValue(item.value);\n seen.add(item.value);\n }\n }, [dataItems]);\n\n const context = useMemo<ListboxContextValue>(\n () => ({ listboxId, store, multiselect, mode, select, highlight: ad.highlight, press }),\n [listboxId, store, multiselect, mode, select, ad.highlight, press],\n );\n\n return {\n listboxId,\n activeValue,\n activeDescendantId,\n items: navigable,\n getItem,\n getOptionId,\n setActiveValue: ad.setActiveValue,\n onKeyDown,\n onKeyUp,\n getComboboxProps: () => {\n const props: ListboxComboboxProps = {\n role: 'combobox',\n 'aria-expanded': open,\n 'aria-controls': listboxId,\n 'aria-haspopup': 'listbox',\n };\n if (activeDescendantId !== undefined) props['aria-activedescendant'] = activeDescendantId;\n if (mode === 'editable') props['aria-autocomplete'] = 'list';\n return props;\n },\n getListboxProps: () => {\n // Standalone: the list takes focus (its ref, for the pointer press) and carries the active\n // descendant; the combobox modes keep focus on the combobox.\n const props: ListboxListProps = standalone\n ? { id: listboxId, role: 'listbox', tabIndex: 0, ref: setListElement }\n : { id: listboxId, role: 'listbox', tabIndex: -1, onMouseDown: preventMouseDown };\n if (standalone && activeDescendantId !== undefined) {\n props['aria-activedescendant'] = activeDescendantId;\n }\n if (multiselect) props['aria-multiselectable'] = true;\n return props;\n },\n context,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* useListboxOption */\n/* ------------------------------------------------------------------ */\n\nconst FLAG_ACTIVE = 1;\nconst FLAG_SELECTED = 2;\nconst FLAG_HIDDEN = 4;\n\n/** What an option registered last (compared after each commit, see {@link useListboxOption}). */\ninterface OptionRegistration {\n store: ListboxStore;\n item: ListboxItem;\n unregister: () => void;\n}\n\nfunction optionFlags(store: ListboxStore, value: string): number {\n return (\n (store.isActive(value) ? FLAG_ACTIVE : 0) |\n (store.isSelected(value) ? FLAG_SELECTED : 0) |\n (store.isHidden(value) ? FLAG_HIDDEN : 0)\n );\n}\n\n/**\n * An option of the surrounding listbox, for option components of your own: the listbox's context\n * comes from `ListboxSurface` or `ListboxProvider` ({@link UseListboxResult.context}). Registers\n * the option (registration mode) and its element, and reads its flags from the listbox store, so\n * it re-renders only when its own active/selected/hidden state changes. Spread `optionProps` onto\n * the `<li>` (`id`, `role=\"option\"`, `aria-selected`, `aria-disabled`, `hidden`, and\n * `data-active`/`data-selected`/`data-disabled` for styling; `aria-posinset` and `aria-setsize`\n * when a `renderOption` of Listbox, Combobox or Dropdown renders it, and in a windowed list a\n * `style` with its height, to merge after yours) and compose its `onClick` —\n * and in standalone mode its `onMouseDown`, the pointer press — with your own handlers. Mark the\n * component with {@link markListboxElement}, so its label is known before it registers.\n *\n * Throws in development when used outside a listbox; in production it logs the error once and\n * renders an inert option.\n *\n * @typeParam E The option element type (`HTMLLIElement` for an `<li>`), so `ref` needs no cast.\n */\nexport function useListboxOption<E extends HTMLElement = HTMLElement>(\n props: UseListboxOptionProps,\n ref?: React.Ref<E>,\n): UseListboxOptionResult<E> {\n const context = useListboxContext('Option');\n const { store, listboxId, multiselect, mode, select, highlight, press } = context;\n const position = useContext(ListboxOptionPositionContext);\n const { value, label, textValue, disabled = false, hidden: hiddenByConsumer = false } = props;\n\n const id = `${listboxId}-opt-${store.getIndex(value)}`;\n const flags = useSyncExternalStore(\n store.subscribe,\n () => optionFlags(store, value),\n () => optionFlags(store, value),\n );\n const active = (flags & FLAG_ACTIVE) !== 0;\n const selected = (flags & FLAG_SELECTED) !== 0;\n const hidden = hiddenByConsumer || (flags & FLAG_HIDDEN) !== 0;\n\n const elementRef = useRef<E | null>(null);\n const setElement = useCallback((element: E | null) => {\n elementRef.current = element;\n }, []);\n const mergedRef = useMergedRefs<E>(ref, setElement);\n const registrationRef = useRef<OptionRegistration | null>(null);\n\n // After every commit of this option: (re-)register when the item changed — including a label\n // read from the element's text, which any re-render can change. A move (keyed reorder) does not\n // register again; the listbox root sees it through its root commit or its MutationObserver.\n useLayoutEffect(() => {\n const text =\n label === undefined && textValue === undefined\n ? elementRef.current?.textContent?.trim()\n : undefined;\n const item: ListboxItem = { value, label: label ?? textValue ?? (text || value) };\n if (textValue !== undefined) item.textValue = textValue;\n if (disabled) item.disabled = true;\n if (hiddenByConsumer) item.hidden = true;\n const current = registrationRef.current;\n if (current && current.store === store && sameItem(current.item, item)) return;\n current?.unregister();\n registrationRef.current = { store, item, unregister: store.register(item, elementRef) };\n });\n useLayoutEffect(\n () => () => {\n registrationRef.current?.unregister();\n registrationRef.current = null;\n },\n [],\n );\n\n const onClick = useCallback(\n (event: React.MouseEvent<E>) => {\n if (disabled) return;\n const item: ListboxItem = { value, label: label ?? textValue ?? value };\n if (textValue !== undefined) item.textValue = textValue;\n select(value, item, event.nativeEvent);\n },\n [select, value, label, textValue, disabled],\n );\n\n const onPointerMove = useCallback(() => {\n if (!disabled && !store.isActive(value)) highlight(value);\n }, [highlight, store, value, disabled]);\n\n // Standalone: the browser would focus the list itself, and focus would scroll the list's\n // active option into view under the pointer before the click; the press focuses the list\n // without scrolling and makes this option active instead. Only the primary button presses: a\n // right or middle press is left to the browser (its context menu, autoscroll), as on a native\n // `<select size>`, and moves no active option.\n const onMouseDown = useCallback(\n (event: React.MouseEvent<E>) => {\n if (event.button !== 0) return;\n event.preventDefault();\n press(value);\n },\n [press, value],\n );\n\n const optionProps: ListboxOptionElementProps<E> = {\n id,\n role: 'option',\n 'aria-selected': selected,\n onClick,\n onPointerMove,\n ref: mergedRef,\n };\n if (mode === 'standalone') optionProps.onMouseDown = onMouseDown;\n if (position) {\n optionProps['aria-setsize'] = position.setSize;\n optionProps['aria-posinset'] = position.index + 1;\n // A windowed list lays its options out at exactly the window's item size.\n if (position.itemSize !== undefined) {\n optionProps.style = { height: position.itemSize, boxSizing: 'border-box', flexShrink: 0 };\n }\n }\n if (disabled) {\n optionProps['aria-disabled'] = true;\n optionProps['data-disabled'] = '';\n }\n if (hidden) optionProps.hidden = true;\n if (active) optionProps['data-active'] = '';\n if (selected) optionProps['data-selected'] = '';\n\n return { id, selected, active, disabled, hidden, multiselect, optionProps };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../components/provider/WaveProvider.cjs"),t=require("../lib/motion.cjs");function n(){return e.useWaveTheme().motion}function r(e,r,i){return t.resolveMotion(e,r,n(),i)}exports.useMotionClasses=r,exports.useMotionPreference=n;
|
|
2
|
+
//# sourceMappingURL=useMotion.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMotion.cjs","names":["useWaveTheme","resolveMotion","require_WaveProvider","require_motion","useMotionPreference","useMotionClasses","slot","recipe","legacyClassName"],"sources":["../../src/hooks/useMotion.ts"],"sourcesContent":["import { useWaveTheme } from '../components/provider/WaveProvider';\nimport { resolveMotion } from '../lib/motion';\nimport type { MotionPreference, MotionSlot } from '../lib/types';\n\n/**\n * The motion preference of the nearest `WaveProvider`: `'default'` outside one.\n *\n * @returns `'default'`, or `'none'` when a provider above turned motion off.\n */\nexport function useMotionPreference(): MotionPreference {\n return useWaveTheme().motion;\n}\n\n/**\n * The motion classes of an animated part against the provider's preference (see `resolveMotion`).\n *\n * @param slot - The part's `motion` prop: `undefined` the default, `null` none, a value replaces it.\n * @param recipe - The part's default motion classes.\n * @param legacyClassName - The part's `className`, for the parts that keep 0.14's behaviour.\n */\nexport function useMotionClasses(\n slot: MotionSlot | null | undefined,\n recipe: string,\n legacyClassName?: string,\n): string {\n return resolveMotion(slot, recipe, useMotionPreference(), legacyClassName);\n}\n"],"mappings":"uGASA,SAAgBI,GAAwC,CACtD,OAAOJ,EAAAA,aAAa,CAAC,CAAC,MACxB,CASA,SAAgBK,EACd,EACA,EACA,EACQ,CACR,OAAOJ,EAAAA,cAAcK,EAAMC,EAAQH,EAAoB,EAAGI,CAAe,CAC3E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useWaveTheme as e } from "../components/provider/WaveProvider.mjs";
|
|
3
|
+
import { resolveMotion as t } from "../lib/motion.mjs";
|
|
4
|
+
//#region src/hooks/useMotion.ts
|
|
5
|
+
function n() {
|
|
6
|
+
return e().motion;
|
|
7
|
+
}
|
|
8
|
+
function r(e, r, i) {
|
|
9
|
+
return t(e, r, n(), i);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { r as useMotionClasses, n as useMotionPreference };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=useMotion.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMotion.mjs","names":["useMotionPreference","useWaveTheme","useMotionClasses","resolveMotion","slot","recipe","legacyClassName"],"sources":["../../src/hooks/useMotion.ts"],"sourcesContent":["import { useWaveTheme } from '../components/provider/WaveProvider';\nimport { resolveMotion } from '../lib/motion';\nimport type { MotionPreference, MotionSlot } from '../lib/types';\n\n/**\n * The motion preference of the nearest `WaveProvider`: `'default'` outside one.\n *\n * @returns `'default'`, or `'none'` when a provider above turned motion off.\n */\nexport function useMotionPreference(): MotionPreference {\n return useWaveTheme().motion;\n}\n\n/**\n * The motion classes of an animated part against the provider's preference (see `resolveMotion`).\n *\n * @param slot - The part's `motion` prop: `undefined` the default, `null` none, a value replaces it.\n * @param recipe - The part's default motion classes.\n * @param legacyClassName - The part's `className`, for the parts that keep 0.14's behaviour.\n */\nexport function useMotionClasses(\n slot: MotionSlot | null | undefined,\n recipe: string,\n legacyClassName?: string,\n): string {\n return resolveMotion(slot, recipe, useMotionPreference(), legacyClassName);\n}\n"],"mappings":";;;;AASA,SAAgBA,IAAwC;CACtD,OAAOC,EAAa,CAAC,CAAC;AACxB;AASA,SAAgBC,EACd,GACA,GACA,GACQ;CACR,OAAOC,EAAcC,GAAMC,GAAQL,EAAoB,GAAGM,CAAe;AAC3E"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";const e=require("../lib/children.cjs");let t=require("react");function n(e){let t=e.slice(e.lastIndexOf(`/.`)+1);return t.slice(Math.max(t.lastIndexOf(`:`),0)+1).startsWith(`$`)}function r(r){let i=[],a=[];for(let{key:o,node:s}of e.flattenChildren(r))s!==``&&((0,t.isValidElement)(s)&&n(o)?i.push({key:o,element:s}):a.push((0,t.isValidElement)(s)?(0,t.cloneElement)(s,{key:o}):s));return{keyed:i,skipped:a}}function i(e,t,n){let r=new Map(e.map(e=>[e.key,e])),i=new Set(t.map(({key:e})=>e)),a=t.map(({key:e,element:t})=>({key:e,element:t,visible:!0,appear:r.get(e)?.appear??n}));return e.forEach((t,n)=>{if(i.has(t.key))return;let r=t.visible?{...t,visible:!1}:t,o=n===0?-1:a.findIndex(({key:t})=>t===e[n-1].key);a.splice(o+1,0,r)}),a}function a(e,n){let{appear:a}=n,{keyed:o,skipped:s}=r(e),[c,l]=(0,t.useState)(()=>({children:e,entries:o.map(({key:e,element:t})=>({key:e,element:t,visible:!0,appear:!1}))})),{entries:u}=c;e!==c.children&&(u=i(c.entries,o,a),l({children:e,entries:u}));let d=(0,t.useCallback)(e=>{l(t=>{let n=t.entries.find(t=>t.key===e&&!t.visible);return n?{...t,entries:t.entries.filter(e=>e!==n)}:t})},[]);return{entries:u,skipped:s,onExited:d}}exports.usePresenceGroup=a;
|
|
2
|
+
//# sourceMappingURL=usePresenceGroup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresenceGroup.cjs","names":["flattenChildren","isValidElement","cloneElement","useState","useCallback","require_children","react","hasConsumerKey","own","key","partitionChildren","keyed","skipped","children","node","mergeEntries","previousByKey","previous","entry","nextKeys","next","merged","appear","kept","before","index","usePresenceGroup","options","state","setState","onExited","exited","current"],"sources":["../../src/hooks/usePresenceGroup.ts"],"sourcesContent":["import { cloneElement, isValidElement, useCallback, useState } from 'react';\nimport type * as React from 'react';\nimport { flattenChildren } from '../lib/children';\n\n/** One child of the group: shown, or kept while it exits. */\nexport interface PresenceGroupEntry {\n /** The child's key among the group's flattened children (`flattenChildren`), e.g. `.$id`. */\n key: string;\n /** The child: its latest element while shown, the last one it had while it exits. */\n element: React.ReactElement;\n /** `false` from the render that removed the child until its item reports the end of its exit. */\n visible: boolean;\n /** `false` for the children of the group's first render, else the group's `appear`. */\n appear: boolean;\n}\n\n/** Returned by {@link usePresenceGroup}. */\nexport interface UsePresenceGroupResult {\n /** The keyed children, shown ones in their order and removed ones kept where they were. */\n entries: readonly PresenceGroupEntry[];\n /** Non-element children and elements without a key of the consumer's, in order (rendered after the entries, unanimated). */\n skipped: readonly React.ReactNode[];\n /** Drops the entry of `key` once its exit has ended (call it from its item's `onExited`). */\n onExited: (key: string) => void;\n}\n\ninterface GroupState {\n /** The children the entries were merged from. */\n children: React.ReactNode;\n entries: readonly PresenceGroupEntry[];\n}\n\n/**\n * Whether the child of a flattened key has a key of the consumer's. `React.Children.toArray` keys\n * a child `.` plus its path through nested arrays, joined by `:`: a key of the consumer's is\n * `$…` (where `:` is escaped), an index is a number (`.1:$id` is a keyed element of an array next\n * to other children, `.0` a child without a key). `flattenChildren` joins a Fragment's key and its\n * children's with `/`, so the child's own segment starts after the last `/.` (a consumer's key may\n * hold a `/`).\n */\nfunction hasConsumerKey(key: string): boolean {\n const own = key.slice(key.lastIndexOf('/.') + 1);\n return own.slice(Math.max(own.lastIndexOf(':'), 0) + 1).startsWith('$');\n}\n\n/** The keyed elements of `children`, and the rest, each in its order. */\nfunction partitionChildren(children: React.ReactNode): {\n keyed: Array<{ key: string; element: React.ReactElement }>;\n skipped: React.ReactNode[];\n} {\n const keyed: Array<{ key: string; element: React.ReactElement }> = [];\n const skipped: React.ReactNode[] = [];\n for (const { key, node } of flattenChildren(children)) {\n // `toArray` keeps an empty string (`{error && <Bar key=\"error\" />}` with `error === ''`),\n // which renders nothing: no child, so it neither renders nor warns.\n if (node === '') continue;\n if (isValidElement(node) && hasConsumerKey(key)) keyed.push({ key, element: node });\n // Keyed by its flattened key, unique across Fragments (its own key repeats in each one).\n else skipped.push(isValidElement(node) ? cloneElement(node, { key }) : node);\n }\n return { keyed, skipped };\n}\n\n/**\n * The entries after a change of the children: the keyed children in their order, shown with\n * their new elements (an entry that exits is shown again), a new key appearing with `appear`; then\n * each previous entry that is no longer among them, not shown, right after the entry that\n * preceded it before (already placed by then, being kept or shown), or first.\n */\nfunction mergeEntries(\n previous: readonly PresenceGroupEntry[],\n next: ReadonlyArray<{ key: string; element: React.ReactElement }>,\n appear: boolean,\n): PresenceGroupEntry[] {\n const previousByKey = new Map(previous.map((entry) => [entry.key, entry]));\n const nextKeys = new Set(next.map(({ key }) => key));\n const merged: PresenceGroupEntry[] = next.map(({ key, element }) => ({\n key,\n element,\n visible: true,\n appear: previousByKey.get(key)?.appear ?? appear,\n }));\n previous.forEach((entry, index) => {\n if (nextKeys.has(entry.key)) return;\n const kept = entry.visible ? { ...entry, visible: false } : entry;\n const before =\n index === 0 ? -1 : merged.findIndex(({ key }) => key === previous[index - 1].key);\n merged.splice(before + 1, 0, kept);\n });\n return merged;\n}\n\n/**\n * Keyed children of a group, with removed ones kept until their exit ends. The caller renders each\n * entry as an item that calls `usePresence(entry.visible, { appear: entry.appear, onExited })`,\n * its `onExited` calling the result's `onExited(entry.key)`, and renders `skipped` after them.\n *\n * - **Children** go through `flattenChildren` (Fragments flattened, consumer keys kept). An\n * element with a key of the consumer's is an entry, also inside a Fragment or an array; any\n * other child (text, a number, an element without a key) is in `skipped`, as given (an element\n * with its flattened key, so the list renders without duplicate keys). An empty string, which\n * renders nothing, is neither.\n * - **Merging**, derived during render from the previous entries: the keyed children in their\n * order, shown; a removed child kept, not shown, right after the child that preceded it (a run\n * of removed children in its order), or first; a key that comes back while it exits is shown\n * again with its new element, so its item, and the state inside it, stay. A reorder moves the\n * entries without removing any. An entry is dropped when its item reports the end of its exit.\n * - **Appearing.** The children of the group's first render (on the server, when hydrating and in\n * a client render) never appear; a child added later appears with `options.appear`.\n *\n * @param children - The group's children.\n * @param options - `appear`: whether a child added after the first render appears.\n */\nexport function usePresenceGroup(\n children: React.ReactNode,\n options: { appear: boolean },\n): UsePresenceGroupResult {\n const { appear } = options;\n const { keyed, skipped } = partitionChildren(children);\n const [state, setState] = useState<GroupState>(() => ({\n children,\n entries: keyed.map(({ key, element }) => ({ key, element, visible: true, appear: false })),\n }));\n\n // Changes of the children, derived during render (C-HOOKS: a previous-value state).\n let { entries } = state;\n if (children !== state.children) {\n entries = mergeEntries(state.entries, keyed, appear);\n setState({ children, entries });\n }\n\n const onExited = useCallback((key: string) => {\n setState((current) => {\n const exited = current.entries.find((entry) => entry.key === key && !entry.visible);\n if (!exited) return current;\n return { ...current, entries: current.entries.filter((entry) => entry !== exited) };\n });\n }, []);\n\n return { entries, skipped, onExited };\n}\n"],"mappings":"2EAwCA,SAASO,EAAe,EAAsB,CAC5C,IAAMC,EAAMC,EAAI,MAAMA,EAAI,YAAY,IAAI,EAAI,CAAC,EAC/C,OAAOD,EAAI,MAAM,KAAK,IAAIA,EAAI,YAAY,GAAG,EAAG,CAAC,EAAI,CAAC,CAAC,CAAC,WAAW,GAAG,CACxE,CAGA,SAASE,EAAkB,EAGzB,CACA,IAAMC,EAA6D,CAAC,EAC9DC,EAA6B,CAAC,EACpC,IAAK,GAAM,CAAE,MAAK,UAAUZ,EAAAA,gBAAgBa,CAAQ,EAG9CC,IAAS,MACb,EAAIb,EAAAA,eAAAA,CAAea,CAAI,GAAKP,EAAeE,CAAG,EAAGE,EAAM,KAAK,CAAE,MAAK,QAASG,CAAK,CAAC,EAE7EF,EAAQ,MAAA,EAAKX,EAAAA,eAAAA,CAAea,CAAI,GAAA,EAAIZ,EAAAA,aAAAA,CAAaY,EAAM,CAAE,KAAI,CAAC,EAAIA,CAAI,GAE7E,MAAO,CAAE,QAAO,SAAQ,CAC1B,CAQA,SAASC,EACP,EACA,EACA,EACsB,CACtB,IAAMC,EAAgB,IAAI,IAAIC,EAAS,IAAK,GAAU,CAACC,EAAM,IAAKA,CAAK,CAAC,CAAC,EACnEC,EAAW,IAAI,IAAIC,EAAK,KAAK,CAAE,SAAUX,CAAG,CAAC,EAC7CY,EAA+BD,EAAK,KAAK,CAAE,MAAK,cAAe,CACnE,MACA,UACA,QAAS,GACT,OAAQJ,EAAc,IAAIP,CAAG,CAAC,EAAE,QAAUa,CAC5C,EAAE,EAQF,OAPAL,EAAS,SAAS,EAAO,IAAU,CACjC,GAAIE,EAAS,IAAID,EAAM,GAAG,EAAG,OAC7B,IAAMK,EAAOL,EAAM,QAAU,CAAE,GAAGA,EAAO,QAAS,EAAM,EAAIA,EACtDM,EACJC,IAAU,EAAI,GAAKJ,EAAO,WAAW,CAAE,SAAUZ,IAAQQ,EAASQ,EAAQ,EAAE,CAAC,GAAG,EAClFJ,EAAO,OAAOG,EAAS,EAAG,EAAGD,CAAI,CACnC,CAAC,EACMF,CACT,CAuBA,SAAgBK,EACd,EACA,EACwB,CACxB,GAAM,CAAE,UAAWC,EACb,CAAE,QAAO,WAAYjB,EAAkBG,CAAQ,EAC/C,CAACe,EAAOC,IAAA,EAAY1B,EAAAA,SAAAA,MAA4B,CACpD,WACA,QAASQ,EAAM,KAAK,CAAE,MAAK,cAAe,CAAE,MAAK,UAAS,QAAS,GAAM,OAAQ,EAAM,EAAE,CAC3F,EAAE,EAGE,CAAE,WAAYiB,EACdf,IAAae,EAAM,WACrB,EAAUb,EAAaa,EAAM,QAASjB,EAAOW,CAAM,EACnDO,EAAS,CAAE,WAAU,SAAQ,CAAC,GAGhC,IAAMC,GAAA,EAAW1B,EAAAA,YAAAA,CAAa,GAAgB,CAC5CyB,EAAU,GAAY,CACpB,IAAME,EAASC,EAAQ,QAAQ,KAAM,GAAUd,EAAM,MAAQT,GAAO,CAACS,EAAM,OAAO,EAElF,OADKa,EACE,CAAE,GAAGC,EAAS,QAASA,EAAQ,QAAQ,OAAQ,GAAUd,IAAUa,CAAM,CAAE,EAD9DC,CAEtB,CAAC,CACH,EAAG,CAAC,CAAC,EAEL,MAAO,CAAE,UAAS,UAAS,UAAS,CACtC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { flattenChildren as e } from "../lib/children.mjs";
|
|
3
|
+
import { cloneElement as t, isValidElement as n, useCallback as r, useState as i } from "react";
|
|
4
|
+
//#region src/hooks/usePresenceGroup.ts
|
|
5
|
+
function a(e) {
|
|
6
|
+
let t = e.slice(e.lastIndexOf("/.") + 1);
|
|
7
|
+
return t.slice(Math.max(t.lastIndexOf(":"), 0) + 1).startsWith("$");
|
|
8
|
+
}
|
|
9
|
+
function o(r) {
|
|
10
|
+
let i = [], o = [];
|
|
11
|
+
for (let { key: s, node: c } of e(r)) c !== "" && (n(c) && a(s) ? i.push({
|
|
12
|
+
key: s,
|
|
13
|
+
element: c
|
|
14
|
+
}) : o.push(n(c) ? t(c, { key: s }) : c));
|
|
15
|
+
return {
|
|
16
|
+
keyed: i,
|
|
17
|
+
skipped: o
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function s(e, t, n) {
|
|
21
|
+
let r = new Map(e.map((e) => [e.key, e])), i = new Set(t.map(({ key: e }) => e)), a = t.map(({ key: e, element: t }) => ({
|
|
22
|
+
key: e,
|
|
23
|
+
element: t,
|
|
24
|
+
visible: !0,
|
|
25
|
+
appear: r.get(e)?.appear ?? n
|
|
26
|
+
}));
|
|
27
|
+
return e.forEach((t, n) => {
|
|
28
|
+
if (i.has(t.key)) return;
|
|
29
|
+
let r = t.visible ? {
|
|
30
|
+
...t,
|
|
31
|
+
visible: !1
|
|
32
|
+
} : t, o = n === 0 ? -1 : a.findIndex(({ key: t }) => t === e[n - 1].key);
|
|
33
|
+
a.splice(o + 1, 0, r);
|
|
34
|
+
}), a;
|
|
35
|
+
}
|
|
36
|
+
function c(e, t) {
|
|
37
|
+
let { appear: n } = t, { keyed: a, skipped: c } = o(e), [l, u] = i(() => ({
|
|
38
|
+
children: e,
|
|
39
|
+
entries: a.map(({ key: e, element: t }) => ({
|
|
40
|
+
key: e,
|
|
41
|
+
element: t,
|
|
42
|
+
visible: !0,
|
|
43
|
+
appear: !1
|
|
44
|
+
}))
|
|
45
|
+
})), { entries: d } = l;
|
|
46
|
+
e !== l.children && (d = s(l.entries, a, n), u({
|
|
47
|
+
children: e,
|
|
48
|
+
entries: d
|
|
49
|
+
}));
|
|
50
|
+
let f = r((e) => {
|
|
51
|
+
u((t) => {
|
|
52
|
+
let n = t.entries.find((t) => t.key === e && !t.visible);
|
|
53
|
+
return n ? {
|
|
54
|
+
...t,
|
|
55
|
+
entries: t.entries.filter((e) => e !== n)
|
|
56
|
+
} : t;
|
|
57
|
+
});
|
|
58
|
+
}, []);
|
|
59
|
+
return {
|
|
60
|
+
entries: d,
|
|
61
|
+
skipped: c,
|
|
62
|
+
onExited: f
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { c as usePresenceGroup };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=usePresenceGroup.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresenceGroup.mjs","names":["hasConsumerKey","own","key","partitionChildren","keyed","skipped","flattenChildren","children","node","isValidElement","cloneElement","mergeEntries","previousByKey","previous","entry","nextKeys","next","merged","appear","kept","before","index","usePresenceGroup","options","state","setState","useState","onExited","useCallback","exited","current"],"sources":["../../src/hooks/usePresenceGroup.ts"],"sourcesContent":["import { cloneElement, isValidElement, useCallback, useState } from 'react';\nimport type * as React from 'react';\nimport { flattenChildren } from '../lib/children';\n\n/** One child of the group: shown, or kept while it exits. */\nexport interface PresenceGroupEntry {\n /** The child's key among the group's flattened children (`flattenChildren`), e.g. `.$id`. */\n key: string;\n /** The child: its latest element while shown, the last one it had while it exits. */\n element: React.ReactElement;\n /** `false` from the render that removed the child until its item reports the end of its exit. */\n visible: boolean;\n /** `false` for the children of the group's first render, else the group's `appear`. */\n appear: boolean;\n}\n\n/** Returned by {@link usePresenceGroup}. */\nexport interface UsePresenceGroupResult {\n /** The keyed children, shown ones in their order and removed ones kept where they were. */\n entries: readonly PresenceGroupEntry[];\n /** Non-element children and elements without a key of the consumer's, in order (rendered after the entries, unanimated). */\n skipped: readonly React.ReactNode[];\n /** Drops the entry of `key` once its exit has ended (call it from its item's `onExited`). */\n onExited: (key: string) => void;\n}\n\ninterface GroupState {\n /** The children the entries were merged from. */\n children: React.ReactNode;\n entries: readonly PresenceGroupEntry[];\n}\n\n/**\n * Whether the child of a flattened key has a key of the consumer's. `React.Children.toArray` keys\n * a child `.` plus its path through nested arrays, joined by `:`: a key of the consumer's is\n * `$…` (where `:` is escaped), an index is a number (`.1:$id` is a keyed element of an array next\n * to other children, `.0` a child without a key). `flattenChildren` joins a Fragment's key and its\n * children's with `/`, so the child's own segment starts after the last `/.` (a consumer's key may\n * hold a `/`).\n */\nfunction hasConsumerKey(key: string): boolean {\n const own = key.slice(key.lastIndexOf('/.') + 1);\n return own.slice(Math.max(own.lastIndexOf(':'), 0) + 1).startsWith('$');\n}\n\n/** The keyed elements of `children`, and the rest, each in its order. */\nfunction partitionChildren(children: React.ReactNode): {\n keyed: Array<{ key: string; element: React.ReactElement }>;\n skipped: React.ReactNode[];\n} {\n const keyed: Array<{ key: string; element: React.ReactElement }> = [];\n const skipped: React.ReactNode[] = [];\n for (const { key, node } of flattenChildren(children)) {\n // `toArray` keeps an empty string (`{error && <Bar key=\"error\" />}` with `error === ''`),\n // which renders nothing: no child, so it neither renders nor warns.\n if (node === '') continue;\n if (isValidElement(node) && hasConsumerKey(key)) keyed.push({ key, element: node });\n // Keyed by its flattened key, unique across Fragments (its own key repeats in each one).\n else skipped.push(isValidElement(node) ? cloneElement(node, { key }) : node);\n }\n return { keyed, skipped };\n}\n\n/**\n * The entries after a change of the children: the keyed children in their order, shown with\n * their new elements (an entry that exits is shown again), a new key appearing with `appear`; then\n * each previous entry that is no longer among them, not shown, right after the entry that\n * preceded it before (already placed by then, being kept or shown), or first.\n */\nfunction mergeEntries(\n previous: readonly PresenceGroupEntry[],\n next: ReadonlyArray<{ key: string; element: React.ReactElement }>,\n appear: boolean,\n): PresenceGroupEntry[] {\n const previousByKey = new Map(previous.map((entry) => [entry.key, entry]));\n const nextKeys = new Set(next.map(({ key }) => key));\n const merged: PresenceGroupEntry[] = next.map(({ key, element }) => ({\n key,\n element,\n visible: true,\n appear: previousByKey.get(key)?.appear ?? appear,\n }));\n previous.forEach((entry, index) => {\n if (nextKeys.has(entry.key)) return;\n const kept = entry.visible ? { ...entry, visible: false } : entry;\n const before =\n index === 0 ? -1 : merged.findIndex(({ key }) => key === previous[index - 1].key);\n merged.splice(before + 1, 0, kept);\n });\n return merged;\n}\n\n/**\n * Keyed children of a group, with removed ones kept until their exit ends. The caller renders each\n * entry as an item that calls `usePresence(entry.visible, { appear: entry.appear, onExited })`,\n * its `onExited` calling the result's `onExited(entry.key)`, and renders `skipped` after them.\n *\n * - **Children** go through `flattenChildren` (Fragments flattened, consumer keys kept). An\n * element with a key of the consumer's is an entry, also inside a Fragment or an array; any\n * other child (text, a number, an element without a key) is in `skipped`, as given (an element\n * with its flattened key, so the list renders without duplicate keys). An empty string, which\n * renders nothing, is neither.\n * - **Merging**, derived during render from the previous entries: the keyed children in their\n * order, shown; a removed child kept, not shown, right after the child that preceded it (a run\n * of removed children in its order), or first; a key that comes back while it exits is shown\n * again with its new element, so its item, and the state inside it, stay. A reorder moves the\n * entries without removing any. An entry is dropped when its item reports the end of its exit.\n * - **Appearing.** The children of the group's first render (on the server, when hydrating and in\n * a client render) never appear; a child added later appears with `options.appear`.\n *\n * @param children - The group's children.\n * @param options - `appear`: whether a child added after the first render appears.\n */\nexport function usePresenceGroup(\n children: React.ReactNode,\n options: { appear: boolean },\n): UsePresenceGroupResult {\n const { appear } = options;\n const { keyed, skipped } = partitionChildren(children);\n const [state, setState] = useState<GroupState>(() => ({\n children,\n entries: keyed.map(({ key, element }) => ({ key, element, visible: true, appear: false })),\n }));\n\n // Changes of the children, derived during render (C-HOOKS: a previous-value state).\n let { entries } = state;\n if (children !== state.children) {\n entries = mergeEntries(state.entries, keyed, appear);\n setState({ children, entries });\n }\n\n const onExited = useCallback((key: string) => {\n setState((current) => {\n const exited = current.entries.find((entry) => entry.key === key && !entry.visible);\n if (!exited) return current;\n return { ...current, entries: current.entries.filter((entry) => entry !== exited) };\n });\n }, []);\n\n return { entries, skipped, onExited };\n}\n"],"mappings":";;;;AAwCA,SAASA,EAAe,GAAsB;CAC5C,IAAMC,IAAMC,EAAI,MAAMA,EAAI,YAAY,IAAI,IAAI,CAAC;CAC/C,OAAOD,EAAI,MAAM,KAAK,IAAIA,EAAI,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,GAAG;AACxE;AAGA,SAASE,EAAkB,GAGzB;CACA,IAAMC,IAA6D,CAAC,GAC9DC,IAA6B,CAAC;CACpC,KAAK,IAAM,EAAE,QAAK,aAAUC,EAAgBC,CAAQ,GAGlD,AAAIC,MAAS,OACTC,EAAeD,CAAI,KAAKR,EAAeE,CAAG,IAAGE,EAAM,KAAK;EAAE;EAAK,SAASI;CAAK,CAAC,IAE7EH,EAAQ,KAAKI,EAAeD,CAAI,IAAIE,EAAaF,GAAM,EAAE,OAAI,CAAC,IAAIA,CAAI;CAE7E,OAAO;EAAE;EAAO;CAAQ;AAC1B;AAQA,SAASG,EACP,GACA,GACA,GACsB;CACtB,IAAMC,IAAgB,IAAI,IAAIC,EAAS,KAAK,MAAU,CAACC,EAAM,KAAKA,CAAK,CAAC,CAAC,GACnEC,IAAW,IAAI,IAAIC,EAAK,KAAK,EAAE,aAAUd,CAAG,CAAC,GAC7Ce,IAA+BD,EAAK,KAAK,EAAE,QAAK,kBAAe;EACnE;EACA;EACA,SAAS;EACT,QAAQJ,EAAc,IAAIV,CAAG,CAAC,EAAE,UAAUgB;CAC5C,EAAE;CAQF,OAPAL,EAAS,SAAS,GAAO,MAAU;EACjC,IAAIE,EAAS,IAAID,EAAM,GAAG,GAAG;EAC7B,IAAMK,IAAOL,EAAM,UAAU;GAAE,GAAGA;GAAO,SAAS;EAAM,IAAIA,GACtDM,IACJC,MAAU,IAAI,KAAKJ,EAAO,WAAW,EAAE,aAAUf,MAAQW,EAASQ,IAAQ,EAAE,CAAC,GAAG;EAClF,EAAO,OAAOD,IAAS,GAAG,GAAGD,CAAI;CACnC,CAAC,GACMF;AACT;AAuBA,SAAgBK,EACd,GACA,GACwB;CACxB,IAAM,EAAE,cAAWC,GACb,EAAE,UAAO,eAAYpB,EAAkBI,CAAQ,GAC/C,CAACiB,GAAOC,KAAYC,SAA4B;EACpD;EACA,SAAStB,EAAM,KAAK,EAAE,QAAK,kBAAe;GAAE;GAAK;GAAS,SAAS;GAAM,QAAQ;EAAM,EAAE;CAC3F,EAAE,GAGE,EAAE,eAAYoB;CAClB,AAAIjB,MAAaiB,EAAM,aACrB,IAAUb,EAAaa,EAAM,SAASpB,GAAOc,CAAM,GACnDO,EAAS;EAAE;EAAU;CAAQ,CAAC;CAGhC,IAAME,IAAWC,GAAa,MAAgB;EAC5C,GAAU,MAAY;GACpB,IAAMC,IAASC,EAAQ,QAAQ,MAAM,MAAUhB,EAAM,QAAQZ,KAAO,CAACY,EAAM,OAAO;GAElF,OADKe,IACE;IAAE,GAAGC;IAAS,SAASA,EAAQ,QAAQ,QAAQ,MAAUhB,MAAUe,CAAM;GAAE,IAD9DC;EAEtB,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,OAAO;EAAE;EAAS;EAAS;CAAS;AACtC"}
|