@lumx/react 4.11.0-next.4 → 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 +19 -6
- package/index.js +48 -21
- package/index.js.map +1 -1
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -1781,6 +1781,8 @@ interface InputLabelProps$1 extends HasClassName, HasTheme {
|
|
|
1781
1781
|
children: JSXElement;
|
|
1782
1782
|
/** Native htmlFor property. */
|
|
1783
1783
|
htmlFor: string;
|
|
1784
|
+
/** Native id property. */
|
|
1785
|
+
id?: string;
|
|
1784
1786
|
/** Whether the component is required or not. */
|
|
1785
1787
|
isRequired?: boolean;
|
|
1786
1788
|
/** ref to the root element */
|
|
@@ -1813,6 +1815,8 @@ interface TextFieldProps$1 extends HasClassName, HasTheme, HasAriaDisabled, HasD
|
|
|
1813
1815
|
helperId?: string;
|
|
1814
1816
|
/** Generated error id for accessibility attributes. */
|
|
1815
1817
|
errorId?: string;
|
|
1818
|
+
/** Generated label id for accessibility attributes (used to link the clear button to the field label). */
|
|
1819
|
+
labelId?: string;
|
|
1816
1820
|
/** Whether the component is required or not. */
|
|
1817
1821
|
isRequired?: boolean;
|
|
1818
1822
|
/** Whether the text field is displayed with valid style or not. */
|
|
@@ -1842,7 +1846,7 @@ interface TextFieldProps$1 extends HasClassName, HasTheme, HasAriaDisabled, HasD
|
|
|
1842
1846
|
/** Ref to the component root. */
|
|
1843
1847
|
ref?: CommonRef;
|
|
1844
1848
|
}
|
|
1845
|
-
type TextFieldPropsToOverride = 'input' | 'IconButton' | 'labelProps' | 'textFieldRef' | 'clearButtonProps' | 'helperId' | 'errorId' | 'isAnyDisabled' | 'isFocus';
|
|
1849
|
+
type TextFieldPropsToOverride = 'input' | 'IconButton' | 'labelProps' | 'textFieldRef' | 'clearButtonProps' | 'helperId' | 'errorId' | 'labelId' | 'isAnyDisabled' | 'isFocus';
|
|
1846
1850
|
|
|
1847
1851
|
/**
|
|
1848
1852
|
* Defines the props of the component.
|
|
@@ -1950,13 +1954,22 @@ interface ComboboxInputProps extends TextFieldProps {
|
|
|
1950
1954
|
value: string;
|
|
1951
1955
|
}) => void;
|
|
1952
1956
|
/**
|
|
1953
|
-
*
|
|
1954
|
-
*
|
|
1957
|
+
* Controls how the combobox filters options as the user types.
|
|
1958
|
+
*
|
|
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`.
|
|
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.
|
|
1955
1969
|
*
|
|
1956
|
-
*
|
|
1957
|
-
* pre-filtering). Options will not be auto-filtered.
|
|
1970
|
+
* @default false (true when filter is 'off')
|
|
1958
1971
|
*/
|
|
1959
|
-
|
|
1972
|
+
openOnFocus?: boolean;
|
|
1960
1973
|
}
|
|
1961
1974
|
|
|
1962
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",
|
|
@@ -7107,15 +7124,17 @@ const {
|
|
|
7107
7124
|
* @param existingAriaDescribedBy Existing aria-describedby value to merge
|
|
7108
7125
|
* @return Object containing helperId, errorId, and combined describedById
|
|
7109
7126
|
*/
|
|
7110
|
-
function generateAccessibilityIds(helper, error, generatedId, existingAriaDescribedBy) {
|
|
7127
|
+
function generateAccessibilityIds(helper, error, generatedId, existingAriaDescribedBy, label) {
|
|
7111
7128
|
const helperId = helper ? `text-field-helper-${generatedId}` : undefined;
|
|
7112
7129
|
const errorId = error ? `text-field-error-${generatedId}` : undefined;
|
|
7130
|
+
const labelId = label ? `text-field-label-${generatedId}` : undefined;
|
|
7113
7131
|
const describedByIds = [errorId, helperId, existingAriaDescribedBy].filter(Boolean);
|
|
7114
7132
|
const describedById = describedByIds.length === 0 ? undefined : describedByIds.join(' ');
|
|
7115
7133
|
return {
|
|
7116
7134
|
helperId,
|
|
7117
7135
|
errorId,
|
|
7118
|
-
describedById
|
|
7136
|
+
describedById,
|
|
7137
|
+
labelId
|
|
7119
7138
|
};
|
|
7120
7139
|
}
|
|
7121
7140
|
|
|
@@ -7148,6 +7167,7 @@ const TextField$1 = props => {
|
|
|
7148
7167
|
textFieldRef,
|
|
7149
7168
|
helperId,
|
|
7150
7169
|
errorId,
|
|
7170
|
+
labelId,
|
|
7151
7171
|
theme,
|
|
7152
7172
|
value,
|
|
7153
7173
|
afterElement,
|
|
@@ -7179,6 +7199,7 @@ const TextField$1 = props => {
|
|
|
7179
7199
|
className: element$K('header'),
|
|
7180
7200
|
children: [label && InputLabel$1({
|
|
7181
7201
|
...labelProps,
|
|
7202
|
+
id: labelId,
|
|
7182
7203
|
htmlFor: textFieldId,
|
|
7183
7204
|
className: element$K('label'),
|
|
7184
7205
|
isRequired,
|
|
@@ -7213,6 +7234,7 @@ const TextField$1 = props => {
|
|
|
7213
7234
|
icon: isValid ? mdiCheckCircle : mdiAlertCircle,
|
|
7214
7235
|
size: Size.xxs
|
|
7215
7236
|
}), clearButtonProps && isNotEmpty && !isAnyDisabled && /*#__PURE__*/jsx(IconButton, {
|
|
7237
|
+
"aria-describedby": labelId,
|
|
7216
7238
|
...clearButtonProps,
|
|
7217
7239
|
className: element$K('input-clear'),
|
|
7218
7240
|
icon: mdiCloseCircle,
|
|
@@ -7500,8 +7522,9 @@ const TextField = forwardRef((props, ref) => {
|
|
|
7500
7522
|
const {
|
|
7501
7523
|
helperId,
|
|
7502
7524
|
errorId,
|
|
7503
|
-
describedById
|
|
7504
|
-
|
|
7525
|
+
describedById,
|
|
7526
|
+
labelId
|
|
7527
|
+
} = generateAccessibilityIds(helper, error, generatedTextFieldId, forwardedProps['aria-describedby'], label);
|
|
7505
7528
|
const [isFocus, setFocus] = useState(false);
|
|
7506
7529
|
|
|
7507
7530
|
/**
|
|
@@ -7571,6 +7594,7 @@ const TextField = forwardRef((props, ref) => {
|
|
|
7571
7594
|
afterElement,
|
|
7572
7595
|
hasError,
|
|
7573
7596
|
helperId,
|
|
7597
|
+
labelId,
|
|
7574
7598
|
multiline,
|
|
7575
7599
|
maxLength,
|
|
7576
7600
|
isRequired,
|
|
@@ -7617,7 +7641,8 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7617
7641
|
inputRef: externalInputRef,
|
|
7618
7642
|
toggleButtonProps,
|
|
7619
7643
|
onSelect,
|
|
7620
|
-
|
|
7644
|
+
filter,
|
|
7645
|
+
openOnFocus,
|
|
7621
7646
|
...otherProps
|
|
7622
7647
|
} = props;
|
|
7623
7648
|
const internalInputRef = useRef(null);
|
|
@@ -7639,14 +7664,15 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7639
7664
|
onChangeRef.current?.(option.value);
|
|
7640
7665
|
onSelectRef.current?.(option);
|
|
7641
7666
|
},
|
|
7642
|
-
|
|
7667
|
+
filter,
|
|
7668
|
+
openOnFocus
|
|
7643
7669
|
});
|
|
7644
7670
|
setHandle(handle);
|
|
7645
7671
|
return () => {
|
|
7646
7672
|
handle.destroy();
|
|
7647
7673
|
setHandle(null);
|
|
7648
7674
|
};
|
|
7649
|
-
}, [
|
|
7675
|
+
}, [filter, openOnFocus, setHandle]);
|
|
7650
7676
|
const handleToggle = useCallback(() => {
|
|
7651
7677
|
setIsOpen(!isOpen);
|
|
7652
7678
|
internalInputRef.current?.focus();
|
|
@@ -7656,6 +7682,7 @@ const ComboboxInput = forwardRef((props, ref) => {
|
|
|
7656
7682
|
ref,
|
|
7657
7683
|
listboxId,
|
|
7658
7684
|
isOpen,
|
|
7685
|
+
filter,
|
|
7659
7686
|
inputRef: mergedInputRef,
|
|
7660
7687
|
textFieldRef: anchorRef,
|
|
7661
7688
|
toggleButtonProps,
|
|
@@ -8149,7 +8176,7 @@ function useComboboxOptionContext() {
|
|
|
8149
8176
|
/**
|
|
8150
8177
|
* Combobox.Option component - wraps ListItem with option role and data-value.
|
|
8151
8178
|
*
|
|
8152
|
-
* When
|
|
8179
|
+
* When filter="auto" is enabled on the parent Combobox.Input, each option registers itself
|
|
8153
8180
|
* with the combobox handle (via an internal ref to its root <li>). When the filter changes,
|
|
8154
8181
|
* the handle calls back with the new match state. When filtered out, the core template renders
|
|
8155
8182
|
* a bare `<li hidden>` — no ARIA roles, no CSS classes — keeping the element in the DOM so
|
|
@@ -9595,7 +9622,7 @@ ListSection.defaultProps = DEFAULT_PROPS$U;
|
|
|
9595
9622
|
*
|
|
9596
9623
|
* Returns null when children is empty so the section header is not rendered as an orphan.
|
|
9597
9624
|
*
|
|
9598
|
-
* When
|
|
9625
|
+
* When filter="auto" is active, the section registers itself with the combobox handle.
|
|
9599
9626
|
* The handle monitors registered options within this section and notifies when all
|
|
9600
9627
|
* are filtered out. When hidden, the core template renders a bare `<li hidden>` wrapper
|
|
9601
9628
|
* so children (options) stay mounted and registered.
|