@cloudscape-design/components 3.0.473 → 3.0.475
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/i18n/dynamic.js +2 -0
- package/i18n/messages/all.all.js +5225 -442
- package/i18n/messages/all.all.json +5230 -470
- package/i18n/messages/all.de.js +334 -23
- package/i18n/messages/all.de.json +338 -27
- package/i18n/messages/all.en-GB.js +334 -23
- package/i18n/messages/all.en-GB.json +338 -27
- package/i18n/messages/all.en.js +334 -23
- package/i18n/messages/all.en.json +338 -27
- package/i18n/messages/all.es.js +339 -19
- package/i18n/messages/all.es.json +340 -23
- package/i18n/messages/all.fr.js +334 -23
- package/i18n/messages/all.fr.json +338 -27
- package/i18n/messages/all.id.js +318 -19
- package/i18n/messages/all.id.json +319 -23
- package/i18n/messages/all.it.js +297 -19
- package/i18n/messages/all.it.json +298 -23
- package/i18n/messages/all.ja.js +339 -19
- package/i18n/messages/all.ja.json +340 -23
- package/i18n/messages/all.ko.js +339 -17
- package/i18n/messages/all.ko.json +340 -21
- package/i18n/messages/all.pt-BR.js +334 -23
- package/i18n/messages/all.pt-BR.json +338 -27
- package/i18n/messages/all.th.d.ts +3 -0
- package/i18n/messages/all.th.js +733 -0
- package/i18n/messages/all.th.json +734 -0
- package/i18n/messages/all.tr.js +339 -19
- package/i18n/messages/all.tr.json +340 -23
- package/i18n/messages/all.zh-CN.js +339 -19
- package/i18n/messages/all.zh-CN.json +340 -23
- package/i18n/messages/all.zh-TW.js +339 -19
- package/i18n/messages/all.zh-TW.json +340 -23
- package/i18n/messages-types.d.ts +5 -2
- package/i18n/messages-types.d.ts.map +1 -1
- package/i18n/messages-types.js.map +1 -1
- package/internal/analytics/components/analytics-funnel.js +3 -3
- package/internal/analytics/components/analytics-funnel.js.map +1 -1
- package/internal/analytics/context/analytics-context.d.ts +1 -1
- package/internal/analytics/context/analytics-context.d.ts.map +1 -1
- package/internal/analytics/context/analytics-context.js +1 -1
- package/internal/analytics/context/analytics-context.js.map +1 -1
- package/internal/analytics/hooks/use-funnel.d.ts.map +1 -1
- package/internal/analytics/hooks/use-funnel.js +2 -2
- package/internal/analytics/hooks/use-funnel.js.map +1 -1
- package/internal/environment.js +1 -1
- package/internal/environment.json +1 -1
- package/internal/manifest.json +1 -1
- package/package.json +1 -1
- package/table/table-role/grid-navigation.d.ts +1 -4
- package/table/table-role/grid-navigation.d.ts.map +1 -1
- package/table/table-role/grid-navigation.js +122 -66
- package/table/table-role/grid-navigation.js.map +1 -1
- package/table/table-role/table-role-helper.d.ts.map +1 -1
- package/table/table-role/table-role-helper.js +2 -0
- package/table/table-role/table-role-helper.js.map +1 -1
- package/table/table-role/utils.d.ts +9 -17
- package/table/table-role/utils.d.ts.map +1 -1
- package/table/table-role/utils.js +3 -83
- package/table/table-role/utils.js.map +1 -1
- package/toggle/styles.css.js +8 -8
- package/toggle/styles.scoped.css +14 -13
- package/toggle/styles.selectors.js +8 -8
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { useEffect, useMemo } from 'react';
|
|
4
|
-
import { defaultIsSuppressed,
|
|
4
|
+
import { defaultIsSuppressed, muteElementFocusables, restoreElementFocusables, getFirstFocusable, getFocusables, findTableRowByAriaRowIndex, findTableRowCellByAriaColIndex, setTabIndex, } from './utils';
|
|
5
5
|
import { KeyCode } from '../../internal/keycode';
|
|
6
|
-
import { nodeContains } from '@cloudscape-design/component-toolkit/dom';
|
|
7
6
|
import { useStableCallback } from '@cloudscape-design/component-toolkit/internal';
|
|
8
7
|
import React from 'react';
|
|
8
|
+
import { nodeBelongs } from '../../internal/utils/node-belongs';
|
|
9
9
|
/**
|
|
10
10
|
* Makes table navigable with keyboard commands.
|
|
11
|
-
* See
|
|
12
|
-
*
|
|
13
|
-
* The hook attaches the GridNavigationHelper helper when active=true.
|
|
14
|
-
* See GridNavigationHelper for more details.
|
|
11
|
+
* See grid-navigation.md
|
|
15
12
|
*/
|
|
16
13
|
export function GridNavigationProvider({ keyboardNavigation, pageSize, getTable, children }) {
|
|
17
|
-
const gridNavigation = useMemo(() => new
|
|
14
|
+
const gridNavigation = useMemo(() => new GridNavigationProcessor(), []);
|
|
18
15
|
const getTableStable = useStableCallback(getTable);
|
|
19
|
-
// Initialize the
|
|
16
|
+
// Initialize the processor with the table container assuming it is mounted synchronously and only once.
|
|
20
17
|
useEffect(() => {
|
|
21
18
|
if (keyboardNavigation) {
|
|
22
19
|
const table = getTableStable();
|
|
@@ -24,43 +21,42 @@ export function GridNavigationProvider({ keyboardNavigation, pageSize, getTable,
|
|
|
24
21
|
}
|
|
25
22
|
return () => gridNavigation.cleanup();
|
|
26
23
|
}, [keyboardNavigation, gridNavigation, getTableStable]);
|
|
27
|
-
// Notify the
|
|
24
|
+
// Notify the processor of the props change.
|
|
28
25
|
useEffect(() => {
|
|
29
26
|
gridNavigation.update({ pageSize });
|
|
30
27
|
}, [gridNavigation, pageSize]);
|
|
28
|
+
// Notify the processor of the new render.
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (keyboardNavigation) {
|
|
31
|
+
gridNavigation.refresh();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
31
34
|
return React.createElement(React.Fragment, null, children);
|
|
32
35
|
}
|
|
33
36
|
/**
|
|
34
37
|
* This helper encapsulates the grid navigation behaviors which are:
|
|
35
38
|
* 1. Responding to keyboard commands and moving the focus accordingly;
|
|
36
39
|
* 2. Muting table interactive elements for only one to be user-focusable at a time;
|
|
37
|
-
* 3. Suppressing the above behaviors when focusing an element inside a dialog or when instructed
|
|
38
|
-
*
|
|
39
|
-
* All behaviors are attached upon initialization and are re-evaluated with every focusin, focusout, and keydown events,
|
|
40
|
-
* and also when a node removal inside the table is observed to ensure consistency at any given moment.
|
|
41
|
-
*
|
|
42
|
-
* When the navigation is suppressed the keyboard commands are no longer intercepted and all table interactive elements are made
|
|
43
|
-
* user-focusable to unblock the Tab navigation. The suppression should only be used for interactive elements inside the table that would
|
|
44
|
-
* otherwise conflict with the navigation. Once the interactive element is deactivated or lose focus the table navigation becomes active again.
|
|
40
|
+
* 3. Suppressing the above behaviors when focusing an element inside a dialog or when instructed explicitly.
|
|
45
41
|
*/
|
|
46
|
-
class
|
|
42
|
+
class GridNavigationProcessor {
|
|
47
43
|
constructor() {
|
|
48
44
|
// Props
|
|
49
45
|
this._pageSize = 0;
|
|
50
46
|
this._table = null;
|
|
51
47
|
// State
|
|
52
|
-
this.prevFocusedCell = null;
|
|
53
48
|
this.focusedCell = null;
|
|
54
49
|
this.onFocusin = (event) => {
|
|
55
50
|
var _a;
|
|
56
|
-
|
|
51
|
+
if (!(event.target instanceof HTMLElement)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const cell = this.findFocusedCell(event.target);
|
|
57
55
|
if (!cell) {
|
|
58
56
|
return;
|
|
59
57
|
}
|
|
60
|
-
this.prevFocusedCell = cell;
|
|
61
58
|
this.focusedCell = cell;
|
|
62
|
-
|
|
63
|
-
ensureSingleFocusable(this.table, cell);
|
|
59
|
+
this.ensureSingleFocusable();
|
|
64
60
|
// Focusing on cell is not eligible when it contains focusable elements in the content.
|
|
65
61
|
// If content focusables are available - move the focus to the first one.
|
|
66
62
|
if (cell.element === cell.cellElement) {
|
|
@@ -68,7 +64,13 @@ class GridNavigationHelper {
|
|
|
68
64
|
}
|
|
69
65
|
};
|
|
70
66
|
this.onFocusout = () => {
|
|
71
|
-
|
|
67
|
+
// When focus leaves the cell and the cell no longer belong to the table it indicates the focused element has been unmounted.
|
|
68
|
+
// In that case the focus needs to be restored on the same coordinates.
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
if (this.focusedCell && !nodeBelongs(this.table, this.focusedCell.element)) {
|
|
71
|
+
this.moveFocusBy(this.focusedCell, { x: 0, y: 0 });
|
|
72
|
+
}
|
|
73
|
+
}, 0);
|
|
72
74
|
};
|
|
73
75
|
this.onKeydown = (event) => {
|
|
74
76
|
if (!this.focusedCell) {
|
|
@@ -96,79 +98,49 @@ class GridNavigationHelper {
|
|
|
96
98
|
switch (key) {
|
|
97
99
|
case KeyCode.up:
|
|
98
100
|
event.preventDefault();
|
|
99
|
-
return moveFocusBy(
|
|
101
|
+
return this.moveFocusBy(from, { y: -1, x: 0 });
|
|
100
102
|
case KeyCode.down:
|
|
101
103
|
event.preventDefault();
|
|
102
|
-
return moveFocusBy(
|
|
104
|
+
return this.moveFocusBy(from, { y: 1, x: 0 });
|
|
103
105
|
case KeyCode.left:
|
|
104
106
|
event.preventDefault();
|
|
105
|
-
return moveFocusBy(
|
|
107
|
+
return this.moveFocusBy(from, { y: 0, x: -1 });
|
|
106
108
|
case KeyCode.right:
|
|
107
109
|
event.preventDefault();
|
|
108
|
-
return moveFocusBy(
|
|
110
|
+
return this.moveFocusBy(from, { y: 0, x: 1 });
|
|
109
111
|
case KeyCode.pageUp:
|
|
110
112
|
event.preventDefault();
|
|
111
|
-
return moveFocusBy(
|
|
113
|
+
return this.moveFocusBy(from, { y: -this.pageSize, x: 0 });
|
|
112
114
|
case KeyCode.pageDown:
|
|
113
115
|
event.preventDefault();
|
|
114
|
-
return moveFocusBy(
|
|
116
|
+
return this.moveFocusBy(from, { y: this.pageSize, x: 0 });
|
|
115
117
|
case KeyCode.home:
|
|
116
118
|
event.preventDefault();
|
|
117
|
-
return moveFocusBy(
|
|
119
|
+
return this.moveFocusBy(from, { y: 0, x: minExtreme });
|
|
118
120
|
case KeyCode.end:
|
|
119
121
|
event.preventDefault();
|
|
120
|
-
return moveFocusBy(
|
|
122
|
+
return this.moveFocusBy(from, { y: 0, x: maxExtreme });
|
|
121
123
|
case -KeyCode.home:
|
|
122
124
|
event.preventDefault();
|
|
123
|
-
return moveFocusBy(
|
|
125
|
+
return this.moveFocusBy(from, { y: minExtreme, x: minExtreme });
|
|
124
126
|
case -KeyCode.end:
|
|
125
127
|
event.preventDefault();
|
|
126
|
-
return moveFocusBy(
|
|
128
|
+
return this.moveFocusBy(from, { y: maxExtreme, x: maxExtreme });
|
|
127
129
|
default:
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
130
132
|
};
|
|
131
|
-
this.onTableNodeMutation = (mutationRecords) => {
|
|
132
|
-
var _a;
|
|
133
|
-
// When focused cell is un-mounted the focusout event handler removes this.cell,
|
|
134
|
-
// while this.prevFocusedCell is retained until the next focusin event.
|
|
135
|
-
const cell = (_a = this.focusedCell) !== null && _a !== void 0 ? _a : this.prevFocusedCell;
|
|
136
|
-
const cellSuppressed = cell ? this.isSuppressed(cell.element) : false;
|
|
137
|
-
// Update table elements focus if new nodes were added.
|
|
138
|
-
if (mutationRecords.some(record => record.addedNodes.length > 0)) {
|
|
139
|
-
muteElementFocusables(this.table, cellSuppressed);
|
|
140
|
-
ensureSingleFocusable(this.table, cell);
|
|
141
|
-
}
|
|
142
|
-
if (cell) {
|
|
143
|
-
for (const record of mutationRecords) {
|
|
144
|
-
if (record.type === 'childList') {
|
|
145
|
-
// The lost focus in an unmount event is reapplied to the table using the previous cell position.
|
|
146
|
-
// The moveFocusBy takes care of finding the closest position if the previous one no longer exists.
|
|
147
|
-
for (const removedNode of Array.from(record.removedNodes)) {
|
|
148
|
-
if (removedNode === cell.element || nodeContains(removedNode, cell.element)) {
|
|
149
|
-
ensureSingleFocusable(this.table, cell);
|
|
150
|
-
moveFocusBy(this.table, cell, { y: 0, x: 0 });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
133
|
}
|
|
158
134
|
init(table) {
|
|
159
135
|
this._table = table;
|
|
160
136
|
this.table.addEventListener('focusin', this.onFocusin);
|
|
161
137
|
this.table.addEventListener('focusout', this.onFocusout);
|
|
162
138
|
this.table.addEventListener('keydown', this.onKeydown);
|
|
163
|
-
|
|
164
|
-
tableNodesObserver.observe(table, { childList: true, subtree: true });
|
|
165
|
-
muteElementFocusables(this.table, false);
|
|
166
|
-
ensureSingleFocusable(this.table, null);
|
|
139
|
+
this.ensureSingleFocusable();
|
|
167
140
|
this.cleanup = () => {
|
|
168
141
|
this.table.removeEventListener('focusin', this.onFocusin);
|
|
169
142
|
this.table.removeEventListener('focusout', this.onFocusout);
|
|
170
143
|
this.table.removeEventListener('keydown', this.onKeydown);
|
|
171
|
-
tableNodesObserver.disconnect();
|
|
172
144
|
restoreElementFocusables(this.table);
|
|
173
145
|
};
|
|
174
146
|
}
|
|
@@ -178,17 +150,101 @@ class GridNavigationHelper {
|
|
|
178
150
|
update({ pageSize }) {
|
|
179
151
|
this._pageSize = pageSize;
|
|
180
152
|
}
|
|
153
|
+
refresh() {
|
|
154
|
+
if (this._table) {
|
|
155
|
+
// Update focused cell indices in case table rows, columns, or firstIndex change.
|
|
156
|
+
if (this.focusedCell) {
|
|
157
|
+
this.focusedCell = this.findFocusedCell(this.focusedCell.element);
|
|
158
|
+
}
|
|
159
|
+
// Ensure newly added elements if any are muted.
|
|
160
|
+
this.ensureSingleFocusable();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
181
163
|
get pageSize() {
|
|
182
164
|
return this._pageSize;
|
|
183
165
|
}
|
|
184
166
|
get table() {
|
|
185
167
|
if (!this._table) {
|
|
186
|
-
throw new Error('Invariant violation:
|
|
168
|
+
throw new Error('Invariant violation: GridNavigationProcessor is used before initialization.');
|
|
187
169
|
}
|
|
188
170
|
return this._table;
|
|
189
171
|
}
|
|
190
172
|
isSuppressed(focusedElement) {
|
|
191
173
|
return defaultIsSuppressed(focusedElement);
|
|
192
174
|
}
|
|
175
|
+
moveFocusBy(cell, delta) {
|
|
176
|
+
var _a;
|
|
177
|
+
(_a = this.getNextFocusable(cell, delta)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Finds focused cell props corresponding the focused element inside the table.
|
|
181
|
+
* The function relies on ARIA colindex/rowindex attributes being correctly applied.
|
|
182
|
+
*/
|
|
183
|
+
findFocusedCell(focusedElement) {
|
|
184
|
+
var _a, _b;
|
|
185
|
+
const cellElement = focusedElement.closest('td,th');
|
|
186
|
+
const rowElement = cellElement === null || cellElement === void 0 ? void 0 : cellElement.closest('tr');
|
|
187
|
+
if (!cellElement || !rowElement) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const colIndex = parseInt((_a = cellElement.getAttribute('aria-colindex')) !== null && _a !== void 0 ? _a : '');
|
|
191
|
+
const rowIndex = parseInt((_b = rowElement.getAttribute('aria-rowindex')) !== null && _b !== void 0 ? _b : '');
|
|
192
|
+
if (isNaN(colIndex) || isNaN(rowIndex)) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const cellFocusables = getFocusables(cellElement);
|
|
196
|
+
const elementIndex = cellFocusables.indexOf(focusedElement);
|
|
197
|
+
return { rowIndex, colIndex, rowElement, cellElement, element: focusedElement, elementIndex };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Finds element to be focused next. The focus can transition between cells or interactive elements inside cells.
|
|
201
|
+
*/
|
|
202
|
+
getNextFocusable(from, delta) {
|
|
203
|
+
var _a;
|
|
204
|
+
// Find next row to move focus into (can be null if the top/bottom is reached).
|
|
205
|
+
const targetAriaRowIndex = from.rowIndex + delta.y;
|
|
206
|
+
const targetRow = findTableRowByAriaRowIndex(this.table, targetAriaRowIndex, delta.y);
|
|
207
|
+
if (!targetRow) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
// Return next interactive cell content element if available.
|
|
211
|
+
const cellFocusables = getFocusables(from.cellElement);
|
|
212
|
+
const nextElementIndex = from.elementIndex + delta.x;
|
|
213
|
+
if (delta.x && from.elementIndex !== -1 && 0 <= nextElementIndex && nextElementIndex < cellFocusables.length) {
|
|
214
|
+
return cellFocusables[nextElementIndex];
|
|
215
|
+
}
|
|
216
|
+
// Find next cell to focus or move focus into (can be null if the left/right edge is reached).
|
|
217
|
+
const targetAriaColIndex = from.colIndex + delta.x;
|
|
218
|
+
const targetCell = findTableRowCellByAriaColIndex(targetRow, targetAriaColIndex, delta.x);
|
|
219
|
+
if (!targetCell) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
// When target cell matches the current cell it means we reached the left or right boundary.
|
|
223
|
+
if (targetCell === from.cellElement && delta.x !== 0) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
// Return cell interactive content or the cell itself.
|
|
227
|
+
const targetCellFocusables = getFocusables(targetCell);
|
|
228
|
+
const focusIndex = delta.x < 0 ? targetCellFocusables.length - 1 : delta.x > 0 ? 0 : from.elementIndex;
|
|
229
|
+
const focusTarget = (_a = targetCellFocusables[focusIndex]) !== null && _a !== void 0 ? _a : targetCell;
|
|
230
|
+
return focusTarget;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Makes the cell element, the first interactive element or the first cell of the table user-focusable.
|
|
234
|
+
*/
|
|
235
|
+
ensureSingleFocusable() {
|
|
236
|
+
var _a;
|
|
237
|
+
const cellSuppressed = this.focusedCell ? this.isSuppressed(this.focusedCell.element) : false;
|
|
238
|
+
muteElementFocusables(this.table, cellSuppressed);
|
|
239
|
+
const firstTableCell = this.table.querySelector('td,th');
|
|
240
|
+
// A single element of the table is made user-focusable.
|
|
241
|
+
// It defaults to the first interactive element of the first cell or the first cell itself otherwise.
|
|
242
|
+
let focusTarget = (_a = (firstTableCell && getFocusables(firstTableCell)[0])) !== null && _a !== void 0 ? _a : firstTableCell;
|
|
243
|
+
// When a navigation-focused element is present in the table it is used for user-navigation instead.
|
|
244
|
+
if (this.focusedCell) {
|
|
245
|
+
focusTarget = this.getNextFocusable(this.focusedCell, { x: 0, y: 0 });
|
|
246
|
+
}
|
|
247
|
+
setTabIndex(focusTarget, 0);
|
|
248
|
+
}
|
|
193
249
|
}
|
|
194
250
|
//# sourceMappingURL=grid-navigation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid-navigation.js","sourceRoot":"lib/default/","sources":["table/table-role/grid-navigation.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { useEffect, useMemo } from 'react';\nimport {\n defaultIsSuppressed,\n findFocusinCell,\n moveFocusBy,\n muteElementFocusables,\n restoreElementFocusables,\n ensureSingleFocusable,\n getFirstFocusable,\n} from './utils';\nimport { FocusedCell, GridNavigationProps } from './interfaces';\nimport { KeyCode } from '../../internal/keycode';\nimport { nodeContains } from '@cloudscape-design/component-toolkit/dom';\nimport { useStableCallback } from '@cloudscape-design/component-toolkit/internal';\nimport React from 'react';\n\n/**\n * Makes table navigable with keyboard commands.\n * See https://www.w3.org/WAI/ARIA/apg/patterns/grid\n *\n * The hook attaches the GridNavigationHelper helper when active=true.\n * See GridNavigationHelper for more details.\n */\nexport function GridNavigationProvider({ keyboardNavigation, pageSize, getTable, children }: GridNavigationProps) {\n const gridNavigation = useMemo(() => new GridNavigationHelper(), []);\n\n const getTableStable = useStableCallback(getTable);\n\n // Initialize the helper with the table container assuming it is mounted synchronously and only once.\n useEffect(() => {\n if (keyboardNavigation) {\n const table = getTableStable();\n table && gridNavigation.init(table);\n }\n return () => gridNavigation.cleanup();\n }, [keyboardNavigation, gridNavigation, getTableStable]);\n\n // Notify the helper of the props change.\n useEffect(() => {\n gridNavigation.update({ pageSize });\n }, [gridNavigation, pageSize]);\n\n return <>{children}</>;\n}\n\n/**\n * This helper encapsulates the grid navigation behaviors which are:\n * 1. Responding to keyboard commands and moving the focus accordingly;\n * 2. Muting table interactive elements for only one to be user-focusable at a time;\n * 3. Suppressing the above behaviors when focusing an element inside a dialog or when instructed by the isSuppressed callback.\n *\n * All behaviors are attached upon initialization and are re-evaluated with every focusin, focusout, and keydown events,\n * and also when a node removal inside the table is observed to ensure consistency at any given moment.\n *\n * When the navigation is suppressed the keyboard commands are no longer intercepted and all table interactive elements are made\n * user-focusable to unblock the Tab navigation. The suppression should only be used for interactive elements inside the table that would\n * otherwise conflict with the navigation. Once the interactive element is deactivated or lose focus the table navigation becomes active again.\n */\nclass GridNavigationHelper {\n // Props\n private _pageSize = 0;\n private _table: null | HTMLTableElement = null;\n\n // State\n private prevFocusedCell: null | FocusedCell = null;\n private focusedCell: null | FocusedCell = null;\n\n public init(table: HTMLTableElement) {\n this._table = table;\n\n this.table.addEventListener('focusin', this.onFocusin);\n this.table.addEventListener('focusout', this.onFocusout);\n this.table.addEventListener('keydown', this.onKeydown);\n\n const tableNodesObserver = new MutationObserver(this.onTableNodeMutation);\n tableNodesObserver.observe(table, { childList: true, subtree: true });\n\n muteElementFocusables(this.table, false);\n ensureSingleFocusable(this.table, null);\n\n this.cleanup = () => {\n this.table.removeEventListener('focusin', this.onFocusin);\n this.table.removeEventListener('focusout', this.onFocusout);\n this.table.removeEventListener('keydown', this.onKeydown);\n\n tableNodesObserver.disconnect();\n\n restoreElementFocusables(this.table);\n };\n }\n\n public cleanup() {\n // Do nothing before initialized.\n }\n\n public update({ pageSize }: { pageSize: number }) {\n this._pageSize = pageSize;\n }\n\n private get pageSize() {\n return this._pageSize;\n }\n\n private get table(): HTMLTableElement {\n if (!this._table) {\n throw new Error('Invariant violation: GridNavigationHelper is used before initialization.');\n }\n return this._table;\n }\n\n private isSuppressed(focusedElement: HTMLElement): boolean {\n return defaultIsSuppressed(focusedElement);\n }\n\n private onFocusin = (event: FocusEvent) => {\n const cell = findFocusinCell(event);\n if (!cell) {\n return;\n }\n\n this.prevFocusedCell = cell;\n this.focusedCell = cell;\n\n muteElementFocusables(this.table, this.isSuppressed(cell.element));\n ensureSingleFocusable(this.table, cell);\n\n // Focusing on cell is not eligible when it contains focusable elements in the content.\n // If content focusables are available - move the focus to the first one.\n if (cell.element === cell.cellElement) {\n getFirstFocusable(cell.cellElement)?.focus();\n }\n };\n\n private onFocusout = () => {\n this.focusedCell = null;\n };\n\n private onKeydown = (event: KeyboardEvent) => {\n if (!this.focusedCell) {\n return;\n }\n\n const ctrlKey = event.ctrlKey ? 1 : 0;\n const altKey = event.altKey ? 1 : 0;\n const shiftKey = event.shiftKey ? 1 : 0;\n const metaKey = event.metaKey ? 1 : 0;\n const numModifiersPressed = ctrlKey + altKey + shiftKey + metaKey;\n\n let key = event.keyCode;\n if (numModifiersPressed === 1 && event.ctrlKey) {\n key = -key;\n } else if (numModifiersPressed) {\n return;\n }\n\n const from = this.focusedCell;\n const minExtreme = Number.NEGATIVE_INFINITY;\n const maxExtreme = Number.POSITIVE_INFINITY;\n\n // Do not intercept any keys when the navigation is suppressed.\n if (this.isSuppressed(from.element)) {\n return;\n }\n\n switch (key) {\n case KeyCode.up:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: -1, x: 0 });\n\n case KeyCode.down:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: 1, x: 0 });\n\n case KeyCode.left:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: 0, x: -1 });\n\n case KeyCode.right:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: 0, x: 1 });\n\n case KeyCode.pageUp:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: -this.pageSize, x: 0 });\n\n case KeyCode.pageDown:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: this.pageSize, x: 0 });\n\n case KeyCode.home:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: 0, x: minExtreme });\n\n case KeyCode.end:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: 0, x: maxExtreme });\n\n case -KeyCode.home:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: minExtreme, x: minExtreme });\n\n case -KeyCode.end:\n event.preventDefault();\n return moveFocusBy(this.table, from, { y: maxExtreme, x: maxExtreme });\n\n default:\n return;\n }\n };\n\n private onTableNodeMutation = (mutationRecords: MutationRecord[]) => {\n // When focused cell is un-mounted the focusout event handler removes this.cell,\n // while this.prevFocusedCell is retained until the next focusin event.\n const cell = this.focusedCell ?? this.prevFocusedCell;\n const cellSuppressed = cell ? this.isSuppressed(cell.element) : false;\n\n // Update table elements focus if new nodes were added.\n if (mutationRecords.some(record => record.addedNodes.length > 0)) {\n muteElementFocusables(this.table, cellSuppressed);\n ensureSingleFocusable(this.table, cell);\n }\n\n if (cell) {\n for (const record of mutationRecords) {\n if (record.type === 'childList') {\n // The lost focus in an unmount event is reapplied to the table using the previous cell position.\n // The moveFocusBy takes care of finding the closest position if the previous one no longer exists.\n for (const removedNode of Array.from(record.removedNodes)) {\n if (removedNode === cell.element || nodeContains(removedNode, cell.element)) {\n ensureSingleFocusable(this.table, cell);\n moveFocusBy(this.table, cell, { y: 0, x: 0 });\n }\n }\n }\n }\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"grid-navigation.js","sourceRoot":"lib/default/","sources":["table/table-role/grid-navigation.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { useEffect, useMemo } from 'react';\nimport {\n defaultIsSuppressed,\n muteElementFocusables,\n restoreElementFocusables,\n getFirstFocusable,\n getFocusables,\n findTableRowByAriaRowIndex,\n findTableRowCellByAriaColIndex,\n setTabIndex,\n} from './utils';\nimport { FocusedCell, GridNavigationProps } from './interfaces';\nimport { KeyCode } from '../../internal/keycode';\nimport { useStableCallback } from '@cloudscape-design/component-toolkit/internal';\nimport React from 'react';\nimport { nodeBelongs } from '../../internal/utils/node-belongs';\n\n/**\n * Makes table navigable with keyboard commands.\n * See grid-navigation.md\n */\nexport function GridNavigationProvider({ keyboardNavigation, pageSize, getTable, children }: GridNavigationProps) {\n const gridNavigation = useMemo(() => new GridNavigationProcessor(), []);\n\n const getTableStable = useStableCallback(getTable);\n\n // Initialize the processor with the table container assuming it is mounted synchronously and only once.\n useEffect(() => {\n if (keyboardNavigation) {\n const table = getTableStable();\n table && gridNavigation.init(table);\n }\n return () => gridNavigation.cleanup();\n }, [keyboardNavigation, gridNavigation, getTableStable]);\n\n // Notify the processor of the props change.\n useEffect(() => {\n gridNavigation.update({ pageSize });\n }, [gridNavigation, pageSize]);\n\n // Notify the processor of the new render.\n useEffect(() => {\n if (keyboardNavigation) {\n gridNavigation.refresh();\n }\n });\n\n return <>{children}</>;\n}\n\n/**\n * This helper encapsulates the grid navigation behaviors which are:\n * 1. Responding to keyboard commands and moving the focus accordingly;\n * 2. Muting table interactive elements for only one to be user-focusable at a time;\n * 3. Suppressing the above behaviors when focusing an element inside a dialog or when instructed explicitly.\n */\nclass GridNavigationProcessor {\n // Props\n private _pageSize = 0;\n private _table: null | HTMLTableElement = null;\n\n // State\n private focusedCell: null | FocusedCell = null;\n\n public init(table: HTMLTableElement) {\n this._table = table;\n\n this.table.addEventListener('focusin', this.onFocusin);\n this.table.addEventListener('focusout', this.onFocusout);\n this.table.addEventListener('keydown', this.onKeydown);\n\n this.ensureSingleFocusable();\n\n this.cleanup = () => {\n this.table.removeEventListener('focusin', this.onFocusin);\n this.table.removeEventListener('focusout', this.onFocusout);\n this.table.removeEventListener('keydown', this.onKeydown);\n\n restoreElementFocusables(this.table);\n };\n }\n\n public cleanup() {\n // Do nothing before initialized.\n }\n\n public update({ pageSize }: { pageSize: number }) {\n this._pageSize = pageSize;\n }\n\n public refresh() {\n if (this._table) {\n // Update focused cell indices in case table rows, columns, or firstIndex change.\n if (this.focusedCell) {\n this.focusedCell = this.findFocusedCell(this.focusedCell.element);\n }\n\n // Ensure newly added elements if any are muted.\n this.ensureSingleFocusable();\n }\n }\n\n private get pageSize() {\n return this._pageSize;\n }\n\n private get table(): HTMLTableElement {\n if (!this._table) {\n throw new Error('Invariant violation: GridNavigationProcessor is used before initialization.');\n }\n return this._table;\n }\n\n private isSuppressed(focusedElement: HTMLElement): boolean {\n return defaultIsSuppressed(focusedElement);\n }\n\n private onFocusin = (event: FocusEvent) => {\n if (!(event.target instanceof HTMLElement)) {\n return;\n }\n\n const cell = this.findFocusedCell(event.target);\n if (!cell) {\n return;\n }\n\n this.focusedCell = cell;\n\n this.ensureSingleFocusable();\n\n // Focusing on cell is not eligible when it contains focusable elements in the content.\n // If content focusables are available - move the focus to the first one.\n if (cell.element === cell.cellElement) {\n getFirstFocusable(cell.cellElement)?.focus();\n }\n };\n\n private onFocusout = () => {\n // When focus leaves the cell and the cell no longer belong to the table it indicates the focused element has been unmounted.\n // In that case the focus needs to be restored on the same coordinates.\n setTimeout(() => {\n if (this.focusedCell && !nodeBelongs(this.table, this.focusedCell.element)) {\n this.moveFocusBy(this.focusedCell, { x: 0, y: 0 });\n }\n }, 0);\n };\n\n private onKeydown = (event: KeyboardEvent) => {\n if (!this.focusedCell) {\n return;\n }\n\n const ctrlKey = event.ctrlKey ? 1 : 0;\n const altKey = event.altKey ? 1 : 0;\n const shiftKey = event.shiftKey ? 1 : 0;\n const metaKey = event.metaKey ? 1 : 0;\n const numModifiersPressed = ctrlKey + altKey + shiftKey + metaKey;\n\n let key = event.keyCode;\n if (numModifiersPressed === 1 && event.ctrlKey) {\n key = -key;\n } else if (numModifiersPressed) {\n return;\n }\n\n const from = this.focusedCell;\n const minExtreme = Number.NEGATIVE_INFINITY;\n const maxExtreme = Number.POSITIVE_INFINITY;\n\n // Do not intercept any keys when the navigation is suppressed.\n if (this.isSuppressed(from.element)) {\n return;\n }\n\n switch (key) {\n case KeyCode.up:\n event.preventDefault();\n return this.moveFocusBy(from, { y: -1, x: 0 });\n\n case KeyCode.down:\n event.preventDefault();\n return this.moveFocusBy(from, { y: 1, x: 0 });\n\n case KeyCode.left:\n event.preventDefault();\n return this.moveFocusBy(from, { y: 0, x: -1 });\n\n case KeyCode.right:\n event.preventDefault();\n return this.moveFocusBy(from, { y: 0, x: 1 });\n\n case KeyCode.pageUp:\n event.preventDefault();\n return this.moveFocusBy(from, { y: -this.pageSize, x: 0 });\n\n case KeyCode.pageDown:\n event.preventDefault();\n return this.moveFocusBy(from, { y: this.pageSize, x: 0 });\n\n case KeyCode.home:\n event.preventDefault();\n return this.moveFocusBy(from, { y: 0, x: minExtreme });\n\n case KeyCode.end:\n event.preventDefault();\n return this.moveFocusBy(from, { y: 0, x: maxExtreme });\n\n case -KeyCode.home:\n event.preventDefault();\n return this.moveFocusBy(from, { y: minExtreme, x: minExtreme });\n\n case -KeyCode.end:\n event.preventDefault();\n return this.moveFocusBy(from, { y: maxExtreme, x: maxExtreme });\n\n default:\n return;\n }\n };\n\n private moveFocusBy(cell: FocusedCell, delta: { x: number; y: number }) {\n this.getNextFocusable(cell, delta)?.focus();\n }\n\n /**\n * Finds focused cell props corresponding the focused element inside the table.\n * The function relies on ARIA colindex/rowindex attributes being correctly applied.\n */\n private findFocusedCell(focusedElement: HTMLElement): null | FocusedCell {\n const cellElement = focusedElement.closest('td,th') as null | HTMLTableCellElement;\n const rowElement = cellElement?.closest('tr');\n\n if (!cellElement || !rowElement) {\n return null;\n }\n\n const colIndex = parseInt(cellElement.getAttribute('aria-colindex') ?? '');\n const rowIndex = parseInt(rowElement.getAttribute('aria-rowindex') ?? '');\n if (isNaN(colIndex) || isNaN(rowIndex)) {\n return null;\n }\n\n const cellFocusables = getFocusables(cellElement);\n const elementIndex = cellFocusables.indexOf(focusedElement);\n\n return { rowIndex, colIndex, rowElement, cellElement, element: focusedElement, elementIndex };\n }\n\n /**\n * Finds element to be focused next. The focus can transition between cells or interactive elements inside cells.\n */\n private getNextFocusable(from: FocusedCell, delta: { y: number; x: number }) {\n // Find next row to move focus into (can be null if the top/bottom is reached).\n const targetAriaRowIndex = from.rowIndex + delta.y;\n const targetRow = findTableRowByAriaRowIndex(this.table, targetAriaRowIndex, delta.y);\n if (!targetRow) {\n return null;\n }\n\n // Return next interactive cell content element if available.\n const cellFocusables = getFocusables(from.cellElement);\n const nextElementIndex = from.elementIndex + delta.x;\n if (delta.x && from.elementIndex !== -1 && 0 <= nextElementIndex && nextElementIndex < cellFocusables.length) {\n return cellFocusables[nextElementIndex];\n }\n\n // Find next cell to focus or move focus into (can be null if the left/right edge is reached).\n const targetAriaColIndex = from.colIndex + delta.x;\n const targetCell = findTableRowCellByAriaColIndex(targetRow, targetAriaColIndex, delta.x);\n if (!targetCell) {\n return null;\n }\n\n // When target cell matches the current cell it means we reached the left or right boundary.\n if (targetCell === from.cellElement && delta.x !== 0) {\n return null;\n }\n\n // Return cell interactive content or the cell itself.\n const targetCellFocusables = getFocusables(targetCell);\n const focusIndex = delta.x < 0 ? targetCellFocusables.length - 1 : delta.x > 0 ? 0 : from.elementIndex;\n const focusTarget = targetCellFocusables[focusIndex] ?? targetCell;\n return focusTarget;\n }\n\n /**\n * Makes the cell element, the first interactive element or the first cell of the table user-focusable.\n */\n private ensureSingleFocusable() {\n const cellSuppressed = this.focusedCell ? this.isSuppressed(this.focusedCell.element) : false;\n muteElementFocusables(this.table, cellSuppressed);\n\n const firstTableCell = this.table.querySelector('td,th') as null | HTMLTableCellElement;\n\n // A single element of the table is made user-focusable.\n // It defaults to the first interactive element of the first cell or the first cell itself otherwise.\n let focusTarget: null | HTMLElement = (firstTableCell && getFocusables(firstTableCell)[0]) ?? firstTableCell;\n\n // When a navigation-focused element is present in the table it is used for user-navigation instead.\n if (this.focusedCell) {\n focusTarget = this.getNextFocusable(this.focusedCell, { x: 0, y: 0 });\n }\n\n setTabIndex(focusTarget, 0);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-role-helper.d.ts","sourceRoot":"lib/default/","sources":["table/table-role/table-role-helper.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,KAAK,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,YAAY,CAAC;AAa7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAuB9C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAWpH;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,uDAS3E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,uDAa5G;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,
|
|
1
|
+
{"version":3,"file":"table-role-helper.d.ts","sourceRoot":"lib/default/","sources":["table/table-role/table-role-helper.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,KAAK,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,YAAY,CAAC;AAa7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAuB9C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,kDAWpH;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,uDAS3E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,uDAa5G;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,0DAeA;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,0DAa/G"}
|
|
@@ -61,6 +61,7 @@ export function getTableColHeaderRoleProps(options) {
|
|
|
61
61
|
const nativeProps = {};
|
|
62
62
|
nativeProps.scope = 'col';
|
|
63
63
|
if (options.tableRole === 'grid') {
|
|
64
|
+
nativeProps.tabIndex = -1;
|
|
64
65
|
nativeProps['aria-colindex'] = options.colIndex + 1;
|
|
65
66
|
}
|
|
66
67
|
if (options.sortingStatus) {
|
|
@@ -71,6 +72,7 @@ export function getTableColHeaderRoleProps(options) {
|
|
|
71
72
|
export function getTableCellRoleProps(options) {
|
|
72
73
|
const nativeProps = {};
|
|
73
74
|
if (options.tableRole === 'grid') {
|
|
75
|
+
nativeProps.tabIndex = -1;
|
|
74
76
|
nativeProps['aria-colindex'] = options.colIndex + 1;
|
|
75
77
|
}
|
|
76
78
|
if (options.isRowHeader) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-role-helper.js","sourceRoot":"lib/default/","sources":["table/table-role/table-role-helper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { TableRole } from './interfaces';\n\ntype SortingStatus = 'sortable' | 'ascending' | 'descending';\n\nconst stateToAriaSort = {\n sortable: 'none',\n ascending: 'ascending',\n descending: 'descending',\n} as const;\nconst getAriaSort = (sortingState: SortingStatus) => stateToAriaSort[sortingState];\n\n// Depending on its content the table can have different semantic representation which includes the\n// ARIA role of the table component (\"table\", \"grid\", \"treegrid\") but also roles and other semantic attributes\n// of the child elements. The TableRole helper encapsulates table's semantic structure.\n\nexport function getTableRoleProps(options: {\n tableRole: TableRole;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n totalItemsCount?: number;\n totalColumnsCount?: number;\n}): React.TableHTMLAttributes<HTMLTableElement> {\n const nativeProps: React.TableHTMLAttributes<HTMLTableElement> = {};\n\n // Browsers have weird mechanism to guess whether it's a data table or a layout table.\n // If we state explicitly, they get it always correctly even with low number of rows.\n nativeProps.role = options.tableRole === 'grid-default' ? 'grid' : options.tableRole;\n\n nativeProps['aria-label'] = options.ariaLabel;\n nativeProps['aria-labelledby'] = options.ariaLabelledBy;\n\n // Incrementing the total count by one to account for the header row.\n nativeProps['aria-rowcount'] = options.totalItemsCount ? options.totalItemsCount + 1 : -1;\n\n if (options.tableRole === 'grid') {\n nativeProps['aria-colcount'] = options.totalColumnsCount;\n }\n\n // Make table component programmatically focusable to attach focusin/focusout for keyboard navigation.\n if (options.tableRole === 'grid') {\n nativeProps.tabIndex = -1;\n }\n\n return nativeProps;\n}\n\nexport function getTableWrapperRoleProps(options: { tableRole: TableRole; isScrollable: boolean; ariaLabel?: string }) {\n const nativeProps: React.HTMLAttributes<HTMLDivElement> = {};\n\n // When the table is scrollable, the wrapper is made focusable so that keyboard users can scroll it horizontally with arrow keys.\n if (options.isScrollable && options.tableRole !== 'grid') {\n nativeProps.role = 'region';\n nativeProps.tabIndex = 0;\n nativeProps['aria-label'] = options.ariaLabel;\n }\n\n return nativeProps;\n}\n\nexport function getTableHeaderRowRoleProps(options: { tableRole: TableRole }) {\n const nativeProps: React.HTMLAttributes<HTMLTableRowElement> = {};\n\n // For grids headers are treated similar to data rows and are indexed accordingly.\n if (options.tableRole === 'grid' || options.tableRole === 'grid-default') {\n nativeProps['aria-rowindex'] = 1;\n }\n\n return nativeProps;\n}\n\nexport function getTableRowRoleProps(options: { tableRole: TableRole; rowIndex: number; firstIndex?: number }) {\n const nativeProps: React.HTMLAttributes<HTMLTableRowElement> = {};\n\n // The data cell indices are incremented by 1 to account for the header cells.\n if (options.tableRole === 'grid') {\n nativeProps['aria-rowindex'] = (options.firstIndex || 1) + options.rowIndex + 1;\n }\n // For tables indices are only added when the first index is not 0 (not the first page/frame).\n else if (options.firstIndex !== undefined) {\n nativeProps['aria-rowindex'] = options.firstIndex + options.rowIndex + 1;\n }\n\n return nativeProps;\n}\n\nexport function getTableColHeaderRoleProps(options: {\n tableRole: TableRole;\n colIndex: number;\n sortingStatus?: SortingStatus;\n}) {\n const nativeProps: React.ThHTMLAttributes<HTMLTableCellElement> = {};\n\n nativeProps.scope = 'col';\n\n if (options.tableRole === 'grid') {\n nativeProps['aria-colindex'] = options.colIndex + 1;\n }\n\n if (options.sortingStatus) {\n nativeProps['aria-sort'] = getAriaSort(options.sortingStatus);\n }\n\n return nativeProps;\n}\n\nexport function getTableCellRoleProps(options: { tableRole: TableRole; colIndex: number; isRowHeader?: boolean }) {\n const nativeProps: React.TdHTMLAttributes<HTMLTableCellElement> = {};\n\n if (options.tableRole === 'grid') {\n nativeProps['aria-colindex'] = options.colIndex + 1;\n }\n\n if (options.isRowHeader) {\n nativeProps.scope = 'row';\n }\n\n return nativeProps;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"table-role-helper.js","sourceRoot":"lib/default/","sources":["table/table-role/table-role-helper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { TableRole } from './interfaces';\n\ntype SortingStatus = 'sortable' | 'ascending' | 'descending';\n\nconst stateToAriaSort = {\n sortable: 'none',\n ascending: 'ascending',\n descending: 'descending',\n} as const;\nconst getAriaSort = (sortingState: SortingStatus) => stateToAriaSort[sortingState];\n\n// Depending on its content the table can have different semantic representation which includes the\n// ARIA role of the table component (\"table\", \"grid\", \"treegrid\") but also roles and other semantic attributes\n// of the child elements. The TableRole helper encapsulates table's semantic structure.\n\nexport function getTableRoleProps(options: {\n tableRole: TableRole;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n totalItemsCount?: number;\n totalColumnsCount?: number;\n}): React.TableHTMLAttributes<HTMLTableElement> {\n const nativeProps: React.TableHTMLAttributes<HTMLTableElement> = {};\n\n // Browsers have weird mechanism to guess whether it's a data table or a layout table.\n // If we state explicitly, they get it always correctly even with low number of rows.\n nativeProps.role = options.tableRole === 'grid-default' ? 'grid' : options.tableRole;\n\n nativeProps['aria-label'] = options.ariaLabel;\n nativeProps['aria-labelledby'] = options.ariaLabelledBy;\n\n // Incrementing the total count by one to account for the header row.\n nativeProps['aria-rowcount'] = options.totalItemsCount ? options.totalItemsCount + 1 : -1;\n\n if (options.tableRole === 'grid') {\n nativeProps['aria-colcount'] = options.totalColumnsCount;\n }\n\n // Make table component programmatically focusable to attach focusin/focusout for keyboard navigation.\n if (options.tableRole === 'grid') {\n nativeProps.tabIndex = -1;\n }\n\n return nativeProps;\n}\n\nexport function getTableWrapperRoleProps(options: { tableRole: TableRole; isScrollable: boolean; ariaLabel?: string }) {\n const nativeProps: React.HTMLAttributes<HTMLDivElement> = {};\n\n // When the table is scrollable, the wrapper is made focusable so that keyboard users can scroll it horizontally with arrow keys.\n if (options.isScrollable && options.tableRole !== 'grid') {\n nativeProps.role = 'region';\n nativeProps.tabIndex = 0;\n nativeProps['aria-label'] = options.ariaLabel;\n }\n\n return nativeProps;\n}\n\nexport function getTableHeaderRowRoleProps(options: { tableRole: TableRole }) {\n const nativeProps: React.HTMLAttributes<HTMLTableRowElement> = {};\n\n // For grids headers are treated similar to data rows and are indexed accordingly.\n if (options.tableRole === 'grid' || options.tableRole === 'grid-default') {\n nativeProps['aria-rowindex'] = 1;\n }\n\n return nativeProps;\n}\n\nexport function getTableRowRoleProps(options: { tableRole: TableRole; rowIndex: number; firstIndex?: number }) {\n const nativeProps: React.HTMLAttributes<HTMLTableRowElement> = {};\n\n // The data cell indices are incremented by 1 to account for the header cells.\n if (options.tableRole === 'grid') {\n nativeProps['aria-rowindex'] = (options.firstIndex || 1) + options.rowIndex + 1;\n }\n // For tables indices are only added when the first index is not 0 (not the first page/frame).\n else if (options.firstIndex !== undefined) {\n nativeProps['aria-rowindex'] = options.firstIndex + options.rowIndex + 1;\n }\n\n return nativeProps;\n}\n\nexport function getTableColHeaderRoleProps(options: {\n tableRole: TableRole;\n colIndex: number;\n sortingStatus?: SortingStatus;\n}) {\n const nativeProps: React.ThHTMLAttributes<HTMLTableCellElement> = {};\n\n nativeProps.scope = 'col';\n\n if (options.tableRole === 'grid') {\n nativeProps.tabIndex = -1;\n nativeProps['aria-colindex'] = options.colIndex + 1;\n }\n\n if (options.sortingStatus) {\n nativeProps['aria-sort'] = getAriaSort(options.sortingStatus);\n }\n\n return nativeProps;\n}\n\nexport function getTableCellRoleProps(options: { tableRole: TableRole; colIndex: number; isRowHeader?: boolean }) {\n const nativeProps: React.TdHTMLAttributes<HTMLTableCellElement> = {};\n\n if (options.tableRole === 'grid') {\n nativeProps.tabIndex = -1;\n nativeProps['aria-colindex'] = options.colIndex + 1;\n }\n\n if (options.isRowHeader) {\n nativeProps.scope = 'row';\n }\n\n return nativeProps;\n}\n"]}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
import { FocusedCell } from './interfaces';
|
|
2
|
-
/**
|
|
3
|
-
* Finds focused cell props corresponding the focused element inside the table.
|
|
4
|
-
* The function relies on ARIA colindex/rowindex attributes being correctly applied.
|
|
5
|
-
*/
|
|
6
|
-
export declare function findFocusinCell(event: FocusEvent): null | FocusedCell;
|
|
7
|
-
/**
|
|
8
|
-
* Moves table focus in the provided direction. The focus can transition between cells or interactive elements inside cells.
|
|
9
|
-
*/
|
|
10
|
-
export declare function moveFocusBy(table: HTMLTableElement, from: FocusedCell, delta: {
|
|
11
|
-
y: number;
|
|
12
|
-
x: number;
|
|
13
|
-
}): void;
|
|
14
|
-
/**
|
|
15
|
-
* Makes the cell element, the first interactive element or the first cell of the table user-focusable.
|
|
16
|
-
*/
|
|
17
|
-
export declare function ensureSingleFocusable(table: HTMLElement, cell: null | FocusedCell): void;
|
|
18
1
|
/**
|
|
19
2
|
* Makes all element focusable children pseudo-focusable unless the grid navigation is suppressed.
|
|
20
3
|
*/
|
|
@@ -34,4 +17,13 @@ export declare function defaultIsSuppressed(target: HTMLElement): boolean;
|
|
|
34
17
|
*/
|
|
35
18
|
export declare function getFocusables(element: HTMLElement): HTMLElement[];
|
|
36
19
|
export declare function getFirstFocusable(element: HTMLElement): HTMLElement | null;
|
|
20
|
+
/**
|
|
21
|
+
* Finds the closest row to the targetAriaRowIndex+delta in the direction of delta.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findTableRowByAriaRowIndex(table: HTMLTableElement, targetAriaRowIndex: number, delta: number): HTMLTableRowElement | null;
|
|
24
|
+
/**
|
|
25
|
+
* Finds the closest column to the targetAriaColIndex+delta in the direction of delta.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findTableRowCellByAriaColIndex(tableRow: HTMLTableRowElement, targetAriaColIndex: number, delta: number): HTMLTableCellElement | null;
|
|
28
|
+
export declare function setTabIndex(element: null | HTMLElement, tabIndex: number): void;
|
|
37
29
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"lib/default/","sources":["table/table-role/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"lib/default/","sources":["table/table-role/utils.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,QAiB9E;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,gBAAgB,QAQjE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,WAiBtD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,iBAEjD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,sBAErD;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,8BAqB5G;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,mBAAmB,EAC7B,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,MAAM,+BAsBd;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,EAAE,QAAQ,EAAE,MAAM,QAIxE"}
|
|
@@ -5,79 +5,6 @@ import { getFocusables as getActualFocusables } from '../../internal/components/
|
|
|
5
5
|
// The elements having tab index -999 are eligible for keyboard navigation but not for Tab navigation.
|
|
6
6
|
const PSEUDO_FOCUSABLE_TAB_INDEX = -999;
|
|
7
7
|
const FOCUSABLES_SELECTOR = `[tabIndex="0"],[tabIndex="${PSEUDO_FOCUSABLE_TAB_INDEX}"]`;
|
|
8
|
-
/**
|
|
9
|
-
* Finds focused cell props corresponding the focused element inside the table.
|
|
10
|
-
* The function relies on ARIA colindex/rowindex attributes being correctly applied.
|
|
11
|
-
*/
|
|
12
|
-
export function findFocusinCell(event) {
|
|
13
|
-
var _a, _b;
|
|
14
|
-
if (!(event.target instanceof HTMLElement)) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const element = event.target;
|
|
18
|
-
const cellElement = element.closest('td,th');
|
|
19
|
-
const rowElement = cellElement === null || cellElement === void 0 ? void 0 : cellElement.closest('tr');
|
|
20
|
-
if (!cellElement || !rowElement) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const colIndex = parseInt((_a = cellElement.getAttribute('aria-colindex')) !== null && _a !== void 0 ? _a : '');
|
|
24
|
-
const rowIndex = parseInt((_b = rowElement.getAttribute('aria-rowindex')) !== null && _b !== void 0 ? _b : '');
|
|
25
|
-
if (isNaN(colIndex) || isNaN(rowIndex)) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const cellFocusables = getFocusables(cellElement);
|
|
29
|
-
const elementIndex = cellFocusables.indexOf(element);
|
|
30
|
-
return { rowIndex, colIndex, rowElement, cellElement, element, elementIndex };
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Moves table focus in the provided direction. The focus can transition between cells or interactive elements inside cells.
|
|
34
|
-
*/
|
|
35
|
-
export function moveFocusBy(table, from, delta) {
|
|
36
|
-
var _a;
|
|
37
|
-
// Find next row to move focus into (can be null if the top/bottom is reached).
|
|
38
|
-
const targetAriaRowIndex = from.rowIndex + delta.y;
|
|
39
|
-
const targetRow = findTableRowByAriaRowIndex(table, targetAriaRowIndex, delta.y);
|
|
40
|
-
if (!targetRow) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
// Move focus to the next interactive cell content element if available.
|
|
44
|
-
const cellFocusables = getFocusables(from.cellElement);
|
|
45
|
-
const nextElementIndex = from.elementIndex + delta.x;
|
|
46
|
-
if (delta.x && from.elementIndex !== -1 && 0 <= nextElementIndex && nextElementIndex < cellFocusables.length) {
|
|
47
|
-
focus(cellFocusables[nextElementIndex]);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
// Find next cell to focus or move focus into (can be null if the left/right edge is reached).
|
|
51
|
-
const targetAriaColIndex = from.colIndex + delta.x;
|
|
52
|
-
const targetCell = findTableRowCellByAriaColIndex(targetRow, targetAriaColIndex, delta.x);
|
|
53
|
-
if (!targetCell) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
// When target cell matches the current cell it means we reached the left or right boundary.
|
|
57
|
-
if (targetCell === from.cellElement) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
// Move focus on the cell interactive content or the cell itself.
|
|
61
|
-
const targetCellFocusables = getFocusables(targetCell);
|
|
62
|
-
const focusIndex = delta.x < 0 ? targetCellFocusables.length - 1 : delta.x > 0 ? 0 : from.elementIndex;
|
|
63
|
-
const focusTarget = (_a = targetCellFocusables[focusIndex]) !== null && _a !== void 0 ? _a : targetCell;
|
|
64
|
-
focus(focusTarget);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Makes the cell element, the first interactive element or the first cell of the table user-focusable.
|
|
68
|
-
*/
|
|
69
|
-
export function ensureSingleFocusable(table, cell) {
|
|
70
|
-
var _a;
|
|
71
|
-
const firstTableCell = table.querySelector('td,th');
|
|
72
|
-
// A single element of the table is made user-focusable.
|
|
73
|
-
// It defaults to the first interactive element of the first cell or the first cell itself otherwise.
|
|
74
|
-
let focusTarget = (_a = (firstTableCell && getFocusables(firstTableCell)[0])) !== null && _a !== void 0 ? _a : firstTableCell;
|
|
75
|
-
// When a navigation-focused element is present in the table it is used for user-navigation instead.
|
|
76
|
-
if (cell && table.contains(cell.element) && isUserFocusable(cell.element)) {
|
|
77
|
-
focusTarget = cell.element;
|
|
78
|
-
}
|
|
79
|
-
setTabIndex(focusTarget, 0);
|
|
80
|
-
}
|
|
81
8
|
/**
|
|
82
9
|
* Makes all element focusable children pseudo-focusable unless the grid navigation is suppressed.
|
|
83
10
|
*/
|
|
@@ -144,7 +71,7 @@ export function getFirstFocusable(element) {
|
|
|
144
71
|
/**
|
|
145
72
|
* Finds the closest row to the targetAriaRowIndex+delta in the direction of delta.
|
|
146
73
|
*/
|
|
147
|
-
function findTableRowByAriaRowIndex(table, targetAriaRowIndex, delta) {
|
|
74
|
+
export function findTableRowByAriaRowIndex(table, targetAriaRowIndex, delta) {
|
|
148
75
|
var _a;
|
|
149
76
|
let targetRow = null;
|
|
150
77
|
const rowElements = Array.from(table.querySelectorAll('tr[aria-rowindex]'));
|
|
@@ -169,7 +96,7 @@ function findTableRowByAriaRowIndex(table, targetAriaRowIndex, delta) {
|
|
|
169
96
|
/**
|
|
170
97
|
* Finds the closest column to the targetAriaColIndex+delta in the direction of delta.
|
|
171
98
|
*/
|
|
172
|
-
function findTableRowCellByAriaColIndex(tableRow, targetAriaColIndex, delta) {
|
|
99
|
+
export function findTableRowCellByAriaColIndex(tableRow, targetAriaColIndex, delta) {
|
|
173
100
|
var _a;
|
|
174
101
|
let targetCell = null;
|
|
175
102
|
const cellElements = Array.from(tableRow.querySelectorAll('td[aria-colindex],th[aria-colindex]'));
|
|
@@ -191,16 +118,9 @@ function findTableRowCellByAriaColIndex(tableRow, targetAriaColIndex, delta) {
|
|
|
191
118
|
}
|
|
192
119
|
return targetCell;
|
|
193
120
|
}
|
|
194
|
-
function
|
|
195
|
-
setTabIndex(element, 0);
|
|
196
|
-
element === null || element === void 0 ? void 0 : element.focus();
|
|
197
|
-
}
|
|
198
|
-
function setTabIndex(element, tabIndex) {
|
|
121
|
+
export function setTabIndex(element, tabIndex) {
|
|
199
122
|
if (element && element.tabIndex !== tabIndex) {
|
|
200
123
|
element.tabIndex = tabIndex;
|
|
201
124
|
}
|
|
202
125
|
}
|
|
203
|
-
function isUserFocusable(element) {
|
|
204
|
-
return element.matches(FOCUSABLES_SELECTOR);
|
|
205
|
-
}
|
|
206
126
|
//# sourceMappingURL=utils.js.map
|