@pixum/combobox 5.10.5 → 5.10.6
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/lib/ComboBox.d.ts +32 -6
- package/lib/ComboBox.js +212 -77
- package/lib/ComboBox.js.map +1 -1
- package/lib/ComboBox.module.css +1 -0
- package/lib/hooks/index.d.ts +2 -0
- package/lib/hooks/index.js +2 -0
- package/lib/hooks/index.js.map +1 -1
- package/lib/hooks/useComboBox.d.ts +60 -0
- package/lib/hooks/useComboBox.js +226 -0
- package/lib/hooks/useComboBox.js.map +1 -0
- package/lib/hooks/useComboBoxFocus.d.ts +35 -0
- package/lib/hooks/useComboBoxFocus.js +49 -0
- package/lib/hooks/useComboBoxFocus.js.map +1 -0
- package/lib/hooks/useIsDesktopScreenWidth.d.ts +5 -0
- package/lib/hooks/useIsDesktopScreenWidth.js +34 -24
- package/lib/hooks/useIsDesktopScreenWidth.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +2 -3
- package/lib/index.js.map +1 -1
- package/lib/partials/ComboBoxNoResults.d.ts +14 -0
- package/lib/partials/ComboBoxNoResults.js +18 -0
- package/lib/partials/ComboBoxNoResults.js.map +1 -0
- package/lib/partials/ComboBoxOptions.d.ts +16 -5
- package/lib/partials/ComboBoxOptions.js +25 -21
- package/lib/partials/ComboBoxOptions.js.map +1 -1
- package/lib/partials/ComboBoxOptionsGroup.d.ts +15 -0
- package/lib/partials/ComboBoxOptionsGroup.js +31 -0
- package/lib/partials/ComboBoxOptionsGroup.js.map +1 -0
- package/lib/partials/ComboBoxOptionsItems.d.ts +32 -17
- package/lib/partials/ComboBoxOptionsItems.js +46 -14
- package/lib/partials/ComboBoxOptionsItems.js.map +1 -1
- package/lib/partials/ComboBoxOptionsSlot.d.ts +17 -16
- package/lib/partials/ComboBoxOptionsSlot.js +26 -11
- package/lib/partials/ComboBoxOptionsSlot.js.map +1 -1
- package/lib/partials/index.d.ts +2 -1
- package/lib/partials/index.js +2 -1
- package/lib/partials/index.js.map +1 -1
- package/lib/types.d.ts +183 -45
- package/lib/utils/index.d.ts +1 -2
- package/lib/utils/index.js +1 -2
- package/lib/utils/index.js.map +1 -1
- package/lib/utils/options.d.ts +77 -0
- package/lib/utils/options.js +145 -0
- package/lib/utils/options.js.map +1 -0
- package/package.json +8 -8
package/lib/ComboBox.d.ts
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import type { ComboBoxProps } from './types';
|
|
2
|
-
import './styles.css';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Searchable select field. On desktop the options appear in a popover, on
|
|
4
|
+
* mobile in a bottom sheet - the behaviour is identical in both cases.
|
|
5
|
+
*
|
|
6
|
+
* This file is the only client boundary of the package: all partials, hooks and
|
|
7
|
+
* utils are plain modules without a `'use client'` directive, so they can be
|
|
8
|
+
* imported from server components as well and only end up in the client bundle
|
|
9
|
+
* through this component.
|
|
10
|
+
*
|
|
11
|
+
* Focus choreography:
|
|
12
|
+
* - focusing the trigger opens the options,
|
|
13
|
+
* - with `withSearch` the focus moves into the search field as soon as it is
|
|
14
|
+
* mounted, and its content is selected so the first keystroke replaces it,
|
|
15
|
+
* - selecting an option, Escape, Alt+ArrowUp and closing the sheet move the
|
|
16
|
+
* focus back to the trigger without re-opening the list,
|
|
17
|
+
* - Tab closes the list and lets the focus move on.
|
|
18
|
+
*
|
|
19
|
+
* Keyboard: ArrowUp/ArrowDown navigate, Home/End jump, Enter selects. Space
|
|
20
|
+
* selects as well - unless an open search field needs it as a real space
|
|
21
|
+
* character, which is what the ARIA authoring practices ask for.
|
|
22
|
+
*
|
|
23
|
+
* Selection: pass a string for single select, or `multiple` plus an array of
|
|
24
|
+
* strings for a checkbox list. `onOptionItemClick` reports the toggled value;
|
|
25
|
+
* the selection itself stays fully controlled by the consumer. The value of an
|
|
26
|
+
* option is its `text`, so `value` can be compared against the visible label as
|
|
27
|
+
* before.
|
|
28
|
+
*
|
|
29
|
+
* Legacy props: `id`, `selectedOption` and a non-element `leadingIcon` are
|
|
30
|
+
* still accepted so existing consumers keep working. They are marked
|
|
31
|
+
* `@deprecated` in the types.
|
|
7
32
|
*
|
|
8
33
|
* @param props - The props for the ComboBox component
|
|
9
|
-
* @returns A responsive combobox
|
|
34
|
+
* @returns A responsive, accessible combobox
|
|
10
35
|
*/
|
|
11
|
-
export declare function ComboBox({ className, name, headline, label,
|
|
36
|
+
export declare function ComboBox({ className, name, id, helperText, headline, label, options, isOpen, setIsOpen, value, selectedOption, multiple, placeholder, searchPattern, noResultsText, inputMode, leadingType, leadingIcon, optionDetails, optionsSlot, withSearch, openOnEnter, disabled, loading, theme, renderValue, onClick, onChange, onOptionItemClick, onOutsideClick, onSheetClose, }: ComboBoxProps): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export default ComboBox;
|
package/lib/ComboBox.js
CHANGED
|
@@ -1,110 +1,245 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useRef, useState, } from 'react';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { isValidElement, useCallback, useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
4
4
|
import BottomSheet from '@pixum/bottom-sheet';
|
|
5
|
-
import Text, {
|
|
6
|
-
import {
|
|
7
|
-
import { ComboBoxOptions } from './partials';
|
|
8
|
-
import { SelectInput, SearchInput } from '@pixum/web-input';
|
|
9
|
-
import { useIsDesktopScreenWidth } from './hooks';
|
|
10
|
-
import clsx from 'clsx';
|
|
5
|
+
import Text, { TEXT_VARIANT } from '@pixum/text';
|
|
6
|
+
import { SearchInput, SelectInput } from '@pixum/web-input';
|
|
11
7
|
import ThemeProvider, { DEFAULT_THEME } from '@pixum/theme-provider';
|
|
12
|
-
import '
|
|
8
|
+
import clsx from 'clsx';
|
|
9
|
+
import { ComboBoxNoResults, ComboBoxOptions } from './partials';
|
|
10
|
+
import { useComboBox, useComboBoxFocus, useIsDesktopScreenWidth } from './hooks';
|
|
11
|
+
import { getOptionLabel, normaliseOptions, toValueArray } from './utils';
|
|
12
|
+
import styles from './ComboBox.module.css';
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Searchable select field. On desktop the options appear in a popover, on
|
|
15
|
+
* mobile in a bottom sheet - the behaviour is identical in both cases.
|
|
16
|
+
*
|
|
17
|
+
* This file is the only client boundary of the package: all partials, hooks and
|
|
18
|
+
* utils are plain modules without a `'use client'` directive, so they can be
|
|
19
|
+
* imported from server components as well and only end up in the client bundle
|
|
20
|
+
* through this component.
|
|
21
|
+
*
|
|
22
|
+
* Focus choreography:
|
|
23
|
+
* - focusing the trigger opens the options,
|
|
24
|
+
* - with `withSearch` the focus moves into the search field as soon as it is
|
|
25
|
+
* mounted, and its content is selected so the first keystroke replaces it,
|
|
26
|
+
* - selecting an option, Escape, Alt+ArrowUp and closing the sheet move the
|
|
27
|
+
* focus back to the trigger without re-opening the list,
|
|
28
|
+
* - Tab closes the list and lets the focus move on.
|
|
29
|
+
*
|
|
30
|
+
* Keyboard: ArrowUp/ArrowDown navigate, Home/End jump, Enter selects. Space
|
|
31
|
+
* selects as well - unless an open search field needs it as a real space
|
|
32
|
+
* character, which is what the ARIA authoring practices ask for.
|
|
33
|
+
*
|
|
34
|
+
* Selection: pass a string for single select, or `multiple` plus an array of
|
|
35
|
+
* strings for a checkbox list. `onOptionItemClick` reports the toggled value;
|
|
36
|
+
* the selection itself stays fully controlled by the consumer. The value of an
|
|
37
|
+
* option is its `text`, so `value` can be compared against the visible label as
|
|
38
|
+
* before.
|
|
39
|
+
*
|
|
40
|
+
* Legacy props: `id`, `selectedOption` and a non-element `leadingIcon` are
|
|
41
|
+
* still accepted so existing consumers keep working. They are marked
|
|
42
|
+
* `@deprecated` in the types.
|
|
17
43
|
*
|
|
18
44
|
* @param props - The props for the ComboBox component
|
|
19
|
-
* @returns A responsive combobox
|
|
45
|
+
* @returns A responsive, accessible combobox
|
|
20
46
|
*/
|
|
21
|
-
export function ComboBox({ className = '', name, headline, label,
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
25
|
-
const [inputPlaceholder, setPlaceholder] = useState(placeholder);
|
|
47
|
+
export function ComboBox({ className = '', name, id, helperText, headline, label, options, isOpen, setIsOpen, value, selectedOption, multiple = false, placeholder, searchPattern = '', noResultsText, inputMode, leadingType, leadingIcon, optionDetails, optionsSlot, withSearch = true, openOnEnter = true, disabled = false, loading = false, theme = DEFAULT_THEME, renderValue, onClick, onChange, onOptionItemClick, onOutsideClick, onSheetClose, }) {
|
|
48
|
+
const listboxId = useId();
|
|
49
|
+
const rootRef = useRef(null);
|
|
26
50
|
const isDesktop = useIsDesktopScreenWidth();
|
|
27
|
-
const
|
|
28
|
-
const showSelectField = !withSearch || (isDesktop && !isOpen) || !isDesktop;
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
!isOpen && (onSheetClose === null || onSheetClose === void 0 ? void 0 : onSheetClose());
|
|
31
|
-
}, [isOpen]);
|
|
51
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
32
52
|
/**
|
|
33
|
-
*
|
|
53
|
+
* Until the first keystroke the trigger text stays visible and the search
|
|
54
|
+
* query stays empty, so consumers do not filter down to the option that is
|
|
55
|
+
* already selected.
|
|
56
|
+
*/
|
|
57
|
+
const [hasTyped, setHasTyped] = useState(false);
|
|
58
|
+
const { triggerRef, searchInputRef, returnFocusToTrigger, isReturningFocus } = useComboBoxFocus();
|
|
59
|
+
const normalisedOptions = useMemo(() => normaliseOptions(options, listboxId), [options, listboxId]);
|
|
60
|
+
/**
|
|
61
|
+
* In the previous version `value` drove both the trigger text and the
|
|
62
|
+
* selection, while
|
|
63
|
+
* `selectedOption` was accepted but never read by this component - it was
|
|
64
|
+
* only passed down internally, filled with `value`.
|
|
34
65
|
*
|
|
35
|
-
*
|
|
66
|
+
* `value` therefore keeps full priority. `selectedOption` is honoured as a
|
|
67
|
+
* fallback so consumers that always set it now get a working selection, but
|
|
68
|
+
* only when it is actually a string: several consumers type it as their own
|
|
69
|
+
* option object, and previously that object was silently ignored rather than
|
|
70
|
+
* stringified.
|
|
36
71
|
*/
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
72
|
+
const selectedValues = useMemo(() => {
|
|
73
|
+
if (value !== undefined)
|
|
74
|
+
return toValueArray(value);
|
|
75
|
+
return typeof selectedOption === 'string'
|
|
76
|
+
? toValueArray(selectedOption)
|
|
77
|
+
: [];
|
|
78
|
+
}, [value, selectedOption]);
|
|
42
79
|
/**
|
|
43
|
-
*
|
|
80
|
+
* Labels of the selected options; falls back to the raw values, both for
|
|
81
|
+
* options that render a custom node instead of a plain `text` and for values
|
|
82
|
+
* that have no matching option at all.
|
|
44
83
|
*/
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
const displayValue = useMemo(() => {
|
|
85
|
+
const labels = normalisedOptions
|
|
86
|
+
.filter(option => selectedValues.includes(option.value))
|
|
87
|
+
.map(getOptionLabel);
|
|
88
|
+
const selectedLabels = labels.length > 0 ? labels : selectedValues;
|
|
89
|
+
return renderValue ? renderValue(selectedLabels) : selectedLabels.join(', ');
|
|
90
|
+
}, [normalisedOptions, selectedValues, renderValue]);
|
|
91
|
+
/**
|
|
92
|
+
* `leadingIcon` is typed as `ReactNode` for backwards compatibility, while
|
|
93
|
+
* `SelectInput` wants a single element. Strings, numbers and fragments are
|
|
94
|
+
* wrapped instead of rejected; `false`/`null`/`undefined` stay empty.
|
|
95
|
+
*/
|
|
96
|
+
const triggerLeadingIcon = useMemo(() => {
|
|
97
|
+
if (leadingIcon === null || leadingIcon === undefined)
|
|
98
|
+
return null;
|
|
99
|
+
if (typeof leadingIcon === 'boolean')
|
|
100
|
+
return null;
|
|
101
|
+
if (isValidElement(leadingIcon))
|
|
102
|
+
return leadingIcon;
|
|
103
|
+
return _jsx(_Fragment, { children: leadingIcon });
|
|
104
|
+
}, [leadingIcon]);
|
|
105
|
+
const open = useCallback(() => {
|
|
106
|
+
if (disabled || loading)
|
|
107
|
+
return;
|
|
108
|
+
setIsOpen(true);
|
|
109
|
+
}, [disabled, loading, setIsOpen]);
|
|
48
110
|
/**
|
|
49
|
-
*
|
|
111
|
+
* Closes the list, resets the search and - unless the focus was moved away
|
|
112
|
+
* deliberately (Tab, outside click) - returns the focus to the trigger.
|
|
113
|
+
*
|
|
114
|
+
* @param restoreFocus - Whether the focus goes back to the trigger
|
|
50
115
|
*/
|
|
51
|
-
const
|
|
116
|
+
const close = useCallback((restoreFocus = true) => {
|
|
52
117
|
setSearchQuery('');
|
|
118
|
+
setHasTyped(false);
|
|
119
|
+
onChange === null || onChange === void 0 ? void 0 : onChange('');
|
|
53
120
|
setIsOpen(false);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
121
|
+
if (restoreFocus)
|
|
122
|
+
returnFocusToTrigger();
|
|
123
|
+
}, [onChange, setIsOpen, returnFocusToTrigger]);
|
|
124
|
+
/**
|
|
125
|
+
* Applies a selection. Single select closes the list, multi select keeps it
|
|
126
|
+
* open so further checkboxes can be toggled.
|
|
127
|
+
*
|
|
128
|
+
* @param optionValue - Value of the selected option
|
|
129
|
+
*/
|
|
130
|
+
const handleSelect = useCallback((optionValue) => {
|
|
131
|
+
onOptionItemClick === null || onOptionItemClick === void 0 ? void 0 : onOptionItemClick(optionValue);
|
|
132
|
+
if (!multiple)
|
|
133
|
+
close();
|
|
134
|
+
}, [multiple, onOptionItemClick, close]);
|
|
135
|
+
const { comboboxAriaProps, listboxAriaProps, onKeyDown, activeOptionId } = useComboBox({
|
|
136
|
+
options: normalisedOptions,
|
|
137
|
+
isOpen,
|
|
138
|
+
open,
|
|
139
|
+
close,
|
|
140
|
+
selectedValues,
|
|
141
|
+
onSelect: handleSelect,
|
|
142
|
+
listboxId,
|
|
143
|
+
searchable: withSearch,
|
|
144
|
+
multiple,
|
|
145
|
+
openOnEnter,
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* While the search field is open it owns the `combobox` role: it is the
|
|
149
|
+
* element the focus lives in, so it has to carry the ARIA state.
|
|
150
|
+
*/
|
|
151
|
+
const showSearchField = withSearch && isOpen;
|
|
152
|
+
/** On desktop the search field replaces the trigger in place. */
|
|
153
|
+
const showTrigger = !isDesktop || !showSearchField;
|
|
154
|
+
/**
|
|
155
|
+
* There must never be two comboboxes at once: while the bottom sheet holds
|
|
156
|
+
* the search field, the trigger behind it is inert.
|
|
157
|
+
*/
|
|
158
|
+
const triggerAriaProps = showSearchField
|
|
159
|
+
? { 'aria-hidden': true, tabIndex: -1 }
|
|
160
|
+
: comboboxAriaProps;
|
|
161
|
+
/** Notifies the consumer once the bottom sheet has actually closed. */
|
|
162
|
+
const wasOpen = useRef(isOpen);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (wasOpen.current && !isOpen)
|
|
165
|
+
onSheetClose === null || onSheetClose === void 0 ? void 0 : onSheetClose();
|
|
166
|
+
wasOpen.current = isOpen;
|
|
167
|
+
}, [isOpen, onSheetClose]);
|
|
168
|
+
/**
|
|
169
|
+
* State of the list at `mousedown` time. The subsequent `click` toggles based
|
|
170
|
+
* on this value - otherwise the focus handler would open the list and the
|
|
171
|
+
* click would close it again straight away.
|
|
172
|
+
*/
|
|
173
|
+
const wasOpenOnPointerDown = useRef(false);
|
|
174
|
+
const handleRootPointerDown = useCallback(() => {
|
|
175
|
+
wasOpenOnPointerDown.current = isOpen;
|
|
176
|
+
}, [isOpen]);
|
|
177
|
+
/**
|
|
178
|
+
* Outside clicks are checked against the root: label, trigger, arrow and
|
|
179
|
+
* popover all live inside it and therefore never count as "outside".
|
|
180
|
+
*/
|
|
57
181
|
useEffect(() => {
|
|
58
182
|
if (!isDesktop || !isOpen)
|
|
59
183
|
return;
|
|
60
|
-
const
|
|
184
|
+
const handleDocumentPointerDown = (event) => {
|
|
61
185
|
var _a;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
// Add listener after a small delay to avoid closing immediately on open
|
|
70
|
-
const timeoutId = setTimeout(() => {
|
|
71
|
-
document.addEventListener('mousedown', handleDocumentClick);
|
|
72
|
-
}, 100);
|
|
73
|
-
return () => {
|
|
74
|
-
clearTimeout(timeoutId);
|
|
75
|
-
document.removeEventListener('mousedown', handleDocumentClick);
|
|
186
|
+
if ((_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))
|
|
187
|
+
return;
|
|
188
|
+
// The focus stays where the user clicked.
|
|
189
|
+
close(false);
|
|
190
|
+
onOutsideClick === null || onOutsideClick === void 0 ? void 0 : onOutsideClick();
|
|
76
191
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* @param optionValue - The value of the clicked option
|
|
82
|
-
*/
|
|
83
|
-
const handleOptionItemClick = (optionValue) => {
|
|
84
|
-
onOptionItemClick === null || onOptionItemClick === void 0 ? void 0 : onOptionItemClick(optionValue);
|
|
85
|
-
};
|
|
192
|
+
document.addEventListener('mousedown', handleDocumentPointerDown);
|
|
193
|
+
return () => document.removeEventListener('mousedown', handleDocumentPointerDown);
|
|
194
|
+
}, [isDesktop, isOpen, close, onOutsideClick]);
|
|
86
195
|
/**
|
|
87
|
-
*
|
|
196
|
+
* Opens the list when the trigger receives the focus - but not when the focus
|
|
197
|
+
* is only coming back from a close.
|
|
88
198
|
*/
|
|
89
|
-
const
|
|
199
|
+
const handleTriggerFocus = useCallback(() => {
|
|
200
|
+
if (isReturningFocus())
|
|
201
|
+
return;
|
|
202
|
+
open();
|
|
203
|
+
}, [isReturningFocus, open]);
|
|
204
|
+
const handleTriggerClick = useCallback(() => {
|
|
90
205
|
if (disabled)
|
|
91
206
|
return;
|
|
92
207
|
onClick === null || onClick === void 0 ? void 0 : onClick();
|
|
93
|
-
|
|
208
|
+
if (wasOpenOnPointerDown.current) {
|
|
209
|
+
close();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
open();
|
|
213
|
+
}, [disabled, onClick, open, close]);
|
|
214
|
+
const handleSearchChange = useCallback((event) => {
|
|
215
|
+
setHasTyped(true);
|
|
216
|
+
setSearchQuery(event.target.value);
|
|
217
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event.target.value);
|
|
218
|
+
}, [onChange]);
|
|
219
|
+
const handleSearchClear = useCallback(() => {
|
|
220
|
+
setHasTyped(true);
|
|
221
|
+
setSearchQuery('');
|
|
222
|
+
onChange === null || onChange === void 0 ? void 0 : onChange('');
|
|
223
|
+
}, [onChange]);
|
|
94
224
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* @returns A text component displaying the no results message
|
|
225
|
+
* Props shared by the desktop and the mobile search field. Both render the
|
|
226
|
+
* same field, only placement and compact styling differ.
|
|
98
227
|
*/
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
228
|
+
const searchInputProps = Object.assign({ ref: searchInputRef, name,
|
|
229
|
+
// Multi select would show a comma separated list, which cannot be edited
|
|
230
|
+
// sensibly - there the field starts empty.
|
|
231
|
+
value: hasTyped || multiple ? searchQuery : displayValue, pattern: searchPattern, placeholder,
|
|
232
|
+
inputMode,
|
|
233
|
+
// The mobile sheet has no visible label, so the field needs a name of its own.
|
|
234
|
+
'aria-label': label, displayLeadingIcon: true, blurOnOutsideClick: false, onChange: handleSearchChange, onClear: handleSearchClear, onKeyDown }, comboboxAriaProps);
|
|
102
235
|
/**
|
|
103
|
-
*
|
|
236
|
+
* The options block is built once and rendered either in the popover or in
|
|
237
|
+
* the bottom sheet.
|
|
104
238
|
*/
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
239
|
+
const optionsPanel = optionsSlot !== null && optionsSlot !== void 0 ? optionsSlot : (normalisedOptions.length > 0 ? (_jsx(ComboBoxOptions, { options: normalisedOptions, listboxAriaProps: listboxAriaProps, selectedValues: selectedValues, activeOptionId: activeOptionId, leadingType: leadingType, optionDetails: optionDetails, onOptionClick: handleSelect })) : (_jsx(ComboBoxNoResults, { text: noResultsText })));
|
|
240
|
+
return (_jsxs(ThemeProvider, { theme: theme, children: [_jsxs("div", { className: styles.comboBox, ref: rootRef, onMouseDownCapture: handleRootPointerDown, children: [showTrigger && (_jsx(SelectInput, Object.assign({ ref: triggerRef, className: clsx(isOpen && 'active', className), name: name, inputInfo: helperText, label: label, value: displayValue, placeholder: placeholder, leadingIcon: triggerLeadingIcon, disabled: disabled, loading: loading, readOnly: true, disableTrailingIconButton: true,
|
|
241
|
+
// The focus has to stay in the field, even when clicking next to it.
|
|
242
|
+
blurOnOutsideClick: false, onClick: handleTriggerClick, onFocus: handleTriggerFocus, onKeyDown: onKeyDown }, triggerAriaProps))), isDesktop && isOpen && (_jsxs(_Fragment, { children: [showSearchField && (_jsx(SearchInput, Object.assign({}, searchInputProps, { label: label }))), optionsPanel] }))] }), !isDesktop && isOpen && (_jsxs(BottomSheet, { className: styles.sheet, open: isOpen, onClose: () => close(), children: [_jsxs("div", { className: styles.header, children: [headline && (_jsx(Text, { variant: TEXT_VARIANT.HEADLINE, className: styles.headline, children: headline })), showSearchField && _jsx(SearchInput, Object.assign({}, searchInputProps, { isCompact: true }))] }), optionsPanel] }))] }));
|
|
109
243
|
}
|
|
244
|
+
export default ComboBox;
|
|
110
245
|
//# sourceMappingURL=ComboBox.js.map
|
package/lib/ComboBox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBox.js","sourceRoot":"","sources":["../src/ComboBox.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,
|
|
1
|
+
{"version":3,"file":"ComboBox.js","sourceRoot":"","sources":["../src/ComboBox.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.comboBox{position:relative;width:100%}.comboBox .headline[class*=text--]+*{margin-top:var(--spacing)}:is(.comboBox,.sheet) :is(.panel,.optionsSlot,.header){position:relative;width:100%}:is(.comboBox,.sheet) .panel{display:flex;flex-direction:column;max-height:inherit;overflow:hidden}:is(.sheet) .panel{box-shadow:none}:is(.comboBox,.sheet) :is(.options,.optionsSlot){display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:auto;scroll-padding-block-start:var(--combobox-sticky-offset,2.75rem)}.sheet .header{align-items:flex-start;height:auto;margin:var(--spacing-5) auto var(--spacing-2);padding:0 var(--spacing-2)}:is(.comboBox,.sheet) ul.optionsSlot{list-style:none;padding:0}:is(.comboBox,.sheet) .option{cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}:is(.comboBox,.sheet) .optionCheckbox[data-selected=true] :global(.menu-item){background-color:initial}:is(.comboBox,.sheet) .option.optionActive :global(.menu-item),:is(.comboBox,.sheet) .optionCheckbox.optionActive :global(.menu-item){background-color:var(--color-canvas--hover)}:is(.comboBox,.sheet) .option[data-selected=true]:not(.optionCheckbox) :global(.menu-item){background-color:var(--color-accent);color:var(--color-surface)}:is(.comboBox,.sheet) .option[aria-disabled=true]{cursor:not-allowed;opacity:.5}@media (forced-colors:active){:is(.comboBox,.sheet) .option.optionActive :global(.menu-item){outline:2px solid Highlight;outline-offset:-2px}}:is(.comboBox,.sheet) .stickyHeader{background-color:var(--color-surface);border-bottom:1px solid var(--color-dim);padding:var(--spacing-1_25) var(--spacing-2);position:-webkit-sticky;position:sticky;top:0}:is(.comboBox,.sheet) .options :global(.text--caption){color:var(--color-on-secondary)}:is(.comboBox,.sheet) .option[data-selected=true]:not(.optionCheckbox) :global(.menu-item .text--caption){color:var(--color-surface)}:is(.comboBox,.sheet) .options :global(.item-header){display:flex;justify-content:space-between}:is(.comboBox,.sheet) .optionDetails{background-color:var(--color-surface);border-top:1px solid var(--color-dim);flex:0 0 auto;padding:var(--spacing-1_25) var(--spacing)}:is(.comboBox,.sheet) .noResults{padding:var(--spacing-2) var(--spacing);text-align:center}@media screen and (min-width:960px){.comboBox :is(.panel,.optionsSlot){border-radius:var(--border-radius-3);margin-top:var(--spacing);max-height:330px;position:absolute}}
|
package/lib/hooks/index.d.ts
CHANGED
package/lib/hooks/index.js
CHANGED
package/lib/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ComboBoxAriaProps, ComboBoxOption, ListboxAriaProps } from '../types';
|
|
2
|
+
export interface UseComboBoxOptions {
|
|
3
|
+
/** Normalised options in render order. */
|
|
4
|
+
options: ComboBoxOption[];
|
|
5
|
+
/** Whether the list is currently open. */
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
/** Opens the list. */
|
|
8
|
+
open: () => void;
|
|
9
|
+
/**
|
|
10
|
+
* Closes the list. `restoreFocus` is `false` when the focus must stay where
|
|
11
|
+
* the user put it (Tab).
|
|
12
|
+
*/
|
|
13
|
+
close: (restoreFocus?: boolean) => void;
|
|
14
|
+
/** Values that are currently selected. */
|
|
15
|
+
selectedValues: string[];
|
|
16
|
+
/** Called with the value of the option that was activated by the keyboard. */
|
|
17
|
+
onSelect: (value: string) => void;
|
|
18
|
+
/** Id of the listbox element. */
|
|
19
|
+
listboxId: string;
|
|
20
|
+
/** Whether the open list offers a freely typeable search field. */
|
|
21
|
+
searchable?: boolean;
|
|
22
|
+
/** Multi select keeps the list open after a selection. */
|
|
23
|
+
multiple?: boolean;
|
|
24
|
+
/** Enter opens the closed list (APG select-only). */
|
|
25
|
+
openOnEnter?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface UseComboBoxResult {
|
|
28
|
+
/** ARIA props for the element that owns the `combobox` role. */
|
|
29
|
+
comboboxAriaProps: ComboBoxAriaProps;
|
|
30
|
+
/** ARIA props for the element that owns the `listbox` role. */
|
|
31
|
+
listboxAriaProps: ListboxAriaProps;
|
|
32
|
+
/** Keyboard handler for the combobox element. */
|
|
33
|
+
onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
|
|
34
|
+
/** DOM id of the active option, `undefined` while closed. */
|
|
35
|
+
activeOptionId: string | undefined;
|
|
36
|
+
/** Index of the active option, `null` while closed or empty. */
|
|
37
|
+
activeIndex: number | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Headless controller for the WAI-ARIA combobox pattern.
|
|
41
|
+
*
|
|
42
|
+
* The focus never moves into the list: the active option is communicated
|
|
43
|
+
* exclusively via `aria-activedescendant`, which keeps the search field
|
|
44
|
+
* typeable while the user navigates.
|
|
45
|
+
*
|
|
46
|
+
* Keyboard (APG combobox with listbox popup):
|
|
47
|
+
* - closed: ArrowUp/ArrowDown/Alt+ArrowDown, Enter, Space open the list
|
|
48
|
+
* - open: ArrowUp/ArrowDown navigate (skipping disabled options),
|
|
49
|
+
* Home/End jump to the first/last option, Enter selects,
|
|
50
|
+
* Escape and Alt+ArrowUp close, Tab closes and lets the focus move on
|
|
51
|
+
* - Space selects while the field is not typeable; as soon as a search field
|
|
52
|
+
* is open it types a space character instead, as APG requires
|
|
53
|
+
*
|
|
54
|
+
* On open the starting point is always the selected option, so ArrowDown moves
|
|
55
|
+
* to the option right after it.
|
|
56
|
+
*
|
|
57
|
+
* @param options - Configuration of the combobox
|
|
58
|
+
* @returns ARIA props, the keyboard handler and the active option
|
|
59
|
+
*/
|
|
60
|
+
export declare function useComboBox({ options, isOpen, open, close, selectedValues, onSelect, listboxId, searchable, multiple, openOnEnter, }: UseComboBoxOptions): UseComboBoxResult;
|