@astryxdesign/core 0.7.7-canary.4a8a76e → 0.7.7-canary.4e495ec

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 (266) hide show
  1. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
  2. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
  3. package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
  4. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  5. package/dist/BottomSheet/BottomSheet.js +79 -9
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheetPanel.js +1 -1
  8. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  9. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  10. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
  13. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  14. package/dist/CheckboxList/CheckboxList.js +7 -1
  15. package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
  16. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  17. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  18. package/dist/CheckboxList/CheckboxListItem.js +10 -2
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +5 -2
  21. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  22. package/dist/DateTimeInput/DateTimeInput.js +5 -2
  23. package/dist/Dialog/Dialog.d.ts.map +1 -1
  24. package/dist/Dialog/Dialog.js +16 -14
  25. package/dist/Drawer/Drawer.d.ts.map +1 -1
  26. package/dist/Drawer/Drawer.js +15 -11
  27. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  28. package/dist/Drawer/useDrawerDialogPresence.js +5 -1
  29. package/dist/Field/PanelSearchInput.d.ts +9 -1
  30. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  31. package/dist/Field/PanelSearchInput.js +3 -1
  32. package/dist/InfoTip/InfoTip.d.ts +2 -2
  33. package/dist/InfoTip/InfoTip.js +4 -4
  34. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  35. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  36. package/dist/Layer/layerAnimations.stylex.js +25 -20
  37. package/dist/Layer/useBackdropPress.d.ts +27 -0
  38. package/dist/Layer/useBackdropPress.d.ts.map +1 -0
  39. package/dist/Layer/useBackdropPress.js +53 -0
  40. package/dist/Layer/usePressPin.d.ts +3 -5
  41. package/dist/Layer/usePressPin.d.ts.map +1 -1
  42. package/dist/Layer/usePressPin.js +4 -6
  43. package/dist/MobileNav/MobileNav.js +0 -1
  44. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  45. package/dist/MultiSelector/hooks.js +2 -4
  46. package/dist/Outline/Outline.d.ts +2 -4
  47. package/dist/Outline/Outline.d.ts.map +1 -1
  48. package/dist/Outline/Outline.js +4 -52
  49. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  50. package/dist/Outline/useOutlineFromMarkdown.js +1 -0
  51. package/dist/Outline/useScrollSpy.js +3 -3
  52. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  53. package/dist/PowerSearch/PowerSearch.js +134 -179
  54. package/dist/RadioList/RadioList.d.ts.map +1 -1
  55. package/dist/RadioList/RadioList.js +5 -0
  56. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  57. package/dist/Resizable/ResizeHandle.js +118 -260
  58. package/dist/Resizable/resizeGesture.d.ts +91 -0
  59. package/dist/Resizable/resizeGesture.d.ts.map +1 -0
  60. package/dist/Resizable/resizeGesture.js +251 -0
  61. package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
  62. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  63. package/dist/SegmentedControl/SegmentedControl.js +115 -8
  64. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  65. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  66. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
  67. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  68. package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
  69. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
  70. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
  71. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
  72. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
  73. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
  74. package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
  75. package/dist/Selector/Selector.d.ts.map +1 -1
  76. package/dist/Selector/Selector.js +96 -67
  77. package/dist/Selector/optionShape.d.ts +24 -0
  78. package/dist/Selector/optionShape.d.ts.map +1 -0
  79. package/dist/Selector/optionShape.js +48 -0
  80. package/dist/Selector/selectableOptionEntries.d.ts +20 -0
  81. package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
  82. package/dist/Selector/selectableOptionEntries.js +38 -0
  83. package/dist/Selector/utils.d.ts +2 -17
  84. package/dist/Selector/utils.d.ts.map +1 -1
  85. package/dist/Selector/utils.js +3 -51
  86. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  87. package/dist/SideNav/SideNavItem.js +3 -9
  88. package/dist/Switch/switchTouch.d.ts +0 -7
  89. package/dist/Switch/switchTouch.d.ts.map +1 -1
  90. package/dist/Switch/switchTouch.js +2 -10
  91. package/dist/Switch/useSwitchTouch.d.ts +2 -11
  92. package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
  93. package/dist/Switch/useSwitchTouch.js +26 -85
  94. package/dist/Token/Token.d.ts +2 -2
  95. package/dist/Token/Token.d.ts.map +1 -1
  96. package/dist/Token/Token.js +12 -4
  97. package/dist/Token/TokenNamingContext.d.ts +14 -0
  98. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  99. package/dist/Token/TokenNamingContext.js +23 -0
  100. package/dist/Tokenizer/Tokenizer.d.ts +46 -59
  101. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  102. package/dist/Tokenizer/Tokenizer.js +35 -58
  103. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
  104. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  105. package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
  106. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
  107. package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
  108. package/dist/Tooltip/Tooltip.d.ts +25 -30
  109. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  110. package/dist/Tooltip/Tooltip.js +24 -5
  111. package/dist/Tooltip/index.d.ts +1 -1
  112. package/dist/Tooltip/index.d.ts.map +1 -1
  113. package/dist/Tooltip/useTooltip.d.ts +28 -29
  114. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  115. package/dist/Tooltip/useTooltip.js +20 -8
  116. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  117. package/dist/TopNav/TopNavItem.js +4 -7
  118. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  119. package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
  120. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  121. package/dist/TopNav/TopNavMenu.js +4 -7
  122. package/dist/astryx.css +76 -5
  123. package/dist/gestures/GestureHost.d.ts.map +1 -1
  124. package/dist/gestures/GestureHost.js +53 -5
  125. package/dist/hooks/actionGroupTracker.d.ts +3 -3
  126. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  127. package/dist/hooks/actionGroupTracker.js +6 -53
  128. package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
  129. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  130. package/dist/hooks/useHighlightedOptionScroll.js +112 -11
  131. package/dist/hooks/useMediaQuery.d.ts +2 -1
  132. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  133. package/dist/hooks/useMediaQuery.js +7 -4
  134. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  135. package/dist/hooks/useMenuPress.js +4 -2
  136. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
  137. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
  138. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
  139. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
  140. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  141. package/dist/i18n/generated-locales/en.generated.js +1 -0
  142. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  143. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  144. package/dist/internal/SheetConfirmation.d.ts +11 -0
  145. package/dist/internal/SheetConfirmation.d.ts.map +1 -0
  146. package/dist/internal/SheetConfirmation.js +92 -0
  147. package/dist/internal/sheetDraft.d.ts +35 -0
  148. package/dist/internal/sheetDraft.d.ts.map +1 -0
  149. package/dist/internal/sheetDraft.js +65 -0
  150. package/dist/internal/touch-sheet.d.ts +21 -0
  151. package/dist/internal/touch-sheet.d.ts.map +1 -0
  152. package/dist/internal/touch-sheet.js +19 -0
  153. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
  154. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  155. package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
  156. package/dist/motion/roles.d.ts +7 -1
  157. package/dist/motion/roles.d.ts.map +1 -1
  158. package/dist/motion/roles.js +20 -1
  159. package/dist/motion/uikit/index.d.ts +6 -5
  160. package/dist/motion/uikit/index.d.ts.map +1 -1
  161. package/dist/motion/uikit/index.js +6 -5
  162. package/dist/motion/uikit/segmented.d.ts +55 -0
  163. package/dist/motion/uikit/segmented.d.ts.map +1 -0
  164. package/dist/motion/uikit/segmented.js +77 -0
  165. package/dist/motion/uikit/springs.d.ts +0 -18
  166. package/dist/motion/uikit/springs.d.ts.map +1 -1
  167. package/dist/motion/uikit/springs.js +5 -21
  168. package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
  169. package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
  170. package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
  171. package/dist/utils/browserClickWait.d.ts +35 -0
  172. package/dist/utils/browserClickWait.d.ts.map +1 -0
  173. package/dist/utils/browserClickWait.js +123 -0
  174. package/dist/utils/clickGuard.d.ts +8 -1
  175. package/dist/utils/clickGuard.d.ts.map +1 -1
  176. package/dist/utils/clickGuard.js +24 -1
  177. package/locales/en.json +4 -0
  178. package/locales/pseudo.json +3 -0
  179. package/package.json +8 -8
  180. package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
  181. package/src/BottomSheet/BottomSheet.tsx +95 -10
  182. package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
  183. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  184. package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
  185. package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
  186. package/src/CheckboxList/CheckboxList.tsx +6 -0
  187. package/src/CheckboxList/CheckboxListContext.tsx +6 -0
  188. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  189. package/src/DateInput/DateInput.tsx +8 -2
  190. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  191. package/src/Dialog/Dialog.tsx +44 -14
  192. package/src/Drawer/Drawer.tsx +28 -11
  193. package/src/Drawer/useDrawerDialogPresence.ts +5 -1
  194. package/src/Field/PanelSearchInput.tsx +11 -0
  195. package/src/InfoTip/InfoTip.doc.mjs +3 -3
  196. package/src/InfoTip/InfoTip.tsx +5 -5
  197. package/src/Layer/layerAnimations.stylex.ts +24 -16
  198. package/src/Layer/useBackdropPress.ts +81 -0
  199. package/src/Layer/usePressPin.ts +5 -11
  200. package/src/MobileNav/MobileNav.tsx +1 -1
  201. package/src/MultiSelector/hooks.ts +2 -4
  202. package/src/Outline/Outline.doc.mjs +177 -51
  203. package/src/Outline/Outline.tsx +5 -57
  204. package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
  205. package/src/Outline/useOutlineFromMarkdown.ts +1 -0
  206. package/src/Outline/useScrollSpy.ts +3 -3
  207. package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
  208. package/src/PowerSearch/PowerSearch.tsx +196 -183
  209. package/src/RadioList/RadioList.doc.mjs +4 -2
  210. package/src/RadioList/RadioList.tsx +4 -0
  211. package/src/Resizable/ResizeHandle.tsx +133 -328
  212. package/src/Resizable/resizeGesture.ts +368 -0
  213. package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
  214. package/src/SegmentedControl/SegmentedControl.tsx +179 -5
  215. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  216. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  217. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1637 -0
  218. package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
  219. package/src/Selector/Selector.tsx +136 -89
  220. package/src/Selector/optionShape.ts +64 -0
  221. package/src/Selector/selectableOptionEntries.ts +39 -0
  222. package/src/Selector/utils.ts +9 -68
  223. package/src/SideNav/SideNavItem.tsx +3 -7
  224. package/src/Switch/switchTouch.ts +2 -10
  225. package/src/Switch/useSwitchTouch.ts +26 -96
  226. package/src/Token/Token.tsx +13 -4
  227. package/src/Token/TokenNamingContext.ts +28 -0
  228. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  229. package/src/Tokenizer/Tokenizer.tsx +81 -86
  230. package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
  231. package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
  232. package/src/Tooltip/Tooltip.doc.mjs +123 -54
  233. package/src/Tooltip/Tooltip.tsx +36 -39
  234. package/src/Tooltip/index.ts +0 -1
  235. package/src/Tooltip/useTooltip.doc.mjs +147 -33
  236. package/src/Tooltip/useTooltip.tsx +39 -32
  237. package/src/TopNav/TopNavItem.tsx +6 -5
  238. package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
  239. package/src/TopNav/TopNavMenu.tsx +6 -5
  240. package/src/gestures/GestureHost.ts +73 -4
  241. package/src/hooks/actionGroupTracker.ts +12 -56
  242. package/src/hooks/useHighlightedOptionScroll.ts +123 -15
  243. package/src/hooks/useMediaQuery.doc.mjs +2 -2
  244. package/src/hooks/useMediaQuery.ts +13 -4
  245. package/src/hooks/useMenuPress.ts +8 -2
  246. package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
  247. package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
  248. package/src/i18n/generated-locales/en.generated.ts +1 -0
  249. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
  250. package/src/internal/SheetConfirmation.tsx +101 -0
  251. package/src/internal/sheetDraft.ts +80 -0
  252. package/src/internal/touch-sheet.ts +21 -0
  253. package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
  254. package/src/motion/MEASUREMENTS.md +7 -0
  255. package/src/motion/README.md +1 -0
  256. package/src/motion/roles.ts +20 -1
  257. package/src/motion/uikit/index.ts +5 -5
  258. package/src/motion/uikit/segmented.ts +74 -0
  259. package/src/motion/uikit/springs.ts +5 -21
  260. package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
  261. package/src/utils/browserClickWait.ts +133 -0
  262. package/src/utils/clickGuard.ts +37 -1
  263. package/dist/internal/tokenizer-presentation.d.ts +0 -21
  264. package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
  265. package/dist/internal/tokenizer-presentation.js +0 -21
  266. package/src/internal/tokenizer-presentation.ts +0 -25
