@pixum/combobox 5.10.5 → 5.10.7

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.
Files changed (45) hide show
  1. package/lib/ComboBox.d.ts +32 -6
  2. package/lib/ComboBox.js +212 -77
  3. package/lib/ComboBox.js.map +1 -1
  4. package/lib/ComboBox.module.css +1 -0
  5. package/lib/hooks/index.d.ts +2 -0
  6. package/lib/hooks/index.js +2 -0
  7. package/lib/hooks/index.js.map +1 -1
  8. package/lib/hooks/useComboBox.d.ts +60 -0
  9. package/lib/hooks/useComboBox.js +226 -0
  10. package/lib/hooks/useComboBox.js.map +1 -0
  11. package/lib/hooks/useComboBoxFocus.d.ts +35 -0
  12. package/lib/hooks/useComboBoxFocus.js +49 -0
  13. package/lib/hooks/useComboBoxFocus.js.map +1 -0
  14. package/lib/hooks/useIsDesktopScreenWidth.d.ts +5 -0
  15. package/lib/hooks/useIsDesktopScreenWidth.js +34 -24
  16. package/lib/hooks/useIsDesktopScreenWidth.js.map +1 -1
  17. package/lib/index.d.ts +4 -4
  18. package/lib/index.js +2 -3
  19. package/lib/index.js.map +1 -1
  20. package/lib/partials/ComboBoxNoResults.d.ts +14 -0
  21. package/lib/partials/ComboBoxNoResults.js +18 -0
  22. package/lib/partials/ComboBoxNoResults.js.map +1 -0
  23. package/lib/partials/ComboBoxOptions.d.ts +16 -5
  24. package/lib/partials/ComboBoxOptions.js +25 -21
  25. package/lib/partials/ComboBoxOptions.js.map +1 -1
  26. package/lib/partials/ComboBoxOptionsGroup.d.ts +15 -0
  27. package/lib/partials/ComboBoxOptionsGroup.js +31 -0
  28. package/lib/partials/ComboBoxOptionsGroup.js.map +1 -0
  29. package/lib/partials/ComboBoxOptionsItems.d.ts +32 -17
  30. package/lib/partials/ComboBoxOptionsItems.js +46 -14
  31. package/lib/partials/ComboBoxOptionsItems.js.map +1 -1
  32. package/lib/partials/ComboBoxOptionsSlot.d.ts +17 -16
  33. package/lib/partials/ComboBoxOptionsSlot.js +26 -11
  34. package/lib/partials/ComboBoxOptionsSlot.js.map +1 -1
  35. package/lib/partials/index.d.ts +2 -1
  36. package/lib/partials/index.js +2 -1
  37. package/lib/partials/index.js.map +1 -1
  38. package/lib/types.d.ts +183 -45
  39. package/lib/utils/index.d.ts +1 -2
  40. package/lib/utils/index.js +1 -2
  41. package/lib/utils/index.js.map +1 -1
  42. package/lib/utils/options.d.ts +77 -0
  43. package/lib/utils/options.js +145 -0
  44. package/lib/utils/options.js.map +1 -0
  45. package/package.json +8 -8
