@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
@@ -483,7 +483,10 @@ export function useMenuPress(options: UseMenuPressOptions): UseMenuPressReturn {
483
483
  // Detach before acting: the activation usually closes the menu.
484
484
  endGesture();
485
485
  // Once armed, the one swallower stays installed.
486
- armClickGuard(doc, {staysInstalled: true});
486
+ armClickGuard(doc, {
487
+ staysInstalled: true,
488
+ cancelsTouchEnd: native?.pointerType === 'touch',
489
+ });
487
490
  if (native != null) {
488
491
  activate(effect.row, native);
489
492
  }
@@ -492,7 +495,10 @@ export function useMenuPress(options: UseMenuPressOptions): UseMenuPressReturn {
492
495
  case 'settle': {
493
496
  endGesture();
494
497
  if (effect.stray) {
495
- armClickGuard(doc, {staysInstalled: true});
498
+ armClickGuard(doc, {
499
+ staysInstalled: true,
500
+ cancelsTouchEnd: native?.pointerType === 'touch',
501
+ });
496
502
  }
497
503
  if (effect.dismiss) {
498
504
  optionsRef.current.onDismiss?.();
@@ -8,6 +8,7 @@
8
8
 
9
9
  const english: Readonly<Record<string, string>> = {
10
10
  "@astryx.powersearch.allFiltersRemoved": "All filters removed",
11
+ "@astryx.powersearch.editableFiltersRemoved": "Editable filters removed",
11
12
  "@astryx.powersearch.editor.apply": "Apply",
12
13
  "@astryx.powersearch.label": "Search",
13
14
  "@astryx.powersearch.placeholder": "Search…",
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const english: Readonly<Record<string, string>> = {
10
+ "@astryx.alertDialog.cancel": "Cancel",
10
11
  "@astryx.textInput.clearLabel": "Clear {label}",
11
12
  "@astryx.tokenizer.add": "Add",
12
13
  "@astryx.tokenizer.addValue": "Add {value}",
@@ -16,11 +17,16 @@ const english: Readonly<Record<string, string>> = {
16
17
  "@astryx.tokenizer.clearAllTitle": "Clear all selected items?",
17
18
  "@astryx.tokenizer.clearSelection": "Clear selection",
18
19
  "@astryx.tokenizer.create": "Create \"{value}\"",
19
- "@astryx.tokenizer.done": "Done",
20
+ "@astryx.tokenizer.discardChanges": "Discard changes",
21
+ "@astryx.tokenizer.discardChangesDescription": "Your changes to {label} haven't been saved.",
22
+ "@astryx.tokenizer.discardChangesTitle": "Discard changes?",
23
+ "@astryx.tokenizer.keepEditing": "Keep editing",
20
24
  "@astryx.tokenizer.noResultsDescription": "Try a different search.",
21
25
  "@astryx.tokenizer.nothingToClear": "Nothing selected",
26
+ "@astryx.tokenizer.save": "Save",
22
27
  "@astryx.tokenizer.search": "Search {label}",
23
28
  "@astryx.tokenizer.searchPlaceholder": "Search…",
29
+ "@astryx.tokenizer.selectionLimit": "Select up to {count, plural, one {# item} other {# items}}",
24
30
  "@astryx.tokenizer.tokenAdded": "Added {label}",
25
31
  "@astryx.tokenizer.tokenRemoved": "Removed {label}",
26
32
  "@astryx.typeahead.emptySearchResults": "No results found",
@@ -315,6 +315,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
315
315
  '@astryx.powersearch.label': 'Search',
316
316
  '@astryx.powersearch.placeholder': 'Search…',
317
317
  '@astryx.powersearch.allFiltersRemoved': 'All filters removed',
318
+ '@astryx.powersearch.editableFiltersRemoved': 'Editable filters removed',
318
319
  '@astryx.resizable.collapsed': 'Collapsed',
319
320
  '@astryx.resizable.handle.label': 'Resize handle',
320
321
  '@astryx.selector.placeholder': 'Select…',
@@ -322,6 +322,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
322
322
  '@astryx.powersearch.label': '⟦Šéàřçĥ⟧',
323
323
  '@astryx.powersearch.placeholder': '⟦Šéàřçĥ…⟧',
324
324
  '@astryx.powersearch.allFiltersRemoved': '⟦Àłł ƒíłţéřš řéɱóṽéð⟧',
325
+ '@astryx.powersearch.editableFiltersRemoved': '⟦Éðíţàƀłé ƒíłţéřš řéɱóṽéð⟧',
325
326
  '@astryx.resizable.collapsed': '⟦Çółłàþšéð⟧',
326
327
  '@astryx.resizable.handle.label': '⟦Řéšížé ĥàñðłé⟧',
327
328
  '@astryx.selector.placeholder': '⟦Šéłéçţ…⟧',
@@ -0,0 +1,101 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file SheetConfirmation.tsx
7
+ * @input Dialog, Layout, Heading, Text, Button; caller-owned copy
8
+ * @output SheetConfirmation — the confirmation a touch sheet opens over
9
+ * itself for Clear all and for discarding a changed draft
10
+ * @position Core-internal, shared by the Tokenizer sheet and the PowerSearch
11
+ * sheets; not a public export
12
+ *
13
+ * AlertDialog fixes its purpose to form and its safe action to ghost. A touch
14
+ * sheet's confirmation allows scrim dismissal. Its two full-width large
15
+ * actions are stacked and separated: the destructive action first in reading
16
+ * order, the safe action focused first. The alert-dialog theme target stays.
17
+ */
18
+
19
+ import {useId} from 'react';
20
+ import * as stylex from '@stylexjs/stylex';
21
+ import {Button} from '../Button/Button';
22
+ import {Dialog} from '../Dialog/Dialog';
23
+ import {Heading} from '../Heading/Heading';
24
+ import {Layout} from '../Layout/Layout';
25
+ import {LayoutContent} from '../Layout/LayoutContent';
26
+ import {LayoutFooter} from '../Layout/LayoutFooter';
27
+ import {Stack} from '../Stack/Stack';
28
+ import {Text} from '../Text/Text';
29
+ import {themeProps} from '../utils/themeProps';
30
+
31
+ export interface SheetConfirmationProps {
32
+ isOpen: boolean;
33
+ onOpenChange: (isOpen: boolean) => void;
34
+ title: string;
35
+ description: string;
36
+ cancelLabel: string;
37
+ actionLabel: string;
38
+ onAction: () => void;
39
+ }
40
+
41
+ const styles = stylex.create({
42
+ action: {width: '100%'},
43
+ });
44
+
45
+ export function SheetConfirmation({
46
+ isOpen,
47
+ onOpenChange,
48
+ title,
49
+ description,
50
+ cancelLabel,
51
+ actionLabel,
52
+ onAction,
53
+ }: SheetConfirmationProps) {
54
+ const titleId = useId();
55
+ const descriptionId = useId();
56
+ return (
57
+ <Dialog
58
+ {...themeProps('alert-dialog')}
59
+ isOpen={isOpen}
60
+ onOpenChange={onOpenChange}
61
+ purpose="info"
62
+ role="alertdialog"
63
+ aria-labelledby={titleId}
64
+ aria-describedby={descriptionId}
65
+ width={400}>
66
+ <Layout
67
+ content={
68
+ <LayoutContent>
69
+ <Heading level={2} id={titleId}>
70
+ {title}
71
+ </Heading>
72
+ <Text type="body" color="secondary" id={descriptionId}>
73
+ {description}
74
+ </Text>
75
+ </LayoutContent>
76
+ }
77
+ footer={
78
+ <LayoutFooter>
79
+ <Stack direction="vertical" gap={4} hAlign="stretch">
80
+ <Button
81
+ label={actionLabel}
82
+ variant="destructive"
83
+ size="lg"
84
+ onClick={onAction}
85
+ xstyle={styles.action}
86
+ />
87
+ <Button
88
+ label={cancelLabel}
89
+ variant="secondary"
90
+ size="lg"
91
+ onClick={() => onOpenChange(false)}
92
+ data-autofocus
93
+ xstyle={styles.action}
94
+ />
95
+ </Stack>
96
+ </LayoutFooter>
97
+ }
98
+ />
99
+ </Dialog>
100
+ );
101
+ }
@@ -0,0 +1,80 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ /**
3
+ * @file sheetDraft.ts
4
+ * @input A sheet's selection at open, its draft, and the last edit made
5
+ * @output SheetDraftChange, hasSameSelection, nameSheetDraftChange — the
6
+ * rules a touch sheet's Save and dismissal share
7
+ * @position Core-internal, shared by the Tokenizer sheet and the Lab sheets
8
+ * through `internal/touch-sheet`; not a public export
9
+ *
10
+ * A touch sheet edits a draft and reports it once, on Save, with one change:
11
+ * the last edit that still holds, else the nearest net add or remove. A
12
+ * dismissal asks before discarding only while the draft differs from the
13
+ * selection at open, as a set: a row unchecked and checked again is no change.
14
+ */
15
+
16
+ interface SheetDraftItem {
17
+ id: string | number;
18
+ }
19
+
20
+ /** An edit of a sheet's draft, and the shape Save reports. */
21
+ export type SheetDraftChange<T> = {
22
+ item: T;
23
+ type: 'add' | 'create' | 'remove';
24
+ };
25
+
26
+ /** Whether two selections hold the same items, in any order. */
27
+ export function hasSameSelection<T extends SheetDraftItem>(
28
+ a: ReadonlyArray<T>,
29
+ b: ReadonlyArray<T>,
30
+ ): boolean {
31
+ if (a.length !== b.length) {
32
+ return false;
33
+ }
34
+ const ids = new Set(a.map(item => item.id));
35
+ return b.every(item => ids.has(item.id));
36
+ }
37
+
38
+ /**
39
+ * The change Save reports for a draft that differs from the selection at
40
+ * open, or `null` when nothing differs. The last edit names it when it still
41
+ * holds: its item is in the draft and was not selected at open, or was
42
+ * selected at open and is gone. Otherwise, after a removal, a `remove` names
43
+ * an item that is gone; otherwise an `add` (`create` when the last edit
44
+ * created that item) names an item that is new; otherwise a `remove` names
45
+ * an item that is gone.
46
+ */
47
+ export function nameSheetDraftChange<T extends SheetDraftItem>(
48
+ valueAtOpen: ReadonlyArray<T>,
49
+ draftValue: ReadonlyArray<T>,
50
+ lastDraftChange: SheetDraftChange<T> | null,
51
+ ): SheetDraftChange<T> | null {
52
+ const wasSelected = (id: T['id']) => valueAtOpen.some(item => item.id === id);
53
+ const isSelected = (id: T['id']) => draftValue.some(item => item.id === id);
54
+ const added = draftValue.find(item => !wasSelected(item.id));
55
+ const removed = valueAtOpen.find(item => !isSelected(item.id));
56
+ const lastChangeIsNet =
57
+ lastDraftChange != null &&
58
+ (lastDraftChange.type === 'remove'
59
+ ? wasSelected(lastDraftChange.item.id) &&
60
+ !isSelected(lastDraftChange.item.id)
61
+ : isSelected(lastDraftChange.item.id) &&
62
+ !wasSelected(lastDraftChange.item.id));
63
+ if (lastChangeIsNet) {
64
+ return lastDraftChange;
65
+ }
66
+ if (lastDraftChange?.type === 'remove' && removed != null) {
67
+ return {item: removed, type: 'remove'};
68
+ }
69
+ if (added != null) {
70
+ const type: SheetDraftChange<T>['type'] =
71
+ lastDraftChange?.item.id === added.id && lastDraftChange.type === 'create'
72
+ ? 'create'
73
+ : 'add';
74
+ return {item: added, type};
75
+ }
76
+ if (removed != null) {
77
+ return {item: removed, type: 'remove'};
78
+ }
79
+ return null;
80
+ }
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ /**
3
+ * @file touch-sheet.ts
4
+ * @input The touch sheets' shared confirmation, search-drag dismissal and
5
+ * draft rules
6
+ * @output `@astryxdesign/core/internal/touch-sheet`: SheetConfirmation,
7
+ * useDismissSearchOnTouchDrag, hasSameSelection and nameSheetDraftChange,
8
+ * re-exported by name for Lab's sheets
9
+ * @position An internal entry reserved for Astryx's own packages:
10
+ * undocumented, outside the public API and its compatibility promise,
11
+ * versioned in lockstep with Core, and allowed only in Core and Lab by the
12
+ * `@astryx/no-core-internal-import` lint rule. Core components import the
13
+ * same modules by relative path. Named re-exports only.
14
+ *
15
+ * SYNC: When modified, update scripts/sync-exports.js.
16
+ */
17
+ export {SheetConfirmation} from './SheetConfirmation';
18
+ export type {SheetConfirmationProps} from './SheetConfirmation';
19
+ export {useDismissSearchOnTouchDrag} from './useDismissSearchOnTouchDrag';
20
+ export {hasSameSelection, nameSheetDraftChange} from './sheetDraft';
21
+ export type {SheetDraftChange} from './sheetDraft';
@@ -0,0 +1,69 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useDismissSearchOnTouchDrag.ts
7
+ * @input Caller-owned search inputs, a focus target, and touch events from a
8
+ * separate results list
9
+ * @output useDismissSearchOnTouchDrag — touch handlers for the results list
10
+ * @position Core-internal, shared by the touch sheets; not a public export
11
+ *
12
+ * A direct vertical touch drag on the results moves focus off a focused
13
+ * search input. The on-screen keyboard then leaves and the rows come into
14
+ * view; the query stays. Focus goes to the caller's target (the sheet's
15
+ * panel), never to the page body behind an open modal. Scroll events are not
16
+ * observed: filtering can clamp scrollTop and fire scroll while someone is
17
+ * typing. Small moves, taps and multi-touch gestures leave focus alone.
18
+ */
19
+
20
+ import {useRef, type TouchEvent} from 'react';
21
+
22
+ const DRAG_THRESHOLD_PX = 8;
23
+
24
+ type SearchInput = HTMLInputElement | null | undefined;
25
+
26
+ export function useDismissSearchOnTouchDrag(
27
+ getSearchInputs: () => ReadonlyArray<SearchInput>,
28
+ getFocusTarget?: () => HTMLElement | null | undefined,
29
+ ) {
30
+ const startYRef = useRef<number | null>(null);
31
+ const reset = () => {
32
+ startYRef.current = null;
33
+ };
34
+
35
+ return {
36
+ onTouchStart: (event: TouchEvent<HTMLElement>) => {
37
+ startYRef.current =
38
+ event.touches.length === 1 ? event.touches[0].clientY : null;
39
+ },
40
+ onTouchMove: (event: TouchEvent<HTMLElement>) => {
41
+ if (event.touches.length !== 1) {
42
+ reset();
43
+ return;
44
+ }
45
+ const startY = startYRef.current;
46
+ if (
47
+ startY == null ||
48
+ Math.abs(event.touches[0].clientY - startY) < DRAG_THRESHOLD_PX
49
+ ) {
50
+ return;
51
+ }
52
+ reset();
53
+ const focusedSearch = getSearchInputs().find(
54
+ input => input != null && input === document.activeElement,
55
+ );
56
+ if (focusedSearch == null) {
57
+ return;
58
+ }
59
+ const target = getFocusTarget?.();
60
+ if (target != null) {
61
+ target.focus({preventScroll: true});
62
+ } else {
63
+ focusedSearch.blur();
64
+ }
65
+ },
66
+ onTouchEnd: reset,
67
+ onTouchCancel: reset,
68
+ };
69
+ }
@@ -219,6 +219,13 @@ Bookmarks sheet.
219
219
  | M-SG18 | iPad S1 (touch; pointer if available) | tap, hold, drag; hover | differences from iPhone | B |
220
220
  | M-SG19 | Safari showing the segmented stories vs S1 | the segmented storyboards side by side | synced recordings (parity proof) | B |
221
221
 
222
+ Done (UIKit lab, iOS 26.2 simulator, Oct 23–24 2026; in the segmented control's
223
+ own table, `uikit/segmented.ts`, tagged `measured:`): M-SG02 (the selection
224
+ spring and the drag clamp; the tracking spring is the shared
225
+ `springs.fingerTracking`; the lift was measured too and is not in the table,
226
+ since a pressed thumb's lift is a theme's) and M-SG03 (the tap's frames,
227
+ fitted per frame).
228
+
222
229
  ## Sheets (UISheetPresentationController)
223
230
 
224
231
  Probe app "SheetProbe" on an iPhone 16 Pro (iPad Pro 11" for M-SH11 / M-SH20 /
@@ -22,6 +22,7 @@ roles through `resolveMotion`.
22
22
  | `uikit/navigation.ts` | UINavigationController: push velocity, parallax, dimming, transition clock, interactive pop finish rule. |
23
23
  | `uikit/progressBar.ts` | UIProgressView's fill: the whole-track duration and curve, its minimum length and empty alpha; the reduced-motion pulse (three roles). |
24
24
  | `uikit/gestures.ts` | Gesture recognizer thresholds, the velocity filter and haptic stand-ins, as `{value, unit}` entries (sources in JSDoc). |
25
+ | `uikit/segmented.ts` | UISegmentedControl: the selection indicator's spring; under Reduce Motion the animator's cross-fade replaces it. |
25
26
  | `uikit/switch.ts` | UISwitch on iOS 26.2: geometry, the lift, settle, travel and tracking springs, the flip distance, the track colour. |
26
27
  | `roles.ts` | The internal motion roles: the springs, durations and curves `spec:AST-094` FR4 names, registered by the shared tables, and a role's CSS form read back. |
27
28
  | `roleValue.ts` | A role's CSS form, written: a spring's duration and bounce, a duration in ms, a curve's `cubic-bezier()`. |
@@ -5,7 +5,8 @@
5
5
  * @input The motion tables (motion/uikit and each component's own table)
6
6
  * @output registerMotionTable, motionRoleOf, motionRoleIn, listMotionRoles,
7
7
  * motionRoleVariable, and the CSS form of a role's value
8
- * (formatMotionRoleValue, parseMotionRoleValue)
8
+ * (formatMotionRoleValue, parseMotionRoleValue), and parseCssEasing for a
9
+ * CSS transition's easing
9
10
  * @position Motion foundation, internal to core: read by the code that
10
11
  * resolves a role when a motion starts. No public API writes a role: touch
11
12
  * motion stays internal until the public motion-token set is decided.
@@ -260,3 +261,21 @@ export function parseMotionRoleValue(
260
261
  ? points
261
262
  : null;
262
263
  }
264
+
265
+ /** The CSS easing keywords, as the control points CSS gives them. */
266
+ const EASING_KEYWORDS: Readonly<Record<string, MotionCurvePoints>> = {
267
+ linear: [0, 0, 1, 1],
268
+ ease: [0.25, 0.1, 0.25, 1],
269
+ 'ease-in': [0.42, 0, 1, 1],
270
+ 'ease-out': [0, 0, 0.58, 1],
271
+ 'ease-in-out': [0.42, 0, 0.58, 1],
272
+ };
273
+
274
+ /**
275
+ * Reads a CSS easing, a keyword or a `cubic-bezier()`, as a curve role's value
276
+ * is read. Returns null for any other easing, such as `steps()`.
277
+ */
278
+ export function parseCssEasing(text: string): MotionCurvePoints | null {
279
+ const value = text.trim();
280
+ return EASING_KEYWORDS[value] ?? parseMotionRoleValue('curve', value);
281
+ }
@@ -4,10 +4,10 @@
4
4
  * @file index.ts
5
5
  * @input The domain files of the UIKit constants table
6
6
  * @output Re-exports `springs` and one constant per spring (`SHEET_PRESENT_SPRING`,
7
- * …), `scroll`, `timing`, `navigation`, `progressBar`, the `slider` and
8
- * switch (`switchControl`) domains, and the gesture thresholds (one
9
- * `UIKIT_*` group per recognizer, their union `UIKIT_GESTURES`, and the
10
- * haptic tables)
7
+ * …), `scroll`, `timing`, `navigation`, `progressBar`, `segmentedControl`,
8
+ * the `slider` and switch (`switchControl`) domains, and the gesture
9
+ * thresholds (one `UIKIT_*` group per recognizer, their union
10
+ * `UIKIT_GESTURES`, and the haptic tables)
11
11
  * @position Entry of the UIKit constants table (motion/uikit). Internal to
12
12
  * core: not exported from the package entry point
13
13
  *
@@ -42,6 +42,7 @@ export {
42
42
  export {navigation, registerNavigationTable} from './navigation';
43
43
  export {progressBar, registerProgressBarTable} from './progressBar';
44
44
  export {registerScrollTable, scroll} from './scroll';
45
+ export {segmentedControl} from './segmented';
45
46
  export {slider} from './slider';
46
47
  export {
47
48
  ACTION_SHEET_DISMISS_SPRING,
@@ -64,7 +65,6 @@ export {
64
65
  NAVIGATION_POP_SPRING,
65
66
  NAVIGATION_PUSH_SPRING,
66
67
  QUICK_CHANGE_SPRING,
67
- SEGMENTED_THUMB_SPRING,
68
68
  SETTLE_SPRING,
69
69
  SHEET_DETENT_SPRING,
70
70
  SHEET_DISMISS_SPRING,
@@ -0,0 +1,74 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file segmented.ts
5
+ * @input Nothing; data only
6
+ * @output Exports `segmentedControl`, UISegmentedControl's selection slide
7
+ * spring and its drag overshoot; and `registerSegmentedTable`, which
8
+ * registers the spring as an internal motion role
9
+ * @position The segmented-control domain of the UIKit constants table
10
+ * (motion/uikit); read by SegmentedControl's touch presentation
11
+ *
12
+ * SYNC: When modified, update:
13
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
14
+ * - /packages/core/src/motion/README.md (domain list)
15
+ */
16
+
17
+ import {registerMotionTable} from '../roles';
18
+
19
+ /**
20
+ * UIKit's own UISegmentedControl, measured with three text segments in a
21
+ * 300 × 31 pt control. A tap and a programmatic change follow the same
22
+ * spring. Every leaf carries a `@source`, and uikit.test.ts checks it.
23
+ *
24
+ * Some UIKit motion is left out on purpose. UIKit cross-fades the two titles
25
+ * that change, over 0.2 s. Astryx switches them at once, because a
26
+ * cross-fade needs a second copy of each title. Under Reduce Motion, UIKit's
27
+ * lens fades in at the new segment and scales from 0.88. Astryx allows no
28
+ * scaling there, so the animator's Reduce Motion cross-fade replaces it.
29
+ * UIKit's lens also lifts and stretches under a finger. A theme role owns
30
+ * the lift, and the stretch is not modeled. The lens chases a dragging
31
+ * finger on the shared `springs.fingerTracking` role, which matches the
32
+ * values UIKit reports for this control.
33
+ */
34
+ export const segmentedControl = {
35
+ /**
36
+ * The selection indicator's slide from the old segment to the new one,
37
+ * after a tap or a programmatic change. Its response is 0.40 s and its
38
+ * damping ratio 0.85. That gives about 1.25 pt of overshoot on a 200 pt
39
+ * move, settled by about 0.6 s. The width follows the same spring when the
40
+ * segments differ in width.
41
+ * @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
42
+ * iPhone 17 Pro, UIViewSpringAnimationBehavior read on the lens position
43
+ * for a tap and a programmatic set (dampingRatio 0.85, response 0.40,
44
+ * tracking off), Oct 23 2026
45
+ * @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
46
+ * iPhone 17 Pro, per-frame log of the lens's presentation position and
47
+ * velocity (method C) for a tap, a programmatic set and an animated set,
48
+ * least-squares spring fit (response 0.395 / 0.405 / 0.400 s, damping
49
+ * ratio 0.855 / 0.845 / 0.850, rms at most 0.65 pt over 200 pt),
50
+ * Oct 23 2026
51
+ */
52
+ selectionSpring: {response: 0.4, dampingRatio: 0.85},
53
+ /**
54
+ * How far past the first or last segment's center the lens follows a
55
+ * dragging finger, in pt. Beyond that, the lens stays put while the finger
56
+ * goes on.
57
+ * @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
58
+ * iPhone 17 Pro, a drag 60 pt past the last segment: the lens stops about
59
+ * 11 pt past its center (312 vs 301), Oct 23 2026
60
+ */
61
+ dragOvershoot: 11,
62
+ } as const;
63
+
64
+ /**
65
+ * Registers this table with the motion registry (motion/roles.ts). The
66
+ * slide's spring becomes an internal motion role. The drag overshoot is not
67
+ * motion, so it stays an internal constant. Nothing registers on import, and
68
+ * a second call does nothing.
69
+ */
70
+ export function registerSegmentedTable(): void {
71
+ registerMotionTable('segmented', segmentedControl, {
72
+ roles: ['selectionSpring'],
73
+ });
74
+ }
@@ -15,11 +15,11 @@
15
15
  * that will confirm or replace them (MEASUREMENTS.md lists them all).
16
16
  * uikit.test.ts enforces both.
17
17
  *
18
- * Most springs are measured on iOS 26.2 or documented by Apple; two are still
19
- * GUESSES (`segmentedThumb`, `banner`). `switchThumbMove` is the switch's
20
- * measured travel, which uikit/switch.ts reads. A guess starts from the
21
- * nearest sourced spring and names it. A component built on the table gets
22
- * the measured value the moment it lands, without changing.
18
+ * Most springs are measured on iOS 26.2 or documented by Apple; one is still
19
+ * a GUESS (`banner`). `switchThumbMove` is the switch's measured travel,
20
+ * which uikit/switch.ts reads. A guess starts from the nearest sourced
21
+ * spring and names it. A component built on the table gets the measured
22
+ * value the moment it lands, without changing.
23
23
  *
24
24
  * Press highlight in/out are fades, not springs: see timing.ts.
25
25
  *
@@ -400,21 +400,6 @@ export const SWITCH_THUMB_MOVE_SPRING = {
400
400
  dampingRatio: 1,
401
401
  } as const;
402
402
 
403
- /**
404
- * UISegmentedControl selection thumb sliding to a new segment.
405
- * @source GUESS — WebKit's UIKit-matched switch thumb spring
406
- * (RenderThemeCocoa.mm `thumbMoveSpring`: response 0.30 s, ratio 0.75),
407
- * kept only until UISegmentedControl's measured spring replaces it;
408
- * UISwitch's measured travel (`SWITCH_THUMB_MOVE_SPRING`)
409
- * belongs to another control
410
- * @measure M-SG02
411
- * @measure M-SG03
412
- */
413
- export const SEGMENTED_THUMB_SPRING = {
414
- response: 0.3,
415
- dampingRatio: 0.75,
416
- } as const;
417
-
418
403
  // ---------------------------------------------------------------------
419
404
  // System
420
405
  // ---------------------------------------------------------------------
@@ -504,7 +489,6 @@ export const springs = {
504
489
  contextMenuMorph: CONTEXT_MENU_MORPH_SPRING,
505
490
  menuReducedMotion: MENU_REDUCED_MOTION_SPRING,
506
491
  switchThumbMove: SWITCH_THUMB_MOVE_SPRING,
507
- segmentedThumb: SEGMENTED_THUMB_SPRING,
508
492
  keyboard: KEYBOARD_SPRING,
509
493
  banner: BANNER_SPRING,
510
494
  sliderRelease: SLIDER_RELEASE_SPRING,
@@ -0,0 +1,22 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file privateAttributes/segmentTitle.stylex.ts
5
+ * @input Nothing
6
+ * @output segmentTitleProps, which marks a SegmentedControl segment's title,
7
+ * and titleOfSegment, which finds that title in a segment
8
+ * @position Owner module of the private segment-title attribute: the only
9
+ * file that writes its name. No package entry exports it.
10
+ */
11
+
12
+ const SEGMENT_TITLE = 'data-astryx-segment-title';
13
+
14
+ /** Spread on a segment's title: marks it for the touch layer. */
15
+ export const segmentTitleProps: Readonly<Record<string, string>> = {
16
+ [SEGMENT_TITLE]: '',
17
+ };
18
+
19
+ /** The title inside `segment`, or null when the label is hidden. */
20
+ export function titleOfSegment(segment: Element): HTMLElement | null {
21
+ return segment.querySelector<HTMLElement>(`[${SEGMENT_TITLE}]`);
22
+ }