@@ -6,61 +6,15 @@
6
6
  * @position Utilities; used by Selector.tsx
7
7
  */
8
8
 
9
- import type {
10
- SelectorOptionType,
11
- SelectorOptionData,
12
- SelectorDivider,
13
- SelectorSection,
14
- } from './types';
9
+ import type {SelectorOptionType, SelectorOptionData} from './types';
10
+ import {getSelectableOptionEntries} from './selectableOptionEntries';
15
11
 
16
- /**
17
- * Type guard: check if option is a selectable option (string or OptionData)
18
- */
19
- export function isOptionData(
20
- option: SelectorOptionType,
21
- ): option is SelectorOptionData | string {
22
- if (typeof option === 'string') {
23
- return true;
24
- }
25
- return !('type' in option);
26
- }
27
-
28
- /**
29
- * Type guard: check if option is a divider
30
- */
31
- export function isDivider(
32
- option: SelectorOptionType,
33
- ): option is SelectorDivider {
34
- return (
35
- typeof option === 'object' && 'type' in option && option.type === 'divider'
36
- );
37
- }
38
-
39
- /**
40
- * Type guard: check if option is a section
41
- */
42
- export function isSection(
43
- option: SelectorOptionType,
44
- ): option is SelectorSection {
45
- return (
46
- typeof option === 'object' && 'type' in option && option.type === 'section'
47
- );
48
- }
49
-
50
- /**
51
- * Normalize string or option to consistent shape
52
- */
53
- export function normalizeOption(
54
- option: string | SelectorOptionData,
55
- ): SelectorOptionData {
56
- if (typeof option === 'string') {
57
- return {value: option, label: option};
58
- }
59
- return {
60
- ...option,
61
- label: option.label ?? option.value,
62
- };
63
- }
12
+ export {
13
+ isOptionData,
14
+ isDivider,
15
+ isSection,
16
+ normalizeOption,
17
+ } from './optionShape';
64
18
 
