@astryxdesign/core 0.1.4-canary.37c19d3 → 0.1.4-canary.39ffd21

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.
Files changed (226) hide show
  1. package/dist/Banner/Banner.d.ts.map +1 -1
  2. package/dist/Banner/Banner.js +1 -8
  3. package/dist/Calendar/Calendar.d.ts.map +1 -1
  4. package/dist/Calendar/Calendar.js +11 -31
  5. package/dist/Calendar/styles.d.ts +2 -2
  6. package/dist/Calendar/styles.d.ts.map +1 -1
  7. package/dist/Calendar/styles.js +2 -2
  8. package/dist/Carousel/Carousel.d.ts.map +1 -1
  9. package/dist/Carousel/Carousel.js +0 -1
  10. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  11. package/dist/Chat/ChatToolCalls.js +1 -8
  12. package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
  13. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  14. package/dist/CheckboxInput/CheckboxInput.js +1 -3
  15. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  16. package/dist/ClickableCard/ClickableCard.js +3 -26
  17. package/dist/CodeBlock/CodeBlock.d.ts +1 -10
  18. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  19. package/dist/CodeBlock/CodeBlock.js +5 -23
  20. package/dist/Collapsible/Collapsible.d.ts +3 -9
  21. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  22. package/dist/Collapsible/Collapsible.js +13 -101
  23. package/dist/Collapsible/CollapsibleGroup.d.ts +7 -39
  24. package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
  25. package/dist/Collapsible/CollapsibleGroup.js +9 -70
  26. package/dist/Collapsible/CollapsibleGroupContext.d.ts +0 -31
  27. package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
  28. package/dist/Collapsible/CollapsibleGroupContext.js +3 -33
  29. package/dist/Collapsible/index.d.ts +0 -1
  30. package/dist/Collapsible/index.d.ts.map +1 -1
  31. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  32. package/dist/ContextMenu/ContextMenu.js +0 -2
  33. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  34. package/dist/DateTimeInput/DateTimeInput.js +0 -2
  35. package/dist/Grid/Grid.d.ts +3 -4
  36. package/dist/Grid/Grid.d.ts.map +1 -1
  37. package/dist/Grid/Grid.js +9 -26
  38. package/dist/Icon/Icon.d.ts.map +1 -1
  39. package/dist/Icon/Icon.js +1 -5
  40. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  41. package/dist/Lightbox/Lightbox.js +2 -26
  42. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  43. package/dist/NumberInput/NumberInput.js +16 -32
  44. package/dist/Pagination/Pagination.d.ts.map +1 -1
  45. package/dist/Pagination/Pagination.js +36 -87
  46. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  47. package/dist/PowerSearch/PowerSearch.js +21 -39
  48. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  49. package/dist/ProgressBar/ProgressBar.js +1 -2
  50. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  51. package/dist/Resizable/ResizeHandle.js +2 -10
  52. package/dist/SideNav/SideNav.d.ts.map +1 -1
  53. package/dist/SideNav/SideNav.js +2 -6
  54. package/dist/TabList/TabMenu.d.ts.map +1 -1
  55. package/dist/TabList/TabMenu.js +7 -34
  56. package/dist/Table/BaseTable.d.ts.map +1 -1
  57. package/dist/Table/BaseTable.js +2 -12
  58. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  59. package/dist/Table/plugins/columnResize/useTableColumnResize.js +2 -5
  60. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  61. package/dist/Table/plugins/pagination/paginateData.js +1 -5
  62. package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
  63. package/dist/Table/plugins/selection/useTableSelectionState.js +1 -11
  64. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  65. package/dist/Table/plugins/sortable/useTableSortableState.js +4 -8
  66. package/dist/Thumbnail/Thumbnail.d.ts +2 -3
  67. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  68. package/dist/Thumbnail/Thumbnail.js +0 -1
  69. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  70. package/dist/TimeInput/TimeInput.js +1 -6
  71. package/dist/Toast/Toast.d.ts.map +1 -1
  72. package/dist/Toast/Toast.js +3 -11
  73. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  74. package/dist/Toast/ToastViewport.js +0 -12
  75. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  76. package/dist/Typeahead/BaseTypeahead.js +2 -5
  77. package/dist/astryx.css +0 -7
  78. package/dist/astryx.umd.js +48 -48
  79. package/dist/astryx.umd.js.map +4 -4
  80. package/dist/hooks/focusableSelector.d.ts +1 -1
  81. package/dist/hooks/focusableSelector.d.ts.map +1 -1
  82. package/dist/hooks/focusableSelector.js +1 -1
  83. package/dist/hooks/useFocusTrap.d.ts +0 -3
  84. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  85. package/dist/hooks/useFocusTrap.js +1 -38
  86. package/dist/theme/defineTheme.d.ts +1 -1
  87. package/dist/theme/defineTheme.d.ts.map +1 -1
  88. package/dist/theme/expandColorScale.d.ts.map +1 -1
  89. package/dist/theme/expandColorScale.js +3 -10
  90. package/dist/theme/hct.d.ts +15 -0
  91. package/dist/theme/hct.d.ts.map +1 -1
  92. package/dist/theme/hct.js +9 -9
  93. package/dist/theme/tokens.d.ts +2 -10
  94. package/dist/theme/tokens.d.ts.map +1 -1
  95. package/dist/theme/tokens.js +4 -220
  96. package/dist/utils/index.d.ts +0 -2
  97. package/dist/utils/index.d.ts.map +1 -1
  98. package/dist/utils/index.js +1 -2
  99. package/dist/utils/timeParser.d.ts.map +1 -1
  100. package/dist/utils/timeParser.js +5 -8
  101. package/package.json +1 -1
  102. package/src/Avatar/Avatar.doc.mjs +0 -11
  103. package/src/Banner/Banner.doc.mjs +0 -8
  104. package/src/Banner/Banner.test.tsx +0 -38
  105. package/src/Banner/Banner.tsx +1 -8
  106. package/src/Calendar/Calendar.doc.mjs +1 -1
  107. package/src/Calendar/Calendar.test.tsx +4 -112
  108. package/src/Calendar/Calendar.tsx +5 -26
  109. package/src/Calendar/styles.ts +2 -12
  110. package/src/Carousel/Carousel.doc.mjs +12 -12
  111. package/src/Carousel/Carousel.test.tsx +0 -13
  112. package/src/Carousel/Carousel.tsx +0 -1
  113. package/src/Chat/Chat.doc.mjs +1 -1
  114. package/src/Chat/ChatToolCalls.test.tsx +0 -55
  115. package/src/Chat/ChatToolCalls.tsx +12 -10
  116. package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
  117. package/src/CheckboxInput/CheckboxInput.test.tsx +4 -71
  118. package/src/CheckboxInput/CheckboxInput.tsx +0 -2
  119. package/src/Citation/Citation.doc.mjs +0 -5
  120. package/src/ClickableCard/ClickableCard.tsx +2 -45
  121. package/src/CodeBlock/CodeBlock.doc.mjs +2 -7
  122. package/src/CodeBlock/CodeBlock.test.tsx +3 -87
  123. package/src/CodeBlock/CodeBlock.tsx +3 -43
  124. package/src/Collapsible/Collapsible.doc.mjs +4 -8
  125. package/src/Collapsible/Collapsible.tsx +10 -86
  126. package/src/Collapsible/CollapsibleGroup.doc.mjs +3 -27
  127. package/src/Collapsible/CollapsibleGroup.test.tsx +0 -288
  128. package/src/Collapsible/CollapsibleGroup.tsx +9 -107
  129. package/src/Collapsible/CollapsibleGroupContext.tsx +2 -42
  130. package/src/Collapsible/index.ts +0 -4
  131. package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +0 -5
  132. package/src/CommandPalette/CommandPaletteGroup.doc.mjs +0 -10
  133. package/src/CommandPalette/CommandPaletteInput.doc.mjs +0 -6
  134. package/src/CommandPalette/CommandPaletteItem.doc.mjs +0 -6
  135. package/src/CommandPalette/CommandPaletteList.doc.mjs +0 -9
  136. package/src/ContextMenu/ContextMenu.doc.mjs +0 -15
  137. package/src/ContextMenu/ContextMenu.test.tsx +0 -15
  138. package/src/ContextMenu/ContextMenu.tsx +0 -2
  139. package/src/ContextMenu/ContextMenuItem.doc.mjs +0 -6
  140. package/src/DateTimeInput/DateTimeInput.test.tsx +0 -70
  141. package/src/DateTimeInput/DateTimeInput.tsx +0 -2
  142. package/src/Dialog/DialogHeader.doc.mjs +0 -45
  143. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
  144. package/src/Field/FieldLabel.test.tsx +2 -2
  145. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -0
  146. package/src/Grid/Grid.test.tsx +18 -43
  147. package/src/Grid/Grid.tsx +9 -26
  148. package/src/Icon/Icon.test.tsx +29 -41
  149. package/src/Icon/Icon.tsx +2 -6
  150. package/src/Layout/LayoutContent.doc.mjs +0 -12
  151. package/src/Layout/LayoutPanel.doc.mjs +0 -40
  152. package/src/Lightbox/Lightbox.doc.mjs +0 -5
  153. package/src/Lightbox/Lightbox.test.tsx +2 -123
  154. package/src/Lightbox/Lightbox.tsx +1 -29
  155. package/src/NavMenu/NavMenu.doc.mjs +0 -15
  156. package/src/NumberInput/NumberInput.test.tsx +3 -48
  157. package/src/NumberInput/NumberInput.tsx +15 -32
  158. package/src/Overlay/Overlay.doc.mjs +0 -6
  159. package/src/Pagination/Pagination.test.tsx +0 -143
  160. package/src/Pagination/Pagination.tsx +23 -75
  161. package/src/Popover/Popover.test.tsx +0 -65
  162. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  163. package/src/PowerSearch/PowerSearch.test.tsx +1 -109
  164. package/src/PowerSearch/PowerSearch.tsx +16 -37
  165. package/src/ProgressBar/ProgressBar.test.tsx +0 -10
  166. package/src/ProgressBar/ProgressBar.tsx +1 -2
  167. package/src/Resizable/ResizeHandle.tsx +2 -10
  168. package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
  169. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  170. package/src/SideNav/SideNav.doc.mjs +1 -7
  171. package/src/SideNav/SideNav.test.tsx +0 -20
  172. package/src/SideNav/SideNav.tsx +2 -6
  173. package/src/TabList/TabList.test.tsx +1 -185
  174. package/src/TabList/TabMenu.tsx +9 -45
  175. package/src/Table/BaseTable.tsx +1 -11
  176. package/src/Table/Table.test.tsx +0 -51
  177. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -13
  178. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +1 -3
  179. package/src/Table/plugins/pagination/paginateData.ts +1 -5
  180. package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -13
  181. package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -23
  182. package/src/Table/plugins/selection/useTableSelectionState.tsx +6 -11
  183. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +1 -35
  184. package/src/Table/plugins/sortable/useTableSortableState.tsx +4 -8
  185. package/src/TextArea/TextArea.test.tsx +3 -3
  186. package/src/TextInput/TextInput.test.tsx +3 -3
  187. package/src/Thumbnail/Thumbnail.test.tsx +2 -36
  188. package/src/Thumbnail/Thumbnail.tsx +2 -4
  189. package/src/TimeInput/TimeInput.test.tsx +0 -37
  190. package/src/TimeInput/TimeInput.tsx +1 -11
  191. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  192. package/src/Toast/Toast.tsx +3 -11
  193. package/src/Toast/ToastViewport.test.tsx +0 -64
  194. package/src/Toast/ToastViewport.tsx +0 -12
  195. package/src/ToggleButton/ToggleButton.doc.mjs +0 -1
  196. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  197. package/src/TopNav/TopNav.doc.mjs +1 -1
  198. package/src/TreeList/TreeList.doc.mjs +2 -2
  199. package/src/Typeahead/BaseTypeahead.doc.mjs +0 -19
  200. package/src/Typeahead/BaseTypeahead.tsx +2 -5
  201. package/src/Typeahead/Typeahead.doc.mjs +1 -1
  202. package/src/Typeahead/Typeahead.test.tsx +1 -45
  203. package/src/hooks/focusableSelector.ts +1 -1
  204. package/src/hooks/useFocusTrap.doc.mjs +2 -4
  205. package/src/hooks/useFocusTrap.test.tsx +1 -149
  206. package/src/hooks/useFocusTrap.ts +1 -50
  207. package/src/theme/SyntaxTheme.doc.mjs +4 -4
  208. package/src/theme/defineTheme.ts +13 -4
  209. package/src/theme/derivedVarRegistry.test.ts +2 -2
  210. package/src/theme/expandColorScale.test.ts +0 -29
  211. package/src/theme/expandColorScale.ts +6 -11
  212. package/src/theme/hct.ts +17 -9
  213. package/src/theme/tokens.test.ts +9 -182
  214. package/src/theme/tokens.ts +4 -239
  215. package/src/theme/useTheme.test.tsx +0 -20
  216. package/src/utils/index.ts +0 -3
  217. package/src/utils/timeParser.test.ts +0 -13
  218. package/src/utils/timeParser.ts +5 -8
  219. package/dist/utils/color.d.ts +0 -42
  220. package/dist/utils/color.d.ts.map +0 -1
  221. package/dist/utils/color.js +0 -150
  222. package/src/Resizable/ResizeHandle.test.tsx +0 -180
  223. package/src/__tests__/TestIcon.tsx +0 -26
  224. package/src/docPropReferences.test.ts +0 -178
  225. package/src/utils/color.test.ts +0 -133
  226. package/src/utils/color.ts +0 -148
