@astryxdesign/core 0.7.3-canary.f94e962 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +8 -4
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +7 -0
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +15 -2
- package/dist/Calendar/Calendar.js +3 -2
- package/dist/Chat/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +25 -2
- package/dist/Chat/ChatSystemMessage.d.ts +4 -0
- package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
- package/dist/Chat/ChatSystemMessage.js +4 -2
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +11 -1
- package/dist/DateRangeInput/DateRangeInput.d.ts +5 -5
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +27 -18
- package/dist/DateTimeInput/DateTimeInput.d.ts +5 -5
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +25 -27
- package/dist/DateTimeInput/NativeDateSegment.d.ts +3 -2
- package/dist/DateTimeInput/NativeDateSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeDateSegment.js +5 -5
- package/dist/DateTimeInput/NativeTimeSegment.d.ts +3 -2
- package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
- package/dist/DateTimeInput/NativeTimeSegment.js +5 -5
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +12 -9
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +9 -4
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +9 -5
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +2 -0
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +48 -15
- package/dist/MultiSelector/MultiSelector.d.ts +4 -4
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.d.ts +2 -0
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.d.ts.map +1 -0
- package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.js +48 -0
- package/dist/MultiSelector/MultiSelector.js +25 -18
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +5 -2
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +13 -3
- package/dist/TimeInput/TimeInput.d.ts +5 -5
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +37 -20
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +17 -6
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +41 -8
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +8 -3
- package/dist/hooks/useGridFocus.d.ts.map +1 -1
- package/dist/hooks/useGridFocus.js +10 -1
- package/package.json +4 -4
- package/scripts/agent-doc-state.mjs +24 -2
- package/src/BottomSheet/BottomSheet.spec.md +26 -53
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +8 -4
- package/src/BottomSheet/BottomSheetSwitcher.spec.md +45 -48
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +26 -0
- package/src/BottomSheet/BottomSheetSwitcher.tsx +6 -0
- package/src/Button/Button.test.tsx +13 -2
- package/src/Button/Button.tsx +23 -2
- package/src/Button/__tests__/Button.a11y.states.ts +4 -3
- package/src/Calendar/Calendar.test.tsx +68 -24
- package/src/Calendar/Calendar.tsx +2 -1
- package/src/Carousel/Carousel.spec.md +146 -0
- package/src/Chat/ChatComposer.spec.md +26 -26
- package/src/Chat/ChatComposer.test.tsx +58 -2
- package/src/Chat/ChatComposer.tsx +25 -1
- package/src/Chat/ChatSystemMessage.doc.mjs +1 -1
- package/src/Chat/ChatSystemMessage.test.tsx +25 -3
- package/src/Chat/ChatSystemMessage.tsx +6 -4
- package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +8 -9
- package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +7 -6
- package/src/CodeBlock/CodeBlock.highlightStyles.test.tsx +87 -0
- package/src/CodeBlock/CodeBlock.tsx +11 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +6 -5
- package/src/DateRangeInput/DateRangeInput.test.tsx +82 -16
- package/src/DateRangeInput/DateRangeInput.tsx +40 -20
- package/src/DateTimeInput/DateTimeInput.doc.mjs +9 -8
- package/src/DateTimeInput/DateTimeInput.test.tsx +126 -11
- package/src/DateTimeInput/DateTimeInput.tsx +42 -26
- package/src/DateTimeInput/DateTimeInputTouch.test.tsx +24 -0
- package/src/DateTimeInput/NativeDateSegment.tsx +9 -6
- package/src/DateTimeInput/NativePickerSegments.test.tsx +31 -0
- package/src/DateTimeInput/NativeTimeSegment.tsx +9 -6
- package/src/DateTimeInput/TouchDateTimeField.tsx +22 -7
- package/src/Dialog/Dialog.test.tsx +39 -0
- package/src/Dialog/Dialog.tsx +9 -4
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +66 -7
- package/src/DropdownMenu/DropdownMenuItem.spec.md +209 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +62 -7
- package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +190 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +10 -4
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +93 -0
- package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +366 -0
- package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +327 -0
- package/src/FieldStatus/FieldStatus.spec.md +4 -4
- package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +0 -18
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +3 -18
- package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +1 -2
- package/src/Markdown/Markdown.helpers.perf.test.ts +25 -19
- package/src/Markdown/Markdown.rerender.test.tsx +143 -0
- package/src/Markdown/Markdown.tsx +89 -35
- package/src/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.ts +71 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +5 -5
- package/src/MultiSelector/MultiSelector.focusableDisabled.test.tsx +121 -0
- package/src/MultiSelector/MultiSelector.test.tsx +49 -7
- package/src/MultiSelector/MultiSelector.tsx +53 -29
- package/src/Pagination/Pagination.test.tsx +5 -1
- package/src/Pagination/Pagination.tsx +3 -1
- package/src/Switch/Switch.test.tsx +56 -0
- package/src/Switch/Switch.tsx +13 -2
- package/src/Table/plugins/columnResize/useTableColumnResize.spec.md +11 -11
- package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +4 -3
- package/src/TimeInput/NativeTimeInput.test.tsx +25 -1
- package/src/TimeInput/Presentation.test.tsx +23 -0
- package/src/TimeInput/TimeInput.doc.mjs +8 -7
- package/src/TimeInput/TimeInput.test.tsx +119 -7
- package/src/TimeInput/TimeInput.tsx +51 -23
- package/src/TimeInput/TouchTimeField.tsx +27 -5
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Timestamp/Timestamp.test.tsx +168 -0
- package/src/Timestamp/Timestamp.tsx +62 -11
- package/src/TreeList/TreeList.test.tsx +28 -0
- package/src/TreeList/TreeListItem.tsx +6 -2
- package/src/__tests__/DisabledTriggerFocus.a11y.chromium.spec.ts +86 -0
- package/src/hooks/useGridFocus.ts +16 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file MultiSelector.disabledFocus.a11y.chromium.spec.ts
|
|
5
|
+
* @input The `Core/MultiSelector` Default story in a built Storybook.
|
|
6
|
+
* @output Real-engine proof of `spec:AST-085` FR4: a trigger disabled while it
|
|
7
|
+
* holds keyboard focus keeps focus and reports itself disabled.
|
|
8
|
+
* @position Run with `pnpm test:a11y-contract`; a DOM emulator does not move
|
|
9
|
+
* focus off an element that becomes disabled, so it cannot show the loss.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { expect, test } from '@playwright/test';
|
|
13
|
+
import { DEFAULT_STORYBOOK_DIR, serveStorybook } from '@astryxdesign/a11y-spec/storybook';
|
|
14
|
+
let storybook;
|
|
15
|
+
test.beforeAll(async () => {
|
|
16
|
+
storybook = await serveStorybook(process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR);
|
|
17
|
+
});
|
|
18
|
+
test.afterAll(async () => {
|
|
19
|
+
await storybook?.close();
|
|
20
|
+
});
|
|
21
|
+
test('a focused trigger keeps focus when it becomes disabled', async ({
|
|
22
|
+
page
|
|
23
|
+
}) => {
|
|
24
|
+
const storyId = 'core-multiselector--default';
|
|
25
|
+
await page.goto(`${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`);
|
|
26
|
+
const trigger = page.getByRole('combobox', {
|
|
27
|
+
name: 'Columns'
|
|
28
|
+
});
|
|
29
|
+
await trigger.focus();
|
|
30
|
+
await expect(trigger).toBeFocused();
|
|
31
|
+
await page.evaluate(id => {
|
|
32
|
+
const channel = window.__STORYBOOK_ADDONS_CHANNEL__;
|
|
33
|
+
if (channel == null) {
|
|
34
|
+
throw new Error('Storybook channel is unavailable');
|
|
35
|
+
}
|
|
36
|
+
channel.emit('updateStoryArgs', {
|
|
37
|
+
storyId: id,
|
|
38
|
+
updatedArgs: {
|
|
39
|
+
isDisabled: true
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}, storyId);
|
|
43
|
+
|
|
44
|
+
// Wait for the disabled re-render in either form, so focus is checked
|
|
45
|
+
// after it lands.
|
|
46
|
+
await expect.poll(async () => trigger.evaluate(el => el.hasAttribute('disabled') || el.getAttribute('aria-disabled') === 'true')).toBe(true);
|
|
47
|
+
await expect(trigger).toBeFocused();
|
|
48
|
+
});
|
|
@@ -122,6 +122,11 @@ const styles = {
|
|
|
122
122
|
k3aq6I: "x3oybdh xk4oym4 x5lqen6 x3y5scg",
|
|
123
123
|
$$css: true
|
|
124
124
|
},
|
|
125
|
+
triggerDisabledFocus: {
|
|
126
|
+
kVAM5u: "xvy26l8 x16it14f",
|
|
127
|
+
kGVxlE: "x1gnnqk1 xwmge8v",
|
|
128
|
+
$$css: true
|
|
129
|
+
},
|
|
125
130
|
triggerGhostDisabled: {
|
|
126
131
|
kKwaWg: "x18o3ruo",
|
|
127
132
|
k3aq6I: "x1c071of x1pdlv7q x1ylxtyb",
|
|
@@ -1393,8 +1398,13 @@ export function MultiSelector({
|
|
|
1393
1398
|
xstyle: styles.popover
|
|
1394
1399
|
});
|
|
1395
1400
|
}
|
|
1401
|
+
|
|
1402
|
+
// A disabled trigger stays focusable but, like a natively disabled button,
|
|
1403
|
+
// runs no caller event handler other than focus and blur, and a press on it
|
|
1404
|
+
// reaches no handler at all (`spec:AST-085` FR2).
|
|
1405
|
+
const callerProps = isDisabled ? Object.fromEntries(Object.entries(rest).filter(([key]) => !/^on[A-Z]/.test(key) || /^on(Focus|Blur)/.test(key))) : rest;
|
|
1396
1406
|
const triggerSharedProps = {
|
|
1397
|
-
...
|
|
1407
|
+
...callerProps,
|
|
1398
1408
|
id: triggerId,
|
|
1399
1409
|
// A caller's description is kept alongside the field's own.
|
|
1400
1410
|
'aria-describedby': [ariaDescribedByProp, ariaDescribedBy].filter(Boolean).join(' ') || undefined,
|
|
@@ -1405,13 +1415,16 @@ export function MultiSelector({
|
|
|
1405
1415
|
// Newly forwarded handlers compose component first: the built-in toggle
|
|
1406
1416
|
// and keyboard model always run. `onFocus` keeps its existing caller-first
|
|
1407
1417
|
// order.
|
|
1408
|
-
onKeyDown: composeEventHandlers(onKeyDown, onKeyDownProp),
|
|
1418
|
+
onKeyDown: isDisabled ? onKeyDown : composeEventHandlers(onKeyDown, onKeyDownProp),
|
|
1409
1419
|
onFocus: event => {
|
|
1410
1420
|
onFocus?.(event);
|
|
1411
1421
|
surface.onTriggerFocus(event);
|
|
1412
1422
|
},
|
|
1413
|
-
onClick:
|
|
1414
|
-
|
|
1423
|
+
onClick: isDisabled ? event => {
|
|
1424
|
+
event.preventDefault();
|
|
1425
|
+
event.stopPropagation();
|
|
1426
|
+
} : composeEventHandlers(handleTriggerButtonClick, onClick),
|
|
1427
|
+
tabIndex: 0,
|
|
1415
1428
|
...{
|
|
1416
1429
|
0: {
|
|
1417
1430
|
className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1ypdohk x16khyan x1a2a7pz xh6dtrn"
|
|
@@ -1468,11 +1481,10 @@ export function MultiSelector({
|
|
|
1468
1481
|
'aria-readonly': isEffectivelyReadOnly || undefined
|
|
1469
1482
|
};
|
|
1470
1483
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
1471
|
-
children: [renderTrigger(triggerProps), htmlName != null && value.map(v => /*#__PURE__*/_jsx("input", {
|
|
1484
|
+
children: [renderTrigger(triggerProps), htmlName != null && !isDisabled && value.map(v => /*#__PURE__*/_jsx("input", {
|
|
1472
1485
|
type: "hidden",
|
|
1473
1486
|
name: htmlName,
|
|
1474
|
-
value: v
|
|
1475
|
-
disabled: isDisabled
|
|
1487
|
+
value: v
|
|
1476
1488
|
}, v)), selectionSurface]
|
|
1477
1489
|
});
|
|
1478
1490
|
}
|
|
@@ -1493,7 +1505,7 @@ export function MultiSelector({
|
|
|
1493
1505
|
status: status?.type ?? null,
|
|
1494
1506
|
disabled: isDisabled ? 'disabled' : null,
|
|
1495
1507
|
readonly: isEffectivelyReadOnly ? 'readonly' : null
|
|
1496
|
-
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'secondary' && styles.triggerSecondary, variant === 'ghost' && styles.triggerGhost, variant !== 'input' && interactionOverlayStyles.backgroundImage, variant !== 'input' && focusOutlineStyles.focusWithin, surface.isTriggerFocusRingSuppressed && selectorPresentationStyles.pointerRestoredFocus, isDisabled && inputWrapperStyles.disabled, isEffectivelyReadOnly && styles.triggerReadOnly, variant !== 'input' && isDisabled && styles.triggerGhostDisabled, variant !== 'input' && isEffectivelyReadOnly && styles.triggerGhostReadOnly, optimisticValue.length === 0 && styles.triggerPlaceholder, variant === 'input' && status && inputStatusBorderStyles[status.type], variant === 'input' && status && !isDisabled && inputStatusHoverShadowStyles[status.type],
|
|
1508
|
+
}), stylex.props(inputWrapperStyles.base, styles.triggerContainer, sizeStyles[size], variant === 'secondary' && styles.triggerSecondary, variant === 'ghost' && styles.triggerGhost, variant !== 'input' && interactionOverlayStyles.backgroundImage, variant !== 'input' && focusOutlineStyles.focusWithin, surface.isTriggerFocusRingSuppressed && selectorPresentationStyles.pointerRestoredFocus, isDisabled && inputWrapperStyles.disabled, variant === 'input' && isDisabled && !surface.isTriggerFocusRingSuppressed && styles.triggerDisabledFocus, isEffectivelyReadOnly && styles.triggerReadOnly, variant !== 'input' && isDisabled && styles.triggerGhostDisabled, variant !== 'input' && isEffectivelyReadOnly && styles.triggerGhostReadOnly, optimisticValue.length === 0 && styles.triggerPlaceholder, variant === 'input' && status && inputStatusBorderStyles[status.type], variant === 'input' && status && !isDisabled && inputStatusHoverShadowStyles[status.type],
|
|
1497
1509
|
// Grouped secondary triggers keep their neutral fill but take the
|
|
1498
1510
|
// group's row height, connected edge radii, and focus stacking.
|
|
1499
1511
|
variant !== 'ghost' && inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
@@ -1521,26 +1533,21 @@ export function MultiSelector({
|
|
|
1521
1533
|
"aria-controls": isEffectivelyReadOnly ? undefined : listboxId,
|
|
1522
1534
|
"aria-readonly": isEffectivelyReadOnly || undefined,
|
|
1523
1535
|
"aria-activedescendant": !isEffectivelyReadOnly && !hasSearch ? activeDescendantId : undefined
|
|
1524
|
-
//
|
|
1525
|
-
// aria-disabled
|
|
1526
|
-
//
|
|
1536
|
+
// A disabled trigger keeps its tab stop and says so through
|
|
1537
|
+
// aria-disabled (`spec:AST-085` FR1); the isDisabled guards in
|
|
1538
|
+
// useMultiCombobox block every way of opening it.
|
|
1527
1539
|
,
|
|
1528
|
-
disabled: isDisabled
|
|
1529
|
-
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
1540
|
+
"aria-disabled": isDisabled ? 'true' : undefined,
|
|
1530
1541
|
children: /*#__PURE__*/_jsx("span", {
|
|
1531
1542
|
...{
|
|
1532
1543
|
className: "x78zum5 x6s0dn4 xzye2dw x1iyjqo2 xs83m0k x1r8uery xeuugli xb3r6kr"
|
|
1533
1544
|
},
|
|
1534
1545
|
children: renderTriggerContent()
|
|
1535
1546
|
})
|
|
1536
|
-
}), htmlName != null && value.map(v => /*#__PURE__*/_jsx("input", {
|
|
1547
|
+
}), htmlName != null && !isDisabled && value.map(v => /*#__PURE__*/_jsx("input", {
|
|
1537
1548
|
type: "hidden",
|
|
1538
1549
|
name: htmlName,
|
|
1539
1550
|
value: v
|
|
1540
|
-
// Disabled native controls are excluded from form submission;
|
|
1541
|
-
// mirror that for the hidden carriers.
|
|
1542
|
-
,
|
|
1543
|
-
disabled: isDisabled
|
|
1544
1551
|
}, v)), isBusy && /*#__PURE__*/_jsx(Spinner, {
|
|
1545
1552
|
size: "sm"
|
|
1546
1553
|
}), hasClear && value.length > 0 && !isDisabled && !isEffectivelyReadOnly && /*#__PURE__*/_jsx(InternalInputClearButton, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/Pagination/Pagination.tsx"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAI5C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,SAAS,CAAC;AAMlD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,oBAAoB,CAAC;AAE3D,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAE7B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGtD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAG9C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAGtB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IAGrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAwHD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CA6CpB;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,UAAU,EAAE,cAAc,EAC1B,OAAO,EACP,QAAQ,EAAE,YAAiB,EAC3B,eAAe,EACf,gBAAgB,EAChB,OAAiB,EACjB,SAAS,EACT,YAAY,EAAE,gBAAgB,EAC9B,IAAI,EAAE,QAAY,EAClB,YAAgB,EAChB,IAAW,EACX,UAAkB,EAClB,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,MAAM,EACrB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/Pagination/Pagination.tsx"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAI5C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,SAAS,CAAC;AAMlD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,oBAAoB,CAAC;AAE3D,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAE7B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGtD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAG9C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAGtB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IAGrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAwHD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CA6CpB;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,UAAU,EAAE,cAAc,EAC1B,OAAO,EACP,QAAQ,EAAE,YAAiB,EAC3B,eAAe,EACf,gBAAgB,EAChB,OAAiB,EACjB,SAAS,EACT,YAAY,EAAE,gBAAgB,EAC9B,IAAI,EAAE,QAAY,EAClB,YAAgB,EAChB,IAAW,EACX,UAAkB,EAClB,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,MAAM,EACrB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,eAAe,sCA0fjB;yBAlhBe,UAAU"}
|
|
@@ -496,8 +496,11 @@ export function Pagination({
|
|
|
496
496
|
// needs to focus the dot, which some browsers (Safari) skip for
|
|
497
497
|
// buttons, so focus it explicitly.
|
|
498
498
|
,
|
|
499
|
-
onClick: e => e.currentTarget.focus()
|
|
500
|
-
|
|
499
|
+
onClick: e => e.currentTarget.focus()
|
|
500
|
+
// Disabled dots stay focusable (spec:AST-085 FR1); the page
|
|
501
|
+
// only follows focus while enabled (handleDotsFocus).
|
|
502
|
+
,
|
|
503
|
+
"aria-disabled": isDisabled || undefined,
|
|
501
504
|
...mergeProps(themeProps('pagination-dot', {
|
|
502
505
|
active: isActive ? 'active' : null,
|
|
503
506
|
size
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../src/Switch/Switch.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../src/Switch/Switch.tsx"],"names":[],"mappings":"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"}
|
package/dist/Switch/Switch.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - /apps/storybook/stories/Switch.stories.tsx (storybook stories)
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/Switch/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
|
-
import { useId, useOptimistic, useTransition, useEffect } from 'react';
|
|
18
|
+
import { useId, useOptimistic, useTransition, useEffect, useRef } from 'react';
|
|
19
19
|
import * as stylex from '@stylexjs/stylex';
|
|
20
20
|
import "../theme/tokens.stylex.js";
|
|
21
21
|
import { colorVars, spacingVars, radiusVars, durationVars, easeVars, typographyVars, typeScaleVars, focusVars } from "../theme/tokens.stylex.js";
|
|
@@ -313,10 +313,20 @@ export function Switch({
|
|
|
313
313
|
const [optimisticValue, setOptimisticValue] = useOptimistic(value);
|
|
314
314
|
const isBusy = isLoading || optimisticValue !== value;
|
|
315
315
|
const announce = useAnnounce();
|
|
316
|
+
// One announcement per busy start (spec:AST-086 FR6). The ref outlives
|
|
317
|
+
// StrictMode's effect replay and rerenders, so only a new busy start (busy
|
|
318
|
+
// again after settling) speaks again.
|
|
319
|
+
const announcedBusyRef = useRef(false);
|
|
316
320
|
useEffect(() => {
|
|
317
|
-
if (isBusy) {
|
|
318
|
-
|
|
321
|
+
if (!isBusy) {
|
|
322
|
+
announcedBusyRef.current = false;
|
|
323
|
+
return;
|
|
319
324
|
}
|
|
325
|
+
if (announcedBusyRef.current) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
announcedBusyRef.current = true;
|
|
329
|
+
announce(t('@astryx.switch.loading'));
|
|
320
330
|
}, [announce, isBusy, t]);
|
|
321
331
|
const isOn = optimisticValue === true;
|
|
322
332
|
|
|
@@ -59,12 +59,12 @@ export interface TimeInputProps extends Omit<FieldInputBaseProps<HTMLInputElemen
|
|
|
59
59
|
/**
|
|
60
60
|
* Explains why the input is disabled. When set together with
|
|
61
61
|
* `isDisabled`, the input shows a tooltip with this text on hover and
|
|
62
|
-
* keyboard focus, and the
|
|
63
|
-
*
|
|
64
|
-
*
|
|
62
|
+
* keyboard focus, and the input is described by it, so the reason is
|
|
63
|
+
* discoverable by keyboard and assistive technology. Typing and arrow-key
|
|
64
|
+
* adjustment stay blocked.
|
|
65
65
|
*
|
|
66
|
-
* Use this instead of wrapping a disabled input in `Tooltip
|
|
67
|
-
*
|
|
66
|
+
* Use this instead of wrapping a disabled input in `Tooltip`, so the
|
|
67
|
+
* reason is tied to the field.
|
|
68
68
|
*
|
|
69
69
|
* @example
|
|
70
70
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeInput.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TimeInput.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAO3C,OAAO,EAGL,KAAK,WAAW,EAKhB,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAIlB,OAAO,EACL,KAAK,aAAa,EAUnB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAgB9C,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"TimeInput.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TimeInput.tsx"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,13 @@ import { effectiveInputPresentation, resolveInputPresentation } from "../utils/i
|
|
|
46
46
|
import { useDevWarning } from "../hooks/useDevWarning.js";
|
|
47
47
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
48
48
|
const TOUCH_POINTER_QUERY = '(pointer: coarse)';
|
|
49
|
+
const styles = {
|
|
50
|
+
disabledFocusRing: {
|
|
51
|
+
kVAM5u: "xvy26l8 x6q1khz",
|
|
52
|
+
kGVxlE: "x1gnnqk1 xuihb5h",
|
|
53
|
+
$$css: true
|
|
54
|
+
}
|
|
55
|
+
};
|
|
49
56
|
const sizeStyles = {
|
|
50
57
|
sm: {
|
|
51
58
|
kZKoxP: "x6k0iem",
|
|
@@ -175,10 +182,10 @@ function TimeField({
|
|
|
175
182
|
}
|
|
176
183
|
}, [announce, inputGroup, status?.message, status?.type]);
|
|
177
184
|
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
185
|
+
// A disabled field never renders native `disabled` (spec:AST-085 FR1): the
|
|
186
|
+
// input stays focusable via aria-disabled, and readOnly plus the value
|
|
187
|
+
// mutation guards block typing. The disabled-reason tooltip's listeners
|
|
188
|
+
// attach to the input container.
|
|
182
189
|
const showsDisabledMessage = isDisabled && !!disabledMessage;
|
|
183
190
|
const disabledMessageTooltip = useTooltip({
|
|
184
191
|
placement: 'above',
|
|
@@ -258,8 +265,8 @@ function TimeField({
|
|
|
258
265
|
|
|
259
266
|
// Handle input text change - update immediately if valid
|
|
260
267
|
const handleInputChange = useCallback(e => {
|
|
261
|
-
//
|
|
262
|
-
//
|
|
268
|
+
// The input is never natively disabled (spec:AST-085 FR1), so guard
|
|
269
|
+
// value mutation explicitly (readOnly also blocks typing).
|
|
263
270
|
if (isDisabled) {
|
|
264
271
|
return;
|
|
265
272
|
}
|
|
@@ -290,6 +297,11 @@ function TimeField({
|
|
|
290
297
|
if (pendingInput === null) {
|
|
291
298
|
return;
|
|
292
299
|
}
|
|
300
|
+
// Disabled: keep the value, drop the draft (spec:AST-085 FR2).
|
|
301
|
+
if (isDisabled) {
|
|
302
|
+
setPendingInput(null);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
293
305
|
if (!pendingInput.trim()) {
|
|
294
306
|
// Empty input clears the value
|
|
295
307
|
if (value !== undefined) {
|
|
@@ -307,9 +319,15 @@ function TimeField({
|
|
|
307
319
|
}
|
|
308
320
|
// Clear pending input - display will revert to formatted value
|
|
309
321
|
setPendingInput(null);
|
|
310
|
-
}, [pendingInput, value, fireChange, hasSeconds, min, max]);
|
|
322
|
+
}, [pendingInput, isDisabled, value, fireChange, hasSeconds, min, max]);
|
|
311
323
|
|
|
312
324
|
// Handle keyboard navigation on input
|
|
325
|
+
// Enter in a focused disabled field would submit a surrounding form (FR2).
|
|
326
|
+
const preventDisabledEnter = e => {
|
|
327
|
+
if (isDisabled && e.key === 'Enter') {
|
|
328
|
+
e.preventDefault();
|
|
329
|
+
}
|
|
330
|
+
};
|
|
313
331
|
const handleInputKeyDown = useCallback(e => {
|
|
314
332
|
// ArrowUp/ArrowDown step the time and preventDefault; an IME candidate
|
|
315
333
|
// window uses those same arrows to navigate candidates, so guard the
|
|
@@ -318,8 +336,7 @@ function TimeField({
|
|
|
318
336
|
if (isImeKeyEvent(e.nativeEvent)) {
|
|
319
337
|
return;
|
|
320
338
|
}
|
|
321
|
-
// Arrow-key adjustment mutates the value; block it while
|
|
322
|
-
// disabled reason (the input keeps focusability via aria-disabled).
|
|
339
|
+
// Arrow-key adjustment mutates the value; block it while disabled.
|
|
323
340
|
if (isDisabled) {
|
|
324
341
|
return;
|
|
325
342
|
}
|
|
@@ -393,7 +410,7 @@ function TimeField({
|
|
|
393
410
|
size,
|
|
394
411
|
status: status?.type ?? null,
|
|
395
412
|
disabled: isDisabled ? 'disabled' : null
|
|
396
|
-
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
413
|
+
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, isDisabled && styles.disabledFocusRing, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
397
414
|
children: [inputGroup && /*#__PURE__*/_jsx(VisuallyHidden, {
|
|
398
415
|
id: inputLabelID,
|
|
399
416
|
children: label
|
|
@@ -420,7 +437,7 @@ function TimeField({
|
|
|
420
437
|
max: max,
|
|
421
438
|
hourFormat: hourFormat,
|
|
422
439
|
isEffectivelyDisabled: isDisabled,
|
|
423
|
-
|
|
440
|
+
isFocusableDisabled: true,
|
|
424
441
|
isEffectivelyRequired: isEffectivelyRequired,
|
|
425
442
|
isBusy: isBusy,
|
|
426
443
|
statusType: status?.type,
|
|
@@ -429,7 +446,8 @@ function TimeField({
|
|
|
429
446
|
...controlProps,
|
|
430
447
|
onFocus: onFocusProp,
|
|
431
448
|
onBlur: onBlurProp,
|
|
432
|
-
onKeyDown: onKeyDownProp
|
|
449
|
+
onKeyDown: chainEventHandlers(preventDisabledEnter, onKeyDownProp),
|
|
450
|
+
onClick: isDisabled ? undefined : controlProps.onClick
|
|
433
451
|
}
|
|
434
452
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
435
453
|
children: [/*#__PURE__*/_jsx("div", {
|
|
@@ -450,15 +468,14 @@ function TimeField({
|
|
|
450
468
|
onChange: handleInputChange,
|
|
451
469
|
onFocus: chainEventHandlers(handleFocus, onFocusProp),
|
|
452
470
|
onBlur: chainEventHandlers(handleBlur, onBlurProp),
|
|
453
|
-
onKeyDown: chainEventHandlers(handleInputKeyDown, onKeyDownProp),
|
|
454
|
-
placeholder: displayPlaceholder
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
//
|
|
471
|
+
onKeyDown: chainEventHandlers(preventDisabledEnter, handleInputKeyDown, onKeyDownProp),
|
|
472
|
+
placeholder: displayPlaceholder,
|
|
473
|
+
"aria-disabled": isDisabled ? 'true' : undefined,
|
|
474
|
+
readOnly: isDisabled || undefined
|
|
475
|
+
// The browser still dispatches a click on an aria-disabled input,
|
|
476
|
+
// so a caller's onClick is skipped too (spec:AST-085 FR2).
|
|
458
477
|
,
|
|
459
|
-
|
|
460
|
-
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
461
|
-
readOnly: showsDisabledMessage || undefined,
|
|
478
|
+
onClick: isDisabled ? undefined : controlProps.onClick,
|
|
462
479
|
autoFocus: hasAutoFocus,
|
|
463
480
|
"data-autofocus": hasAutoFocus || undefined,
|
|
464
481
|
"aria-describedby": ariaDescribedBy,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"AAoEA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAqHhD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAe,EACf,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,GAAG,EACH,GAAG,EACH,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAGlB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,aAAa,EAC3B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EACN,aAA0B,EAC1B,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,GAAG,SAAS,EACb,EAAE,cAAc,+BAiZhB;yBApbe,cAAc"}
|
|
@@ -30,7 +30,7 @@ import { useResolvedRequired } from "../hooks/useResolvedRequired.js";
|
|
|
30
30
|
import { Icon } from "../Icon/index.js";
|
|
31
31
|
import { useTranslator } from "../i18n/index.js";
|
|
32
32
|
import "../theme/tokens.stylex.js";
|
|
33
|
-
import { sizeVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
33
|
+
import { colorVars, sizeVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
34
34
|
import { useSize } from "../SizeContext/SizeContext.js";
|
|
35
35
|
import { Spinner } from "../Spinner/index.js";
|
|
36
36
|
import { useTooltip } from "../Tooltip/index.js";
|
|
@@ -60,6 +60,11 @@ const sizeStyles = {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
const styles = {
|
|
63
|
+
disabledFocusRing: {
|
|
64
|
+
kVAM5u: "xvy26l8 x6q1khz",
|
|
65
|
+
kGVxlE: "x1gnnqk1 xuihb5h",
|
|
66
|
+
$$css: true
|
|
67
|
+
},
|
|
63
68
|
iconButton: {
|
|
64
69
|
k1xSpc: "x78zum5",
|
|
65
70
|
kGNEyG: "x6s0dn4",
|
|
@@ -309,11 +314,11 @@ export function TouchTimeField({
|
|
|
309
314
|
size,
|
|
310
315
|
status: status?.type ?? null,
|
|
311
316
|
disabled: isDisabled ? 'disabled' : null
|
|
312
|
-
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
317
|
+
}), stylex.props(inputWrapperStyles.base, sizeStyles[size], isDisabled && inputWrapperStyles.disabled, isDisabled && styles.disabledFocusRing, status && inputStatusBorderStyles[status.type], status && !isDisabled && inputStatusHoverShadowStyles[status.type], status && inputStatusFocusWithinStyles[status.type], inputGroup && groupStyles.inGroup, controlXstyle), controlClassName, controlStyle),
|
|
313
318
|
children: [/*#__PURE__*/_jsx("button", {
|
|
314
319
|
type: "button",
|
|
315
320
|
onClick: openSheet,
|
|
316
|
-
disabled: isEffectivelyDisabled,
|
|
321
|
+
"aria-disabled": isEffectivelyDisabled || undefined,
|
|
317
322
|
"aria-label": t('@astryx.timeInput.openPicker', {
|
|
318
323
|
label
|
|
319
324
|
}),
|
|
@@ -334,10 +339,16 @@ export function TouchTimeField({
|
|
|
334
339
|
readOnly: true,
|
|
335
340
|
inputMode: "none",
|
|
336
341
|
onChange: () => {},
|
|
337
|
-
onClick: chainEventHandlers(openSheet, onClickProp)
|
|
342
|
+
onClick: isEffectivelyDisabled ? undefined : chainEventHandlers(openSheet, onClickProp)
|
|
343
|
+
// Enter would submit a surrounding form while disabled (FR2).
|
|
344
|
+
,
|
|
345
|
+
onKeyDown: chainEventHandlers(e => {
|
|
346
|
+
if (isEffectivelyDisabled && e.key === 'Enter') {
|
|
347
|
+
e.preventDefault();
|
|
348
|
+
}
|
|
349
|
+
}, controlProps.onKeyDown),
|
|
338
350
|
placeholder: placeholder,
|
|
339
|
-
disabled: isEffectivelyDisabled
|
|
340
|
-
"aria-disabled": showsDisabledMessage ? 'true' : undefined,
|
|
351
|
+
"aria-disabled": isEffectivelyDisabled ? 'true' : undefined,
|
|
341
352
|
"aria-describedby": ariaDescribedBy,
|
|
342
353
|
"aria-required": isEffectivelyRequired ? 'true' : undefined,
|
|
343
354
|
"aria-invalid": status?.type === 'error' ? 'true' : undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../src/Timestamp/Timestamp.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../src/Timestamp/Timestamp.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAK9E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAK5C,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,kBAAkB,CAAC;AAY1B,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,gBAAgB,GAChB,MAAM,GACN,MAAM,GACN,WAAW,GACX,cAAc,GACd,WAAW,GACX,MAAM,GACN,aAAa,GACb,kBAAkB,GAClB,aAAa,GACb,cAAc,CAAC;AAEnB,MAAM,WAAW,cAAe,SAAQ,SAAS,CAAC,eAAe,CAAC;IAChE,kDAAkD;IAClD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC,uFAAuF;IACvF,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACtD;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;OAEG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsFD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,MAAe,EACf,aAAsC,EACtC,UAAiB,EACjB,cAAc,EACd,eAAuB,EACvB,MAAc,EACd,IAAmB,EACnB,IAAI,EACJ,KAAmB,EACnB,MAAM,EACN,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,aAAa,EAAE,MAAM,EACrB,GAAG,IAAI,EACR,EAAE,cAAc,sCAoMhB;yBAtNe,SAAS"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* - /apps/storybook/stories/Timestamp.stories.tsx
|
|
17
17
|
* - /packages/cli/assets/templates/blocks/components/Timestamp/ (showcase blocks)
|
|
18
18
|
*/
|
|
19
|
-
import { lazy, Suspense, useEffect, useRef, useState } from 'react';
|
|
19
|
+
import { lazy, Suspense, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
21
|
import { Text } from "../Text/index.js";
|
|
22
22
|
import { mergeProps } from "../utils/index.js";
|
|
@@ -76,6 +76,13 @@ function getLiveInterval(diffSeconds) {
|
|
|
76
76
|
return 300_000; // every 5 minutes
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// Whether this render may read the current time. Server renders (including
|
|
80
|
+
// static prerenders) and the hydration pass read `false`, so the markup never
|
|
81
|
+
// depends on when it was rendered; client renders read `true`.
|
|
82
|
+
const subscribeToNothing = () => () => {};
|
|
83
|
+
const canReadClockOnClient = () => true;
|
|
84
|
+
const canReadClockOnServer = () => false;
|
|
85
|
+
|
|
79
86
|
/** Whether a format is non-relative (i.e. shows a fixed date/time). */
|
|
80
87
|
function isAbsoluteFormat(format) {
|
|
81
88
|
return format !== 'relative' && format !== 'relative_short' && format !== 'auto';
|
|
@@ -134,7 +141,23 @@ export function Timestamp({
|
|
|
134
141
|
const locale = useLocale();
|
|
135
142
|
const timeRef = useRef(null);
|
|
136
143
|
const mergedTimeRef = useMergedRefs(ref, timeRef);
|
|
137
|
-
|
|
144
|
+
// Only relative and auto formats depend on the current time. Absolute
|
|
145
|
+
// formats never read the clock, so they render identically on the server,
|
|
146
|
+
// in a static prerender, and on the client. A clock-dependent format reads
|
|
147
|
+
// it on the client only: server renders and the hydration pass show the
|
|
148
|
+
// full date and time in UTC, and the client switches to the relative phrase
|
|
149
|
+
// right after hydration.
|
|
150
|
+
const needsClock = !isAbsoluteFormat(format);
|
|
151
|
+
const canReadClock = useSyncExternalStore(subscribeToNothing, canReadClockOnClient, canReadClockOnServer);
|
|
152
|
+
const [now, setNow] = useState(() => needsClock && canReadClock ? new Date() : null);
|
|
153
|
+
// Read the clock during render once it may be read: after hydration (the
|
|
154
|
+
// store re-renders with its client value) and when `format` switches from
|
|
155
|
+
// an absolute value to a clock-dependent one, so the placeholder never
|
|
156
|
+
// commits on the client.
|
|
157
|
+
if (needsClock && canReadClock && now === null) {
|
|
158
|
+
// eslint-disable-next-line @eslint-react/purity -- seeds clock state once, like the initializer; never runs on the server or while hydrating
|
|
159
|
+
setNow(new Date());
|
|
160
|
+
}
|
|
138
161
|
const date = parseValue(value);
|
|
139
162
|
// An unparseable value (a malformed date string, or a NaN timestamp from
|
|
140
163
|
// missing data) yields an Invalid Date, and formatting one throws "Invalid
|
|
@@ -143,13 +166,23 @@ export function Timestamp({
|
|
|
143
166
|
const isValidDate = !Number.isNaN(date.getTime());
|
|
144
167
|
const isoString = isValidDate ? date.toISOString() : '';
|
|
145
168
|
|
|
146
|
-
// Determine effective format
|
|
147
|
-
|
|
148
|
-
const
|
|
169
|
+
// Determine effective format. Until the clock is read, a clock-dependent
|
|
170
|
+
// format renders as the full date and time.
|
|
171
|
+
const isClockPending = needsClock && now === null;
|
|
172
|
+
const diffSeconds = now === null ? 0 : Math.round((now.getTime() - date.getTime()) / 1000);
|
|
173
|
+
const effectiveFormat = isClockPending ? 'date_time' : format === 'auto' ? Math.abs(diffSeconds) <= autoThreshold ? 'relative' : 'date_time' : format;
|
|
149
174
|
|
|
150
|
-
// Format the display text
|
|
151
|
-
//
|
|
152
|
-
|
|
175
|
+
// Format the display text in the viewer's own zone; only the tooltip names
|
|
176
|
+
// others. The one exception is the pre-hydration placeholder, written in
|
|
177
|
+
// UTC with its label so every zone produces the same markup.
|
|
178
|
+
const displayText = !isValidDate ? '' : effectiveFormat === 'relative' ? formatRelativeTime(date, now ?? date, locale, 'long') : effectiveFormat === 'relative_short' ? formatRelativeTime(date, now ?? date, locale, 'narrow') : isAbsoluteFormat(effectiveFormat) ? formatInstant(date, effectiveFormat, locale,
|
|
179
|
+
// The pre-hydration placeholder is written in UTC, labelled, so
|
|
180
|
+
// the server and the hydrating client produce the same text in
|
|
181
|
+
// every time zone.
|
|
182
|
+
isClockPending ? {
|
|
183
|
+
timeZone: 'UTC',
|
|
184
|
+
isTimezoneShown: true
|
|
185
|
+
} : {
|
|
153
186
|
isTimezoneShown
|
|
154
187
|
}) : '';
|
|
155
188
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeListItem.d.ts","sourceRoot":"","sources":["../../src/TreeList/TreeListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAyB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAoB3C,OAAO,KAAK,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"TreeListItem.d.ts","sourceRoot":"","sources":["../../src/TreeList/TreeListItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAyB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAoB3C,OAAO,KAAK,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,iBAAiB,CAAC;AA8RtE,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB;;;;OAIG;IACH,OAAO,EAAE,eAAe,CAAC;IACzB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAMD,wBAAgB,YAAY,CAAC,EAC3B,EAAE,EACF,KAAK,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,IAAI,EACJ,MAAM,EACN,UAAkB,EAClB,UAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,MAAM,EACN,eAAe,EACf,UAAU,EACV,QAAQ,EACR,OAAO,EACP,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,yBAAyB,+BAkT3B;yBA7Ue,YAAY"}
|
|
@@ -27,6 +27,7 @@ import { useLinkComponent } from "../Link/useLinkComponent.js";
|
|
|
27
27
|
import { TreeListBranches } from "./TreeListBranches.js";
|
|
28
28
|
import { themeProps } from "../utils/themeProps.js";
|
|
29
29
|
import { useTranslator } from "../i18n/index.js";
|
|
30
|
+
import { disabledInteractionProps } from "../utils/disabledInteraction.js";
|
|
30
31
|
|
|
31
32
|
// =============================================================================
|
|
32
33
|
// Styles
|
|
@@ -311,13 +312,16 @@ export function TreeListItem({
|
|
|
311
312
|
// Stable identity for TreeList's activateItem selector — do not
|
|
312
313
|
// remove without also updating TreeList.tsx.
|
|
313
314
|
,
|
|
314
|
-
"data-tree-toggle": ""
|
|
315
|
-
|
|
315
|
+
"data-tree-toggle": ""
|
|
316
|
+
// Disabled stays focusable (spec:AST-085 FR1) but inert (FR2).
|
|
317
|
+
,
|
|
318
|
+
"aria-disabled": isDisabled || undefined
|
|
316
319
|
// Roving tabindex lives on the treeitem row; the chevron toggle is not
|
|
317
320
|
// a separate tab stop. Row-level Enter/Space forwards to this button.
|
|
318
321
|
,
|
|
319
322
|
tabIndex: -1,
|
|
320
323
|
onClick: handleToggle,
|
|
324
|
+
...(isDisabled ? disabledInteractionProps() : null),
|
|
321
325
|
...mergeProps(
|
|
322
326
|
// Stable theme target for the expand/collapse control. `data-tree-toggle`
|
|
323
327
|
// stays as the functional activation hook; this adds a themeable
|
|
@@ -365,7 +369,8 @@ export function TreeListItem({
|
|
|
365
369
|
}) : onClick != null ? /*#__PURE__*/_jsx("button", {
|
|
366
370
|
type: "button",
|
|
367
371
|
onClick: onClick,
|
|
368
|
-
disabled: isDisabled,
|
|
372
|
+
"aria-disabled": isDisabled || undefined,
|
|
373
|
+
...(isDisabled ? disabledInteractionProps() : null),
|
|
369
374
|
"aria-labelledby": labelId,
|
|
370
375
|
"aria-describedby": description != null ? descriptionId : undefined
|
|
371
376
|
// Roving tabindex lives on the treeitem row; inner action is not a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGridFocus.d.ts","sourceRoot":"","sources":["../../src/hooks/useGridFocus.ts"],"names":[],"mappings":"AAsBA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAEjD;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5D;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAE3D;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAExB;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACrE;;OAEG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEnC;;OAEG;IACH,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"useGridFocus.d.ts","sourceRoot":"","sources":["../../src/hooks/useGridFocus.ts"],"names":[],"mappings":"AAsBA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;IAEjD;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5D;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAE3D;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAExB;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IACrE;;OAEG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAEnC;;OAEG;IACH,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CAAC,CAAC,CAAC,CAsZvB"}
|
|
@@ -163,7 +163,16 @@ export function useGridFocus(options) {
|
|
|
163
163
|
for (const el of targets) {
|
|
164
164
|
setTabIndex(el, el === tabbable ? 0 : -1);
|
|
165
165
|
}
|
|
166
|
-
|
|
166
|
+
// A cell that stopped being focusable (an aria-disabled day, for example,
|
|
167
|
+
// which stays reachable by focus) can still hold the stop from earlier
|
|
168
|
+
// navigation; it never stays tabbable, so the grid keeps one Tab stop.
|
|
169
|
+
for (const cell of getCells()) {
|
|
170
|
+
const el = cellFocusable(cell) ? null : resolveFocusTarget(cell);
|
|
171
|
+
if (el != null) {
|
|
172
|
+
setTabIndex(el, -1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}, [getFocusTargets, getCells, cellFocusable, resolveFocusTarget, setTabIndex]);
|
|
167
176
|
|
|
168
177
|
// Keep the tab stop valid across renders (cells added/removed, focusability
|
|
169
178
|
// toggled). Runs after every commit but only when roving tabindex is on.
|