65
19
  /**
66
20
  * Get all selectable options (flattening sections)
@@ -68,18 +22,5 @@ export function normalizeOption(
68
22
  export function getSelectableOptions(
69
23
  options: SelectorOptionType[],
70
24
  ): SelectorOptionData[] {
71
- const result: SelectorOptionData[] = [];
72
-
73
- for (const option of options) {
74
- if (isOptionData(option)) {
75
- result.push(normalizeOption(option));
76
- } else if (isSection(option)) {
77
- for (const opt of option.options) {
78
- result.push(normalizeOption(opt));
79
- }
80
- }
81
- // Skip dividers
82
- }
83
-
84
- return result;
25
+ return getSelectableOptionEntries(options).map(entry => entry.item);
85
26
  }
@@ -72,7 +72,7 @@ import {
72
72
  SideNavCollapseContext,
73
73
  } from './SideNavCollapseContext';
74
74
  import {useSideNavRenderMode} from './SideNavRenderContext';
75
- import {useAppShellMobile} from '../AppShell/AppShellMobileContext';
75
+ import {useCloseMobileDrawerOnActivate} from '../AppShell/useCloseMobileDrawerOnActivate';
76
76
  import {themeProps} from '../utils/themeProps';
77
77
  import {disabledInteractionProps} from '../utils/disabledInteraction';
78
78
 
@@ -535,8 +535,8 @@ export function SideNavItem({
535
535
  const t = useComponentTranslator();
536
536
  const {isCollapsed} = useSideNavCollapse();
537
537
  const renderMode = useSideNavRenderMode();
538
- const {closeMobileNav} = useAppShellMobile();
539
538
  const isInDrawer = renderMode === 'drawer' || renderMode === 'drawer-content';
539
+ const closeDrawerAfter = useCloseMobileDrawerOnActivate(isInDrawer);
540
540
  const id = useId();
541
541
  const hasChildren = !!children;
542
542
  const depth = use(SideNavItemDepthContext);
@@ -625,11 +625,7 @@ export function SideNavItem({
625
625
  toggleItemCollapse();
626
626
  return;
627
627
  }
628
- onClick?.(e);
629
- // Close the mobile nav when a nav item is activated inside the drawer
630
- if (isInDrawer) {
631
- closeMobileNav();
632
- }
628
+ closeDrawerAfter(onClick)?.(e);
633
629
  };
634
630
 
635
631
  const handleToggleClick = (e: React.MouseEvent) => {
@@ -4,8 +4,8 @@
4
4
  * @file switchTouch.ts
5
5
  * @input The UIKit switch constants (motion/uikit/switch.ts) and a track size
6
6
  * @output The pure model of Switch's touch presentation: thumb geometry, the
7
- * drag's target, the flip rule, the commit on release and its click wait,
8
- * and when the lifted thumb may settle
7
+ * drag's target, the flip rule, the commit on release, and when the lifted
8
+ * thumb may settle
9
9
  * @position Pure half of Switch's touch presentation; driven by
10
10
  * useSwitchTouch.ts, tested by switchTouch.test.ts without a browser
11
11
  *
@@ -158,14 +158,6 @@ export function stepFlip(
158
158
  return isPast === flip.isArmed ? flip : {...flip, isArmed: isPast};
159
159
  }
160
160
 
161
- /**
162
- * Passive: how long a lift waits for the browser's own click before the
163
- * switch clicks its input itself, ms. The browser sends that click with the
164
- * lift's `touchend`. The surface's `touch-action` leaves no double-tap delay.
165
- * So this only runs out for a lift the browser does not count as a click.
166
- */
167
- export const BROWSER_CLICK_WAIT_MS = 300;
168
-
169
161
  /**
170
162
  * What a release commits: the side a drag flipped to; when nothing flipped,
171
163
  * the other side of the value, wherever the finger lifts. A press slid off
@@ -31,9 +31,9 @@ import {solveSpring, toSpring, type SpringDescription} from '../motion/spring';
31
31
  import {UIKIT_GESTURES} from '../motion/uikit/gestures';
32
32
  import {registerSwitchTable, switchControl} from '../motion/uikit/switch';
33
33
  import {useTouchPresentation} from '../utils/touchPresentation';
34
- import {armClickGuard, withoutClickGuard} from '../utils/clickGuard';
34
+ import {awaitBrowserClick, clickForLift} from '../utils/browserClickWait';
35
+ import {armClickGuard} from '../utils/clickGuard';
35
36
  import {
36
- BROWSER_CLICK_WAIT_MS,
37
37
  commitOnRelease,
38
38
  dragTarget,
39
39
  settleTime,
@@ -253,11 +253,8 @@ export class SwitchTouchController {
253
253
  private isTracking = false;
254
254
  /** Which presentation the switch has: the touch one, or its own. */
