@astryxdesign/core 0.7.3 → 0.7.4-canary.10b832e

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 (131) hide show
  1. package/dist/BottomSheet/BottomSheet.d.ts +18 -6
  2. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  3. package/dist/BottomSheet/BottomSheet.js +268 -58
  4. package/dist/BottomSheet/BottomSheetSwitcher.d.ts +41 -12
  5. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetSwitcher.js +294 -165
  7. package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts +51 -1
  8. package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts.map +1 -1
  9. package/dist/BottomSheet/BottomSheetSwitcherContext.js +13 -1
  10. package/dist/Carousel/Carousel.d.ts.map +1 -1
  11. package/dist/Carousel/Carousel.js +7 -11
  12. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  13. package/dist/Chat/ChatMessageList.js +8 -1
  14. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  15. package/dist/Chat/useTriggerMenu.js +46 -7
  16. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  17. package/dist/CheckboxInput/CheckboxInput.js +6 -0
  18. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  19. package/dist/ComplexSelector/ComplexSelector.js +6 -1
  20. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  21. package/dist/ContextMenu/ContextMenu.js +12 -0
  22. package/dist/DateInput/DateInput.d.ts.map +1 -1
  23. package/dist/DateInput/DateInput.js +6 -1
  24. package/dist/DateInput/NativeDateField.d.ts.map +1 -1
  25. package/dist/DateInput/NativeDateField.js +6 -1
  26. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  27. package/dist/DateInput/TouchDateField.js +9 -1
  28. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  29. package/dist/DateRangeInput/DateRangeInput.js +6 -1
  30. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  31. package/dist/DateTimeInput/DateTimeInput.js +6 -1
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +9 -1
  34. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  35. package/dist/DropdownMenu/DropdownMenuSubMenu.js +7 -1
  36. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  37. package/dist/MultiSelector/MultiSelector.js +6 -1
  38. package/dist/Selector/Selector.d.ts.map +1 -1
  39. package/dist/Selector/Selector.js +6 -1
  40. package/dist/TextArea/TextArea.d.ts.map +1 -1
  41. package/dist/TextArea/TextArea.js +6 -1
  42. package/dist/TextInput/TextInput.d.ts.map +1 -1
  43. package/dist/TextInput/TextInput.js +6 -1
  44. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  45. package/dist/Thumbnail/Thumbnail.js +9 -2
  46. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  47. package/dist/TimeInput/TimeInput.js +6 -1
  48. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  49. package/dist/TimeInput/TouchTimeField.js +9 -1
  50. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  51. package/dist/Tokenizer/Tokenizer.js +9 -1
  52. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  53. package/dist/Typeahead/BaseTypeahead.js +7 -1
  54. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  55. package/dist/Typeahead/Typeahead.js +9 -1
  56. package/dist/astryx.css +2 -0
  57. package/dist/hooks/useBusyAnnouncement.d.ts +22 -0
  58. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -0
  59. package/dist/hooks/useBusyAnnouncement.js +87 -0
  60. package/dist/hooks/useMenuPress.d.ts +2 -2
  61. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  62. package/dist/hooks/useMenuPress.js +5 -1
  63. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  64. package/dist/hooks/useTypeahead.js +7 -2
  65. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  66. package/dist/i18n/generated-locales/en.generated.js +1 -0
  67. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  68. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  69. package/locales/en.json +6 -2
  70. package/locales/pseudo.json +3 -0
  71. package/package.json +4 -4
  72. package/src/BottomSheet/BottomSheet.doc.mjs +2 -2
  73. package/src/BottomSheet/BottomSheet.spec.md +2 -5
  74. package/src/BottomSheet/BottomSheet.tsx +260 -47
  75. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -2
  76. package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +11 -10
  77. package/src/BottomSheet/BottomSheetSwitcher.spec.md +28 -35
  78. package/src/BottomSheet/BottomSheetSwitcher.test.tsx +830 -48
  79. package/src/BottomSheet/BottomSheetSwitcher.tsx +396 -215
  80. package/src/BottomSheet/BottomSheetSwitcherContext.ts +57 -2
  81. package/src/Carousel/Carousel.test.tsx +8 -12
  82. package/src/Carousel/Carousel.tsx +6 -13
  83. package/src/Chat/ChatComposerInput.test.tsx +167 -1
  84. package/src/Chat/ChatMessageList.test.tsx +6 -2
  85. package/src/Chat/ChatMessageList.tsx +7 -1
  86. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +20 -12
  87. package/src/Chat/useTriggerMenu.tsx +55 -6
  88. package/src/CheckboxInput/CheckboxInput.tsx +6 -0
  89. package/src/ComplexSelector/ComplexSelector.tsx +5 -1
  90. package/src/ContextMenu/ContextMenu.test.tsx +34 -0
  91. package/src/ContextMenu/ContextMenu.tsx +13 -1
  92. package/src/DateInput/DateInput.tsx +5 -1
  93. package/src/DateInput/DateInputTouch.test.tsx +45 -0
  94. package/src/DateInput/NativeDateField.test.tsx +10 -2
  95. package/src/DateInput/NativeDateField.tsx +5 -1
  96. package/src/DateInput/TouchDateField.tsx +14 -2
  97. package/src/DateRangeInput/DateRangeInput.tsx +5 -1
  98. package/src/DateTimeInput/DateTimeInput.tsx +5 -1
  99. package/src/DateTimeInput/TouchDateTimeField.tsx +14 -2
  100. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +10 -1
  101. package/src/MultiSelector/MultiSelector.test.tsx +18 -4
  102. package/src/MultiSelector/MultiSelector.tsx +5 -1
  103. package/src/ProgressBar/ProgressBar.test.tsx +4 -21
  104. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +114 -0
  105. package/src/ProgressBar/__tests__/ProgressBar.a11y.known-failures.ts +12 -0
  106. package/src/ProgressBar/__tests__/ProgressBar.a11y.renders.tsx +38 -0
  107. package/src/ProgressBar/__tests__/ProgressBar.a11y.states.ts +123 -0
  108. package/src/ProgressBar/__tests__/ProgressBar.a11y.test.tsx +107 -0
  109. package/src/Selector/Selector.test.tsx +22 -5
  110. package/src/Selector/Selector.tsx +5 -1
  111. package/src/TextArea/TextArea.test.tsx +9 -1
  112. package/src/TextArea/TextArea.tsx +5 -1
  113. package/src/TextInput/TextInput.tsx +5 -1
  114. package/src/Thumbnail/Thumbnail.test.tsx +6 -1
  115. package/src/Thumbnail/Thumbnail.tsx +12 -3
  116. package/src/TimeInput/TimeInput.tsx +5 -1
  117. package/src/TimeInput/TouchTimeField.tsx +12 -2
  118. package/src/Tokenizer/Tokenizer.test.tsx +17 -12
  119. package/src/Tokenizer/Tokenizer.tsx +10 -1
  120. package/src/Typeahead/BaseTypeahead.tsx +14 -1
  121. package/src/Typeahead/Typeahead.test.tsx +32 -41
  122. package/src/Typeahead/Typeahead.tsx +10 -1
  123. package/src/__tests__/BusyOwnerAnnouncement.test.tsx +576 -0
  124. package/src/hooks/__tests__/MenuPressShadowRoot.a11y.chromium.spec.ts +50 -0
  125. package/src/hooks/useBusyAnnouncement.ts +100 -0
  126. package/src/hooks/useMenuPress.test.tsx +27 -1
  127. package/src/hooks/useMenuPress.ts +12 -3
  128. package/src/hooks/useTypeahead.test.tsx +42 -1
  129. package/src/hooks/useTypeahead.ts +7 -2
  130. package/src/i18n/generated-locales/en.generated.ts +1 -0
  131. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBusyAnnouncement.d.ts","sourceRoot":"","sources":["../../src/hooks/useBusyAnnouncement.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AA+CxD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,GACpC,IAAI,CAwBN"}
