@lumx/core 4.11.0-next.1 → 4.11.0-next.3
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/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/package.json +2 -2
|
@@ -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/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@floating-ui/dom": "^1.7.5",
|
|
10
|
-
"@lumx/icons": "^4.11.0-next.
|
|
10
|
+
"@lumx/icons": "^4.11.0-next.3",
|
|
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.11.0-next.
|
|
72
|
+
"version": "4.11.0-next.3",
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
75
75
|
"@testing-library/dom": "^10.4.1",
|