255
255
  private presentation: SwitchPresentation = 'touch';
256
- /** The press-time listeners under a fine pointer, while armed. */
257
- private pressListeners: (() => void) | null = null;
258
- private pressListenersTimer: ReturnType<typeof setTimeout> | null = null;
259
- /** Passive: a lift is waiting for the browser's own click. */
260
- private browserClickTimer: ReturnType<typeof setTimeout> | null = null;
256
+ /** Passive: a lift is waiting for the browser's own click; this ends it. */
257
+ private endBrowserClickWait: (() => void) | null = null;
261
258
  private reconcileTimer: ReturnType<typeof setTimeout> | null = null;
262
259
 
263
260
  constructor(isOn: boolean, geometry: SwitchTouchGeometry) {
@@ -328,73 +325,7 @@ export class SwitchTouchController {
328
325
  }
329
326
  },
330
327
  });
331
- // The lift's own click activates the input natively: the switch then
332
- // clicks nothing itself.
333
- const onClick = (event: MouseEvent) => {
334
- if (event.isTrusted && this.browserClickTimer != null) {
335
- clearTimeout(this.browserClickTimer);
336
- this.browserClickTimer = null;
337
- this.scheduleReconcile();
338
- }
339
- };
340
- const listen = (): (() => void) => {
341
- surface.addEventListener('click', onClick, true);
342
- return () => {
343
- surface.removeEventListener('click', onClick, true);
344
- };
345
- };
346
- if (this.presentation === 'pointer') {
347
- // Under a fine pointer only the press the host arms listens at rest.
348
- // The switch's click listener comes with a press and goes after its
349
- // click wait.
350
- this.listenForPress = listen;
351
- return [
352
- host.attachConsumer(surface, tracker),
353
- () => tracker.dispose(),
354
- () => this.stopPressListeners(),
355
- ];
356
- }
357
- return [
358
- host.attachConsumer(surface, tracker),
359
- () => tracker.dispose(),
360
- listen(),
361
- ];
362
- }
363
-
364
- /** Under a fine pointer: adds the switch's own listeners for a press. */
365
- private listenForPress: (() => () => void) | null = null;
366
-
367
- private startPressListeners(): void {
368
- if (this.pressListenersTimer != null) {
369
- clearTimeout(this.pressListenersTimer);
370
- this.pressListenersTimer = null;
371
- }
372
- if (this.pressListeners == null && this.listenForPress != null) {
373
- this.pressListeners = this.listenForPress();
374
- }
375
- }
376
-
377
- /** The press's click window is over: its listeners go. */
378
- private endPressListeners(afterMs: number): void {
379
- if (this.pressListeners == null) {
380
- return;
381
- }
382
- if (this.pressListenersTimer != null) {
383
- clearTimeout(this.pressListenersTimer);
384
- }
385
- this.pressListenersTimer = setTimeout(() => {
386
- this.pressListenersTimer = null;
387
- this.stopPressListeners();
388
- }, afterMs);
389
- }
390
-
391
- private stopPressListeners(): void {
392
- if (this.pressListenersTimer != null) {
393
- clearTimeout(this.pressListenersTimer);
394
- this.pressListenersTimer = null;
395
- }
396
- this.pressListeners?.();
397
- this.pressListeners = null;
328
+ return [host.attachConsumer(surface, tracker), () => tracker.dispose()];
398
329
  }
