@lumx/react 4.11.0-next.5 → 4.11.0-next.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/index.d.ts +14 -5
- package/index.js +37 -17
- package/index.js.map +1 -1
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -1954,13 +1954,22 @@ interface ComboboxInputProps extends TextFieldProps {
|
|
|
1954
1954
|
value: string;
|
|
1955
1955
|
}) => void;
|
|
1956
1956
|
/**
|
|
1957
|
-
*
|
|
1958
|
-
* Each `Combobox.Option` registers itself and hides when it doesn't match the input value.
|
|
1957
|
+
* Controls how the combobox filters options as the user types.
|
|
1959
1958
|
*
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
1959
|
+
* - `'auto'` (default) — Options are automatically filtered client-side.
|
|
1960
|
+
* - `'manual'` — Filtering is the consumer's responsibility.
|
|
1961
|
+
* - `'off'` — Like `'manual'`, but the input is rendered as `readOnly`
|
|
1962
|
+
* and `openOnFocus` defaults to `true`.
|
|
1962
1963
|
*/
|
|
1963
|
-
|
|
1964
|
+
filter?: 'auto' | 'manual' | 'off';
|
|
1965
|
+
/**
|
|
1966
|
+
* When true, the combobox opens automatically when the input receives focus.
|
|
1967
|
+
* When false (default, unless `filter` is `'off'`), the combobox only opens
|
|
1968
|
+
* on click, typing, or keyboard navigation.
|
|
1969
|
+
*
|
|
1970
|
+
* @default false (true when filter is 'off')
|
|
1971
|
+
*/
|
|
1972
|
+
openOnFocus?: boolean;
|
|
1964
1973
|
}
|
|
1965
1974
|
|
|
1966
1975
|
/**
|
package/index.js
CHANGED
|
@@ -3371,6 +3371,7 @@ function setupListbox(handle, signal, notify) {
|
|
|
3371
3371
|
const trigger = handle.trigger;
|
|
3372
3372
|
const listbox = handle.listbox;
|
|
3373
3373
|
const isGrid = listbox.getAttribute('role') === 'grid';
|
|
3374
|
+
const itemSelector = '[role="option"]';
|
|
3374
3375
|
|
|
3375
3376
|
// ── Focus navigation ──────────────────────────────────────
|
|
3376
3377
|
|
|
@@ -3378,13 +3379,25 @@ function setupListbox(handle, signal, notify) {
|
|
|
3378
3379
|
onActivate: item => {
|
|
3379
3380
|
item.setAttribute('data-focus-visible-added', 'true');
|
|
3380
3381
|
trigger.setAttribute('aria-activedescendant', item.id);
|
|
3381
|
-
// Scroll to the element in listbox or else the item
|
|
3382
|
-
const toScrollTo = item.closest('[role=listbox] > *') || item;
|
|
3383
|
-
toScrollTo.scrollIntoView({
|
|
3384
|
-
behavior: 'smooth',
|
|
3385
|
-
block: 'nearest'
|
|
3386
|
-
});
|
|
3387
3382
|
notify('activeDescendantChange', item.id);
|
|
3383
|
+
requestAnimationFrame(() => {
|
|
3384
|
+
// Last item in listbox
|
|
3385
|
+
const lastItem = !isGrid &&
|
|
3386
|
+
// Last item: find last element containing itemSelector and not followed by elements containing itemSelector and then get the itemSelector inside
|
|
3387
|
+
listbox.querySelector(`:scope > :has(${itemSelector}):not(:has(~ * ${itemSelector})) ${itemSelector}`);
|
|
3388
|
+
if (item === lastItem) {
|
|
3389
|
+
// Scroll to the end of the listbox (shouldsnap to the end of the scroll container thanks to CSS scroll snap)
|
|
3390
|
+
listbox.lastElementChild?.scrollIntoView({
|
|
3391
|
+
block: 'nearest'
|
|
3392
|
+
});
|
|
3393
|
+
} else {
|
|
3394
|
+
// Scroll to the element in listbox or else the item
|
|
3395
|
+
const toScrollTo = item.closest('[role=listbox] > *') || item;
|
|
3396
|
+
toScrollTo.scrollIntoView({
|
|
3397
|
+
block: 'nearest'
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3400
|
+
});
|
|
3388
3401
|
},
|
|
3389
3402
|
onDeactivate: item => {
|
|
3390
3403
|
item.removeAttribute('data-focus-visible-added');
|
|
@@ -3406,9 +3419,7 @@ function setupListbox(handle, signal, notify) {
|
|
|
3406
3419
|
} else {
|
|
3407
3420
|
focusNav = createListFocusNavigation({
|
|
3408
3421
|
container: listbox,
|
|
3409
|
-
|
|
3410
|
-
// hidden placeholder), so no :not([data-filtered]) filter is needed here.
|
|
3411
|
-
itemSelector: '[role="option"]',
|
|
3422
|
+
itemSelector,
|
|
3412
3423
|
getActiveItem: () => {
|
|
3413
3424
|
const id = trigger.getAttribute('aria-activedescendant');
|
|
3414
3425
|
return id ? document.getElementById(id) : null;
|
|
@@ -6876,9 +6887,11 @@ const ComboboxButton = Object.assign(forwardRefPolymorphic((props, ref) => {
|
|
|
6876
6887
|
*/
|
|
6877
6888
|
function setupComboboxInput(input, options) {
|
|
6878
6889
|
const {
|
|
6879
|
-
|
|
6890
|
+
filter = 'auto',
|
|
6880
6891
|
onSelect: optionOnSelect
|
|
6881
6892
|
} = options;
|
|
6893
|
+
const openOnFocus = options.openOnFocus ?? filter === 'off';
|
|
6894
|
+
const autoFilter = filter === 'auto';
|
|
6882
6895
|
|
|
6883
6896
|
/** Check if the input is disabled (native `disabled` attribute or `aria-disabled="true"`). */
|
|
6884
6897
|
const isDisabled = () => input.disabled || input.getAttribute('aria-disabled') === 'true';
|
|
@@ -6931,11 +6944,13 @@ function setupComboboxInput(input, options) {
|
|
|
6931
6944
|
signal
|
|
6932
6945
|
});
|
|
6933
6946
|
|
|
6934
|
-
// Open on focus.
|
|
6947
|
+
// Open on focus (only when openOnFocus is enabled).
|
|
6935
6948
|
input.addEventListener('focus', () => {
|
|
6936
6949
|
if (isDisabled()) return;
|
|
6937
6950
|
combobox.focusNav?.clear();
|
|
6938
|
-
|
|
6951
|
+
if (openOnFocus) {
|
|
6952
|
+
combobox.setIsOpen(true);
|
|
6953
|
+
}
|
|
6939
6954
|
}, {
|
|
6940
6955
|
signal
|
|
6941
6956
|
});
|
|
@@ -7040,6 +7055,7 @@ const ComboboxInput$1 = (props, {
|
|
|
7040
7055
|
textFieldRef,
|
|
7041
7056
|
toggleButtonProps,
|
|
7042
7057
|
handleToggle,
|
|
7058
|
+
filter,
|
|
7043
7059
|
theme,
|
|
7044
7060
|
...forwardedProps
|
|
7045
7061
|
} = props;
|
|
@@ -7049,6 +7065,7 @@ const ComboboxInput$1 = (props, {
|
|
|
7049
7065
|
const isAnyDisabled = disabledState.disabled || disabledState['aria-disabled'] || undefined;
|
|
7050
7066
|
return /*#__PURE__*/jsx(TextField, {
|
|
7051
7067
|
autoComplete: "off",
|
|
7068
|
+
readOnly: filter === 'off' || undefined,
|
|
7052
7069
|
...forwardedProps,
|
|
7053
7070
|
ref: ref,
|
|
7054
7071
|
role: "combobox",
|
|
@@ -7624,7 +7641,8 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7624
7641
|
inputRef: externalInputRef,
|
|
7625
7642
|
toggleButtonProps,
|
|
7626
7643
|
onSelect,
|
|
7627
|
-
|
|
7644
|
+
filter,
|
|
7645
|
+
openOnFocus,
|
|
7628
7646
|
...otherProps
|
|
7629
7647
|
} = props;
|
|
7630
7648
|
const internalInputRef = useRef(null);
|
|
@@ -7646,14 +7664,15 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7646
7664
|
onChangeRef.current?.(option.value);
|
|
7647
7665
|
onSelectRef.current?.(option);
|
|
7648
7666
|
},
|
|
7649
|
-
|
|
7667
|
+
filter,
|
|
7668
|
+
openOnFocus
|
|
7650
7669
|
});
|
|
7651
7670
|
setHandle(handle);
|
|
7652
7671
|
return () => {
|
|
7653
7672
|
handle.destroy();
|
|
7654
7673
|
setHandle(null);
|
|
7655
7674
|
};
|
|
7656
|
-
}, [
|
|
7675
|
+
}, [filter, openOnFocus, setHandle]);
|
|
7657
7676
|
const handleToggle = useCallback(() => {
|
|
7658
7677
|
setIsOpen(!isOpen);
|
|
7659
7678
|
internalInputRef.current?.focus();
|
|
@@ -7663,6 +7682,7 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7663
7682
|
ref,
|
|
7664
7683
|
listboxId,
|
|
7665
7684
|
isOpen,
|
|
7685
|
+
filter,
|
|
7666
7686
|
inputRef: mergedInputRef,
|
|
7667
7687
|
textFieldRef: anchorRef,
|
|
7668
7688
|
toggleButtonProps,
|
|
@@ -8156,7 +8176,7 @@ function useComboboxOptionContext() {
|
|
|
8156
8176
|
/**
|
|
8157
8177
|
* Combobox.Option component - wraps ListItem with option role and data-value.
|
|
8158
8178
|
*
|
|
8159
|
-
* When
|
|
8179
|
+
* When filter="auto" is enabled on the parent Combobox.Input, each option registers itself
|
|
8160
8180
|
* with the combobox handle (via an internal ref to its root <li>). When the filter changes,
|
|
8161
8181
|
* the handle calls back with the new match state. When filtered out, the core template renders
|
|
8162
8182
|
* a bare `<li hidden>` — no ARIA roles, no CSS classes — keeping the element in the DOM so
|
|
@@ -9602,7 +9622,7 @@ ListSection.defaultProps = DEFAULT_PROPS$U;
|
|
|
9602
9622
|
*
|
|
9603
9623
|
* Returns null when children is empty so the section header is not rendered as an orphan.
|
|
9604
9624
|
*
|
|
9605
|
-
* When
|
|
9625
|
+
* When filter="auto" is active, the section registers itself with the combobox handle.
|
|
9606
9626
|
* The handle monitors registered options within this section and notifies when all
|
|
9607
9627
|
* are filtered out. When hidden, the core template renders a bare `<li hidden>` wrapper
|
|
9608
9628
|
* so children (options) stay mounted and registered.
|