@momentum-design/components 0.134.9 → 0.134.10

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.
@@ -1,24 +1,43 @@
1
1
  import { Provider } from '../../models';
2
2
  import { ShortestDistanceWeights, SpatialNavigationContextValue, SpatialNavigationActionToKeyMap } from './spatialnavigationprovider.types';
3
3
  /**
4
- * This component manages focus using spatial navigation and provides context for child components.
5
- *
6
- * Place it at the root of the application.
4
+ * Spatial navigation focus manager
7
5
  *
8
6
  * [Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among
9
7
  * elements on a 2D plane, common on TVs and game consoles with remotes or gamepads.
10
8
  *
9
+ * It should have only one instance and it should placed at the root of the application.
10
+ *
11
11
  * ## Focus management
12
12
  *
13
13
  * The provider listens to keyboard events and moves focus among elements based on arrow key input.
14
14
  * You can influence or override this behavior.
15
15
  *
16
- * Note: The algorithm is distance-based, so the UI should be designed so focusable elements are
17
- * predictably reachable. Relative element positions should remain stable; responsive layouts can
18
- * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected
19
- * behavior in Storybook when resizing. See the "Limitations" section.
16
+ * ### Steps
20
17
  *
21
- * ### Automatic
18
+ * Spatial navigation goes trough the following steps after each keydown:
19
+ *
20
+ * 1. Handle `keydown` in the capture phase.
21
+ * When active element has `data-spatial-{direction}` attribute then prevent all component navigation and call the
22
+ * provider own `keydown` handler (see step 3).
23
+ * 2. Component own `keydown` handler executed (bubble phase) (e.g., list moves focus internally) it it was not
24
+ * prevented.
25
+ * 3. Spatial Navigation Provider's `keydown` handler executed (bubble phase)
26
+ * - If key event was not prevented in step 1. emit `navbeforeprocess` to check if any component want to handle
27
+ * the key event itself. If `navbeforeprocess` event is prevented, stop here.
28
+ * - If the component did not handle `keydown`, it calculate the next focusable item
29
+ * - if the active element has `data-spatial-{direction}` attribute, it will try to focus the element with the id.
30
+ * - Otherwise calculate the next focused item based on the direction and distances.
31
+ * - If there is no next item, it emits `navnotarget` event
32
+ * - Otherwise emit `navbeforefocus`,
33
+ * - If this event prevented, nothing happens
34
+ * - Otherwise the focus moves to the next element
35
+ *
36
+ * ### Determine next focus
37
+ *
38
+ * The provider use multiple ways to determine the next focused element. The order defined in the "Steps" section.
39
+ *
40
+ * #### Calculated focus
22
41
  *
23
42
  * By default, the next focus target is computed from element positions:
24
43
  *
@@ -35,9 +54,14 @@ import { ShortestDistanceWeights, SpatialNavigationContextValue, SpatialNavigati
35
54
  * Elements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they
36
55
  * are focusable.
37
56
  *
38
- * ### Overwrite next element
57
+ * Note: The algorithm is distance-based, so the UI should be designed to focusable elements are
58
+ * predictably reachable. Relative element positions should remain stable; responsive layouts can
59
+ * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected
60
+ * behavior in Storybook when resizing. See the "Limitations" section.
61
+ *
62
+ * #### Overwrite next element
39
63
  *
40
- * Override automatic navigation by adding one of these attributes to a focusable element:
64
+ * Override calculated navigation by adding one of these attributes to a focusable element:
41
65
  *
42
66
  * - `data-spatial-up`
43
67
  * - `data-spatial-down`
@@ -46,7 +70,7 @@ import { ShortestDistanceWeights, SpatialNavigationContextValue, SpatialNavigati
46
70
  *
47
71
  * Each attribute value must be the id of the element to focus when the corresponding key is pressed.
48
72
  *
49
- * ### Element internal navigation
73
+ * #### Element internal navigation
50
74
  *
51
75
  * Complex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves
52
76
  * focus internally on Down until the last item, after which Down should fall back to provider navigation.
@@ -78,15 +102,15 @@ import { ShortestDistanceWeights, SpatialNavigationContextValue, SpatialNavigati
78
102
  *
79
103
  * Supported data attributes:
80
104
  *
81
- * | Attribute | Value | Default | Description |
82
- * |------------------------|-------------|---------|-------------------------------------------------------------------------------------|
83
- * | `data-spatial-left` | element id | N/A | Focus this element when Left is pressed |
84
- * | `data-spatial-up` | element id | N/A | Focus this element when Up is pressed |
85
- * | `data-spatial-right` | element id | N/A | Focus this element when Right is pressed |
86
- * | `data-spatial-down` | element id | N/A | Focus this element when Down is pressed |
87
- * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |
88
- * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex="-1"`) |
89
- * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |
105
+ * | Attribute | Value | Default | Description |
106
+ * |--------------------------|---------------------------|---------|-------------------------------------------------------------------------------|
107
+ * | `data-spatial-left` | empty string / element id | N/A | Prevent native navigation in Left direction and focus element if exists |
108
+ * | `data-spatial-up` | empty string / element id | N/A | Prevent native navigation in Up direction and focus element if exists |
109
+ * | `data-spatial-right` | empty string / element id | N/A | Prevent native navigation in Right direction and focus element if exists |
110
+ * | `data-spatial-down` | empty string / element id | N/A | Prevent native navigation in Down direction and focus element if exists |
111
+ * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |
112
+ * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex="-1"`) |
113
+ * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |
90
114
  *
91
115
  * ## Event emitting order
92
116
  *
@@ -246,7 +270,11 @@ declare class SpatialNavigationProvider extends Provider<SpatialNavigationContex
246
270
  /**
247
271
  * List of navigation keys
248
272
  */
249
- get navigationKeys(): string[];
273
+ isNavigationKey(key: string): boolean;
274
+ /**
275
+ * List of navigation keys
276
+ */
277
+ isDirectionKey(key: string): boolean;
250
278
  constructor();
251
279
  connectedCallback(): void;
252
280
  disconnectedCallback(): void;
@@ -285,6 +313,15 @@ declare class SpatialNavigationProvider extends Provider<SpatialNavigationContex
285
313
  * @internal
286
314
  */
287
315
  private emitNavBeforeFocusEvent;
316
+ /**
317
+ * Check if the current active element has instruction to find the next focusable
318
+ * We look for the element in all the shadow DOMs in the composed path of the active element,
319
+ * so mdc component can use this feature as well.
320
+ *
321
+ * @param currentDomActiveElement - The current active element in the DOM
322
+ * @param direction - Direction
323
+ */
324
+ private getElementIdForDirectionAttr;
288
325
  /**
289
326
  * Focus the next element in the given direction.
290
327
  *
@@ -322,6 +359,7 @@ declare class SpatialNavigationProvider extends Provider<SpatialNavigationContex
322
359
  * @internal
323
360
  */
324
361
  private getActiveElement;