399
330
 
400
331
  /** Undo everything: listeners, timers, animations and inline styles. */
@@ -403,7 +334,6 @@ export class SwitchTouchController {
403
334
  detach();
404
335
  }
405
336
  this.detachers = [];
406
- this.listenForPress = null;
407
337
  this.clearTimers();
408
338
  for (const animator of [this.thumb, this.fade, this.shadow]) {
409
339
  const element = animator?.element;
@@ -576,7 +506,6 @@ export class SwitchTouchController {
576
506
  switch (event) {
577
507
  case 'touchDown':
578
508
  this.isTracking = true;
579
- this.startPressListeners();
580
509
  this.touchDown(time);
581
510
  return;
582
511
  case 'touchDragInside':
@@ -612,7 +541,6 @@ export class SwitchTouchController {
612
541
  byBrowserClick: true,
613
542
  clicksIfNoBrowserClick: touch.pointerType === 'touch',
614
543
  });
615
- this.endPressListeners(BROWSER_CLICK_WAIT_MS);
616
544
  return;
617
545
  }
618
546
  if (
@@ -625,7 +553,6 @@ export class SwitchTouchController {
625
553
  // off toggles.
626
554
  this.released = true;
627
555
  this.abandon(time);
628
- this.endPressListeners(0);
629
556
  return;
630
557
  }
631
558
  // A drag that flipped, or a press slid off and lifted outside, which
@@ -637,7 +564,6 @@ export class SwitchTouchController {
637
564
  armClickGuard(this.elements.input.ownerDocument);
638
565
  }
639
566
  this.release(time);
640
- this.endPressListeners(0);
641
567
  return;
642
568
  case 'touchCancel':
643
569
  // The browser took the touch for its scroll, or a recognizer above
@@ -647,7 +573,6 @@ export class SwitchTouchController {
647
573
  this.released = true;
648
574
  this.abandon(time);
649
575
  }
650
- this.endPressListeners(0);
651
576
  return;
652
577
  case 'touchDragEnter':
653
578
  case 'touchDragExit':
@@ -729,14 +654,23 @@ export class SwitchTouchController {
729
654
  this.showSide(commit, time, undefined, true);
730
655
  }
731
656
  this.pressEnded(time, true);
732
- if (byBrowserClick) {
733
- this.browserClickTimer = setTimeout(() => {
734
- this.browserClickTimer = null;
735
- if (clicksIfNoBrowserClick && commit !== this.isOn) {
736
- this.clickInput();
737
- }
738
- this.scheduleReconcile();
739
- }, BROWSER_CLICK_WAIT_MS);
657
+ const surface = this.elements?.surface;
658
+ if (byBrowserClick && surface != null) {
659
+ // The lift's own click activates the input natively: the switch then
660
+ // clicks nothing itself.
661
+ this.endBrowserClickWait = awaitBrowserClick(surface, {
662
+ onClicked: () => {
663
+ this.endBrowserClickWait = null;
664
+ this.scheduleReconcile();
665
+ },
666
+ onNoClick: () => {
667
+ this.endBrowserClickWait = null;
668
+ if (clicksIfNoBrowserClick && commit !== this.isOn) {
669
+ this.clickInput();
670
+ }
671
+ this.scheduleReconcile();
672
+ },
673
+ });
740
674
  return;
741
675
  }
742
676
  if (commit !== this.isOn) {
@@ -753,11 +687,9 @@ export class SwitchTouchController {
753
687
  */
754
688
  private clickInput(): void {
755
689
  const input = this.elements?.input;
756
- if (input == null) {
757
- return;
690
+ if (input != null) {
691
+ clickForLift(input);
758
692
  }
759
- armClickGuard(input.ownerDocument);
760
- withoutClickGuard(() => input.click());
761
693
  }
762
694
 
763
695
  /** If the owner kept the old value, the visuals go back to it. */
@@ -1038,10 +970,8 @@ export class SwitchTouchController {
1038
970
  }
1039
971
 
1040
972
  private clearTimers(): void {
1041
- if (this.browserClickTimer != null) {
1042
- clearTimeout(this.browserClickTimer);
1043
- this.browserClickTimer = null;
1044
- }
973
+ this.endBrowserClickWait?.();
974
+ this.endBrowserClickWait = null;
1045
975
  if (this.reconcileTimer != null) {
1046
976
  clearTimeout(this.reconcileTimer);
1047
977
  this.reconcileTimer = null;
@@ -17,7 +17,7 @@
17
17
  * - /packages/cli/assets/templates/blocks/components/Token/ (showcase blocks)
18
18
  */
19
19
 
20
- import type {ReactNode} from 'react';
20
+ import {use, type ReactNode} from 'react';
21
21
  import * as stylex from '@stylexjs/stylex';
22
22
  import {
23
23
  colorVars,
@@ -41,6 +41,7 @@ import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
41
41
  import {usePressFeedback} from '../hooks/usePressFeedback';
42
42
  import type {TokenColorMap} from './index';
43
43
  import {disabledInteractionProps} from '../utils/disabledInteraction';
44
+ import {TokenNamingContext} from './TokenNamingContext';
44
45
  import {
45
46
  registerEnglishFallback,
46
47
  useComponentTranslator,
@@ -309,6 +310,8 @@ export function Token({
309
310
  'data-testid': testId,
310
311
  'aria-labelledby': ariaLabelledBy,
311
312
  'aria-describedby': ariaDescribedBy,
313
+ 'aria-haspopup': ariaHasPopup,
314
+ 'aria-expanded': ariaExpanded,
312
315
  ref,
313
316
  ...rest
314
317
  }: TokenProps) {
@@ -319,6 +322,7 @@ export function Token({
319
322
  const t = useComponentTranslator();
320
323
  const LinkComponent = useLinkComponent();
321
324
  const role = useInteractiveRole({href, onClick, isDisabled});
325
+ const naming = use(TokenNamingContext);
322
326
 
323
327
  // When role is 'button' via context (no explicit onClick), treat as
324
328
  // if onClick was provided — the popover attaches its own handler.
@@ -327,7 +331,9 @@ export function Token({
327
331
  const removeButton = onRemove != null && (
328
332
  <button
329
333
  type="button"
330
- aria-label={t('@astryx.token.remove', {label})}
334
+ aria-label={t('@astryx.token.remove', {
335
+ label: naming?.removeName ?? label,
336
+ })}
331
337
  onClick={e => {
332
338
  e.stopPropagation();
333
339
  onRemove(e);
@@ -352,12 +358,14 @@ export function Token({
352
358
  </>
353
359
  );
354
360
 
355
- // Naming inputs reach the element that owns the token's action: the inner
356
- // button of a clickable token, otherwise the root
361
+ // Naming and popup inputs reach the element that owns the token's action:
362
+ // the inner button of a clickable token, otherwise the root
357
363
  // (architecture:public-component-api INV5).
358
364
  const labellingProps = {
359
365
  'aria-labelledby': ariaLabelledBy,
360
366
  'aria-describedby': ariaDescribedBy,
367
+ 'aria-haspopup': ariaHasPopup,
368
+ 'aria-expanded': ariaExpanded,
361
369
  };
362
370
 
363
371
  const sharedProps = {
@@ -532,6 +540,7 @@ export function Token({
532
540
  <button
533
541
  type="button"
534
542
  onClick={effectiveOnClick}
543
+ aria-label={ariaLabelledBy == null ? naming?.actionName : undefined}
535
544
  {...labellingProps}
536
545
  aria-disabled={isDisabled || undefined}
537
546
  {...(isDisabled ? disabledInteractionProps() : null)}
@@ -0,0 +1,28 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file TokenNamingContext.ts
7
+ * @input Naming parts from a caller whose token value renders outside its label
8
+ * @output Internal context: the names of a token's action and remove button
9
+ * @position Internal to Core; not exported from the Token entry point
10
+ */
11
+
12
+ import {createContext} from 'react';
13
+
14
+ /**
15
+ * Names for a Token whose visible text runs past its label. PowerSearch
16
+ * provides them: its tokens show the value as `endContent`.
17
+ *
18
+ * - `actionName` names a clickable token's inner button in place of `label`.
19
+ * - `removeName` names the remove button in place of `label`. The remove word
20
+ * still comes from the catalog, so each locale keeps its word order.
21
+ */
22
+ export interface TokenNaming {
23
+ actionName?: string;
24
+ removeName?: string;
25
+ }
26
+
27
+ export const TokenNamingContext = createContext<TokenNaming | null>(null);
28
+ TokenNamingContext.displayName = 'TokenNamingContext';
@@ -292,6 +292,13 @@ export const docs = {
292
292
  type: 'CSSProperties',
293
293
  description: 'Inline styles for the visual control box.',
294
294
  },
295
+ {
296
+ name: 'experimental',
297
+ type: "{presentation?: 'popover' | 'bottom-sheet' | 'adaptive'}",
298
+ stability: 'experimental',
299
+ description:
300
+ "Experimental options: anything in this object may change or be removed in a patch release, and omitting it keeps the stable behavior. presentation (default 'popover') chooses how the field presents its choices. popover is the inline combobox with its anchored suggestion menu. bottom-sheet replaces it with a trigger showing the selected tokens that opens one tall modal sheet: a search row on top, full-width checkbox rows that keep their place while the sheet is open, a Create row with hasCreate, and a footer pinned in every state with a confirmed Clear all and Save; the sheet edits a draft, so onChange fires once, when Save commits it, and a swipe, scrim or Escape close of a changed draft asks before discarding. adaptive uses the bottom sheet on compact touch screens and the popover otherwise, resolved the same way Selector resolves it. In the sheet presentation the trigger is the control (ref, id, aria-* and data-* reach it; with experimental passed, ref also accepts a Ref<HTMLInputElement | HTMLButtonElement> — an input-only ref still type-checks against that wider type, but with adaptive or bottom-sheet it receives the trigger button at runtime, so type the ref for both elements; TokenizerProps keeps its released ref type and gains only the optional experimental), and renderItem, renderToken, endContent, hasClear, hasEntriesOnFocus, menuWidth, tokenOverflowBehavior and hasAutoFocus have no surface to act on; the field warns once in development when they are passed. For example: <Tokenizer label=\"Tags\" searchSource={tagSource} value={tags} onChange={setTags} hasCreate experimental={{presentation: 'adaptive'}} />.",
301
+ },
295
302
  ],
296
303
  theming: {
297
304
  targets: [
@@ -631,6 +638,13 @@ export const docsZh = {
631
638
  type: 'CSSProperties',
632
639
  description: '可视控件框的内联样式。',
633
640
  },
641
+ {
642
+ name: 'experimental',
643
+ type: "{presentation?: 'popover' | 'bottom-sheet' | 'adaptive'}",
644
+ stability: 'experimental',
645
+ description:
646
+ "实验性选项:此对象中的任何内容都可能在补丁版本中更改或移除;省略它即保持稳定行为。presentation(默认 'popover')决定字段如何呈现选项:popover 为内联组合框及其锚定建议菜单;bottom-sheet 改为一个显示已选标记的触发按钮,点按后打开一个高的模态底部面板——顶部为搜索行,下方为整行复选框行(面板打开期间位置保持不变),hasCreate 时有「创建」行,底部固定显示需确认的「全部清除」和「保存」;面板编辑的是草稿,onChange 仅在「保存」提交时触发一次,草稿有改动时滑动、点击遮罩或按 Escape 关闭会先询问是否放弃;adaptive 在紧凑触摸屏上使用底部面板,其他情况使用 popover,解析方式与 Selector 相同。在底部面板呈现中,触发按钮即为控件(ref、id、aria-* 和 data-* 都作用于它;传入 experimental 后,ref 也接受 Ref<HTMLInputElement | HTMLButtonElement>——仅为输入框类型的 ref 在这个更宽的类型下仍能通过类型检查,但在 adaptive 或 bottom-sheet 下运行时收到的是触发按钮,因此请为两种元素声明 ref 的类型;TokenizerProps 保留已发布的 ref 类型,仅新增可选的 experimental);renderItem、renderToken、endContent、hasClear、hasEntriesOnFocus、menuWidth、tokenOverflowBehavior 和 hasAutoFocus 没有作用对象,传入时开发模式下会警告一次。例如:<Tokenizer label=\"标签\" searchSource={tagSource} value={tags} onChange={setTags} hasCreate experimental={{presentation: 'adaptive'}} />。",
647
+ },
634
648
  ],
635
649
  theming: {
636
650
  targets: [
@@ -830,5 +844,7 @@ export const docsDense = {
830
844
  'Icon at input start, before tokens. Icon name, SVG component, or ReactNode.',
831
845
  endContent: 'Content at input row end. For buttons, counts, controls.',
832
846
  handleRef: 'Imperative handle for focus() and blur() control.',
847
+ experimental:
848
+ "Experimental; may change in a patch. presentation: 'popover' (default, inline combobox) | 'bottom-sheet' (trigger + one searchable modal sheet of checkbox rows editing a draft that Save commits; onChange fires once, on Save) | 'adaptive' (sheet on compact touch, else popover).",
833
849
  },
834
850
  };