@@ -0,0 +1,226 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { findEnabledIndex } from '../utils';
4
+ /**
5
+ * Headless controller for the WAI-ARIA combobox pattern.
6
+ *
7
+ * The focus never moves into the list: the active option is communicated
8
+ * exclusively via `aria-activedescendant`, which keeps the search field
9
+ * typeable while the user navigates.
10
+ *
11
+ * Keyboard (APG combobox with listbox popup):
12
+ * - closed: ArrowUp/ArrowDown/Alt+ArrowDown, Enter, Space open the list
13
+ * - open: ArrowUp/ArrowDown navigate (skipping disabled options),
14
+ * Home/End jump to the first/last option, Enter selects,
15
+ * Escape and Alt+ArrowUp close, Tab closes and lets the focus move on
16
+ * - Space selects while the field is not typeable; as soon as a search field
17
+ * is open it types a space character instead, as APG requires
18
+ *
19
+ * On open the starting point is always the selected option, so ArrowDown moves
20
+ * to the option right after it.
21
+ *
22
+ * @param options - Configuration of the combobox
23
+ * @returns ARIA props, the keyboard handler and the active option
24
+ */
25
+ export function useComboBox({ options, isOpen, open, close, selectedValues, onSelect, listboxId, searchable = true, multiple = false, openOnEnter = true, }) {
26
+ var _a;
27
+ const [activeIndex, setActiveIndex] = useState(null);
28
+ /**
29
+ * Read inside effects only. Keeping the selection in a ref avoids re-running
30
+ * the "open" effect when a consumer passes a new array instance per render.
31
+ */
32
+ const selectedValuesRef = useRef(selectedValues);
33
+ selectedValuesRef.current = selectedValues;
34
+ /** Tracks the closed -> open edge so navigation is not reset while open. */
35
+ const wasOpen = useRef(false);
36
+ /**
37
+ * Active option requested while the list was still closed (Home/End). The
38
+ * open effect consumes it instead of falling back to the selected option.
39
+ */
40
+ const pendingActiveIndex = useRef(null);
41
+ /** Typing is only possible while an open search field is rendered. */
42
+ const canType = isOpen && searchable;
43
+ /**
44
+ * Sets the starting point when the list opens: the selected option, or the
45
+ * first enabled one as a fallback.
46
+ */
47
+ useEffect(() => {
48
+ if (!isOpen) {
49
+ wasOpen.current = false;
50
+ setActiveIndex(null);
51
+ return;
52
+ }
53
+ if (wasOpen.current)
54
+ return;
55
+ wasOpen.current = true;
56
+ const pending = pendingActiveIndex.current;
57
+ pendingActiveIndex.current = null;
58
+ if (pending !== null) {
59
+ setActiveIndex(pending);
60
+ return;
61
+ }
62
+ const selectedIndex = options.findIndex(option => !option.disabled && selectedValuesRef.current.includes(option.value));
63
+ setActiveIndex(selectedIndex !== -1 ? selectedIndex : findEnabledIndex(options, -1, 1));
64
+ }, [isOpen, options]);
65
+ /**
66
+ * Keeps the active index valid while the list is filtered down.
67
+ */
68
+ useEffect(() => {
69
+ if (!isOpen)
70
+ return;
71
+ setActiveIndex(previous => {
72
+ const isStillValid = previous !== null &&
73
+ previous < options.length &&
74
+ !options[previous].disabled;
75
+ if (isStillValid)
76
+ return previous;
77
+ return findEnabledIndex(options, -1, 1);
78
+ });
79
+ }, [isOpen, options]);
80
+ /**
81
+ * Moves the active option by `delta` positions, wrapping around and skipping
82
+ * disabled options.
83
+ *
84
+ * @param delta - Number of positions to move; negative moves backwards
85
+ */
86
+ const move = useCallback((delta) => setActiveIndex(previous => findEnabledIndex(options, previous !== null && previous !== void 0 ? previous : (delta > 0 ? -1 : 0), delta)), [options]);
87
+ /**
88
+ * Selects the active option. In single select the list closes afterwards,
89
+ * in multi select it stays open so further options can be toggled.
90
+ */
91
+ const selectActiveOption = useCallback(() => {
92
+ if (activeIndex === null)
93
+ return;
94
+ const option = options[activeIndex];
95
+ if (!option || option.disabled)
96
+ return;
97
+ onSelect(option.value);
98
+ }, [activeIndex, options, onSelect]);
99
+ const onKeyDown = useCallback(event => {
100
+ switch (event.key) {
101
+ case 'ArrowDown':
102
+ case 'ArrowUp': {
103
+ event.preventDefault();
104
+ if (!isOpen) {
105
+ open();
106
+ break;
107
+ }
108
+ // Alt+ArrowUp closes and keeps the current selection.
109
+ if (event.key === 'ArrowUp' && event.altKey) {
110
+ close();
111
+ break;
112
+ }
113
+ // Alt+ArrowDown only opens, it never moves the active option.
114
+ if (event.altKey)
115
+ break;
116
+ move(event.key === 'ArrowDown' ? 1 : -1);
117
+ break;
118
+ }
119
+ case 'Enter': {
120
+ if (!isOpen) {
121
+ if (!openOnEnter)
122
+ break;
123
+ // preventDefault stops an implicit form submit.
124
+ event.preventDefault();
125
+ open();
126
+ break;
127
+ }
128
+ if (activeIndex === null)
129
+ break;
130
+ event.preventDefault();
131
+ selectActiveOption();
132
+ break;
133
+ }
134
+ case ' ': {
135
+ // An open search field must receive a real space character.
136
+ if (canType)
137
+ break;
138
+ event.preventDefault();
139
+ if (isOpen && activeIndex !== null) {
140
+ selectActiveOption();
141
+ break;
142
+ }
143
+ open();
144
+ break;
145
+ }
146
+ case 'Escape': {
147
+ if (!isOpen)
148
+ break;
149
+ event.preventDefault();
150
+ close();
151
+ break;
152
+ }
153
+ case 'Tab': {
154
+ // No preventDefault: the focus moves on, the popup just closes.
155
+ if (isOpen)
156
+ close(false);
157
+ break;
158
+ }
159
+ case 'Home':
160
+ case 'End': {
161
+ if (options.length === 0)
162
+ break;
163
+ event.preventDefault();
164
+ const targetIndex = event.key === 'Home'
165
+ ? findEnabledIndex(options, -1, 1)
166
+ : findEnabledIndex(options, options.length, -1);
167
+ // Closed: open first, the effect picks the index up from the ref.
168
+ if (!isOpen) {
169
+ pendingActiveIndex.current = targetIndex;
170
+ open();
171
+ break;
172
+ }
173
+ setActiveIndex(targetIndex);
174
+ break;
175
+ }
176
+ default:
177
+ break;
178
+ }
179
+ }, [
180
+ isOpen,
181
+ open,
182
+ close,
183
+ move,
184
+ activeIndex,
185
+ selectActiveOption,
186
+ canType,
187
+ openOnEnter,
188
+ options,
189
+ ]);
190
+ const activeOptionId = isOpen && activeIndex !== null ? (_a = options[activeIndex]) === null || _a === void 0 ? void 0 : _a.id : undefined;
191
+ /**
192
+ * Scrolls the active option into view. Purely DOM based, so it works for the
193
+ * desktop popover and the bottom sheet alike.
194
+ */
195
+ useEffect(() => {
196
+ if (!activeOptionId)
197
+ return;
198
+ const frameId = requestAnimationFrame(() => {
199
+ var _a;
200
+ (_a = document
201
+ .getElementById(activeOptionId)) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'nearest' });
202
+ });
203
+ return () => cancelAnimationFrame(frameId);
204
+ }, [activeOptionId]);
205
+ const comboboxAriaProps = useMemo(() => ({
206
+ role: 'combobox',
207
+ 'aria-expanded': isOpen,
208
+ 'aria-haspopup': 'listbox',
209
+ 'aria-controls': isOpen ? listboxId : undefined,
210
+ 'aria-activedescendant': activeOptionId,
211
+ 'aria-autocomplete': searchable ? 'list' : 'none',
212
+ }), [isOpen, listboxId, activeOptionId, searchable]);
213
+ const listboxAriaProps = useMemo(() => ({
214
+ role: 'listbox',
215
+ id: listboxId,
216
+ 'aria-multiselectable': multiple || undefined,
217
+ }), [listboxId, multiple]);
218
+ return {
219
+ comboboxAriaProps,
220
+ listboxAriaProps,
221
+ onKeyDown,
222
+ activeOptionId,
223
+ activeIndex,
224
+ };
225
+ }
226
+ //# sourceMappingURL=useComboBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useComboBox.js","sourceRoot":"","sources":["../../src/hooks/useComboBox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface UseComboBoxFocusResult {
2
+ /**
3
+ * Ref for the trigger field (SelectInput).
4
+ *
5
+ * `| null` is part of the type parameter: since React 19 `useRef<T>(null)`
6
+ * returns `RefObject<T | null>` instead of the old `RefObject<T>`.
7
+ */
8
+ triggerRef: React.RefObject<HTMLInputElement | null>;
9
+ /**
10
+ * Callback ref for the search field. Focuses it the moment it is mounted -
11
+ * in the desktop popover as well as in the bottom sheet.
12
+ */
13
+ searchInputRef: (input: HTMLInputElement | null) => void;
14
+ /** Moves the focus back to the trigger without re-opening the list. */
15
+ returnFocusToTrigger: () => void;
16
+ /**
17
+ * Whether the current focus event on the trigger was caused by
18
+ * `returnFocusToTrigger` and must therefore not open the list.
19
+ */
20
+ isReturningFocus: () => boolean;
21
+ }
22
+ /**
23
+ * Bundles the focus choreography of the ComboBox:
24
+ *
25
+ * - opening moves the focus into the search field as soon as it mounts,
26
+ * - closing moves it back to the trigger,
27
+ * - the focus handler of the trigger can tell a returning focus from a real
28
+ * user focus and therefore does not re-open the list it just closed.
29
+ *
30
+ * The search field is wired up with a callback ref, so both the popover and the
31
+ * bottom sheet share one implementation and no polling is needed.
32
+ *
33
+ * @returns Refs and helpers for the focus handling
34
+ */
35
+ export declare function useComboBoxFocus(): UseComboBoxFocusResult;
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useRef } from 'react';
3
+ /**
4
+ * Bundles the focus choreography of the ComboBox:
5
+ *
6
+ * - opening moves the focus into the search field as soon as it mounts,
7
+ * - closing moves it back to the trigger,
8
+ * - the focus handler of the trigger can tell a returning focus from a real
9
+ * user focus and therefore does not re-open the list it just closed.
10
+ *
11
+ * The search field is wired up with a callback ref, so both the popover and the
12
+ * bottom sheet share one implementation and no polling is needed.
13
+ *
14
+ * @returns Refs and helpers for the focus handling
15
+ */
16
+ export function useComboBoxFocus() {
17
+ const triggerRef = useRef(null);
18
+ const returningFocus = useRef(false);
19
+ const frameRef = useRef(null);
20
+ useEffect(() => () => {
21
+ if (frameRef.current !== null)
22
+ cancelAnimationFrame(frameRef.current);
23
+ }, []);
24
+ const searchInputRef = useCallback((input) => {
25
+ if (!input)
26
+ return;
27
+ input.focus();
28
+ // One frame later, so Safari does not drop the selection again when
29
+ // `readOnly` changes within the same render.
30
+ requestAnimationFrame(() => {
31
+ var _a;
32
+ (_a = input.setSelectionRange) === null || _a === void 0 ? void 0 : _a.call(input, 0, input.value.length);
33
+ });
34
+ }, []);
35
+ const returnFocusToTrigger = useCallback(() => {
36
+ returningFocus.current = true;
37
+ // Wait for the commit that unmounts the popover / bottom sheet, otherwise
38
+ // the focus would end up on `<body>`.
39
+ frameRef.current = requestAnimationFrame(() => {
40
+ var _a;
41
+ (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
42
+ // `focus()` dispatches synchronously, so the flag has served its purpose.
43
+ returningFocus.current = false;
44
+ });
45
+ }, []);
46
+ const isReturningFocus = useCallback(() => returningFocus.current, []);
47
+ return { triggerRef, searchInputRef, returnFocusToTrigger, isReturningFocus };
48
+ }
49
+ //# sourceMappingURL=useComboBoxFocus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useComboBoxFocus.js","sourceRoot":"","sources":["../../src/hooks/useComboBoxFocus.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAwBtD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,UAAU,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAA;IACjD,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAA;IAE5C,SAAS,CACP,GAAG,EAAE,CAAC,GAAG,EAAE;QACT,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI;YAAE,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;IACvE,CAAC,EACD,EAAE,CACH,CAAA;IAED,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,KAA8B,EAAE,EAAE;QACpE,IAAI,CAAC,KAAK;YAAE,OAAM;QAElB,KAAK,CAAC,KAAK,EAAE,CAAA;QAEb,oEAAoE;QACpE,6CAA6C;QAC7C,qBAAqB,CAAC,GAAG,EAAE;;YACzB,MAAA,KAAK,CAAC,iBAAiB,sDAAG,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAClD,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5C,cAAc,CAAC,OAAO,GAAG,IAAI,CAAA;QAE7B,0EAA0E;QAC1E,sCAAsC;QACtC,QAAQ,CAAC,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE;;YAC5C,MAAA,UAAU,CAAC,OAAO,0CAAE,KAAK,EAAE,CAAA;YAC3B,0EAA0E;YAC1E,cAAc,CAAC,OAAO,GAAG,KAAK,CAAA;QAChC,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,gBAAgB,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAEtE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAA;AAC/E,CAAC"}
@@ -1 +1,6 @@
1
+ /**
2
+ * Custom hook that detects whether the viewport width is desktop size (>= 960px).
3
+ *
4
+ * @returns True if the viewport width is 960px or greater, false otherwise
5
+ */
1
6
  export declare function useIsDesktopScreenWidth(): boolean;
@@ -1,36 +1,46 @@
1
- import { useState, useEffect } from 'react';
1
+ 'use client';
2
+ import { useSyncExternalStore } from 'react';
3
+ const DESKTOP_MEDIA_QUERY = '(min-width: 960px)';
2
4
  /**
3
- * Custom hook that detects whether the viewport width is desktop size (>= 960px).
4
- * Listens to window resize events to update the state dynamically.
5
+ * Subscribes to the MediaQueryList. Using `change` instead of `resize` means
6
+ * there is only one re-render when the breakpoint is actually crossed.
5
7
  *
6
- * @returns True if the viewport width is 960px or greater, false otherwise
8
+ * @param onStoreChange - Callback invoked when the breakpoint match changes
9
+ * @returns An unsubscribe function that removes the listener
7
10
  */
8
- function isBrowser() {
9
- return typeof window !== 'undefined';
11
+ function subscribe(onStoreChange) {
12
+ if (typeof window === 'undefined' || !window.matchMedia) {
13
+ return () => { };
14
+ }
15
+ const mediaQueryList = window.matchMedia(DESKTOP_MEDIA_QUERY);
16
+ mediaQueryList.addEventListener('change', onStoreChange);
17
+ return () => mediaQueryList.removeEventListener('change', onStoreChange);
10
18
  }
11
19
  /**
12
- * Checks if the current viewport width is desktop size (>= 960px).
20
+ * Client snapshot: the current match state of the desktop breakpoint.
13
21
  *
14
- * @returns True if the viewport is desktop size, false otherwise or in non-browser environments
22
+ * @returns True if the desktop breakpoint currently matches, false otherwise
15
23
  */
16
- function isInitialScreenDesktop() {
17
- if (isBrowser()) {
18
- return window.innerWidth >= 960;
19
- }
24
+ function getSnapshot() {
25
+ if (typeof window === 'undefined' || !window.matchMedia)
26
+ return false;
27
+ return window.matchMedia(DESKTOP_MEDIA_QUERY).matches;
28
+ }
29
+ /**
30
+ * Server snapshot: deliberately `false` (mobile first), so that the server
31
+ * render and the first client render match deterministically.
32
+ *
33
+ * @returns Always false
34
+ */
35
+ function getServerSnapshot() {
20
36
  return false;
21
37
  }
38
+ /**
39
+ * Custom hook that detects whether the viewport width is desktop size (>= 960px).
40
+ *
41
+ * @returns True if the viewport width is 960px or greater, false otherwise
42
+ */
22
43
  export function useIsDesktopScreenWidth() {
23
- const [isDesktop, setIsDesktop] = useState(isInitialScreenDesktop);
24
- useEffect(() => {
25
- const checkScreenSize = () => {
26
- setIsDesktop(window.innerWidth >= 960);
27
- };
28
- checkScreenSize();
29
- window.addEventListener('resize', checkScreenSize);
30
- return () => {
31
- window.removeEventListener('resize', checkScreenSize);
32
- };
33
- }, []);
34
- return isDesktop;
44
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
35
45
  }
36
46
  //# sourceMappingURL=useIsDesktopScreenWidth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useIsDesktopScreenWidth.js","sourceRoot":"","sources":["../../src/hooks/useIsDesktopScreenWidth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAE3C;;;;;GAKG;AAEH,SAAS,SAAS;IAChB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAA;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB;IAC7B,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,OAAO,MAAM,CAAC,UAAU,IAAI,GAAG,CAAA;IACjC,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,sBAAsB,CAAC,CAAA;IAE3E,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,eAAe,GAAG,GAAG,EAAE;YAC3B,YAAY,CAAC,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC,CAAA;QACxC,CAAC,CAAA;QAED,eAAe,EAAE,CAAA;QACjB,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAA;QAElD,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAA;QACvD,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,SAAS,CAAA;AAClB,CAAC"}
1
+ {"version":3,"file":"useIsDesktopScreenWidth.js","sourceRoot":"","sources":["../../src/hooks/useIsDesktopScreenWidth.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAE5C,MAAM,mBAAmB,GAAG,oBAAoB,CAAA;AAEhD;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,aAAyB;IAC1C,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACxD,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAA;IAC7D,cAAc,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAA;IAExD,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,mBAAmB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAA;AAC1E,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW;IAClB,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,OAAO,KAAK,CAAA;IAErE,OAAO,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAA;AACvD,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB;IACxB,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;AACxE,CAAC"}
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ComboBox } from './ComboBox';
2
- export default ComboBox;
3
- export * from './types';
4
- export { ComboBoxOptionsSlot } from './partials';
1
+ export { ComboBox, default } from './ComboBox';
2
+ export { ComboBoxOptionsSlot, type ComboBoxOptionsSlotProps } from './partials';
3
+ export { useComboBox, useIsDesktopScreenWidth } from './hooks';
4
+ export type { ComboBoxAriaProps, ComboBoxOption, ComboBoxOptionGroup, ComboBoxOptionItemProps, ComboBoxOptionsInput, ComboBoxOptionsProps, ComboBoxOptionsPanelProps, ComboBoxProps, ListboxAriaProps, } from './types';
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { ComboBox } from './ComboBox';
2
- export default ComboBox;
3
- export * from './types';
1
+ export { ComboBox, default } from './ComboBox';
4
2
  export { ComboBoxOptionsSlot } from './partials';
3
+ export { useComboBox, useIsDesktopScreenWidth } from './hooks';
5
4
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,eAAe,QAAQ,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAiC,MAAM,YAAY,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA"}
@@ -0,0 +1,14 @@
1
+ export interface ComboBoxNoResultsProps {
2
+ /** Message shown when no option matches the search. */
3
+ text?: string;
4
+ }
5
+ /**
6
+ * Empty state of the options list.
7
+ *
8
+ * `role="status"` makes screen readers announce the message when the list is
9
+ * filtered down to nothing.
10
+ *
11
+ * @param props - The props for the ComboBoxNoResults component
12
+ * @returns The no-results message
13
+ */
14
+ export declare function ComboBoxNoResults({ text }: ComboBoxNoResultsProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Text, { TEXT_VARIANT } from '@pixum/text';
3
+ import styles from '../ComboBox.module.css';
4
+ /**
5
+ * Empty state of the options list.
6
+ *
7
+ * `role="status"` makes screen readers announce the message when the list is
8
+ * filtered down to nothing.
9
+ *
10
+ * @param props - The props for the ComboBoxNoResults component
11
+ * @returns The no-results message
12
+ */
13
+ export function ComboBoxNoResults({ text }) {
14
+ if (!text)
15
+ return null;
16
+ return (_jsx(Text, { variant: TEXT_VARIANT.SUBHEADLINE, className: styles.noResults, children: text }));
17
+ }
18
+ //# sourceMappingURL=ComboBoxNoResults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComboBoxNoResults.js","sourceRoot":"","sources":["../../src/partials/ComboBoxNoResults.tsx"],"names":[],"mappings":";AAAA,OAAO,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,MAAM,MAAM,wBAAwB,CAAA;AAO3C;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,IAAI,EAA0B;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,OAAO,CACL,KAAC,IAAI,IAAC,OAAO,EAAE,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,YACjE,IAAI,GACA,CACR,CAAA;AACH,CAAC"}
@@ -1,10 +1,21 @@
1
- import type { ComboBoxOptionsProps } from '../types';
1
+ import type { ComboBoxOptionsPanelProps } from '../types';
2
2
  /**
3
- * ComboBoxOptions component that renders a list of options in a menu.
4
- * Handles both grouped and ungrouped options and automatically scrolls to the selected option.
3
+ * Renders the options panel of the ComboBox.
4
+ *
5
+ * The panel is split in two: the `Menu` provides the visible box, the element
6
+ * inside it carries the `listbox` role and is the only part that scrolls. That
7
+ * keeps `optionDetails` a real footer - it stays visible without
8
+ * `position: sticky` and can no longer cover the active option - and it keeps
9
+ * the listbox free of anything that is not an option or a group, as ARIA
10
+ * requires.
11
+ *
12
+ * Flat and grouped options share one render path: the options arrive already
13
+ * normalised, `groupOptions` only splits them into their visual groups again.
14
+ * The listbox is not focusable - the focus stays in the input field, the active
15
+ * option is announced through `aria-activedescendant`.
5
16
  *
6
17
  * @param props - The props for the ComboBoxOptions component
7
- * @returns A menu containing the combobox options
18
+ * @returns The options panel
8
19
  */
9
- export declare function ComboBoxOptions({ options, onClick, selectedOption, leadingType, optionDetails, }: ComboBoxOptionsProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function ComboBoxOptions({ options, listboxAriaProps, selectedValues, activeOptionId, leadingType, optionDetails, onOptionClick, }: ComboBoxOptionsPanelProps): import("react/jsx-runtime").JSX.Element;
10
21
  export default ComboBoxOptions;
@@ -1,29 +1,33 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect } from 'react';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
2
  import Menu from '@pixum/menu';
4
- import { useIsDesktopScreenWidth } from '../hooks';
5
- import { scrollOptionToViewport } from '../utils';
6
- import { ComboxBoxOptionsWithGrouping } from './ComboBoxOptionsWithGrouping';
7
- import { ComboBoxOptionsItems } from './ComboBoxOptionsItems';
3
+ import Text, { TEXT_VARIANT } from '@pixum/text';
4
+ import clsx from 'clsx';
5
+ import { groupOptions } from '../utils';
6
+ import { ComboBoxOptionsGroup } from './ComboBoxOptionsGroup';
7
+ import styles from '../ComboBox.module.css';
8
8
  /**
9
- * ComboBoxOptions component that renders a list of options in a menu.
10
- * Handles both grouped and ungrouped options and automatically scrolls to the selected option.
9
+ * Renders the options panel of the ComboBox.
10
+ *
11
+ * The panel is split in two: the `Menu` provides the visible box, the element
12
+ * inside it carries the `listbox` role and is the only part that scrolls. That
13
+ * keeps `optionDetails` a real footer - it stays visible without
14
+ * `position: sticky` and can no longer cover the active option - and it keeps
15
+ * the listbox free of anything that is not an option or a group, as ARIA
16
+ * requires.
17
+ *
18
+ * Flat and grouped options share one render path: the options arrive already
19
+ * normalised, `groupOptions` only splits them into their visual groups again.
20
+ * The listbox is not focusable - the focus stays in the input field, the active
21
+ * option is announced through `aria-activedescendant`.
11
22
  *
12
23
  * @param props - The props for the ComboBoxOptions component
13
- * @returns A menu containing the combobox options
24
+ * @returns The options panel
14
25
  */
15
- export function ComboBoxOptions({ options, onClick, selectedOption, leadingType, optionDetails, }) {
16
- const isDesktop = useIsDesktopScreenWidth();
17
- const optionsWithoutGroup = Array.isArray(options);
18
- const optionsWithGroup = Object.entries(options);
19
- useEffect(() => {
20
- // Wait for next browser frame to ensure DOM is fully rendered
21
- const frameId = requestAnimationFrame(() => {
22
- scrollOptionToViewport(isDesktop);
23
- });
24
- return () => cancelAnimationFrame(frameId);
25
- }, [isDesktop, selectedOption]);
26
- return (_jsxs(Menu, { className: "combobox__options scrollable", "data-tappable": "true", children: [optionsWithoutGroup ? (_jsx(ComboBoxOptionsItems, { items: options, selectedOption: selectedOption, onClick: onClick, leadingType: leadingType })) : (_jsx(_Fragment, { children: optionsWithGroup.map(([group, items]) => (_jsx(ComboxBoxOptionsWithGrouping, { group: group, items: items, selectedOption: selectedOption, onClick: onClick, leadingType: leadingType }, `combobox__options-group-${group}`))) })), optionDetails && (_jsx("div", { className: "combobox__option-details text--caption", children: optionDetails }))] }));
26
+ export function ComboBoxOptions({ options, listboxAriaProps, selectedValues, activeOptionId, leadingType, optionDetails, onOptionClick, }) {
27
+ return (_jsxs(Menu, { className: styles.panel, "data-tappable": "true", children: [_jsx("div", Object.assign({ className: clsx(styles.options, 'scrollable'), tabIndex: -1 }, listboxAriaProps, { children: groupOptions(options).map(group => {
28
+ var _a;
29
+ return (_jsx(ComboBoxOptionsGroup, { group: group.group, options: group.options, selectedValues: selectedValues, activeOptionId: activeOptionId, leadingType: leadingType, onOptionClick: onOptionClick }, `combobox__options-group-${(_a = group.group) !== null && _a !== void 0 ? _a : 'default'}`));
30
+ }) })), optionDetails && (_jsx(Text, { variant: TEXT_VARIANT.CAPTION, className: styles.optionDetails, children: optionDetails }))] }));
27
31
  }
28
32
  export default ComboBoxOptions;
29
33
  //# sourceMappingURL=ComboBoxOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ComboBoxOptions.js","sourceRoot":"","sources":["../../src/partials/ComboBoxOptions.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,IAAI,MAAM,aAAa,CAAA;AAC9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAA;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAEjD,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,OAAO,EACP,cAAc,EACd,WAAW,EACX,aAAa,GACQ;IACrB,MAAM,SAAS,GAAG,uBAAuB,EAAE,CAAA;IAC3C,MAAM,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAClD,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEhD,SAAS,CAAC,GAAG,EAAE;QACb,8DAA8D;QAC9D,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,EAAE;YACzC,sBAAsB,CAAC,SAAS,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,EAAE,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAA;IAC5C,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAA;IAE/B,OAAO,CACL,MAAC,IAAI,IAAC,SAAS,EAAC,8BAA8B,mBAAe,MAAM,aAChE,mBAAmB,CAAC,CAAC,CAAC,CACrB,KAAC,oBAAoB,IACnB,KAAK,EAAE,OAAO,EACd,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,GACxB,CACH,CAAC,CAAC,CAAC,CACF,4BACG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CACxC,KAAC,4BAA4B,IAE3B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,IALnB,2BAA2B,KAAK,EAAE,CAMvC,CACH,CAAC,GACD,CACJ,EACA,aAAa,IAAI,CAChB,cAAK,SAAS,EAAC,wCAAwC,YACpD,aAAa,GACV,CACP,IACI,CACR,CAAA;AACH,CAAC;AAED,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"ComboBoxOptions.js","sourceRoot":"","sources":["../../src/partials/ComboBoxOptions.tsx"],"names":[],"mappings":";AAAA,OAAO,IAAI,MAAM,aAAa,CAAA;AAC9B,OAAO,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,MAAM,MAAM,wBAAwB,CAAA;AAG3C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,aAAa,EACb,aAAa,GACa;IAC1B,OAAO,CACL,MAAC,IAAI,IAAC,SAAS,EAAE,MAAM,CAAC,KAAK,mBAAgB,MAAM,aACjD,4BACE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,EAC7C,QAAQ,EAAE,CAAC,CAAC,IACR,gBAAgB,cAEnB,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;;oBAAC,OAAA,CAClC,KAAC,oBAAoB,IAEnB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,cAAc,EAAE,cAAc,EAC9B,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,IANvB,2BAA2B,MAAA,KAAK,CAAC,KAAK,mCAAI,SAAS,EAAE,CAO1D,CACH,CAAA;iBAAA,CAAC,IACE,EAEL,aAAa,IAAI,CAChB,KAAC,IAAI,IAAC,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,aAAa,YACjE,aAAa,GACT,CACR,IACI,CACR,CAAA;AACH,CAAC;AAED,eAAe,eAAe,CAAA"}
@@ -0,0 +1,15 @@
1
+ import type { ComboBoxOptionsItemsProps } from './ComboBoxOptionsItems';
2
+ export interface ComboBoxOptionsGroupProps extends ComboBoxOptionsItemsProps {
3
+ /** Group headline. Without it the options are rendered without a header. */
4
+ group?: string;
5
+ }
6
+ /**
7
+ * Renders one group of options with an optional sticky headline.
8
+ *
9
+ * Flat lists use the same component with `group` left undefined, so there is
10
+ * only one place that renders options.
11
+ *
12
+ * @param props - The props for the ComboBoxOptionsGroup component
13
+ * @returns A group container with an optional header and its options
14
+ */
15
+ export declare function ComboBoxOptionsGroup({ group, ...itemProps }: ComboBoxOptionsGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import Text, { TEXT_VARIANT } from '@pixum/text';
14
+ import { ComboBoxOptionsItems } from './ComboBoxOptionsItems';
15
+ import styles from '../ComboBox.module.css';
16
+ /**
17
+ * Renders one group of options with an optional sticky headline.
18
+ *
19
+ * Flat lists use the same component with `group` left undefined, so there is
20
+ * only one place that renders options.
21
+ *
22
+ * @param props - The props for the ComboBoxOptionsGroup component
23
+ * @returns A group container with an optional header and its options
24
+ */
25
+ export function ComboBoxOptionsGroup(_a) {
26
+ var { group } = _a, itemProps = __rest(_a, ["group"]);
27
+ if (!group)
28
+ return _jsx(ComboBoxOptionsItems, Object.assign({}, itemProps));
29
+ return (_jsxs("div", { role: "group", "aria-label": group, children: [_jsx(Text, { variant: TEXT_VARIANT.HEADLINE, className: styles.stickyHeader, children: group }), _jsx(ComboBoxOptionsItems, Object.assign({}, itemProps))] }));
30
+ }
31
+ //# sourceMappingURL=ComboBoxOptionsGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComboBoxOptionsGroup.js","sourceRoot":"","sources":["../../src/partials/ComboBoxOptionsGroup.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,MAAM,MAAM,wBAAwB,CAAA;AAQ3C;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAGT;QAHS,EACnC,KAAK,OAEqB,EADvB,SAAS,cAFuB,SAGpC,CADa;IAEZ,IAAI,CAAC,KAAK;QAAE,OAAO,KAAC,oBAAoB,oBAAK,SAAS,EAAI,CAAA;IAE1D,OAAO,CACL,eAAK,IAAI,EAAC,OAAO,gBAAa,KAAK,aACjC,KAAC,IAAI,IAAC,OAAO,EAAE,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,YAAY,YACjE,KAAK,GACD,EACP,KAAC,oBAAoB,oBAAK,SAAS,EAAI,IACnC,CACP,CAAA;AACH,CAAC"}