362
+ private handleKeyDownBefore;
325
363
  /**
326
364
  * Handle keydown event
327
365
  *
@@ -17,24 +17,43 @@ import { orderElementsByDistance } from './spatialnavigationprovider.utils';
17
17
  import { SpatialNavigationEvent } from './spatialnavigationprovider.events';
18
18
  // AI-Assisted
19
19
  /**
20
- * This component manages focus using spatial navigation and provides context for child components.
21
- *
22
- * Place it at the root of the application.
20
+ * Spatial navigation focus manager
23
21
  *
24
22
  * [Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among
25
23
  * elements on a 2D plane, common on TVs and game consoles with remotes or gamepads.
26
24
  *
25
+ * It should have only one instance and it should placed at the root of the application.
26
+ *
27
27
  * ## Focus management
28
28
  *
29
29
  * The provider listens to keyboard events and moves focus among elements based on arrow key input.
30
30
  * You can influence or override this behavior.
31
31
  *
32
- * Note: The algorithm is distance-based, so the UI should be designed so focusable elements are
33
- * predictably reachable. Relative element positions should remain stable; responsive layouts can
34
- * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected
35
- * behavior in Storybook when resizing. See the "Limitations" section.
32
+ * ### Steps
33
+ *
34
+ * Spatial navigation goes trough the following steps after each keydown:
36
35
  *
37
- * ### Automatic
36
+ * 1. Handle `keydown` in the capture phase.
37
+ * When active element has `data-spatial-{direction}` attribute then prevent all component navigation and call the
38
+ * provider own `keydown` handler (see step 3).
39
+ * 2. Component own `keydown` handler executed (bubble phase) (e.g., list moves focus internally) it it was not
40
+ * prevented.
41
+ * 3. Spatial Navigation Provider's `keydown` handler executed (bubble phase)
42
+ * - If key event was not prevented in step 1. emit `navbeforeprocess` to check if any component want to handle
43
+ * the key event itself. If `navbeforeprocess` event is prevented, stop here.
44
+ * - If the component did not handle `keydown`, it calculate the next focusable item
45
+ * - if the active element has `data-spatial-{direction}` attribute, it will try to focus the element with the id.
46
+ * - Otherwise calculate the next focused item based on the direction and distances.
47
+ * - If there is no next item, it emits `navnotarget` event
48
+ * - Otherwise emit `navbeforefocus`,
49
+ * - If this event prevented, nothing happens
50
+ * - Otherwise the focus moves to the next element
51
+ *
52
+ * ### Determine next focus
53
+ *
54
+ * The provider use multiple ways to determine the next focused element. The order defined in the "Steps" section.
55
+ *
56
+ * #### Calculated focus
38
57
  *
39
58
  * By default, the next focus target is computed from element positions:
40
59
  *
@@ -51,9 +70,14 @@ import { SpatialNavigationEvent } from './spatialnavigationprovider.events';
51
70
  * Elements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they
52
71
  * are focusable.
53
72
  *
54
- * ### Overwrite next element
73
+ * Note: The algorithm is distance-based, so the UI should be designed to focusable elements are
74
+ * predictably reachable. Relative element positions should remain stable; responsive layouts can
75
+ * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected
76
+ * behavior in Storybook when resizing. See the "Limitations" section.
77
+ *
78
+ * #### Overwrite next element
55
79
  *
56
- * Override automatic navigation by adding one of these attributes to a focusable element:
80
+ * Override calculated navigation by adding one of these attributes to a focusable element:
57
81
  *
58
82
  * - `data-spatial-up`
59
83
  * - `data-spatial-down`
@@ -62,7 +86,7 @@ import { SpatialNavigationEvent } from './spatialnavigationprovider.events';
62
86
  *
63
87
  * Each attribute value must be the id of the element to focus when the corresponding key is pressed.
64
88
  *
65
- * ### Element internal navigation
89
+ * #### Element internal navigation
66
90
  *
67
91
  * Complex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves
68
92
  * focus internally on Down until the last item, after which Down should fall back to provider navigation.
@@ -94,15 +118,15 @@ import { SpatialNavigationEvent } from './spatialnavigationprovider.events';
94
118
  *
95
119
  * Supported data attributes:
96
120
  *
97
- * | Attribute | Value | Default | Description |
98
- * |------------------------|-------------|---------|-------------------------------------------------------------------------------------|
99
- * | `data-spatial-left` | element id | N/A | Focus this element when Left is pressed |
100
- * | `data-spatial-up` | element id | N/A | Focus this element when Up is pressed |
101
- * | `data-spatial-right` | element id | N/A | Focus this element when Right is pressed |
102
- * | `data-spatial-down` | element id | N/A | Focus this element when Down is pressed |
103
- * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |
104
- * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex="-1"`) |
105
- * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |
121
+ * | Attribute | Value | Default | Description |
122
+ * |--------------------------|---------------------------|---------|-------------------------------------------------------------------------------|
123
+ * | `data-spatial-left` | empty string / element id | N/A | Prevent native navigation in Left direction and focus element if exists |
124
+ * | `data-spatial-up` | empty string / element id | N/A | Prevent native navigation in Up direction and focus element if exists |
125
+ * | `data-spatial-right` | empty string / element id | N/A | Prevent native navigation in Right direction and focus element if exists |
126
+ * | `data-spatial-down` | empty string / element id | N/A | Prevent native navigation in Down direction and focus element if exists |
127
+ * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |
128
+ * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex="-1"`) |
129
+ * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |
106
130
  *
107
131
  * ## Event emitting order
108
132
  *
@@ -220,7 +244,7 @@ class SpatialNavigationProvider extends Provider {
220
244
  /**
221
245
  * List of navigation keys
222
246
  */