@@ -19,7 +19,6 @@
19
19
  import {
20
20
  useCallback,
21
21
  useEffect,
22
- useMemo,
23
22
  useRef,
24
23
  useState,
25
24
  type MouseEvent as ReactMouseEvent,
@@ -29,7 +28,6 @@ import * as stylex from '@stylexjs/stylex';
29
28
  import {colorVars, spacingVars, typeScaleVars} from '../theme/tokens.stylex';
30
29
  import {Icon} from '../Icon';
31
30
  import {IconButton} from '../IconButton';
32
- import {useAnnounce} from '../hooks/useAnnounce';
33
31
  import {useScrollLock} from '../hooks/useScrollLock';
34
32
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
35
33
  import {mergeProps, mergeRefs} from '../utils';
@@ -308,10 +306,7 @@ export function Lightbox({
308
306
  const dragStartRef = useRef({x: 0, y: 0, panX: 0, panY: 0});
309
307
 
310
308
  // Resolve current media item
311
- const mediaArray = useMemo(
312
- () => (Array.isArray(media) ? media : [media]),
313
- [media],
314
- );
309
+ const mediaArray = Array.isArray(media) ? media : [media];
315
310
  const isGallery = mediaArray.length > 1;
316
311
  const currentItem =
317
312
  mediaArray.length > 0
@@ -334,29 +329,6 @@ export function Lightbox({
334
329
  setPan({x: 0, y: 0});
335
330
  }, [index, currentItem?.src]);
336
331
 
337
- // Announce gallery navigation to screen readers. Moving between images only
338
- // updates the visual counter, which is silent to assistive tech, so mirror
339
- // each change in a polite live region ("<alt>, 3 of 12", or "Image 3 of 12"
340
- // when the image has no alt). Announce only when the image changes during an
341
- // already-open session — not on mount, not when opening (even at a new
342
- // index, since the dialog's aria-label already names the current image), and
343
- // not on close.
344
- const announce = useAnnounce();
345
- const prevIndexRef = useRef(index);
346
- const wasOpenRef = useRef(isOpen);
347
- useEffect(() => {
348
- const indexChanged = prevIndexRef.current !== index;
349
- const wasOpen = wasOpenRef.current;
350
- prevIndexRef.current = index;
351
- wasOpenRef.current = isOpen;
352
- if (!indexChanged || !isOpen || !wasOpen) {
353
- return;
354
- }
355
- const item = mediaArray[Math.min(index, mediaArray.length - 1)];
356
- const position = `${index + 1} of ${mediaArray.length}`;
357
- announce(item?.alt ? `${item.alt}, ${position}` : `Image ${position}`);
358
- }, [index, isOpen, announce, mediaArray]);
359
-
360
332
  // Open/close dialog
361
333
  useIsomorphicLayoutEffect(() => {
362
334
  const dialog = dialogRef.current;
@@ -17,27 +17,12 @@ export const docs = {
17
17
  'NavHeadingMenuItem renders individual selectable items. ' +
18
18
  'Pass as the menu prop of SideNavHeading or TopNavHeading.',
19
19
  },
20
- playground: {
21
- defaults: {
22
- children: [
23
- {__element: 'NavHeadingMenuItem', props: {label: 'Dashboard', href: '#'}},
24
- {__element: 'NavHeadingMenuItem', props: {label: 'Analytics', href: '#'}},
25
- {__element: 'NavHeadingMenuItem', props: {label: 'Settings', href: '#'}},
26
- ],
27
- },
28
- },
29
20
  props: [
30
21
  {name: 'children', type: 'ReactNode', required: true, description: 'Menu items.'},
31
22
  {name: 'size', type: "'sm' | 'md' | 'lg'", default: "'md'", description: 'Controls min-width and item padding.'},
32
23
  {name: 'minWidth', type: 'number | string', description: 'Minimum width override.'},
33
24
  {name: 'xstyle', type: 'StyleXStyles', description: 'StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}.'},
34
25
  ],
35
- theming: {
36
- targets: [
37
- {className: 'astryx-nav-heading-menu', visualProps: ['size']},
38
- {className: 'astryx-nav-heading-menu-item', visualProps: ['size']},
39
- ],
40
- },
41
26
  };
42
27
 
43
28
  /** @type {import('../docs-types').TranslationDoc} */
@@ -12,7 +12,7 @@
12
12
  import {describe, it, expect, vi, beforeEach} from 'vitest';
13
13
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
- import {TestIcon} from '../__tests__/TestIcon';
15
+ import {HashtagIcon} from '@heroicons/react/24/outline';
16
16
  import {NumberInput} from './NumberInput';
17
17
 
18
18
  // Mock showPopover/hidePopover since jsdom does not implement them. Used by the
@@ -174,7 +174,7 @@ describe('NumberInput', () => {
174
174
  label="Count"
175
175
  value={null}
176
176
  onChange={() => {}}
177
- startIcon={TestIcon}
177
+ startIcon={HashtagIcon}
178
178
  />,
179
179
  );
180
180
  expect(screen.getByRole('spinbutton')).toBeInTheDocument();
@@ -707,7 +707,7 @@ describe('NumberInput', () => {
707
707
  label="Qty"
708
708
  value={0}
709
709
  onChange={() => {}}
710
- startIcon={<TestIcon />}
710
+ startIcon={<HashtagIcon />}
711
711
  />,
712
712
  );
713
713
 
@@ -867,48 +867,3 @@ describe('NumberInput', () => {
867
867
  });
868
868
  });
869
869
  });
870
-
871
- describe('keyboard clearing with hasClear (#3599)', () => {
872
- it('commits null when the input is emptied and blurred', () => {
873
- const onChange = vi.fn();
874
- render(
875
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
876
- );
877
- const input = screen.getByLabelText('Qty');
878
- fireEvent.change(input, {target: {value: ''}});
879
- fireEvent.blur(input);
880
- expect(onChange).toHaveBeenCalledWith(null);
881
- });
882
-
883
- it('commits null when the input is emptied and Enter is pressed', () => {
884
- const onChange = vi.fn();
885
- render(
886
- <NumberInput label="Qty" hasClear value={42} onChange={onChange} />,
887
- );
888
- const input = screen.getByLabelText('Qty');
889
- fireEvent.change(input, {target: {value: ''}});
890
- fireEvent.keyDown(input, {key: 'Enter'});
891
- expect(onChange).toHaveBeenCalledWith(null);
892
- });
893
-
894
- it('does not fire when emptied and blurred with no prior value', () => {
895
- const onChange = vi.fn();
896
- render(
897
- <NumberInput label="Qty" hasClear value={null} onChange={onChange} />,
898
- );
899
- const input = screen.getByLabelText('Qty');
900
- fireEvent.change(input, {target: {value: ''}});
901
- fireEvent.blur(input);
902
- expect(onChange).not.toHaveBeenCalled();
903
- });
904
-
905
- it('still reverts on blur when hasClear is not set', () => {
906
- const onChange = vi.fn();
907
- render(<NumberInput label="Qty" value={42} onChange={onChange} />);
908
- const input = screen.getByLabelText('Qty');
909
- fireEvent.change(input, {target: {value: ''}});
910
- fireEvent.blur(input);
911
- expect(onChange).not.toHaveBeenCalled();
912
- expect((input as HTMLInputElement).value).toBe('42');
913
- });
914
- });
@@ -503,21 +503,13 @@ export function NumberInput({
503
503
  const handleBlur = useCallback(
504
504
  (e: FocusEvent<HTMLInputElement>) => {
505
505
  if (pendingInput !== null) {
506
- if (hasClear && pendingInput.trim() === '') {
507
- // Keyboard clearing honors the clearable contract: an emptied
508
- // input commits null instead of silently reverting on blur.
509
- if (value != null) {
510
- onChange(null);
511
- }
512
- } else {
513
- const parsed = parseNumberInput(pendingInput, {
514
- min,
515
- max,
516
- isIntegerOnly,
517
- });
518
- if (parsed !== null && parsed !== value) {
519
- onChange(parsed);
520
- }
506
+ const parsed = parseNumberInput(pendingInput, {
507
+ min,
508
+ max,
509
+ isIntegerOnly,
510
+ });
511
+ if (parsed !== null && parsed !== value) {
512
+ onChange(parsed);
521
513
  }
522
514
  }
523
515
 
@@ -525,7 +517,7 @@ export function NumberInput({
525
517
  setPendingInput(null);
526
518
  onBlur?.(e);
527
519
  },
528
- [pendingInput, value, onChange, min, max, isIntegerOnly, onBlur, hasClear],
520
+ [pendingInput, value, onChange, min, max, isIntegerOnly, onBlur],
529
521
  );
530
522
 
531
523
  // Handle keyboard events
@@ -534,21 +526,13 @@ export function NumberInput({
534
526
  if (e.key === 'Enter') {
535
527
  // Validate and commit on Enter
536
528
  if (pendingInput !== null) {
537
- if (hasClear && pendingInput.trim() === '') {
538
- // Same clearable contract as blur: Enter on an emptied input
539
- // commits null instead of reverting.
540
- if (value != null) {
541
- onChange(null);
542
- }
543
- } else {
544
- const parsed = parseNumberInput(pendingInput, {
545
- min,
546
- max,
547
- isIntegerOnly,
548
- });
549
- if (parsed !== null && parsed !== value) {
550
- onChange(parsed);
551
- }
529
+ const parsed = parseNumberInput(pendingInput, {
530
+ min,
531
+ max,
532
+ isIntegerOnly,
533
+ });
534
+ if (parsed !== null && parsed !== value) {
535
+ onChange(parsed);
552
536
  }
553
537
  }
554
538
  onEnter?.();
@@ -564,7 +548,6 @@ export function NumberInput({
564
548
  isIntegerOnly,
565
549
  onEnter,
566
550
  onKeyDown,
567
- hasClear,
568
551
  ],
569
552
  );
570
553
 
@@ -180,12 +180,6 @@ export const docs = {
180
180
  description: 'Ref forwarded to the overlay root element.',
181
181
  },
182
182
  ],
183
- theming: {
184
- targets: [
185
- {className: 'astryx-overlay'},
186
- {className: 'astryx-overlay-scrim', visualProps: ['position']},
187
- ],
188
- },
189
183
  };
190
184
 
191
185
  /** @type {import('../docs-types').TranslationDoc} */
@@ -345,149 +345,6 @@ describe('Pagination', () => {
345
345
  const activeDot = screen.getByRole('button', {name: 'Go to page 3'});
346
346
  expect(activeDot).toHaveAttribute('aria-current', 'page');
347
347
  });
348
-
349
- // -------------------------------------------------------------------------
350
- // Keyboard navigation (roving tabindex + arrow keys via useListFocus).
351
- // Selection follows focus: arrow/Home/End move focus and select that page.
352
- // -------------------------------------------------------------------------
353
-
354
- it('uses roving tabindex — active dot has tabIndex 0, others -1', () => {
355
- render(
356
- <Pagination
357
- page={2}
358
- onChange={() => {}}
359
- totalPages={4}
360
- variant="dots"
361
- />,
362
- );
363
- expect(
364
- screen.getByRole('button', {name: 'Go to page 2'}),
365
- ).toHaveAttribute('tabindex', '0');
366
- expect(
367
- screen.getByRole('button', {name: 'Go to page 1'}),
368
- ).toHaveAttribute('tabindex', '-1');
369
- expect(
370
- screen.getByRole('button', {name: 'Go to page 3'}),
371
- ).toHaveAttribute('tabindex', '-1');
372
- });
373
-
374
- it('ArrowRight moves focus to the next dot and selects it', async () => {
375
- const user = userEvent.setup();
376
- const onChange = vi.fn();
377
- render(
378
- <Pagination
379
- page={2}
380
- onChange={onChange}
381
- totalPages={4}
382
- variant="dots"
383
- />,
384
- );
385
- screen.getByRole('button', {name: 'Go to page 2'}).focus();
386
- await user.keyboard('{ArrowRight}');
387
- expect(onChange).toHaveBeenCalledWith(3);
388
- expect(screen.getByRole('button', {name: 'Go to page 3'})).toHaveFocus();
389
- });
390
-
391
- it('ArrowLeft moves focus to the previous dot and selects it', async () => {
392
- const user = userEvent.setup();
393
- const onChange = vi.fn();
394
- render(
395
- <Pagination
396
- page={3}
397
- onChange={onChange}
398
- totalPages={4}
399
- variant="dots"
400
- />,
401
- );
402
- screen.getByRole('button', {name: 'Go to page 3'}).focus();
403
- await user.keyboard('{ArrowLeft}');
404
- expect(onChange).toHaveBeenCalledWith(2);
405
- expect(screen.getByRole('button', {name: 'Go to page 2'})).toHaveFocus();
406
- });
407
-
408
- it('Home selects the first page, End the last', async () => {
409
- const user = userEvent.setup();
410
- const onChange = vi.fn();
411
- const {rerender} = render(
412
- <Pagination
413
- page={3}
414
- onChange={onChange}
415
- totalPages={5}
416
- variant="dots"
417
- />,
418
- );
419
- screen.getByRole('button', {name: 'Go to page 3'}).focus();
420
- await user.keyboard('{Home}');
421
- expect(onChange).toHaveBeenCalledWith(1);
422
- expect(screen.getByRole('button', {name: 'Go to page 1'})).toHaveFocus();
423
-
424
- onChange.mockClear();
425
- rerender(
426
- <Pagination
427
- page={3}
428
- onChange={onChange}
429
- totalPages={5}
430
- variant="dots"
431
- />,
432
- );
433
- screen.getByRole('button', {name: 'Go to page 3'}).focus();
434
- await user.keyboard('{End}');
435
- expect(onChange).toHaveBeenCalledWith(5);
436
- expect(screen.getByRole('button', {name: 'Go to page 5'})).toHaveFocus();
437
- });
438
-
439
- it('wraps from the last dot to the first with ArrowRight', async () => {
440
- const user = userEvent.setup();
441
- const onChange = vi.fn();
442
- render(
443
- <Pagination
444
- page={4}
445
- onChange={onChange}
446
- totalPages={4}
447
- variant="dots"
448
- />,
449
- );
450
- screen.getByRole('button', {name: 'Go to page 4'}).focus();
451
- await user.keyboard('{ArrowRight}');
452
- expect(onChange).toHaveBeenCalledWith(1);
453
- expect(screen.getByRole('button', {name: 'Go to page 1'})).toHaveFocus();
454
- });
455
-
456
- it('wraps from the first dot to the last with ArrowLeft', async () => {
457
- const user = userEvent.setup();
458
- const onChange = vi.fn();
459
- render(
460
- <Pagination
461
- page={1}
462
- onChange={onChange}
463
- totalPages={4}
464
- variant="dots"
465
- />,
466
- );
467
- screen.getByRole('button', {name: 'Go to page 1'}).focus();
468
- await user.keyboard('{ArrowLeft}');
469
- expect(onChange).toHaveBeenCalledWith(4);
470
- expect(screen.getByRole('button', {name: 'Go to page 4'})).toHaveFocus();
471
- });
472
-
473
- it('does not navigate with arrow keys when disabled', async () => {
474
- const user = userEvent.setup();
475
- const onChange = vi.fn();
476
- render(
477
- <Pagination
478
- page={2}
479
- onChange={onChange}
480
- totalPages={4}
481
- variant="dots"
482
- isDisabled
483
- />,
484
- );
485
- const dot = screen.getByRole('button', {name: 'Go to page 2'});
486
- expect(dot).toBeDisabled();
487
- dot.focus();
488
- await user.keyboard('{ArrowRight}');
489
- expect(onChange).not.toHaveBeenCalled();
490
- });
491
348
  });
492
349
 
493
350
  // ---------------------------------------------------------------------------
@@ -35,7 +35,6 @@ import {Icon} from '../Icon';
35
35
  import {Selector} from '../Selector';
36
36
  import {Text} from '../Text';
37
37
  import {useAnnounce} from '../hooks/useAnnounce';
38
- import {useListFocus} from '../hooks/useListFocus';
39
38
  import {mergeProps} from '../utils';
40
39
  import type {BaseProps} from '../BaseProps';
41
40
  import {themeProps} from '../utils/themeProps';
@@ -371,21 +370,6 @@ export function Pagination({
371
370
  // in-flight target instead of stalling on the last committed page.
372
371
  const [optimisticPage, setOptimisticPage] = useOptimistic(page);
373
372
 
374
- // Roving-tabindex + arrow/Home/End keyboard nav for the dots variant, owned
375
- // by the shared useListFocus primitive (mirrors SegmentedControl). It stamps a
376
- // single tab stop across the dots and moves focus horizontally; selection
377
- // follows focus via handleDotsFocus so arrow keys move the active page.
378
- const {
379
- listRef: dotsListRef,
380
- handleKeyDown: handleDotsKeyDown,
381
- handleFocus: handleDotsRovingFocus,
382
- } = useListFocus<HTMLDivElement>({
383
- itemSelector: 'button',
384
- hasRovingTabIndex: true,
385
- wrap: true,
386
- orientation: 'horizontal',
387
- });
388
-
389
373
  // Compute pagination state
390
374
  const computedTotalPages =
391
375
  totalPagesProp ??
@@ -426,28 +410,6 @@ export function Pagination({
426
410
  });
427
411
  };
428
412
 
429
- // Selection-follows-focus for the dots (APG radiogroup pattern): useListFocus
430
- // only *moves* focus, so when focus lands on a dot -- via arrow/Home/End, or a
431
- // click that focuses it -- we select that dot's page. handleDotsRovingFocus
432
- // keeps the roving tab stop in sync. The current page is skipped so tabbing
433
- // into the group is a no-op.
434
- const handleDotsFocus = (e: React.FocusEvent) => {
435
- handleDotsRovingFocus(e);
436
- if (isDisabled) {
437
- return;
438
- }
439
- const focused = (e.target as HTMLElement | null)?.closest<HTMLElement>(
440
- 'button[data-page]',
441
- );
442
- if (!focused) {
443
- return;
444
- }
445
- const nextPage = Number(focused.dataset.page);
446
- if (Number.isFinite(nextPage) && nextPage !== optimisticPage) {
447
- handlePageChange(nextPage);
448
- }
449
- };
450
-
451
413
  const handlePrevious = () => {
452
414
  if (hasPrevious) {
453
415
  handlePageChange(optimisticPage - 1);
@@ -556,47 +518,33 @@ export function Pagination({
556
518
  if (computedTotalPages == null) {
557
519
  return null;
558
520
  }
559
-
560
521
  return (
561
522
  <div
562
- ref={dotsListRef}
563
523
  {...stylex.props(styles.dotsContainer)}
564
524
  role="group"
565
- aria-label="Page indicators"
566
- onKeyDown={handleDotsKeyDown}
567
- onFocus={handleDotsFocus}>
568
- {Array.from({length: computedTotalPages}, (_, i) => {
569
- const isActive = i + 1 === optimisticPage;
570
- return (
571
- <button
572
- key={i + 1}
573
- type="button"
574
- data-page={i + 1}
575
- aria-label={`Go to page ${i + 1}`}
576
- aria-current={isActive ? 'page' : undefined}
577
- // The active dot is the single roving tab stop; useListFocus
578
- // maintains it as focus and the active page move.
579
- tabIndex={isActive ? 0 : -1}
580
- // Selection is driven by focus (handleDotsFocus); clicking only
581
- // needs to focus the dot, which some browsers (Safari) skip for
582
- // buttons, so focus it explicitly.
583
- onClick={e => e.currentTarget.focus()}
584
- disabled={isDisabled}
585
- {...mergeProps(
586
- themeProps('pagination-dot', {
587
- active: isActive ? 'active' : null,
588
- size,
589
- }),
590
- stylex.props(
591
- styles.dot,
592
- isSm && styles.dotSm,
593
- isActive && styles.dotActive,
594
- isDisabled && styles.dotDisabled,
595
- ),
596
- )}
597
- />
598
- );
599
- })}
525
+ aria-label="Page indicators">
526
+ {Array.from({length: computedTotalPages}, (_, i) => (
527
+ <button
528
+ key={i + 1}
529
+ type="button"
530
+ aria-label={`Go to page ${i + 1}`}
531
+ aria-current={i + 1 === optimisticPage ? 'page' : undefined}
532
+ onClick={() => handlePageChange(i + 1)}
533
+ disabled={isDisabled}
534
+ {...mergeProps(
535
+ themeProps('pagination-dot', {
536
+ active: i + 1 === optimisticPage ? 'active' : null,
537
+ size,
538
+ }),
539
+ stylex.props(
540
+ styles.dot,
541
+ isSm && styles.dotSm,
542
+ i + 1 === optimisticPage && styles.dotActive,
543
+ isDisabled && styles.dotDisabled,
544
+ ),
545
+ )}
546
+ />
547
+ ))}
600
548
  </div>
601
549
  );
602
550
  }
@@ -354,69 +354,4 @@ describe('Popover', () => {
354
354
  expect(trigger).toHaveAttribute('aria-expanded', 'true');
355
355
  });
356
356
  });
357
-
358
- describe('focus restoration', () => {
359
- it('returns focus to the trigger when closed via Escape', () => {
360
- render(
361
- <Popover
362
- content={
363
- <button type="button" data-testid="inside-content">
364
- Inside
365
- </button>
366
- }
367
- label="Test">
368
- <button type="button">Open</button>
369
- </Popover>,
370
- );
371
- const trigger = screen.getByRole('button', {name: 'Open'});
372
- trigger.focus();
373
- expect(trigger).toHaveFocus();
374
-
375
- fireEvent.click(trigger);
376
- expect(trigger).toHaveAttribute('aria-expanded', 'true');
377
-
378
- // Move focus into the open popover, as a keyboard user would.
379
- const inside = screen.getByTestId('inside-content');
380
- inside.focus();
381
- expect(inside).toHaveFocus();
382
-
383
- fireEvent.keyDown(document, {key: 'Escape'});
384
- expect(trigger).toHaveAttribute('aria-expanded', 'false');
385
- expect(trigger).toHaveFocus();
386
- });
387
-
388
- it('returns focus to the trigger on light dismiss', () => {
389
- render(
390
- <Popover
391
- content={
392
- <button type="button" data-testid="inside-content">
393
- Inside
394
- </button>
395
- }
396
- label="Test">
397
- <button type="button">Open</button>
398
- </Popover>,
399
- );
400
- const trigger = screen.getByRole('button', {name: 'Open'});
401
- trigger.focus();
402
-
403
- fireEvent.click(trigger);
404
- expect(trigger).toHaveAttribute('aria-expanded', 'true');
405
-
406
- const inside = screen.getByTestId('inside-content');
407
- inside.focus();
408
- expect(inside).toHaveFocus();
409
-
410
- // Simulate the browser's light dismiss for popover="auto": clicking
411
- // outside fires a `toggle` event with newState "closed".
412
- const popoverEl = document.querySelector('[popover]');
413
- expect(popoverEl).not.toBeNull();
414
- const toggleEvent = new Event('toggle');
415
- Object.defineProperty(toggleEvent, 'newState', {value: 'closed'});
416
- fireEvent(popoverEl as HTMLElement, toggleEvent);
417
-
418
- expect(trigger).toHaveAttribute('aria-expanded', 'false');
419
- expect(trigger).toHaveFocus();
420
- });
421
- });
422
357
  });
@@ -120,7 +120,7 @@ export const docs = {
120
120
  name: 'resultCount',
121
121
  type: 'number | string',
122
122
  description:
123
- 'Number of results matching the current filters. When a number, formatted as "N results". When a string, displayed as-is. Changes are announced to screen readers via a polite live region.',
123
+ 'Number of results matching the current filters. When a number, formatted as "N results". When a string, displayed as-is.',
124
124
  },
125
125
  {
126
126
  name: 'size',
@@ -261,7 +261,7 @@ export const docsZh = {
261
261
  name: 'resultCount',
262
262
  type: 'number | string',
263
263
  description:
264
- '匹配当前过滤器的结果数量。数字类型时格式化为"N results"。字符串类型时按原样显示。数量变化会通过 polite 实时区域向屏幕阅读器播报。',
264
+ '匹配当前过滤器的结果数量。数字类型时格式化为"N results"。字符串类型时按原样显示。',
265
265
  },
266
266
  {
267
267
  name: 'size',