@lumx/core 4.10.0 → 4.11.0-alpha.1
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/components-and-utils.css +19 -14
- package/js/components/AlertDialog/Tests.d.ts +11 -0
- package/js/components/Chip/SelectionChipGroup.d.ts +56 -0
- package/js/components/Chip/SelectionChipGroupTests.d.ts +18 -0
- package/js/components/Chip/index.d.ts +81 -0
- package/js/components/Chip/setupSelectionChipGroupEvents.d.ts +28 -0
- package/js/components/Combobox/ComboboxInput.d.ts +5 -0
- package/js/components/Combobox/setupComboboxInput.d.ts +14 -6
- package/js/components/Dialog/Tests.d.ts +12 -0
- package/js/components/InputLabel/index.d.ts +2 -0
- package/js/utils/InfiniteScroll/setupInfiniteScrollObserver.d.ts +8 -0
- package/js/utils/browser/querySelectorInclusive.d.ts +11 -0
- package/js/utils/browser/trackContainerFocus.d.ts +11 -0
- package/js/utils/focusNavigation/createListFocusNavigation.d.ts +9 -4
- package/js/utils/focusNavigation/index.d.ts +1 -1
- package/js/utils/focusNavigation/setupRovingTabIndex.d.ts +20 -25
- package/js/utils/focusNavigation/types.d.ts +28 -4
- package/lumx.css +19 -14
- package/package.json +4 -3
- package/scss/components/chip/_index.scss +4 -17
- package/scss/components/combobox/_index.scss +15 -0
package/components-and-utils.css
CHANGED
|
@@ -4988,21 +4988,10 @@ table {
|
|
|
4988
4988
|
/* Selection Chip group
|
|
4989
4989
|
========================================================================== */
|
|
4990
4990
|
.lumx-selection-chip-group {
|
|
4991
|
-
|
|
4992
|
-
}
|
|
4993
|
-
.lumx-selection-chip-group:empty {
|
|
4994
|
-
display: none;
|
|
4995
|
-
}
|
|
4996
|
-
.lumx-selection-chip-group__item {
|
|
4997
|
-
display: flex;
|
|
4998
|
-
overflow: hidden;
|
|
4991
|
+
align-content: flex-start;
|
|
4999
4992
|
}
|
|
5000
|
-
.lumx-selection-chip-
|
|
5001
|
-
|
|
5002
|
-
}
|
|
5003
|
-
.lumx-selection-chip-group__chip .lumx-chip__label {
|
|
5004
|
-
overflow: hidden;
|
|
5005
|
-
text-overflow: ellipsis;
|
|
4993
|
+
.lumx-selection-chip-group, .lumx-selection-chip-group .lumx-chip, .lumx-selection-chip-group .lumx-chip__label {
|
|
4994
|
+
min-width: 0;
|
|
5006
4995
|
}
|
|
5007
4996
|
|
|
5008
4997
|
/* ==========================================================================
|
|
@@ -5020,6 +5009,22 @@ table {
|
|
|
5020
5009
|
}
|
|
5021
5010
|
.lumx-combobox-popover__scroll {
|
|
5022
5011
|
overflow-y: auto;
|
|
5012
|
+
scroll-snap-type: y proximity;
|
|
5013
|
+
}
|
|
5014
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5015
|
+
.lumx-combobox-popover__scroll {
|
|
5016
|
+
scroll-behavior: smooth;
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
.lumx-combobox-popover__scroll::before {
|
|
5020
|
+
content: "";
|
|
5021
|
+
display: block;
|
|
5022
|
+
scroll-snap-align: start;
|
|
5023
|
+
}
|
|
5024
|
+
.lumx-combobox-popover__scroll::after {
|
|
5025
|
+
content: "";
|
|
5026
|
+
display: block;
|
|
5027
|
+
scroll-snap-align: end;
|
|
5023
5028
|
}
|
|
5024
5029
|
|
|
5025
5030
|
.lumx-combobox-popover:empty, .lumx-combobox-popover:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SetupOptions } from '../../../testing';
|
|
2
|
+
/**
|
|
3
|
+
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
4
|
+
*/
|
|
5
|
+
export declare const setup: (propsOverride: any | undefined, { render, ...options }: SetupOptions<any>) => {
|
|
6
|
+
props: any;
|
|
7
|
+
alertDialog: HTMLElement | null;
|
|
8
|
+
wrapper: Partial<import("../../../testing").SetupResult>;
|
|
9
|
+
};
|
|
10
|
+
declare const _default: (renderOptions: SetupOptions<any>) => void;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { LumxClassName, HasClassName, HasTheme } from '../../types';
|
|
2
|
+
import type { Selector } from '../../types/Selector';
|
|
3
|
+
import type { ChipProps } from '.';
|
|
4
|
+
/**
|
|
5
|
+
* Framework-specific components injected by React/Vue wrappers.
|
|
6
|
+
*/
|
|
7
|
+
export interface SelectionChipGroupComponents {
|
|
8
|
+
Chip: any;
|
|
9
|
+
ChipGroup: any;
|
|
10
|
+
Icon: any;
|
|
11
|
+
Text: any;
|
|
12
|
+
Tooltip: any;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Props that consumers can override per chip via getChipProps.
|
|
16
|
+
* Excludes internal/framework props that the core always controls.
|
|
17
|
+
*/
|
|
18
|
+
export type ChipOverrideProps = Pick<ChipProps, 'isDisabled' | 'color' | 'isHighlighted' | 'isSelected' | 'className' | 'before' | 'after'>;
|
|
19
|
+
/**
|
|
20
|
+
* Defines the props of the component.
|
|
21
|
+
*/
|
|
22
|
+
export interface SelectionChipGroupProps<O> extends HasClassName, HasTheme {
|
|
23
|
+
/** Option object id selector (either a property name or a function to get the id) */
|
|
24
|
+
getOptionId: Selector<O>;
|
|
25
|
+
/** Option object name selector. Falls back to the id if not defined */
|
|
26
|
+
getOptionName?: Selector<O, string | undefined | null>;
|
|
27
|
+
/** Selected options array */
|
|
28
|
+
value?: O[];
|
|
29
|
+
/** Label for accessibility (aria-label on the listbox) */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Label for the remove action (visually hidden text appended to each chip) */
|
|
32
|
+
chipRemoveLabel?: string;
|
|
33
|
+
/** Disabled state for all chips */
|
|
34
|
+
isDisabled?: boolean;
|
|
35
|
+
/** Customize chip props per option */
|
|
36
|
+
getChipProps?: (option: O) => Partial<ChipProps>;
|
|
37
|
+
/** Ref for the container element */
|
|
38
|
+
ref?: any;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Component display name.
|
|
42
|
+
*/
|
|
43
|
+
export declare const COMPONENT_NAME = "SelectionChipGroup";
|
|
44
|
+
/**
|
|
45
|
+
* Component default class name and class prefix.
|
|
46
|
+
*/
|
|
47
|
+
export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
|
|
48
|
+
/**
|
|
49
|
+
* SelectionChipGroup component.
|
|
50
|
+
* Renders a listbox of removable selection chips using component injection for framework-agnostic rendering.
|
|
51
|
+
*
|
|
52
|
+
* @param props Component props.
|
|
53
|
+
* @param components Framework-specific components injected by the wrapper (Chip, ChipGroup, Icon, Tooltip).
|
|
54
|
+
* @return JSX element, or null when value is empty.
|
|
55
|
+
*/
|
|
56
|
+
export declare const SelectionChipGroup: <O>(props: SelectionChipGroupProps<O>, { Chip, ChipGroup, Icon, Text, Tooltip }: SelectionChipGroupComponents) => import("react").JSX.Element | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SetupOptions } from '../../../testing';
|
|
2
|
+
/**
|
|
3
|
+
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
4
|
+
*/
|
|
5
|
+
export declare const setup: (propsOverride: any | undefined, { render, ...options }: SetupOptions<any>) => {
|
|
6
|
+
props: any;
|
|
7
|
+
selectionChipGroup: HTMLElement | null;
|
|
8
|
+
wrapper: Partial<import("../../../testing").SetupResult>;
|
|
9
|
+
};
|
|
10
|
+
interface CoreTestOptions extends SetupOptions<any> {
|
|
11
|
+
/**
|
|
12
|
+
* Render a stateful SelectionChipGroup that updates its own value on change.
|
|
13
|
+
* When provided, enables roving tabindex recovery tests.
|
|
14
|
+
*/
|
|
15
|
+
renderStateful?: () => void;
|
|
16
|
+
}
|
|
17
|
+
declare const _default: (renderOptions: CoreTestOptions) => void;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ColorPalette, Size } from '../../constants';
|
|
2
|
+
import { HasTheme, HasAriaDisabled, JSXElement, LumxClassName, HasClassName, HasDisabled, CommonRef, GenericProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Chip sizes.
|
|
5
|
+
*/
|
|
6
|
+
export type ChipSize = Extract<Size, 's' | 'm'>;
|
|
7
|
+
/**
|
|
8
|
+
* Defines the props of the component.
|
|
9
|
+
*/
|
|
10
|
+
export interface ChipProps extends HasClassName, HasDisabled, HasTheme, HasAriaDisabled {
|
|
11
|
+
/** A component to be rendered after the content. */
|
|
12
|
+
after?: JSXElement;
|
|
13
|
+
/** A component to be rendered before the content. */
|
|
14
|
+
before?: JSXElement;
|
|
15
|
+
/** Color variant. */
|
|
16
|
+
color?: ColorPalette;
|
|
17
|
+
/** Whether the component is clickable or not. */
|
|
18
|
+
isClickable?: boolean;
|
|
19
|
+
/** Whether the chip is currently in a highlighted state or not. */
|
|
20
|
+
isHighlighted?: boolean;
|
|
21
|
+
/** Whether the component is selected or not. */
|
|
22
|
+
isSelected?: boolean;
|
|
23
|
+
/** Size variant. */
|
|
24
|
+
size?: ChipSize;
|
|
25
|
+
/** href for the chip if it is a link */
|
|
26
|
+
href?: string;
|
|
27
|
+
/** reference to the root element */
|
|
28
|
+
ref?: CommonRef;
|
|
29
|
+
/** On "after" element clicked callback. */
|
|
30
|
+
handleAfterClick?: (event: any) => void;
|
|
31
|
+
/** On element key down callback. */
|
|
32
|
+
handleKeyDown?: (event: any) => void;
|
|
33
|
+
/** On "before" element clicked callback. */
|
|
34
|
+
handleBeforeClick?: (event: any) => void;
|
|
35
|
+
/** On element clicked callback. */
|
|
36
|
+
handleClick?: (event: any) => void;
|
|
37
|
+
/** name of the prop for handling key down events */
|
|
38
|
+
keyDownProp?: string;
|
|
39
|
+
/** name of the prop for tab index */
|
|
40
|
+
tabIndexProp?: string;
|
|
41
|
+
/** Children */
|
|
42
|
+
children?: JSXElement;
|
|
43
|
+
/** Props to apply when the component is in a disabled state. */
|
|
44
|
+
disabledStateProps: GenericProps;
|
|
45
|
+
/** Whether the "after" element has a click handler. */
|
|
46
|
+
hasAfterClick?: boolean;
|
|
47
|
+
/** Whether the "before" element has a click handler. */
|
|
48
|
+
hasBeforeClick?: boolean;
|
|
49
|
+
/** Whether the component has a main click handler. */
|
|
50
|
+
hasOnClick?: boolean;
|
|
51
|
+
/** Whether any part of the component is disabled. */
|
|
52
|
+
isAnyDisabled?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export type ChipPropsToOverride = 'disabledStateProps' | 'hasOnClick' | 'hasBeforeClick' | 'hasAfterClick' | 'before' | 'after' | 'keyDownProp' | 'tabIndexProp' | 'isAnyDisabled';
|
|
55
|
+
/**
|
|
56
|
+
* Component display name.
|
|
57
|
+
*/
|
|
58
|
+
export declare const COMPONENT_NAME = "Chip";
|
|
59
|
+
/**
|
|
60
|
+
* Component default class name and class prefix.
|
|
61
|
+
*/
|
|
62
|
+
export declare const CLASSNAME: LumxClassName<typeof COMPONENT_NAME>;
|
|
63
|
+
export declare const block: {
|
|
64
|
+
(additionalClasses: import("classnames/types").ClassValue[]): string;
|
|
65
|
+
(modifiers?: import("../../utils/classNames/bem/modifier").Modifier, additionalClasses?: import("classnames/types").ClassValue[]): string;
|
|
66
|
+
}, element: {
|
|
67
|
+
(elem: string, additionalClasses: import("classnames/types").ClassValue[]): string;
|
|
68
|
+
(elem: string, modifiers?: import("../../utils/classNames/bem/modifier").Modifier, additionalClasses?: import("classnames/types").ClassValue[]): string;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Component default props.
|
|
72
|
+
*/
|
|
73
|
+
export declare const DEFAULT_PROPS: Partial<ChipProps>;
|
|
74
|
+
/**
|
|
75
|
+
* Chip component.
|
|
76
|
+
*
|
|
77
|
+
* @param props Component props.
|
|
78
|
+
* @param ref Component ref.
|
|
79
|
+
* @return React element.
|
|
80
|
+
*/
|
|
81
|
+
export declare const Chip: (props: ChipProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Selector } from '../../types/Selector';
|
|
2
|
+
/**
|
|
3
|
+
* Options for setting up selection chip group event handlers.
|
|
4
|
+
* All option accessors are wrapped in getters so that React/Vue can provide
|
|
5
|
+
* reactive values without the core needing to know about reactivity systems.
|
|
6
|
+
*/
|
|
7
|
+
export interface SetupSelectionChipGroupEventsOptions<O> {
|
|
8
|
+
/** Getter for the current selected options array. */
|
|
9
|
+
getValue: () => O[] | undefined;
|
|
10
|
+
/** Getter for the option id selector. */
|
|
11
|
+
getOptionId: Selector<O>;
|
|
12
|
+
/** Callback when the option array changes. */
|
|
13
|
+
onChange: (newValue?: O[]) => void;
|
|
14
|
+
/** Getter for the chip group container element. */
|
|
15
|
+
getContainer: () => HTMLElement | null | undefined;
|
|
16
|
+
/** Getter for the associated input element (optional). */
|
|
17
|
+
getInput?: () => HTMLInputElement | null | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Attach delegated click and keydown event listeners on the chip group container,
|
|
21
|
+
* and optionally an input backspace handler.
|
|
22
|
+
*
|
|
23
|
+
* Uses addEventListener on the container element. Events bubble up from individual
|
|
24
|
+
* chips, so there is no need to re-attach listeners when chips change.
|
|
25
|
+
*
|
|
26
|
+
* @returns A cleanup function that removes all attached event listeners.
|
|
27
|
+
*/
|
|
28
|
+
export declare function setupSelectionChipGroupEvents<O>(options: SetupSelectionChipGroupEventsOptions<O>): () => void;
|
|
@@ -17,6 +17,11 @@ export interface ComboboxInputProps extends HasClassName, HasTheme {
|
|
|
17
17
|
toggleButtonProps?: Record<string, any>;
|
|
18
18
|
/** Toggle callback for the chevron button. */
|
|
19
19
|
handleToggle?(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Controls how the combobox filters options as the user types.
|
|
22
|
+
* When `'off'`, the input is rendered as `readOnly`.
|
|
23
|
+
*/
|
|
24
|
+
filter?: 'auto' | 'manual' | 'off';
|
|
20
25
|
}
|
|
21
26
|
/**
|
|
22
27
|
* Injected framework-specific components for ComboboxInput rendering.
|
|
@@ -2,14 +2,22 @@ import type { ComboboxCallbacks, ComboboxHandle } from './types';
|
|
|
2
2
|
/** Options for configuring the input-mode combobox controller. */
|
|
3
3
|
export interface SetupComboboxInputOptions extends ComboboxCallbacks {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Each registered `Combobox.Option` receives filter state updates and hides itself
|
|
7
|
-
* when it does not match the current input value.
|
|
5
|
+
* Controls how the combobox filters options as the user types.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - `'auto'` (default) — Options are automatically filtered client-side.
|
|
8
|
+
* - `'manual'` — Filtering is the consumer's responsibility.
|
|
9
|
+
* - `'off'` — Like `'manual'`, and `openOnFocus` defaults to `true`.
|
|
10
|
+
* The core template renders the input as `readOnly`.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
filter?: 'auto' | 'manual' | 'off';
|
|
13
|
+
/**
|
|
14
|
+
* When true, the combobox opens automatically when the input receives focus.
|
|
15
|
+
* When false (default, unless `filter` is `'off'`), the combobox only opens
|
|
16
|
+
* on click, typing, or keyboard navigation.
|
|
17
|
+
*
|
|
18
|
+
* @default false (true when filter is 'off')
|
|
19
|
+
*/
|
|
20
|
+
openOnFocus?: boolean;
|
|
13
21
|
}
|
|
14
22
|
/**
|
|
15
23
|
* Set up a combobox with an input trigger (autocomplete/filter pattern).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SetupOptions } from '../../../testing';
|
|
2
|
+
/**
|
|
3
|
+
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
4
|
+
*/
|
|
5
|
+
export declare const setup: (propsOverride: any | undefined, { render, ...options }: SetupOptions<any>) => {
|
|
6
|
+
props: any;
|
|
7
|
+
dialog: HTMLElement | null;
|
|
8
|
+
container: HTMLElement | null;
|
|
9
|
+
wrapper: Partial<import("../../../testing").SetupResult>;
|
|
10
|
+
};
|
|
11
|
+
declare const _default: (renderOptions: SetupOptions<any>) => void;
|
|
12
|
+
export default _default;
|
|
@@ -9,6 +9,8 @@ export interface InputLabelProps extends HasClassName, HasTheme {
|
|
|
9
9
|
children: JSXElement;
|
|
10
10
|
/** Native htmlFor property. */
|
|
11
11
|
htmlFor: string;
|
|
12
|
+
/** Native id property. */
|
|
13
|
+
id?: string;
|
|
12
14
|
/** Whether the component is required or not. */
|
|
13
15
|
isRequired?: boolean;
|
|
14
16
|
/** ref to the root element */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type EventCallback = (evt?: Event) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Sets up an IntersectionObserver on the given element.
|
|
4
|
+
* Calls `callback` when at least one observed entry is intersecting.
|
|
5
|
+
* Returns a cleanup function that unobserves the element.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setupInfiniteScrollObserver(element: Element, callback: EventCallback, options?: IntersectionObserverInit): () => void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like `querySelectorAll`, but also tests the root node itself.
|
|
3
|
+
*
|
|
4
|
+
* Yields the root element first (if it matches), then all matching descendants
|
|
5
|
+
* in document order. Being a generator, callers that only need the first match
|
|
6
|
+
* can break early without collecting the full list.
|
|
7
|
+
*
|
|
8
|
+
* @param node The starting DOM node.
|
|
9
|
+
* @param selector CSS selector to match against.
|
|
10
|
+
*/
|
|
11
|
+
export declare function querySelectorInclusive(node: Node, selector: string): Generator<HTMLElement>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track whether the container currently has focus.
|
|
3
|
+
*
|
|
4
|
+
* We can't rely on `document.activeElement` inside MutationObserver callbacks because
|
|
5
|
+
* the browser moves focus to `<body>` before they fire. focusout with `relatedTarget === null`
|
|
6
|
+
* (element removed from DOM) keeps the flag true so the observer can move focus to a fallback.
|
|
7
|
+
*/
|
|
8
|
+
export declare function trackContainerFocus(container: HTMLElement, signal: AbortSignal): {
|
|
9
|
+
readonly hasFocus: boolean;
|
|
10
|
+
reset(): void;
|
|
11
|
+
};
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import type { FocusNavigationCallbacks,
|
|
1
|
+
import type { FocusNavigationCallbacks, ListFocusNavigationController, ListNavigationOptions } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Create a focus navigation controller for a 1D list.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* This controller is **stateless** — it does not maintain an internal reference to
|
|
6
|
+
* the active item. Instead it reads the active item from the DOM each time via the
|
|
7
|
+
* `getActiveItem` callback provided in the options. This avoids any desync between
|
|
8
|
+
* the controller's internal state and the actual DOM.
|
|
9
|
+
*
|
|
10
|
+
* @param options List navigation options (container, itemSelector, direction, wrap, getActiveItem).
|
|
6
11
|
* @param callbacks Callbacks for focus state changes.
|
|
7
12
|
* @param signal AbortSignal for cleanup.
|
|
8
|
-
* @returns
|
|
13
|
+
* @returns ListFocusNavigationController instance.
|
|
9
14
|
*/
|
|
10
|
-
export declare function createListFocusNavigation(options: ListNavigationOptions, callbacks: FocusNavigationCallbacks, signal: AbortSignal):
|
|
15
|
+
export declare function createListFocusNavigation(options: ListNavigationOptions, callbacks: FocusNavigationCallbacks, signal: AbortSignal): ListFocusNavigationController;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions, ListNavigationOptions, NavigationOptions, } from './types';
|
|
1
|
+
export type { FocusNavigationCallbacks, FocusNavigationController, GridNavigationOptions, ListFocusNavigationController, ListNavigationOptions, NavigationOptions, } from './types';
|
|
2
2
|
export type { RovingTabIndexOptions } from './setupRovingTabIndex';
|
|
3
3
|
export { createListFocusNavigation } from './createListFocusNavigation';
|
|
4
4
|
export { createGridFocusNavigation } from './createGridFocusNavigation';
|
|
@@ -1,42 +1,37 @@
|
|
|
1
1
|
import type { FocusNavigationController } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Options for the roving tabindex setup.
|
|
4
|
-
*/
|
|
2
|
+
/** Options for {@link setupRovingTabIndex}. */
|
|
5
3
|
export interface RovingTabIndexOptions {
|
|
6
|
-
/**
|
|
4
|
+
/** Container element holding the focusable items. */
|
|
7
5
|
container: HTMLElement;
|
|
8
|
-
/** CSS selector
|
|
6
|
+
/** CSS selector identifying focusable items within the container. */
|
|
9
7
|
itemSelector: string;
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
* - `'horizontal'` (default): Left/Right navigate
|
|
13
|
-
* - `'vertical'`: Up/Down navigate
|
|
9
|
+
* Navigation axis — determines which arrow keys navigate.
|
|
10
|
+
* - `'horizontal'` (default): Left/Right navigate.
|
|
11
|
+
* - `'vertical'`: Up/Down navigate.
|
|
14
12
|
*/
|
|
15
13
|
direction?: 'horizontal' | 'vertical';
|
|
14
|
+
/** CSS selector matching disabled items (skipped during navigation). */
|
|
15
|
+
itemDisabledSelector?: string;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Attribute name indicating the selected item (e.g. `'aria-selected'`, `'aria-checked'`).
|
|
18
|
+
* When set, the roving tabindex will observe changes to this attribute and keep
|
|
19
|
+
* `tabindex="0"` in sync with the item whose attribute value is `"true"`.
|
|
20
|
+
* Default: `'aria-selected'`.
|
|
19
21
|
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
+
itemSelectedAttr?: string;
|
|
23
|
+
/** Called when an item receives focus via keyboard navigation. */
|
|
22
24
|
onItemFocused?: (item: HTMLElement) => void;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Set up the roving tabindex pattern on a container element.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
* -
|
|
29
|
-
* -
|
|
30
|
-
* -
|
|
31
|
-
*
|
|
32
|
-
* The consumer is responsible for setting the initial tabindex values on items
|
|
33
|
-
* (`tabindex="0"` on the active item, `tabindex="-1"` on the rest). On setup, the item
|
|
34
|
-
* with `tabindex="0"` is silently adopted as the initial active item.
|
|
35
|
-
*
|
|
36
|
-
* The setup is torn down when the provided `signal` is aborted.
|
|
29
|
+
* - Keyboard navigation (Arrow keys, Home, End)
|
|
30
|
+
* - tabindex management (`0` on active, `-1` on others)
|
|
31
|
+
* - Mount normalization (single-tabstop invariant)
|
|
32
|
+
* - DOM mutation handling via MutationObserver:
|
|
33
|
+
* removal recovery, insertion normalization, disabled-state changes
|
|
37
34
|
*
|
|
38
|
-
*
|
|
39
|
-
* @param signal AbortSignal for teardown.
|
|
40
|
-
* @returns The underlying {@link FocusNavigationController} for programmatic access.
|
|
35
|
+
* Torn down when `signal` is aborted.
|
|
41
36
|
*/
|
|
42
37
|
export declare function setupRovingTabIndex(options: RovingTabIndexOptions, signal: AbortSignal): FocusNavigationController;
|
|
@@ -33,11 +33,18 @@ export interface ListNavigationOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
itemDisabledSelector?: string;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Callback returning the currently active item from the DOM.
|
|
37
|
+
*
|
|
38
|
+
* The list navigation controller does **not** maintain an internal active-item
|
|
39
|
+
* reference; instead it delegates to this callback every time it needs the current
|
|
40
|
+
* active item (e.g. before navigating by offset).
|
|
41
|
+
*
|
|
42
|
+
* The callback is expected to read from the DOM (e.g. query `[tabindex="0"]` for
|
|
43
|
+
* roving tabindex, or read `aria-activedescendant` for combobox patterns).
|
|
44
|
+
*
|
|
45
|
+
* Default: `() => null` (no active item).
|
|
39
46
|
*/
|
|
40
|
-
|
|
47
|
+
getActiveItem?: () => HTMLElement | null;
|
|
41
48
|
}
|
|
42
49
|
/** Options for 2D grid navigation. */
|
|
43
50
|
export interface GridNavigationOptions {
|
|
@@ -100,3 +107,20 @@ export interface FocusNavigationController {
|
|
|
100
107
|
/** Navigate right (next item in horizontal list, next cell in grid). */
|
|
101
108
|
goRight(): boolean;
|
|
102
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Extended controller for 1D list navigation.
|
|
112
|
+
* Adds list-specific methods that don't apply to grid navigation.
|
|
113
|
+
*/
|
|
114
|
+
export interface ListFocusNavigationController extends FocusNavigationController {
|
|
115
|
+
/** Combined CSS selector matching enabled (non-disabled) items. */
|
|
116
|
+
readonly enabledItemSelector: string;
|
|
117
|
+
/**
|
|
118
|
+
* Find the nearest enabled item to the given anchor node.
|
|
119
|
+
* The anchor does not need to be an item or even an HTMLElement — it's used
|
|
120
|
+
* as a positional reference to find the closest enabled item in DOM order.
|
|
121
|
+
* Prefers the next item; falls back to the previous item.
|
|
122
|
+
*
|
|
123
|
+
* @returns The nearest enabled item, or null if no enabled items exist.
|
|
124
|
+
*/
|
|
125
|
+
findNearestEnabled(anchor: Node): HTMLElement | null;
|
|
126
|
+
}
|
package/lumx.css
CHANGED
|
@@ -5669,21 +5669,10 @@ table {
|
|
|
5669
5669
|
/* Selection Chip group
|
|
5670
5670
|
========================================================================== */
|
|
5671
5671
|
.lumx-selection-chip-group {
|
|
5672
|
-
|
|
5673
|
-
}
|
|
5674
|
-
.lumx-selection-chip-group:empty {
|
|
5675
|
-
display: none;
|
|
5676
|
-
}
|
|
5677
|
-
.lumx-selection-chip-group__item {
|
|
5678
|
-
display: flex;
|
|
5679
|
-
overflow: hidden;
|
|
5672
|
+
align-content: flex-start;
|
|
5680
5673
|
}
|
|
5681
|
-
.lumx-selection-chip-
|
|
5682
|
-
|
|
5683
|
-
}
|
|
5684
|
-
.lumx-selection-chip-group__chip .lumx-chip__label {
|
|
5685
|
-
overflow: hidden;
|
|
5686
|
-
text-overflow: ellipsis;
|
|
5674
|
+
.lumx-selection-chip-group, .lumx-selection-chip-group .lumx-chip, .lumx-selection-chip-group .lumx-chip__label {
|
|
5675
|
+
min-width: 0;
|
|
5687
5676
|
}
|
|
5688
5677
|
|
|
5689
5678
|
/* ==========================================================================
|
|
@@ -5701,6 +5690,22 @@ table {
|
|
|
5701
5690
|
}
|
|
5702
5691
|
.lumx-combobox-popover__scroll {
|
|
5703
5692
|
overflow-y: auto;
|
|
5693
|
+
scroll-snap-type: y proximity;
|
|
5694
|
+
}
|
|
5695
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
5696
|
+
.lumx-combobox-popover__scroll {
|
|
5697
|
+
scroll-behavior: smooth;
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
.lumx-combobox-popover__scroll::before {
|
|
5701
|
+
content: "";
|
|
5702
|
+
display: block;
|
|
5703
|
+
scroll-snap-align: start;
|
|
5704
|
+
}
|
|
5705
|
+
.lumx-combobox-popover__scroll::after {
|
|
5706
|
+
content: "";
|
|
5707
|
+
display: block;
|
|
5708
|
+
scroll-snap-align: end;
|
|
5704
5709
|
}
|
|
5705
5710
|
|
|
5706
5711
|
.lumx-combobox-popover:empty, .lumx-combobox-popover:not(:has(.lumx-combobox-option)):not(:has(.lumx-combobox-state)):not(:has(.lumx-combobox-option-skeleton)),
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@floating-ui/dom": "^1.7.5",
|
|
10
|
-
"@lumx/icons": "^4.
|
|
10
|
+
"@lumx/icons": "^4.11.0-alpha.1",
|
|
11
11
|
"classnames": "^2.3.2",
|
|
12
12
|
"focus-visible": "^5.0.2",
|
|
13
13
|
"lodash": "4.18.1",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"update-version-changelog": "yarn version-changelog ../../CHANGELOG.md"
|
|
70
70
|
},
|
|
71
71
|
"sideEffects": false,
|
|
72
|
-
"version": "4.
|
|
72
|
+
"version": "4.11.0-alpha.1",
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
75
75
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -92,5 +92,6 @@
|
|
|
92
92
|
"vite": "^7.3.1",
|
|
93
93
|
"vite-tsconfig-paths": "^5.1.4",
|
|
94
94
|
"vitest": "^4.0.18"
|
|
95
|
-
}
|
|
95
|
+
},
|
|
96
|
+
"stableVersion": "4.10.0"
|
|
96
97
|
}
|
|
@@ -191,23 +191,10 @@
|
|
|
191
191
|
========================================================================== */
|
|
192
192
|
|
|
193
193
|
.#{$lumx-base-prefix}-selection-chip-group {
|
|
194
|
-
|
|
194
|
+
align-content: flex-start;
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
&__item {
|
|
201
|
-
display: flex;
|
|
202
|
-
overflow: hidden;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
&__chip {
|
|
206
|
-
overflow: hidden;
|
|
207
|
-
|
|
208
|
-
.#{$lumx-base-prefix}-chip__label {
|
|
209
|
-
overflow: hidden;
|
|
210
|
-
text-overflow: ellipsis;
|
|
196
|
+
// Shrink chips in constrained space
|
|
197
|
+
&, & .#{$lumx-base-prefix}-chip, & .#{$lumx-base-prefix}-chip__label {
|
|
198
|
+
min-width: 0;
|
|
211
199
|
}
|
|
212
|
-
}
|
|
213
200
|
}
|
|
@@ -18,6 +18,21 @@
|
|
|
18
18
|
|
|
19
19
|
&__scroll {
|
|
20
20
|
overflow-y: auto;
|
|
21
|
+
scroll-snap-type: y proximity;
|
|
22
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
23
|
+
scroll-behavior: smooth;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&::before {
|
|
27
|
+
content: '';
|
|
28
|
+
display: block;
|
|
29
|
+
scroll-snap-align: start;
|
|
30
|
+
}
|
|
31
|
+
&::after {
|
|
32
|
+
content: '';
|
|
33
|
+
display: block;
|
|
34
|
+
scroll-snap-align: end;
|
|
35
|
+
}
|
|
21
36
|
}
|
|
22
37
|
}
|
|
23
38
|
|