223
- get navigationKeys() {
247
+ isNavigationKey(key) {
224
248
  return [
225
249
  this.navigationKeyMapping.up,
226
250
  this.navigationKeyMapping.down,
@@ -228,7 +252,18 @@ class SpatialNavigationProvider extends Provider {
228
252
  this.navigationKeyMapping.right,
229
253
  this.navigationKeyMapping.enter,
230
254
  this.navigationKeyMapping.escape,
231
- ];
255
+ ].includes(key);
256
+ }
257
+ /**
258
+ * List of navigation keys
259
+ */
260
+ isDirectionKey(key) {
261
+ return [
262
+ this.navigationKeyMapping.up,
263
+ this.navigationKeyMapping.down,
264
+ this.navigationKeyMapping.left,
265
+ this.navigationKeyMapping.right,
266
+ ].includes(key);
232
267
  }
233
268
  constructor() {
234
269
  // initialize the context by running the Provider constructor:
@@ -266,6 +301,21 @@ class SpatialNavigationProvider extends Provider {
266
301
  * @internal
267
302
  */
268
303
  this.initialHistoryLength = window.history.length;
304
+ this.handleKeyDownBefore = (evt) => {
305
+ if (evt.shiftKey || evt.ctrlKey || evt.altKey || evt.metaKey || !this.isNavigationKey(evt.key)) {
306
+ return;
307
+ }
308
+ const action = this.context.value.keyToActionMap[evt.key];
309
+ if (this.isDirectionKey(evt.key) &&
310
+ this.getElementIdForDirectionAttr(evt.target, action) !== undefined) {
311
+ // prevent native key events
312
+ evt.preventDefault();
313
+ // prevent MDC component key events
314
+ evt.stopImmediatePropagation();
315
+ // Need to call Spatial navigation key handler manually after all propagation stopped
316
+ this.handleKeyDown(evt);
317
+ }
318
+ };
269
319
  /**
270
320
  * Handle keydown event
271
321
  *
@@ -274,7 +324,7 @@ class SpatialNavigationProvider extends Provider {
274
324
  */
275
325
  this.handleKeyDown = (evt) => {
276
326
  var _a;
277
- if (evt.shiftKey || evt.ctrlKey || evt.altKey || evt.metaKey || !this.navigationKeys.includes(evt.key)) {
327
+ if (evt.shiftKey || evt.ctrlKey || evt.altKey || evt.metaKey || !this.isNavigationKey(evt.key)) {
278
328
  return;
279
329
  }
280
330
  const action = this.context.value.keyToActionMap[evt.key];
@@ -352,12 +402,14 @@ class SpatialNavigationProvider extends Provider {
352
402
  }
353
403
  connectedCallback() {
354
404
  super.connectedCallback();
405
+ document.addEventListener('keydown', this.handleKeyDownBefore, { capture: true });
355
406
  document.addEventListener('keydown', this.handleKeyDown);
356
407
  document.addEventListener('focus', this.handleFocus);
357
408
  this.initActiveElement();
358
409
  }
359
410
  disconnectedCallback() {
360
411
  super.disconnectedCallback();
412
+ document.removeEventListener('keydown', this.handleKeyDownBefore, { capture: true });
361
413
  document.removeEventListener('keydown', this.handleKeyDown);
362
414
  document.removeEventListener('focus', this.handleFocus);
363
415
  this.activeElement = undefined;
@@ -461,6 +513,19 @@ class SpatialNavigationProvider extends Provider {
461
513
  }
462
514
  return false;
463
515
  }
516
+ /**
517
+ * Check if the current active element has instruction to find the next focusable
518
+ * We look for the element in all the shadow DOMs in the composed path of the active element,
519
+ * so mdc component can use this feature as well.
520
+ *
521
+ * @param currentDomActiveElement - The current active element in the DOM
522
+ * @param direction - Direction
523
+ */
524
+ getElementIdForDirectionAttr(currentDomActiveElement, direction) {
525
+ var _a;
526
+ const dataAttrName = `data-spatial-${direction}`;
527
+ return (_a = currentDomActiveElement === null || currentDomActiveElement === void 0 ? void 0 : currentDomActiveElement.getAttribute(dataAttrName)) !== null && _a !== void 0 ? _a : undefined;
528
+ }
464
529
  /**
465
530
  * Focus the next element in the given direction.
466
531
  *
@@ -471,10 +536,6 @@ class SpatialNavigationProvider extends Provider {
471
536
  */
472
537
  focusNextInFocusableAria(elements, direction) {
473
538
  var _a, _b;
474
- // Do nothing when there is no focusable element
475
- if (elements.length === 0) {
476
- return undefined;
477
- }
478
539
  let currentActiveElement = this.getActiveElement();
479
540
  const currentDomActiveElement = getDomActiveElement();
480
541
  // Sync current active element if necessary
@@ -492,10 +553,6 @@ class SpatialNavigationProvider extends Provider {
492
553
  }
493
554
  this.setActiveElement(currentActiveElement);
494
555
  }
495
- // If DOM active element is not in the ficus area then move focus back to the current active element
496
- if (currentActiveElement !== getDomActiveElement()) {
497
- return currentActiveElement;
498
- }
499
556
  // Check if the current active element has instruction to find the next focusable
500
557
  // We look for the element in all the shadow DOMs in the composed path of the active element,
501
558
  // so mdc component can use this feature as well.
@@ -510,6 +567,14 @@ class SpatialNavigationProvider extends Provider {
510
567
  }
511
568
  }
512
569
  }
570
+ // If DOM active element is not in the focus area then move focus back to the current active element
571
+ if (currentActiveElement !== getDomActiveElement()) {
572
+ return currentActiveElement;
573
+ }
574
+ // Do nothing when there is no focusable element
575
+ if (elements.length === 0) {
576
+ return undefined;
577
+ }
513
578
  // Find the closest element in the given direction
514
579
  const results = orderElementsByDistance(currentActiveElement, elements, direction, this.distanceCalculationWeights);
515
580
  return (_b = results[0]) === null || _b === void 0 ? void 0 : _b.candidate;
@@ -173,8 +173,8 @@
173
173
  "attribute": "disabled",
174
174
  "reflects": true,
175
175
  "inheritedFrom": {
176
- "name": "DisabledMixin",
177
- "module": "utils/mixins/DisabledMixin.js"
176
+ "name": "AccordionButton",
177
+ "module": "components/accordionbutton/accordionbutton.component.js"
178
178
  }
179
179
  },
180
180
  {
@@ -493,8 +493,8 @@
493
493
  "default": "undefined",
494
494
  "fieldName": "disabled",
495
495
  "inheritedFrom": {
496
- "name": "DisabledMixin",
497
- "module": "src/utils/mixins/DisabledMixin.ts"
496
+ "name": "AccordionButton",
497
+ "module": "src/components/accordionbutton/accordionbutton.component.ts"
498
498
  }
499
499
  },
500
500
  {
@@ -44925,7 +44925,7 @@
44925
44925
  "declarations": [
44926
44926
  {
44927
44927
  "kind": "class",
44928
- "description": "This component manages focus using spatial navigation and provides context for child components.\n\nPlace it at the root of the application.\n\n[Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among\nelements on a 2D plane, common on TVs and game consoles with remotes or gamepads.\n\n## Focus management\n\nThe provider listens to keyboard events and moves focus among elements based on arrow key input.\nYou can influence or override this behavior.\n\nNote: The algorithm is distance-based, so the UI should be designed so focusable elements are\npredictably reachable. Relative element positions should remain stable; responsive layouts can\nmake navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected\nbehavior in Storybook when resizing. See the \"Limitations\" section.\n\n### Automatic\n\nBy default, the next focus target is computed from element positions:\n\n1. Find the nearest focus area (scrollable container or active focus trap) relative to the current element.\n2. Collect focusable elements in that area.\n3. Compute distances from the current element to candidates using the W3C \"find the shortest\n distance\" algorithm: https://www.w3.org/TR/css-nav-1/#find-the-shortest-distance\n4. If no candidates are found, repeat from step 1, skipping areas already checked.\n5. Focus the closest candidate.\n\nElements with `data-spatial-focusable` are treated as focusable even if they would otherwise not be\n(e.g., `tabindex=\"-1\"`).\n\nElements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they\nare focusable.\n\n### Overwrite next element\n\nOverride automatic navigation by adding one of these attributes to a focusable element:\n\n- `data-spatial-up`\n- `data-spatial-down`\n- `data-spatial-left`\n- `data-spatial-right`\n\nEach attribute value must be the id of the element to focus when the corresponding key is pressed.\n\n### Element internal navigation\n\nComplex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves\nfocus internally on Down until the last item, after which Down should fall back to provider navigation.\n\nTo prevent the provider from handling a key, listen to `navbeforeprocess` and call `event.preventDefault()`.\nThis event fires after the component handles `keydown`.\n\n### Cancel focus change\n\nBefore focusing a computed target, the provider dispatches `navbeforefocus` on the current element. Call\n`event.preventDefault()` on this event to cancel the focus change.\n\n## Enter action\n\nPressing Enter triggers `.click()` on the currently focused element.\n\nYou can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n\n## Escape/Back action\n\nPressing Escape tries to find a focusable element with `data-spatial-go-back` and clicks it. If none exists,\nthe provider calls `history.back()`.\n\nYou can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n\nYou can also intercept the back click by listening to `navback` and calling `event.preventDefault()`.\n\n## Control data attributes\n\nSupported data attributes:\n\n| Attribute | Value | Default | Description |\n|------------------------|-------------|---------|-------------------------------------------------------------------------------------|\n| `data-spatial-left` | element id | N/A | Focus this element when Left is pressed |\n| `data-spatial-up` | element id | N/A | Focus this element when Up is pressed |\n| `data-spatial-right` | element id | N/A | Focus this element when Right is pressed |\n| `data-spatial-down` | element id | N/A | Focus this element when Down is pressed |\n| `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |\n| `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex=\"-1\"`) |\n| `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |\n\n## Event emitting order\n\nOn a navigation key press, events fire in this order:\n\n1. `navbeforeprocess` on the currently focused element.\n2. If not prevented:\na. Arrow keys: `navbeforefocus` on the currently focused element.\nb. Enter: `.click()` on the currently focused element.\nc. Escape/Back: `navback` on the provider, then `.click()` on the go-back element or `history.back()`.\n3. If no target is found in the requested direction: `navnotarget` on the provider.\n\n## Handle complex components\n\n### Generic components\n\nComponents that handle navigation internally should prevent the provider from acting. Handle `navbeforeprocess`\nand call `event.preventDefault()` for keys you process yourself.\n\n### Form inputs\n\nNative inputs often submit on Enter, which is not desirable here. Enter should toggle or activate the control\n(e.g., check/uncheck). Provide a dedicated submit button users can navigate to and press Enter on.\n\n### Utilities for complex components\n\n#### KeyToActionMixin\n\nMaps key events to action names. Call `getActionForKeyEvent` to get the action for a keyboard event. Also provides\n`getKeyboardNavMode` to check whether navigation is spatial or default.\n\n#### KeyDownHandledMixin\n\nNotify the provider when a component handled `keydown` internally. Call `keyDownEventHandled` whenever you process\nkeydown yourself.\n\n## Platform specific behaviors\n\nConsider remote/gamepad constraints. Often focus alone is not enough and users press Enter to \"enter\" an interactive mode:\n- Select: Enter opens options rather than arrow keys opening a popover.\n- Text inputs: see the next section.\n- Slider: Enter to start adjusting, arrow keys to change value, Enter/Escape to stop.\n\n### Text inputs\n\nOn TV-like platforms without physical keyboards, Enter/focus on an input should open a virtual keyboard instead of submitting\nthe form. Users must close the keyboard (Escape) to continue spatial navigation.\n\nIf navigation keys are mapped to letters (e.g., `w/a/s/d`), they should navigate, not change input values. Inputs should\nbe edited via the virtual keyboard.\n\nNote: Stories do not emulate virtual keyboards, so letter-based navigation may change input values in Storybook.\n\n## Debugging\n\n### Storybook toolbar\n\nEnable \"Spatial navigation\" in the toolbar. Key mapping:\n- Up - ArrowUp\n- Left - ArrowLeft\n- Down - ArrowDown\n- Right - ArrowRight\n- Enter - Enter\n- Escape - Escape\n\nWith wrapper: wraps the component in a 3x3 grid with surrounding buttons for testing.\nWithout wrapper: renders the component alone.\n\n### Visual debugger\n\nWith spatial navigation enabled, press Shift + navigation key to visualize calculations:\n\n- Star: next active element\n- `#{number}`: candidate order by distance\n- `D: {distance}`: computed distance\n\n## Limitations\n\n### Completeness\n\nThe algorithm cannot guarantee reachability to all elements using the four directions. Some components can be isolated.\n\nWorkarounds:\n- Use data attributes to explicitly link navigation targets.\n- Arrange DOM to improve spatial consistency:\n- Group focusable elements using dedicated components (lists, menus, etc.).\n- Avoid complex grid-like layouts with variable-sized items.\n- Avoid overlap along horizontal or vertical axes.\n- Avoid nested focusable elements where possible.\n- Tune algorithm weights to match your UI layout.\n\n### Scrollable containers\n\nContent scrolling is not supported yet, e.g.:\n- Focused element larger than the viewport.\n- Scrollable content without interactive children.\n\n### Nested providers\n\nOnly one provider instance is supported in the application at a time.",
44928
+ "description": "Spatial navigation focus manager\n\n[Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among\nelements on a 2D plane, common on TVs and game consoles with remotes or gamepads.\n\nIt should have only one instance and it should placed at the root of the application.\n\n## Focus management\n\nThe provider listens to keyboard events and moves focus among elements based on arrow key input.\nYou can influence or override this behavior.\n\n### Steps\n\nSpatial navigation goes trough the following steps after each keydown:\n\n1. Handle `keydown` in the capture phase.\n When active element has `data-spatial-{direction}` attribute then prevent all component navigation and call the\n provider own `keydown` handler (see step 3).\n2. Component own `keydown` handler executed (bubble phase) (e.g., list moves focus internally) it it was not\n prevented.\n3. Spatial Navigation Provider's `keydown` handler executed (bubble phase)\n - If key event was not prevented in step 1. emit `navbeforeprocess` to check if any component want to handle\n the key event itself. If `navbeforeprocess` event is prevented, stop here.\n - If the component did not handle `keydown`, it calculate the next focusable item\n - if the active element has `data-spatial-{direction}` attribute, it will try to focus the element with the id.\n - Otherwise calculate the next focused item based on the direction and distances.\n - If there is no next item, it emits `navnotarget` event\n - Otherwise emit `navbeforefocus`,\n - If this event prevented, nothing happens\n - Otherwise the focus moves to the next element\n\n### Determine next focus\n\nThe provider use multiple ways to determine the next focused element. The order defined in the \"Steps\" section.\n\n#### Calculated focus\n\nBy default, the next focus target is computed from element positions:\n\n1. Find the nearest focus area (scrollable container or active focus trap) relative to the current element.\n2. Collect focusable elements in that area.\n3. Compute distances from the current element to candidates using the W3C \"find the shortest\n distance\" algorithm: https://www.w3.org/TR/css-nav-1/#find-the-shortest-distance\n4. If no candidates are found, repeat from step 1, skipping areas already checked.\n5. Focus the closest candidate.\n\nElements with `data-spatial-focusable` are treated as focusable even if they would otherwise not be\n(e.g., `tabindex=\"-1\"`).\n\nElements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they\nare focusable.\n\nNote: The algorithm is distance-based, so the UI should be designed to focusable elements are\npredictably reachable. Relative element positions should remain stable; responsive layouts can\nmake navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected\nbehavior in Storybook when resizing. See the \"Limitations\" section.\n\n#### Overwrite next element\n\nOverride calculated navigation by adding one of these attributes to a focusable element:\n\n- `data-spatial-up`\n- `data-spatial-down`\n- `data-spatial-left`\n- `data-spatial-right`\n\nEach attribute value must be the id of the element to focus when the corresponding key is pressed.\n\n#### Element internal navigation\n\nComplex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves\nfocus internally on Down until the last item, after which Down should fall back to provider navigation.\n\nTo prevent the provider from handling a key, listen to `navbeforeprocess` and call `event.preventDefault()`.\nThis event fires after the component handles `keydown`.\n\n### Cancel focus change\n\nBefore focusing a computed target, the provider dispatches `navbeforefocus` on the current element. Call\n`event.preventDefault()` on this event to cancel the focus change.\n\n## Enter action\n\nPressing Enter triggers `.click()` on the currently focused element.\n\nYou can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n\n## Escape/Back action\n\nPressing Escape tries to find a focusable element with `data-spatial-go-back` and clicks it. If none exists,\nthe provider calls `history.back()`.\n\nYou can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n\nYou can also intercept the back click by listening to `navback` and calling `event.preventDefault()`.\n\n## Control data attributes\n\nSupported data attributes:\n\n| Attribute | Value | Default | Description |\n|--------------------------|---------------------------|---------|-------------------------------------------------------------------------------|\n| `data-spatial-left` | empty string / element id | N/A | Prevent native navigation in Left direction and focus element if exists |\n| `data-spatial-up` | empty string / element id | N/A | Prevent native navigation in Up direction and focus element if exists |\n| `data-spatial-right` | empty string / element id | N/A | Prevent native navigation in Right direction and focus element if exists |\n| `data-spatial-down` | empty string / element id | N/A | Prevent native navigation in Down direction and focus element if exists |\n| `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |\n| `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex=\"-1\"`) |\n| `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |\n\n## Event emitting order\n\nOn a navigation key press, events fire in this order:\n\n1. `navbeforeprocess` on the currently focused element.\n2. If not prevented:\na. Arrow keys: `navbeforefocus` on the currently focused element.\nb. Enter: `.click()` on the currently focused element.\nc. Escape/Back: `navback` on the provider, then `.click()` on the go-back element or `history.back()`.\n3. If no target is found in the requested direction: `navnotarget` on the provider.\n\n## Handle complex components\n\n### Generic components\n\nComponents that handle navigation internally should prevent the provider from acting. Handle `navbeforeprocess`\nand call `event.preventDefault()` for keys you process yourself.\n\n### Form inputs\n\nNative inputs often submit on Enter, which is not desirable here. Enter should toggle or activate the control\n(e.g., check/uncheck). Provide a dedicated submit button users can navigate to and press Enter on.\n\n### Utilities for complex components\n\n#### KeyToActionMixin\n\nMaps key events to action names. Call `getActionForKeyEvent` to get the action for a keyboard event. Also provides\n`getKeyboardNavMode` to check whether navigation is spatial or default.\n\n#### KeyDownHandledMixin\n\nNotify the provider when a component handled `keydown` internally. Call `keyDownEventHandled` whenever you process\nkeydown yourself.\n\n## Platform specific behaviors\n\nConsider remote/gamepad constraints. Often focus alone is not enough and users press Enter to \"enter\" an interactive mode:\n- Select: Enter opens options rather than arrow keys opening a popover.\n- Text inputs: see the next section.\n- Slider: Enter to start adjusting, arrow keys to change value, Enter/Escape to stop.\n\n### Text inputs\n\nOn TV-like platforms without physical keyboards, Enter/focus on an input should open a virtual keyboard instead of submitting\nthe form. Users must close the keyboard (Escape) to continue spatial navigation.\n\nIf navigation keys are mapped to letters (e.g., `w/a/s/d`), they should navigate, not change input values. Inputs should\nbe edited via the virtual keyboard.\n\nNote: Stories do not emulate virtual keyboards, so letter-based navigation may change input values in Storybook.\n\n## Debugging\n\n### Storybook toolbar\n\nEnable \"Spatial navigation\" in the toolbar. Key mapping:\n- Up - ArrowUp\n- Left - ArrowLeft\n- Down - ArrowDown\n- Right - ArrowRight\n- Enter - Enter\n- Escape - Escape\n\nWith wrapper: wraps the component in a 3x3 grid with surrounding buttons for testing.\nWithout wrapper: renders the component alone.\n\n### Visual debugger\n\nWith spatial navigation enabled, press Shift + navigation key to visualize calculations:\n\n- Star: next active element\n- `#{number}`: candidate order by distance\n- `D: {distance}`: computed distance\n\n## Limitations\n\n### Completeness\n\nThe algorithm cannot guarantee reachability to all elements using the four directions. Some components can be isolated.\n\nWorkarounds:\n- Use data attributes to explicitly link navigation targets.\n- Arrange DOM to improve spatial consistency:\n- Group focusable elements using dedicated components (lists, menus, etc.).\n- Avoid complex grid-like layouts with variable-sized items.\n- Avoid overlap along horizontal or vertical axes.\n- Avoid nested focusable elements where possible.\n- Tune algorithm weights to match your UI layout.\n\n### Scrollable containers\n\nContent scrolling is not supported yet, e.g.:\n- Focused element larger than the viewport.\n- Scrollable content without interactive children.\n\n### Nested providers\n\nOnly one provider instance is supported in the application at a time.",
44929
44929
  "name": "SpatialNavigationProvider",
44930
44930
  "members": [
44931
44931
  {
@@ -44950,6 +44950,33 @@
44950
44950
  "privacy": "public",
44951
44951
  "description": "Weights used in the distance calculation algorithm"
44952
44952
  },
44953
+ {
44954
+ "kind": "method",
44955
+ "name": "getElementIdForDirectionAttr",
44956
+ "privacy": "private",
44957
+ "return": {
44958
+ "type": {
44959
+ "text": "string | undefined"
44960
+ }
44961
+ },
44962
+ "parameters": [
44963
+ {
44964
+ "name": "currentDomActiveElement",
44965
+ "type": {
44966
+ "text": "HTMLElement | null"
44967
+ },
44968
+ "description": "The current active element in the DOM"
44969
+ },
44970
+ {
44971
+ "name": "direction",
44972
+ "type": {
44973
+ "text": "Direction"
44974
+ },
44975
+ "description": "Direction"
44976
+ }
44977
+ ],
44978
+ "description": "Check if the current active element has instruction to find the next focusable\nWe look for the element in all the shadow DOMs in the composed path of the active element,\nso mdc component can use this feature as well."
44979
+ },
44953
44980
  {
44954
44981
  "kind": "method",
44955
44982
  "name": "goBack",
@@ -44961,6 +44988,47 @@
44961
44988
  },
44962
44989
  "description": "Handle back action\n\nEither trigger click on goBack element if any\notherwise call default go back handler"
44963
44990
  },
44991
+ {
44992
+ "kind": "field",
44993
+ "name": "handleKeyDownBefore",
44994
+ "privacy": "private"
44995
+ },
44996
+ {
44997
+ "kind": "method",
44998
+ "name": "isDirectionKey",
44999
+ "return": {
45000
+ "type": {
45001
+ "text": "boolean"
45002
+ }
45003
+ },
45004
+ "parameters": [
45005
+ {
45006
+ "name": "key",
45007
+ "type": {
45008
+ "text": "string"
45009
+ }
45010
+ }
45011
+ ],
45012
+ "description": "List of navigation keys"
45013
+ },
45014
+ {
45015
+ "kind": "method",
45016
+ "name": "isNavigationKey",
45017
+ "return": {
45018
+ "type": {
45019
+ "text": "boolean"
45020
+ }
45021
+ },
45022
+ "parameters": [
45023
+ {
45024
+ "name": "key",
45025
+ "type": {
45026
+ "text": "string"
45027
+ }
45028
+ }
45029
+ ],
45030
+ "description": "List of navigation keys"
45031
+ },
44964
45032
  {
44965
45033
  "kind": "field",
44966
45034
  "name": "navigationKeyMapping",
@@ -44970,12 +45038,6 @@
44970
45038
  "privacy": "public",
44971
45039
  "description": "Key mapping for spatial navigation actions\nIt is possible to map left/right/up/down/enter/escape actions to any valid key"
44972
45040
  },
44973
- {
44974
- "kind": "field",
44975
- "name": "navigationKeys",
44976
- "description": "List of navigation keys",
44977
- "readonly": true
44978
- },
44979
45041
  {
44980
45042
  "kind": "method",
44981
45043
  "name": "pressEnter",
@@ -45025,7 +45087,7 @@
45025
45087
  "module": "/src/models"
45026
45088
  },
45027
45089
  "tagName": "mdc-spatialnavigationprovider",
45028
- "jsDoc": "/**\n * This component manages focus using spatial navigation and provides context for child components.\n *\n * Place it at the root of the application.\n *\n * [Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among\n * elements on a 2D plane, common on TVs and game consoles with remotes or gamepads.\n *\n * ## Focus management\n *\n * The provider listens to keyboard events and moves focus among elements based on arrow key input.\n * You can influence or override this behavior.\n *\n * Note: The algorithm is distance-based, so the UI should be designed so focusable elements are\n * predictably reachable. Relative element positions should remain stable; responsive layouts can\n * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected\n * behavior in Storybook when resizing. See the \"Limitations\" section.\n *\n * ### Automatic\n *\n * By default, the next focus target is computed from element positions:\n *\n * 1. Find the nearest focus area (scrollable container or active focus trap) relative to the current element.\n * 2. Collect focusable elements in that area.\n * 3. Compute distances from the current element to candidates using the W3C \"find the shortest\n * distance\" algorithm: https://www.w3.org/TR/css-nav-1/#find-the-shortest-distance\n * 4. If no candidates are found, repeat from step 1, skipping areas already checked.\n * 5. Focus the closest candidate.\n *\n * Elements with `data-spatial-focusable` are treated as focusable even if they would otherwise not be\n * (e.g., `tabindex=\"-1\"`).\n *\n * Elements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they\n * are focusable.\n *\n * ### Overwrite next element\n *\n * Override automatic navigation by adding one of these attributes to a focusable element:\n *\n * - `data-spatial-up`\n * - `data-spatial-down`\n * - `data-spatial-left`\n * - `data-spatial-right`\n *\n * Each attribute value must be the id of the element to focus when the corresponding key is pressed.\n *\n * ### Element internal navigation\n *\n * Complex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves\n * focus internally on Down until the last item, after which Down should fall back to provider navigation.\n *\n * To prevent the provider from handling a key, listen to `navbeforeprocess` and call `event.preventDefault()`.\n * This event fires after the component handles `keydown`.\n *\n * ### Cancel focus change\n *\n * Before focusing a computed target, the provider dispatches `navbeforefocus` on the current element. Call\n * `event.preventDefault()` on this event to cancel the focus change.\n *\n * ## Enter action\n *\n * Pressing Enter triggers `.click()` on the currently focused element.\n *\n * You can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n *\n * ## Escape/Back action\n *\n * Pressing Escape tries to find a focusable element with `data-spatial-go-back` and clicks it. If none exists,\n * the provider calls `history.back()`.\n *\n * You can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n *\n * You can also intercept the back click by listening to `navback` and calling `event.preventDefault()`.\n *\n * ## Control data attributes\n *\n * Supported data attributes:\n *\n * | Attribute | Value | Default | Description |\n * |------------------------|-------------|---------|-------------------------------------------------------------------------------------|\n * | `data-spatial-left` | element id | N/A | Focus this element when Left is pressed |\n * | `data-spatial-up` | element id | N/A | Focus this element when Up is pressed |\n * | `data-spatial-right` | element id | N/A | Focus this element when Right is pressed |\n * | `data-spatial-down` | element id | N/A | Focus this element when Down is pressed |\n * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |\n * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex=\"-1\"`) |\n * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |\n *\n * ## Event emitting order\n *\n * On a navigation key press, events fire in this order:\n *\n * 1. `navbeforeprocess` on the currently focused element.\n * 2. If not prevented:\n * a. Arrow keys: `navbeforefocus` on the currently focused element.\n * b. Enter: `.click()` on the currently focused element.\n * c. Escape/Back: `navback` on the provider, then `.click()` on the go-back element or `history.back()`.\n * 3. If no target is found in the requested direction: `navnotarget` on the provider.\n *\n * ## Handle complex components\n *\n * ### Generic components\n *\n * Components that handle navigation internally should prevent the provider from acting. Handle `navbeforeprocess`\n * and call `event.preventDefault()` for keys you process yourself.\n *\n * ### Form inputs\n *\n * Native inputs often submit on Enter, which is not desirable here. Enter should toggle or activate the control\n * (e.g., check/uncheck). Provide a dedicated submit button users can navigate to and press Enter on.\n *\n * ### Utilities for complex components\n *\n * #### KeyToActionMixin\n *\n * Maps key events to action names. Call `getActionForKeyEvent` to get the action for a keyboard event. Also provides\n * `getKeyboardNavMode` to check whether navigation is spatial or default.\n *\n * #### KeyDownHandledMixin\n *\n * Notify the provider when a component handled `keydown` internally. Call `keyDownEventHandled` whenever you process\n * keydown yourself.\n *\n * ## Platform specific behaviors\n *\n * Consider remote/gamepad constraints. Often focus alone is not enough and users press Enter to \"enter\" an interactive mode:\n * - Select: Enter opens options rather than arrow keys opening a popover.\n * - Text inputs: see the next section.\n * - Slider: Enter to start adjusting, arrow keys to change value, Enter/Escape to stop.\n *\n * ### Text inputs\n *\n * On TV-like platforms without physical keyboards, Enter/focus on an input should open a virtual keyboard instead of submitting\n * the form. Users must close the keyboard (Escape) to continue spatial navigation.\n *\n * If navigation keys are mapped to letters (e.g., `w/a/s/d`), they should navigate, not change input values. Inputs should\n * be edited via the virtual keyboard.\n *\n * Note: Stories do not emulate virtual keyboards, so letter-based navigation may change input values in Storybook.\n *\n * ## Debugging\n *\n * ### Storybook toolbar\n *\n * Enable \"Spatial navigation\" in the toolbar. Key mapping:\n * - Up - ArrowUp\n * - Left - ArrowLeft\n * - Down - ArrowDown\n * - Right - ArrowRight\n * - Enter - Enter\n * - Escape - Escape\n *\n * With wrapper: wraps the component in a 3x3 grid with surrounding buttons for testing.\n * Without wrapper: renders the component alone.\n *\n * ### Visual debugger\n *\n * With spatial navigation enabled, press Shift + navigation key to visualize calculations:\n *\n * - Star: next active element\n * - `#{number}`: candidate order by distance\n * - `D: {distance}`: computed distance\n *\n * ## Limitations\n *\n * ### Completeness\n *\n * The algorithm cannot guarantee reachability to all elements using the four directions. Some components can be isolated.\n *\n * Workarounds:\n * - Use data attributes to explicitly link navigation targets.\n * - Arrange DOM to improve spatial consistency:\n * - Group focusable elements using dedicated components (lists, menus, etc.).\n * - Avoid complex grid-like layouts with variable-sized items.\n * - Avoid overlap along horizontal or vertical axes.\n * - Avoid nested focusable elements where possible.\n * - Tune algorithm weights to match your UI layout.\n *\n * ### Scrollable containers\n *\n * Content scrolling is not supported yet, e.g.:\n * - Focused element larger than the viewport.\n * - Scrollable content without interactive children.\n *\n * ### Nested providers\n *\n * Only one provider instance is supported in the application at a time.\n *\n * @event navbeforeprocess - (React: onNavBeforeProcess) This event dispatched before spatial navigation process any key event.\n * It can be canceled to prevent any action from spatial navigation, e.g.: back, click or calculating the next candidate.\n * @event navbeforefocus - (React: onNavBeforeFocus) This event is dispatched before the focus is changing to the next element.\n * It can be canceled to prevent the focus change. @see https://www.w3.org/TR/css-nav-1/#event-type-navbeforefocus\n * @event navback - (React: onNavBack) This event dispatched a back navigation triggered by the user.\n * The event's detail contains the goBackElement if any. It is cancelable to prevent click\n * action on the goBackElement.\n * @event navnotarget - (React: onNavNoTarget) This event is dispatched when there is no target to focus in the current focus area and\n * in the given direction .\n *\n * @tagname mdc-spatialnavigationprovider\n */",
45090
+ "jsDoc": "/**\n * Spatial navigation focus manager\n *\n * [Spatial navigation](https://en.wikipedia.org/wiki/Spatial_navigation) lets users move focus among\n * elements on a 2D plane, common on TVs and game consoles with remotes or gamepads.\n *\n * It should have only one instance and it should placed at the root of the application.\n *\n * ## Focus management\n *\n * The provider listens to keyboard events and moves focus among elements based on arrow key input.\n * You can influence or override this behavior.\n *\n * ### Steps\n *\n * Spatial navigation goes trough the following steps after each keydown:\n *\n * 1. Handle `keydown` in the capture phase.\n * When active element has `data-spatial-{direction}` attribute then prevent all component navigation and call the\n * provider own `keydown` handler (see step 3).\n * 2. Component own `keydown` handler executed (bubble phase) (e.g., list moves focus internally) it it was not\n * prevented.\n * 3. Spatial Navigation Provider's `keydown` handler executed (bubble phase)\n * - If key event was not prevented in step 1. emit `navbeforeprocess` to check if any component want to handle\n * the key event itself. If `navbeforeprocess` event is prevented, stop here.\n * - If the component did not handle `keydown`, it calculate the next focusable item\n * - if the active element has `data-spatial-{direction}` attribute, it will try to focus the element with the id.\n * - Otherwise calculate the next focused item based on the direction and distances.\n * - If there is no next item, it emits `navnotarget` event\n * - Otherwise emit `navbeforefocus`,\n * - If this event prevented, nothing happens\n * - Otherwise the focus moves to the next element\n *\n * ### Determine next focus\n *\n * The provider use multiple ways to determine the next focused element. The order defined in the \"Steps\" section.\n *\n * #### Calculated focus\n *\n * By default, the next focus target is computed from element positions:\n *\n * 1. Find the nearest focus area (scrollable container or active focus trap) relative to the current element.\n * 2. Collect focusable elements in that area.\n * 3. Compute distances from the current element to candidates using the W3C \"find the shortest\n * distance\" algorithm: https://www.w3.org/TR/css-nav-1/#find-the-shortest-distance\n * 4. If no candidates are found, repeat from step 1, skipping areas already checked.\n * 5. Focus the closest candidate.\n *\n * Elements with `data-spatial-focusable` are treated as focusable even if they would otherwise not be\n * (e.g., `tabindex=\"-1\"`).\n *\n * Elements with `data-spatial-exclude` are excluded (with its subtree) from the navigation, even if they\n * are focusable.\n *\n * Note: The algorithm is distance-based, so the UI should be designed to focusable elements are\n * predictably reachable. Relative element positions should remain stable; responsive layouts can\n * make navigation unpredictable. This is less of an issue on fixed-size TV UIs but can show unexpected\n * behavior in Storybook when resizing. See the \"Limitations\" section.\n *\n * #### Overwrite next element\n *\n * Override calculated navigation by adding one of these attributes to a focusable element:\n *\n * - `data-spatial-up`\n * - `data-spatial-down`\n * - `data-spatial-left`\n * - `data-spatial-right`\n *\n * Each attribute value must be the id of the element to focus when the corresponding key is pressed.\n *\n * #### Element internal navigation\n *\n * Complex components (List, Combobox, Tree, etc.) may handle their own navigation. For example, a List moves\n * focus internally on Down until the last item, after which Down should fall back to provider navigation.\n *\n * To prevent the provider from handling a key, listen to `navbeforeprocess` and call `event.preventDefault()`.\n * This event fires after the component handles `keydown`.\n *\n * ### Cancel focus change\n *\n * Before focusing a computed target, the provider dispatches `navbeforefocus` on the current element. Call\n * `event.preventDefault()` on this event to cancel the focus change.\n *\n * ## Enter action\n *\n * Pressing Enter triggers `.click()` on the currently focused element.\n *\n * You can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n *\n * ## Escape/Back action\n *\n * Pressing Escape tries to find a focusable element with `data-spatial-go-back` and clicks it. If none exists,\n * the provider calls `history.back()`.\n *\n * You can prevent this by listening to `navbeforeprocess` and calling `event.preventDefault()`.\n *\n * You can also intercept the back click by listening to `navback` and calling `event.preventDefault()`.\n *\n * ## Control data attributes\n *\n * Supported data attributes:\n *\n * | Attribute | Value | Default | Description |\n * |--------------------------|---------------------------|---------|-------------------------------------------------------------------------------|\n * | `data-spatial-left` | empty string / element id | N/A | Prevent native navigation in Left direction and focus element if exists |\n * | `data-spatial-up` | empty string / element id | N/A | Prevent native navigation in Up direction and focus element if exists |\n * | `data-spatial-right` | empty string / element id | N/A | Prevent native navigation in Right direction and focus element if exists |\n * | `data-spatial-down` | empty string / element id | N/A | Prevent native navigation in Down direction and focus element if exists |\n * | `data-spatial-go-back` | N/A | N/A | First focusable element with this attribute is clicked on Back/Escape |\n * | `data-spatial-focusable` | N/A | N/A | Treat element as focusable even if it normally is not (e.g., `tabindex=\"-1\"`) |\n * | `data-spatial-exclude` | N/A | N/A | Exclude focusable element (and its subtree) from the navigation |\n *\n * ## Event emitting order\n *\n * On a navigation key press, events fire in this order:\n *\n * 1. `navbeforeprocess` on the currently focused element.\n * 2. If not prevented:\n * a. Arrow keys: `navbeforefocus` on the currently focused element.\n * b. Enter: `.click()` on the currently focused element.\n * c. Escape/Back: `navback` on the provider, then `.click()` on the go-back element or `history.back()`.\n * 3. If no target is found in the requested direction: `navnotarget` on the provider.\n *\n * ## Handle complex components\n *\n * ### Generic components\n *\n * Components that handle navigation internally should prevent the provider from acting. Handle `navbeforeprocess`\n * and call `event.preventDefault()` for keys you process yourself.\n *\n * ### Form inputs\n *\n * Native inputs often submit on Enter, which is not desirable here. Enter should toggle or activate the control\n * (e.g., check/uncheck). Provide a dedicated submit button users can navigate to and press Enter on.\n *\n * ### Utilities for complex components\n *\n * #### KeyToActionMixin\n *\n * Maps key events to action names. Call `getActionForKeyEvent` to get the action for a keyboard event. Also provides\n * `getKeyboardNavMode` to check whether navigation is spatial or default.\n *\n * #### KeyDownHandledMixin\n *\n * Notify the provider when a component handled `keydown` internally. Call `keyDownEventHandled` whenever you process\n * keydown yourself.\n *\n * ## Platform specific behaviors\n *\n * Consider remote/gamepad constraints. Often focus alone is not enough and users press Enter to \"enter\" an interactive mode:\n * - Select: Enter opens options rather than arrow keys opening a popover.\n * - Text inputs: see the next section.\n * - Slider: Enter to start adjusting, arrow keys to change value, Enter/Escape to stop.\n *\n * ### Text inputs\n *\n * On TV-like platforms without physical keyboards, Enter/focus on an input should open a virtual keyboard instead of submitting\n * the form. Users must close the keyboard (Escape) to continue spatial navigation.\n *\n * If navigation keys are mapped to letters (e.g., `w/a/s/d`), they should navigate, not change input values. Inputs should\n * be edited via the virtual keyboard.\n *\n * Note: Stories do not emulate virtual keyboards, so letter-based navigation may change input values in Storybook.\n *\n * ## Debugging\n *\n * ### Storybook toolbar\n *\n * Enable \"Spatial navigation\" in the toolbar. Key mapping:\n * - Up - ArrowUp\n * - Left - ArrowLeft\n * - Down - ArrowDown\n * - Right - ArrowRight\n * - Enter - Enter\n * - Escape - Escape\n *\n * With wrapper: wraps the component in a 3x3 grid with surrounding buttons for testing.\n * Without wrapper: renders the component alone.\n *\n * ### Visual debugger\n *\n * With spatial navigation enabled, press Shift + navigation key to visualize calculations:\n *\n * - Star: next active element\n * - `#{number}`: candidate order by distance\n * - `D: {distance}`: computed distance\n *\n * ## Limitations\n *\n * ### Completeness\n *\n * The algorithm cannot guarantee reachability to all elements using the four directions. Some components can be isolated.\n *\n * Workarounds:\n * - Use data attributes to explicitly link navigation targets.\n * - Arrange DOM to improve spatial consistency:\n * - Group focusable elements using dedicated components (lists, menus, etc.).\n * - Avoid complex grid-like layouts with variable-sized items.\n * - Avoid overlap along horizontal or vertical axes.\n * - Avoid nested focusable elements where possible.\n * - Tune algorithm weights to match your UI layout.\n *\n * ### Scrollable containers\n *\n * Content scrolling is not supported yet, e.g.:\n * - Focused element larger than the viewport.\n * - Scrollable content without interactive children.\n *\n * ### Nested providers\n *\n * Only one provider instance is supported in the application at a time.\n *\n * @event navbeforeprocess - (React: onNavBeforeProcess) This event dispatched before spatial navigation process any key event.\n * It can be canceled to prevent any action from spatial navigation, e.g.: back, click or calculating the next candidate.\n * @event navbeforefocus - (React: onNavBeforeFocus) This event is dispatched before the focus is changing to the next element.\n * It can be canceled to prevent the focus change. @see https://www.w3.org/TR/css-nav-1/#event-type-navbeforefocus\n * @event navback - (React: onNavBack) This event dispatched a back navigation triggered by the user.\n * The event's detail contains the goBackElement if any. It is cancelable to prevent click\n * action on the goBackElement.\n * @event navnotarget - (React: onNavNoTarget) This event is dispatched when there is no target to focus in the current focus area and\n * in the given direction .\n *\n * @tagname mdc-spatialnavigationprovider\n */",
45029
45091
  "customElement": true
45030
45092
  }
45031
45093
  ],