@@ -0,0 +1,87 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useBusyAnnouncement.ts
7
+ * @input Whether the owning control is busy, the words to announce, and a ref
8
+ * to an element inside the control
9
+ * @output Announces each busy start once, through the shared polite channel
10
+ * or a persistent region inside the open modal dialog holding the control
11
+ * @position Core a11y hook for controls that show a decorative busy Spinner
12
+ *
13
+ * The busy Spinner inside a control is decorative (hidden, no live role); the
14
+ * control announces its busy state instead (`spec:AST-086` FR8), once per busy
15
+ * start (FR6): rerenders and StrictMode replay do not repeat it, and settling
16
+ * re-arms it. While a native modal is open, the body-level channel is not
17
+ * exposed, so a control inside it announces through one persistent polite
18
+ * region in that dialog, inserted empty and filled a frame later (Platform
19
+ * support, FR3).
20
+ *
21
+ * SYNC: deliberately NOT exported from /packages/core/src/hooks/index.ts —
22
+ * this is a shared implementation detail of Astryx's busy controls.
23
+ */
24
+ import { useEffect, useRef } from 'react';
25
+ import { useAnnounce } from "./useAnnounce.js";
26
+ const REGION_ATTR = 'data-astryx-modal-busy-region';
27
+ const VISUALLY_HIDDEN_CSS = 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;' + 'overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
28
+ const CLEAR_DELAY_MS = 2000; // Matches useAnnounce.
29
+
30
+ // The pending write and clear of each in-dialog region; a new write cancels
31
+ // them so an earlier message can neither land late nor clear a later one.
32
+ const pendingByRegion = new WeakMap();
33
+ function announceInModal(dialog, message) {
34
+ let region = dialog.querySelector(`:scope > [${REGION_ATTR}]`);
35
+ if (region == null) {
36
+ region = document.createElement('div');
37
+ region.setAttribute(REGION_ATTR, '');
38
+ region.setAttribute('role', 'status');
39
+ region.setAttribute('aria-live', 'polite');
40
+ region.setAttribute('aria-atomic', 'true');
41
+ region.style.cssText = VISUALLY_HIDDEN_CSS;
42
+ dialog.appendChild(region);
43
+ }
44
+ const target = region;
45
+ const previous = pendingByRegion.get(target);
46
+ if (previous != null) {
47
+ cancelAnimationFrame(previous.frame);
48
+ clearTimeout(previous.timer);
49
+ }
50
+ target.textContent = '';
51
+ const pending = {
52
+ frame: 0
53
+ };
54
+ pending.frame = requestAnimationFrame(() => {
55
+ target.textContent = message;
56
+ pending.timer = setTimeout(() => {
57
+ target.textContent = '';
58
+ pendingByRegion.delete(target);
59
+ }, CLEAR_DELAY_MS);
60
+ });
61
+ pendingByRegion.set(target, pending);
62
+ }
63
+ export function useBusyAnnouncement(isBusy, message, elementRef) {
64
+ const announce = useAnnounce();
65
+ const hasAnnouncedRef = useRef(false);
66
+ useEffect(() => {
67
+ if (!isBusy) {
68
+ hasAnnouncedRef.current = false;
69
+ return;
70
+ }
71
+ if (hasAnnouncedRef.current) {
72
+ return;
73
+ }
74
+ // Choose the route a frame later: a Dialog opening in the same commit
75
+ // calls showModal() in its own Effect, which runs after this one.
76
+ const frame = requestAnimationFrame(() => {
77
+ hasAnnouncedRef.current = true;
78
+ const dialog = elementRef.current?.closest('dialog');
79
+ if (dialog?.open && dialog.matches(':modal')) {
80
+ announceInModal(dialog, message);
81
+ } else {
82
+ announce(message, 'polite');
83
+ }
84
+ });
85
+ return () => cancelAnimationFrame(frame);
86
+ }, [announce, elementRef, isBusy, message]);
87
+ }
@@ -110,8 +110,8 @@ export interface UseMenuPressOptions {
110
110
  getScroller?: () => HTMLElement | null;
111
111
  /**
112
112
  * Resolve the element under a viewport point. Defaults to
113
- * `document.elementFromPoint`, falling back to the event target where the
114
- * document lacks it (jsdom). A test seam.
113
+ * `elementFromPoint` on the menu's own document or shadow root, falling back
114
+ * to the event target where the document lacks it (jsdom). A test seam.
115
115
  */
116
116
  hitTest?: (x: number, y: number) => Element | null;
117
117
  /** @default 500 */
@@ -1 +1 @@
1
- {"version":3,"file":"useMenuPress.d.ts","sourceRoot":"","sources":["../../src/hooks/useMenuPress.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAA0C,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9E,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAG5B,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,iFAAiF;AACjF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAchD;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AA6DD,0DAA0D;AAC1D,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAMD,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACvC,uEAAuE;IACvE,UAAU,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC3C;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAChE;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/D,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IACnD,mBAAmB;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE;QACT,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAChE,CAAC,iBAAiB,CAAC,EAAE,EAAE,CAAC;KACzB,CAAC;IACF,yEAAyE;IACzE,YAAY,EAAE;QACZ,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAChE,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;KAC/D,CAAC;IACF;;;;OAIG;IACH,uBAAuB,EAAE,MAAM,OAAO,CAAC;IACvC,uEAAuE;IACvE,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAkED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA2iB7E"}
1
+ {"version":3,"file":"useMenuPress.d.ts","sourceRoot":"","sources":["../../src/hooks/useMenuPress.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAA0C,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9E,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,oBAAoB,CAAC;AAG5B,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,iFAAiF;AACjF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAchD;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AA6DD,0DAA0D;AAC1D,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAMD,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACvC,uEAAuE;IACvE,UAAU,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC3C;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAChE;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/D,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IACnD,mBAAmB;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE;QACT,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAChE,CAAC,iBAAiB,CAAC,EAAE,EAAE,CAAC;KACzB,CAAC;IACF,yEAAyE;IACzE,YAAY,EAAE;QACZ,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAChE,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;KAC/D,CAAC;IACF;;;;OAIG;IACH,uBAAuB,EAAE,MAAM,OAAO,CAAC;IACvC,uEAAuE;IACvE,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAkED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAojB7E"}
@@ -256,7 +256,11 @@ export function useMenuPress(options) {
256
256
  if (hitTest != null) {
257
257
  element = hitTest(event.clientX, event.clientY);
258
258
  } else if (typeof doc.elementFromPoint === 'function') {
259
- element = doc.elementFromPoint(event.clientX, event.clientY);
259
+ // Hit-test in the menu's own tree: inside a shadow root the document
260
+ // answers with the shadow host, never the row under the pointer.
261
+ const tree = menu?.getRootNode();
262
+ const scope = tree != null && tree !== doc && typeof tree.elementFromPoint === 'function' ? tree : doc;
263
+ element = scope.elementFromPoint(event.clientX, event.clientY);
260
264
  } else {
261
265
  element = event.target instanceof Element ? event.target : null;
262
266
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useTypeahead.d.ts","sourceRoot":"","sources":["../../src/hooks/useTypeahead.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,aAAa,EAAE,MAAM,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAE9D;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAE/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,aAAa,KAAK,OAAO,CAAC;IAC/D,gDAAgD;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AA+BD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA8F7E"}
1
+ {"version":3,"file":"useTypeahead.d.ts","sourceRoot":"","sources":["../../src/hooks/useTypeahead.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,aAAa,EAAE,MAAM,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAE9D;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;IAE/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,aAAa,KAAK,OAAO,CAAC;IAC/D,gDAAgD;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AA+BD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAmG7E"}
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file useTypeahead.ts
7
- * @input Uses React useCallback/useRef
7
+ * @input Uses React useCallback/useEffect/useRef
8
8
  * @output Exports useTypeahead hook for first-character (type-to-focus) search
9
9
  * @position Core hook; adds APG typeahead to menus, listboxes, and other
10
10
  * collections. Composes with useListFocus/useGridFocus (or any collection)
@@ -13,7 +13,7 @@
13
13
  * SYNC: When modified, update:
14
14
  * - /packages/core/src/hooks/index.ts
15
15
  */
16
- import { useCallback, useRef } from 'react';
16
+ import { useCallback, useEffect, useRef } from 'react';
17
17
  /**
18
18
  * Whether a key event represents a single printable character (a type-to-focus
19
19
  * candidate) rather than a control/navigation key or a shortcut chord.
@@ -79,6 +79,11 @@ export function useTypeahead(options) {
79
79
  timeoutRef.current = undefined;
80
80
  }
81
81
  }, []);
82
+
83
+ // A collection that unmounts, or that an `<Activity>` boundary hides,
84
+ // mid-typing leaves no reset timer running and comes back with an empty
85
+ // buffer, as the reset would have left it.
86
+ useEffect(() => reset, [reset]);
82
87
  const scheduleReset = useCallback(() => {
83
88
  if (timeoutRef.current) {
84
89
  clearTimeout(timeoutRef.current);
@@ -1 +1 @@
1
- {"version":3,"file":"en.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/en.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8jBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"en.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/en.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+jBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -315,6 +315,7 @@ const runtimeCatalog = {
315
315
  '@astryx.chatToolCalls.status.complete': 'Complete',
316
316
  '@astryx.chatToolCalls.status.error': 'Failed',
317
317
  '@astryx.chatToolCalls.groupLabel': '{count} tool calls',
318
+ '@astryx.chatTriggerMenu.searching': 'Searching…',
318
319
  '@astryx.chatTriggerMenu.suggestions': 'Suggestions',
319
320
  '@astryx.citation.label': 'Citation {number}: {title}',
320
321
  '@astryx.codeBlock.copied': 'Copied',
@@ -1 +1 @@
1
- {"version":3,"file":"pseudo.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/pseudo.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAukBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"pseudo.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/pseudo.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwkBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -315,6 +315,7 @@ const runtimeCatalog = {
315
315
  '@astryx.chatToolCalls.status.complete': '⟦Çóɱþłéţé⟧',
316
316
  '@astryx.chatToolCalls.status.error': '⟦Ƒàíłéð⟧',
317
317
  '@astryx.chatToolCalls.groupLabel': '⟦{count} ţóół çàłłš⟧',
318
+ '@astryx.chatTriggerMenu.searching': '⟦Šéàřçĥíñĝ…⟧',
318
319
  '@astryx.chatTriggerMenu.suggestions': '⟦Šúĝĝéšţíóñš⟧',
319
320
  '@astryx.citation.label': '⟦Çíţàţíóñ {number}: {title}⟧',
320
321
  '@astryx.codeBlock.copied': '⟦Çóþíéð⟧',
package/locales/en.json CHANGED
@@ -901,7 +901,7 @@
901
901
  },
902
902
  "@astryx.typeahead.loading": {
903
903
  "defaultMessage": "Loading",
904
- "description": "Screen-reader-only name for a spinner shown inside a Typeahead dropdown while suggestions are being fetched. Present-progressive form."
904
+ "description": "Screen-reader-only name for the spinner a Typeahead, Tokenizer or directly used BaseTypeahead shows while suggestions are being fetched; also announced once through the shared announcement channel when each search starts. Present-progressive form."
905
905
  },
906
906
  "@astryx.typeahead.searchResults": {
907
907
  "defaultMessage": "Search results",
@@ -1169,7 +1169,7 @@
1169
1169
  },
1170
1170
  "@astryx.spinner.loading": {
1171
1171
  "defaultMessage": "Loading",
1172
- "description": "Screen-reader-only default name for a standalone Spinner's role=\"status\" element, used when the consumer supplies neither `aria-label` nor a visible string label. Present-progressive form. Kept separate from `button.loading` and `typeahead.loading` — translations may diverge."
1172
+ "description": "Screen-reader-only default name for a standalone Spinner's role=\"status\" element, used when the consumer supplies neither `aria-label` nor a visible string label; also announced once when a busy input or selector starts loading, in place of its decorative Spinner. Present-progressive form. Kept separate from `button.loading` and `typeahead.loading` — translations may diverge."
1173
1173
  },
1174
1174
  "@astryx.chatComposerDrawer.expand": {
1175
1175
  "defaultMessage": "Expand {label}",
@@ -1231,6 +1231,10 @@
1231
1231
  "defaultMessage": "{count} tool calls",
1232
1232
  "description": "Summary label shown in the group header when multiple tool calls are expanded. {count} is the number of calls."
1233
1233
  },
1234
+ "@astryx.chatTriggerMenu.searching": {
1235
+ "defaultMessage": "Searching…",
1236
+ "description": "Shown, and announced politely to screen readers, while a chat composer's @-mention or slash-command menu waits for async search results. Present participle; trailing `…` is one character."
1237
+ },
1234
1238
  "@astryx.chatTriggerMenu.suggestions": {
1235
1239
  "defaultMessage": "Suggestions",
1236
1240
  "description": "Screen-reader-only fallback label for the popover listbox opened by a trigger character (`@`, `/`) in the chat composer when the trigger provides no specific label."
@@ -923,6 +923,9 @@
923
923
  "@astryx.chatToolCalls.groupLabel": {
924
924
  "defaultMessage": "⟦{count} ţóół çàłłš⟧"
925
925
  },
926
+ "@astryx.chatTriggerMenu.searching": {
927
+ "defaultMessage": "⟦Šéàřçĥíñĝ…⟧"
928
+ },
926
929
  "@astryx.chatTriggerMenu.suggestions": {
927
930
  "defaultMessage": "⟦Šúĝĝéšţíóñš⟧"
928
931
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.3",
3
+ "version": "0.7.4-canary.10b832e",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -792,6 +792,8 @@
792
792
  "react-dom": ">=19.0.0"
793
793
  },
794
794
  "devDependencies": {
795
+ "@astryxdesign/a11y-spec": "0.7.4-canary.10b832e",
796
+ "@astryxdesign/cli": "0.7.4-canary.10b832e",
795
797
  "@babel/cli": "^7.29.7",
796
798
  "@babel/core": "^7.29.7",
797
799
  "@babel/preset-react": "^7.29.7",
@@ -802,9 +804,7 @@
802
804
  "@testing-library/jest-dom": "^6.6.0",
803
805
  "@testing-library/react": "^16.3.2",
804
806
  "@types/babel__core": "^7.20.5",
805
- "tailwindcss": "^4.3.2",
806
- "@astryxdesign/a11y-spec": "0.0.0",
807
- "@astryxdesign/cli": "0.7.3"
807
+ "tailwindcss": "^4.3.2"
808
808
  },
809
809
  "dependencies": {
810
810
  "intl-messageformat": "^11.2.9"
@@ -95,7 +95,7 @@ export const docs = {
95
95
  derived: [{property: 'padding', expand: 'container'}],
96
96
  },
97
97
  description:
98
- "A mobile touch sheet that rises from the bottom edge, with animated entrance and exit, a grab handle, optional drag-to-resize snap points, and purpose-controlled dismissal. Like Dialog, it is a padded container: content starts at the theme's bottom-sheet padding (--spacing-4 by default), and a lone Section child or bleed children such as Table and Divider align against it. A standalone sheet owns a native <dialog>; inside BottomSheetSwitcher it renders a panel in the switcher's shared dialog. In both modes, ref and shared DOM props target the visual panel <div>.",
98
+ "A mobile touch sheet that rises from the bottom edge, with animated entrance and exit, a grab handle, optional drag-to-resize snap points, and purpose-controlled dismissal. Like Dialog, it is a padded container: content starts at the theme's bottom-sheet padding (--spacing-4 by default), and a lone Section child or bleed children such as Table and Divider align against it. A sheet always owns a native <dialog>; inside BottomSheetSwitcher the switcher presents and closes that dialog as the flow moves between steps. In both modes, ref and shared DOM props target the visual panel <div>.",
99
99
  props: [
100
100
  {
101
101
  name: 'isOpen',
@@ -165,7 +165,7 @@ export const docs = {
165
165
  name: 'hasScrim',
166
166
  type: 'boolean',
167
167
  description:
168
- "For a standalone BottomSheet, whether to render a scrim, the semi-transparent overlay that covers and blocks the background. true (default) uses showModal(): top layer, focus trap, ::backdrop scrim, body scroll lock, and tap-scrim-to-dismiss when purpose='info', with the background inert. false uses show() with no scrim, leaving the page behind interactive and scrollable. For a multi-step flow, configure hasScrim on BottomSheetSwitcher instead; it owns one shared dialog across every child.",
168
+ "For a standalone BottomSheet, whether to render a scrim, the semi-transparent overlay that covers and blocks the background. true (default) uses showModal(): top layer, native focus containment, ::backdrop scrim, body scroll lock, and tap-scrim-to-dismiss when purpose='info', with the background inert. false uses show() with no scrim, leaving the page behind interactive and scrollable. For a multi-step flow, configure hasScrim on BottomSheetSwitcher instead; it applies to every child's dialog.",
169
169
  default: 'true',
170
170
  },
171
171
  ],
@@ -66,9 +66,7 @@ Consumer migration instructions belong in consumer docs and release notes.
66
66
  `architecture:container-padding`.
67
67
  - Standalone sheet presentation, including its optional native-dialog Scrim.
68
68
  - The native dialog every sheet renders, standalone or switcher-managed
69
- (`component:BottomSheetSwitcher` DEC-1). Implementation gap: a
70
- switcher-managed sheet renders a panel inside the switcher's one shared
71
- dialog and owns no dialog of its own.
69
+ (`component:BottomSheetSwitcher` DEC-1).
72
70
 
73
71
  **Does not own / non-goals**
74
72
 
@@ -107,8 +105,7 @@ in `BottomSheet.doc.mjs`.
107
105
  without a Scrim.
108
106
  - A switcher-managed sheet renders the same panel anatomy in its own dialog while
109
107
  BottomSheetSwitcher coordinates that dialog's lifecycle and the flow's one Scrim
110
- (`component:BottomSheetSwitcher` DEC-1). Implementation gap: the panel
111
- renders inside the switcher's one shared dialog.
108
+ (`component:BottomSheetSwitcher` DEC-1).
112
109
 
113
110
  ### Transformation and precedence order
114
111