@astryxdesign/core 0.7.5 → 0.7.6-canary.0ef60dd

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 (559) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/Badge/Badge.d.ts.map +1 -1
  7. package/dist/Badge/Badge.js +7 -1
  8. package/dist/BottomSheet/BottomSheet.js +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  11. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  12. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  13. package/dist/BottomSheet/useSheetGestures.js +30 -3
  14. package/dist/Button/Button.js +4 -4
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  17. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  18. package/dist/ClickableCard/ClickableCard.js +37 -4
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +12 -4
  21. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  22. package/dist/DateInput/MonthScroller.js +101 -15
  23. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  24. package/dist/DateInput/TouchDateField.js +8 -3
  25. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  26. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  27. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  28. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  29. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  30. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  31. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  34. package/dist/Dialog/Dialog.d.ts.map +1 -1
  35. package/dist/Dialog/Dialog.js +34 -9
  36. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  37. package/dist/Dialog/DialogHeader.js +5 -4
  38. package/dist/Divider/Divider.d.ts.map +1 -1
  39. package/dist/Divider/Divider.js +2 -1
  40. package/dist/Drawer/Drawer.d.ts +1 -0
  41. package/dist/Drawer/Drawer.d.ts.map +1 -1
  42. package/dist/Drawer/Drawer.js +7 -2
  43. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  44. package/dist/DropdownMenu/DropdownMenu.js +7 -3
  45. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  46. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  47. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
  48. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
  50. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  51. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +2 -1
  54. package/dist/Field/FieldLabel.d.ts +1 -1
  55. package/dist/Field/FieldLabel.d.ts.map +1 -1
  56. package/dist/Field/FieldLabel.js +4 -4
  57. package/dist/Field/InputClearButton.js +3 -3
  58. package/dist/Field/PanelSearchInput.js +2 -2
  59. package/dist/FieldStatus/FieldStatus.js +2 -2
  60. package/dist/FileInput/FileInput.d.ts.map +1 -1
  61. package/dist/FileInput/FileInput.js +7 -1
  62. package/dist/Heading/Heading.js +1 -1
  63. package/dist/Icon/Icon.js +1 -1
  64. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  65. package/dist/Indicator/CheckIndicator.js +2 -1
  66. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  67. package/dist/Indicator/CheckboxIndicator.js +3 -1
  68. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  69. package/dist/Indicator/RadioIndicator.js +3 -1
  70. package/dist/Item/Item.d.ts.map +1 -1
  71. package/dist/Item/Item.js +2 -1
  72. package/dist/Item/ItemSwipeLayer.js +1 -1
  73. package/dist/Layer/popoverOperations.d.ts +27 -0
  74. package/dist/Layer/popoverOperations.d.ts.map +1 -0
  75. package/dist/Layer/popoverOperations.js +159 -0
  76. package/dist/Layer/useLayer.d.ts +1 -1
  77. package/dist/Layer/useLayer.d.ts.map +1 -1
  78. package/dist/Layer/useLayer.js +93 -59
  79. package/dist/Layout/Layout.js +1 -1
  80. package/dist/Layout/LayoutContent.js +1 -1
  81. package/dist/Layout/LayoutFooter.js +1 -1
  82. package/dist/Layout/LayoutHeader.js +1 -1
  83. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  84. package/dist/Lightbox/Lightbox.js +5 -0
  85. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  86. package/dist/List/List.js +1 -1
  87. package/dist/List/ListItem.js +3 -3
  88. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  89. package/dist/MobileNav/MobileNav.js +7 -2
  90. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
  91. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  92. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  93. package/dist/MultiSelector/MultiSelector.js +26 -10
  94. package/dist/NumberInput/NumberInput.d.ts +3 -1
  95. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  96. package/dist/NumberInput/NumberInput.js +3 -3
  97. package/dist/NumberInput/numberParser.d.ts +3 -0
  98. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  99. package/dist/NumberInput/numberParser.js +7 -44
  100. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  101. package/dist/ProgressBar/ProgressBar.js +16 -12
  102. package/dist/Resizable/useResizable.d.ts.map +1 -1
  103. package/dist/Resizable/useResizable.js +28 -2
  104. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  105. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  106. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  107. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  108. package/dist/Selector/useSelectorPresentation.js +20 -4
  109. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  110. package/dist/Skeleton/Skeleton.js +1 -1
  111. package/dist/Slider/Slider.d.ts.map +1 -1
  112. package/dist/Slider/Slider.js +10 -2
  113. package/dist/Spinner/Spinner.d.ts.map +1 -1
  114. package/dist/Spinner/Spinner.js +3 -3
  115. package/dist/Stack/Stack.js +1 -1
  116. package/dist/Stack/StackItem.js +1 -1
  117. package/dist/TabList/TabList.d.ts.map +1 -1
  118. package/dist/TabList/TabList.js +16 -1
  119. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  120. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  121. package/dist/Text/Text.js +1 -1
  122. package/dist/Text/useTruncation.d.ts.map +1 -1
  123. package/dist/Text/useTruncation.js +23 -5
  124. package/dist/TextArea/TextArea.d.ts +1 -1
  125. package/dist/TextArea/TextArea.d.ts.map +1 -1
  126. package/dist/TextArea/TextArea.js +13 -2
  127. package/dist/TextInput/TextInput.d.ts.map +1 -1
  128. package/dist/TextInput/TextInput.js +3 -3
  129. package/dist/TimeInput/TimeInput.d.ts +6 -2
  130. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  131. package/dist/TimeInput/TimeInput.js +21 -14
  132. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  133. package/dist/TimeInput/TouchTimeField.js +5 -4
  134. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  135. package/dist/Timestamp/Timestamp.js +2 -2
  136. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  137. package/dist/Toast/Toast.d.ts.map +1 -1
  138. package/dist/Toast/Toast.js +60 -20
  139. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  140. package/dist/Toast/ToastViewport.js +82 -51
  141. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  142. package/dist/Tokenizer/Tokenizer.js +13 -2
  143. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  144. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  145. package/dist/Toolbar/Toolbar.js +2 -1
  146. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  147. package/dist/TopNav/TopNavMegaMenu.js +3 -0
  148. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  149. package/dist/TopNav/TopNavMenu.js +1 -0
  150. package/dist/Typeahead/Typeahead.d.ts +1 -1
  151. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  152. package/dist/Typeahead/Typeahead.js +9 -1
  153. package/dist/astryx.css +20 -4
  154. package/dist/gestures/GestureArena.d.ts +6 -0
  155. package/dist/gestures/GestureArena.d.ts.map +1 -1
  156. package/dist/gestures/GestureArena.js +39 -0
  157. package/dist/gestures/GestureHost.d.ts +352 -0
  158. package/dist/gestures/GestureHost.d.ts.map +1 -0
  159. package/dist/gestures/GestureHost.js +1773 -0
  160. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  161. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  162. package/dist/gestures/GestureRecognizer.js +16 -0
  163. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  164. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  165. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  166. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  167. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  168. package/dist/gestures/PanGestureRecognizer.js +3 -0
  169. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  170. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  171. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  172. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  173. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  174. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  175. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  176. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  177. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  178. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  179. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  180. package/dist/gestures/TapGestureRecognizer.js +3 -0
  181. package/dist/gestures/VelocityTracker.d.ts +6 -2
  182. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  183. package/dist/gestures/VelocityTracker.js +7 -3
  184. package/dist/gestures/haptics.d.ts +65 -0
  185. package/dist/gestures/haptics.d.ts.map +1 -0
  186. package/dist/gestures/haptics.js +182 -0
  187. package/dist/gestures/nativeScroll.d.ts +42 -0
  188. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  189. package/dist/gestures/nativeScroll.js +228 -0
  190. package/dist/gestures/touchAction.d.ts +60 -0
  191. package/dist/gestures/touchAction.d.ts.map +1 -0
  192. package/dist/gestures/touchAction.js +157 -0
  193. package/dist/gestures/touchActionRule.d.ts +34 -0
  194. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  195. package/dist/gestures/touchActionRule.js +140 -0
  196. package/dist/gestures/types.d.ts +18 -7
  197. package/dist/gestures/types.d.ts.map +1 -1
  198. package/dist/gestures/types.js +22 -2
  199. package/dist/hooks/index.d.ts +1 -1
  200. package/dist/hooks/index.d.ts.map +1 -1
  201. package/dist/hooks/modalAnnounce.d.ts.map +1 -1
  202. package/dist/hooks/modalAnnounce.js +107 -61
  203. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  204. package/dist/hooks/useAdaptivePresentation.js +4 -1
  205. package/dist/hooks/useAnnounce.d.ts +101 -42
  206. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  207. package/dist/hooks/useAnnounce.js +161 -75
  208. package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
  209. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  210. package/dist/hooks/useAnnounceRenderedText.js +5 -3
  211. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  212. package/dist/hooks/useBusyAnnouncement.js +6 -12
  213. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  214. package/dist/hooks/useMenuHover.js +12 -1
  215. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  216. package/dist/i18n/generated-locales/en.generated.js +5 -0
  217. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  218. package/dist/i18n/generated-locales/pseudo.generated.js +5 -0
  219. package/dist/motion/animator.d.ts +7 -1
  220. package/dist/motion/animator.d.ts.map +1 -1
  221. package/dist/motion/animator.js +119 -27
  222. package/dist/theme/defineTheme.d.ts +19 -5
  223. package/dist/theme/defineTheme.d.ts.map +1 -1
  224. package/dist/theme/defineTheme.js +64 -4
  225. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  226. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  227. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  228. package/dist/theme/domainTokens/index.d.ts +5 -4
  229. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  230. package/dist/theme/domainTokens/index.js +5 -9
  231. package/dist/theme/index.d.ts +1 -0
  232. package/dist/theme/index.d.ts.map +1 -1
  233. package/dist/theme/motionKnobs.d.ts +56 -0
  234. package/dist/theme/motionKnobs.d.ts.map +1 -0
  235. package/dist/theme/motionKnobs.js +48 -0
  236. package/dist/theme/resolveThemeValues.d.ts +11 -4
  237. package/dist/theme/resolveThemeValues.d.ts.map +1 -1
  238. package/dist/theme/resolveThemeValues.js +40 -4
  239. package/dist/theme/themeAdaptations.d.ts +7 -3
  240. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  241. package/dist/theme/themeAdaptations.js +36 -7
  242. package/dist/theme/themeMotion.d.ts +96 -0
  243. package/dist/theme/themeMotion.d.ts.map +1 -0
  244. package/dist/theme/themeMotion.js +177 -0
  245. package/dist/theme/tokenReferences.d.ts +31 -0
  246. package/dist/theme/tokenReferences.d.ts.map +1 -0
  247. package/dist/theme/tokenReferences.js +132 -0
  248. package/dist/theme/tokens.d.ts +1 -0
  249. package/dist/theme/tokens.d.ts.map +1 -1
  250. package/dist/theme/tokens.js +9 -129
  251. package/dist/theme/tokens.stylex.d.ts +12 -0
  252. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  253. package/dist/theme/tokens.stylex.js +12 -0
  254. package/dist/utils/focusReturn.d.ts +17 -1
  255. package/dist/utils/focusReturn.d.ts.map +1 -1
  256. package/dist/utils/focusReturn.js +11 -4
  257. package/dist/utils/getNodeText.d.ts +8 -0
  258. package/dist/utils/getNodeText.d.ts.map +1 -0
  259. package/dist/utils/getNodeText.js +37 -0
  260. package/dist/utils/interactionModality.d.ts +11 -2
  261. package/dist/utils/interactionModality.d.ts.map +1 -1
  262. package/dist/utils/interactionModality.js +58 -5
  263. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  264. package/dist/utils/interactionOverlay.stylex.js +17 -8
  265. package/dist/utils/plainDate.d.ts +2 -0
  266. package/dist/utils/plainDate.d.ts.map +1 -1
  267. package/dist/utils/plainDate.js +46 -3
  268. package/dist/utils/presentation.d.ts +50 -0
  269. package/dist/utils/presentation.d.ts.map +1 -0
  270. package/dist/utils/presentation.js +125 -0
  271. package/dist/utils/pressFeedback.d.ts.map +1 -1
  272. package/dist/utils/pressFeedback.js +20 -1
  273. package/dist/utils/primaryClick.d.ts +11 -0
  274. package/dist/utils/primaryClick.d.ts.map +1 -0
  275. package/dist/utils/primaryClick.js +25 -0
  276. package/dist/utils/timeFormat.d.ts +47 -0
  277. package/dist/utils/timeFormat.d.ts.map +1 -0
  278. package/dist/utils/timeFormat.js +240 -0
  279. package/dist/utils/touchPresentation.d.ts.map +1 -1
  280. package/dist/utils/touchPresentation.js +5 -3
  281. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  282. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  283. package/dist/utils/touchPresentation.stylex.js +33 -0
  284. package/dist/utils/unicodeDigits.d.ts +20 -0
  285. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  286. package/dist/utils/unicodeDigits.js +70 -0
  287. package/locales/en.json +23 -3
  288. package/locales/pseudo.json +15 -0
  289. package/package.json +5 -4
  290. package/src/AlertDialog/AlertDialog.tsx +6 -5
  291. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  292. package/src/Avatar/Avatar.spec.md +22 -22
  293. package/src/Avatar/Avatar.tsx +18 -1
  294. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  295. package/src/Badge/Badge.doc.mjs +1 -1
  296. package/src/Badge/Badge.tsx +19 -2
  297. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  298. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  299. package/src/BottomSheet/BottomSheet.tsx +1 -1
  300. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  301. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  302. package/src/BottomSheet/useSheetGestures.ts +37 -6
  303. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  304. package/src/Button/Button.tsx +4 -4
  305. package/src/Calendar/Calendar.spec.md +87 -48
  306. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  307. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  308. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  309. package/src/ClickableCard/ClickableCard.test.tsx +96 -0
  310. package/src/ClickableCard/ClickableCard.tsx +48 -4
  311. package/src/Collapsible/Collapsible.spec.md +2 -0
  312. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  313. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  314. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  315. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  316. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  317. package/src/DateInput/DateInput.test.tsx +115 -3
  318. package/src/DateInput/DateInput.tsx +29 -14
  319. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  320. package/src/DateInput/MonthScroller.tsx +106 -20
  321. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  322. package/src/DateInput/TouchDateField.tsx +14 -3
  323. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  324. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  325. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  326. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  327. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  328. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  329. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  330. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  331. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  332. package/src/Dialog/Dialog.test.tsx +216 -2
  333. package/src/Dialog/Dialog.tsx +42 -9
  334. package/src/Dialog/DialogHeader.tsx +5 -4
  335. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  336. package/src/Divider/Divider.tsx +2 -1
  337. package/src/Drawer/Drawer.spec.md +111 -80
  338. package/src/Drawer/Drawer.test.tsx +35 -0
  339. package/src/Drawer/Drawer.tsx +13 -2
  340. package/src/Drawer/DrawerHeader.spec.md +181 -0
  341. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  342. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  343. package/src/DropdownMenu/DropdownMenu.tsx +10 -3
  344. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  345. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  346. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  347. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  348. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
  349. package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
  350. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  351. package/src/EmptyState/EmptyState.tsx +2 -1
  352. package/src/Field/FieldLabel.tsx +5 -4
  353. package/src/Field/InputClearButton.tsx +3 -3
  354. package/src/Field/PanelSearchInput.tsx +2 -2
  355. package/src/FieldStatus/FieldStatus.tsx +2 -2
  356. package/src/FileInput/FileInput.test.tsx +21 -0
  357. package/src/FileInput/FileInput.tsx +4 -1
  358. package/src/FormLayout/FormLayout.spec.md +7 -8
  359. package/src/Heading/Heading.tsx +1 -1
  360. package/src/Icon/Icon.tsx +1 -1
  361. package/src/Indicator/CheckIndicator.spec.md +47 -42
  362. package/src/Indicator/CheckIndicator.tsx +2 -1
  363. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  364. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  365. package/src/Indicator/Indicator.spec.md +188 -0
  366. package/src/Indicator/RadioIndicator.tsx +3 -1
  367. package/src/Item/Item.tsx +2 -1
  368. package/src/Item/ItemSwipeLayer.tsx +1 -1
  369. package/src/Kbd/Kbd.spec.md +81 -38
  370. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
  371. package/src/Layer/popoverOperations.test.ts +205 -0
  372. package/src/Layer/popoverOperations.ts +172 -0
  373. package/src/Layer/useLayer.test.tsx +348 -1
  374. package/src/Layer/useLayer.tsx +118 -71
  375. package/src/Layout/Layout.spec.md +127 -131
  376. package/src/Layout/Layout.tsx +1 -1
  377. package/src/Layout/LayoutContent.spec.md +205 -0
  378. package/src/Layout/LayoutContent.tsx +1 -1
  379. package/src/Layout/LayoutFooter.spec.md +190 -0
  380. package/src/Layout/LayoutFooter.tsx +1 -1
  381. package/src/Layout/LayoutHeader.spec.md +193 -0
  382. package/src/Layout/LayoutHeader.tsx +1 -1
  383. package/src/Layout/LayoutPanel.spec.md +194 -0
  384. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  385. package/src/Lightbox/Lightbox.test.tsx +18 -0
  386. package/src/Lightbox/Lightbox.tsx +5 -0
  387. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  388. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  389. package/src/List/List.tsx +1 -1
  390. package/src/List/ListItem.spec.md +205 -0
  391. package/src/List/ListItem.tsx +3 -3
  392. package/src/MetadataList/MetadataList.spec.md +187 -0
  393. package/src/MetadataList/MetadataListItem.spec.md +165 -0
  394. package/src/MobileNav/MobileNav.test.tsx +15 -0
  395. package/src/MobileNav/MobileNav.tsx +9 -1
  396. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  397. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  398. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
  399. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  400. package/src/MultiSelector/MultiSelector.test.tsx +270 -1
  401. package/src/MultiSelector/MultiSelector.tsx +25 -10
  402. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  403. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  404. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  405. package/src/NumberInput/NumberInput.spec.md +59 -37
  406. package/src/NumberInput/NumberInput.tsx +13 -3
  407. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  408. package/src/NumberInput/numberParser.ts +7 -45
  409. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  410. package/src/ProgressBar/ProgressBar.tsx +58 -10
  411. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  412. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  413. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  414. package/src/Resizable/useResizable.test.ts +43 -0
  415. package/src/Resizable/useResizable.ts +32 -9
  416. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  417. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  418. package/src/Selector/Selector.test.tsx +90 -1
  419. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  420. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  421. package/src/Selector/useSelectorPresentation.ts +31 -3
  422. package/src/SideNav/SideNav.test.tsx +27 -0
  423. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  424. package/src/Skeleton/Skeleton.tsx +3 -1
  425. package/src/Slider/Slider.test.tsx +31 -0
  426. package/src/Slider/Slider.tsx +8 -3
  427. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  428. package/src/Spinner/Spinner.tsx +5 -3
  429. package/src/Stack/Stack.tsx +1 -1
  430. package/src/Stack/StackItem.tsx +1 -1
  431. package/src/TabList/TabList.test.tsx +58 -4
  432. package/src/TabList/TabList.tsx +16 -1
  433. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  434. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  435. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  436. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  437. package/src/Text/Text.tsx +1 -1
  438. package/src/Text/useTruncation.test.tsx +128 -1
  439. package/src/Text/useTruncation.ts +23 -5
  440. package/src/TextArea/TextArea.spec.md +10 -13
  441. package/src/TextArea/TextArea.test.tsx +60 -0
  442. package/src/TextArea/TextArea.tsx +15 -1
  443. package/src/TextInput/TextInput.test.tsx +21 -0
  444. package/src/TextInput/TextInput.tsx +3 -1
  445. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  446. package/src/TimeInput/Presentation.test.tsx +21 -0
  447. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  448. package/src/TimeInput/TimeInput.spec.md +45 -48
  449. package/src/TimeInput/TimeInput.test.tsx +76 -0
  450. package/src/TimeInput/TimeInput.tsx +36 -18
  451. package/src/TimeInput/TouchTimeField.tsx +4 -6
  452. package/src/Timestamp/Timestamp.spec.md +6 -12
  453. package/src/Timestamp/Timestamp.test.tsx +30 -5
  454. package/src/Timestamp/Timestamp.tsx +6 -2
  455. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  456. package/src/Toast/Toast.spec.md +14 -20
  457. package/src/Toast/Toast.tsx +104 -36
  458. package/src/Toast/ToastViewport.test.tsx +719 -1
  459. package/src/Toast/ToastViewport.tsx +100 -50
  460. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  461. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  462. package/src/Tokenizer/Tokenizer.tsx +14 -2
  463. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  464. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  465. package/src/Toolbar/Toolbar.spec.md +5 -6
  466. package/src/Toolbar/Toolbar.test.tsx +30 -0
  467. package/src/Toolbar/Toolbar.tsx +2 -1
  468. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  469. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  470. package/src/TopNav/TopNav.test.tsx +27 -0
  471. package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
  472. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  473. package/src/TopNav/TopNavMenu.test.tsx +17 -0
  474. package/src/TopNav/TopNavMenu.tsx +1 -0
  475. package/src/Typeahead/Typeahead.test.tsx +40 -0
  476. package/src/Typeahead/Typeahead.tsx +12 -1
  477. package/src/__tests__/pressState.ts +8 -4
  478. package/src/gestures/GestureArena.ts +35 -0
  479. package/src/gestures/GestureHost.ts +2065 -0
  480. package/src/gestures/GestureRecognizer.ts +18 -0
  481. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  482. package/src/gestures/PanGestureRecognizer.ts +4 -0
  483. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  484. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  485. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  486. package/src/gestures/TapGestureRecognizer.ts +4 -0
  487. package/src/gestures/VelocityTracker.ts +10 -3
  488. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  489. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  490. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  491. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  492. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  493. package/src/gestures/__tests__/arena.test.ts +5 -0
  494. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  495. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  496. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  497. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  498. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  499. package/src/gestures/__tests__/haptics.test.ts +187 -0
  500. package/src/gestures/__tests__/harness.ts +21 -2
  501. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  502. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  503. package/src/gestures/haptics.ts +203 -0
  504. package/src/gestures/nativeScroll.ts +277 -0
  505. package/src/gestures/touchAction.ts +191 -0
  506. package/src/gestures/touchActionRule.ts +157 -0
  507. package/src/gestures/types.ts +30 -9
  508. package/src/hooks/index.ts +5 -1
  509. package/src/hooks/modalAnnounce.ts +129 -72
  510. package/src/hooks/useAdaptivePresentation.ts +4 -1
  511. package/src/hooks/useAnnounce.doc.mjs +35 -3
  512. package/src/hooks/useAnnounce.replace.test.tsx +516 -0
  513. package/src/hooks/useAnnounce.ts +209 -102
  514. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
  515. package/src/hooks/useAnnounceRenderedText.ts +17 -3
  516. package/src/hooks/useBusyAnnouncement.ts +7 -10
  517. package/src/hooks/useMenuHover.ts +15 -1
  518. package/src/i18n/generated-locales/en.generated.ts +6 -0
  519. package/src/i18n/generated-locales/pseudo.generated.ts +6 -0
  520. package/src/motion/README.md +11 -1
  521. package/src/motion/animator.test.ts +151 -3
  522. package/src/motion/animator.ts +149 -33
  523. package/src/theme/Theme.doc.mjs +1 -1
  524. package/src/theme/defineTheme.ts +117 -8
  525. package/src/theme/derivedVarRegistry.test.ts +4 -0
  526. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  527. package/src/theme/domainTokens/index.ts +6 -10
  528. package/src/theme/expandMotionScale.test.ts +1 -1
  529. package/src/theme/index.ts +5 -0
  530. package/src/theme/motionKnobs.ts +82 -0
  531. package/src/theme/resolveThemeValues.ts +68 -6
  532. package/src/theme/themeAdaptations.ts +52 -12
  533. package/src/theme/themeMotion.test.ts +620 -0
  534. package/src/theme/themeMotion.ts +221 -0
  535. package/src/theme/tokenReferences.ts +149 -0
  536. package/src/theme/tokens.stylex.ts +10 -0
  537. package/src/theme/tokens.ts +15 -158
  538. package/src/utils/focusReturn.ts +17 -6
  539. package/src/utils/getNodeText.test.ts +24 -0
  540. package/src/utils/getNodeText.ts +37 -0
  541. package/src/utils/interactionModality.test.ts +45 -1
  542. package/src/utils/interactionModality.ts +64 -5
  543. package/src/utils/interactionOverlay.stylex.ts +12 -3
  544. package/src/utils/plainDate.test.ts +90 -0
  545. package/src/utils/plainDate.ts +58 -3
  546. package/src/utils/presentation.test.ts +380 -0
  547. package/src/utils/presentation.ts +180 -0
  548. package/src/utils/pressFeedback.test.ts +138 -0
  549. package/src/utils/pressFeedback.ts +34 -11
  550. package/src/utils/pressPaintPriority.test.ts +80 -0
  551. package/src/utils/primaryClick.test.ts +38 -0
  552. package/src/utils/primaryClick.ts +28 -0
  553. package/src/utils/timeFormat.test.ts +204 -0
  554. package/src/utils/timeFormat.ts +314 -0
  555. package/src/utils/touchPresentation.stylex.ts +43 -0
  556. package/src/utils/touchPresentation.test.ts +140 -1
  557. package/src/utils/touchPresentation.ts +5 -3
  558. package/src/utils/unicodeDigits.test.ts +44 -0
  559. package/src/utils/unicodeDigits.ts +71 -0
@@ -0,0 +1,380 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file presentation.test.ts
5
+ * @input Uses vitest, React Testing Library, react-dom/server and a
6
+ * query-aware matchMedia and CSS.supports stub
7
+ * @output A caller's explicit presentation wins: `'popover'` pins the fine
8
+ * surface and input modes on an iPhone; an omitted value follows the
9
+ * component's default (`'popover'` or `'touch'`), distinguishable from an
10
+ * explicit `'popover'`; `'adaptive'` and `'bottom-sheet'` keep their sheet
11
+ * meaning with touch modes under a finger, the same on every touch device;
12
+ * the surface equals useAdaptivePresentation's for every
13
+ * value on every device; live pointer changes; the server and hydrating
14
+ * render, which no hint can put into touch modes; the one-moment read
15
+ * @position Unit tests of packages/core/src/utils/presentation.ts
16
+ */
17
+
18
+ import {act, renderHook} from '@testing-library/react';
19
+ import {createElement, type ComponentProps, type ReactNode} from 'react';
20
+ import {hydrateRoot, type Root} from 'react-dom/client';
21
+ import {renderToString} from 'react-dom/server';
22
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
23
+ import {
24
+ useAdaptivePresentation,
25
+ type AdaptivePresentation,
26
+ } from '../hooks/useAdaptivePresentation';
27
+ import {
28
+ readPresentation,
29
+ usePresentation,
30
+ type PresentationDefault,
31
+ type UsePresentationOptions,
32
+ } from './presentation';
33
+ import {Theme, defineTheme} from '../theme';
34
+ import {IOS_SUPPORTS_QUERY} from './touchPresentation';
35
+ import {
36
+ compactDefinition,
37
+ DEFAULT_COMPACT_DEFINITION,
38
+ } from '../theme/themeAdaptations';
39
+
40
+ const device = {coarse: false, width: 1280, height: 800, iosWebKit: false};
41
+ const listeners = new Set<() => void>();
42
+
43
+ /** Answers a media query list: `,` is or, `and` is and. */
44
+ function answer(query: string): boolean {
45
+ return query.split(', ').some(alternative =>
46
+ alternative.split(' and ').every(part => {
47
+ if (part === '(pointer: coarse)') {
48
+ return device.coarse;
49
+ }
50
+ const range = /^\((width|height) < (\d+)px\)$/.exec(part);
51
+ if (range != null) {
52
+ return device[range[1] as 'width' | 'height'] < Number(range[2]);
53
+ }
54
+ throw new Error(`unexpected query ${query}`);
55
+ }),
56
+ );
57
+ }
58
+
59
+ function setDevice(next: Partial<typeof device>): void {
60
+ Object.assign(device, next);
61
+ for (const listener of [...listeners]) {
62
+ listener();
63
+ }
64
+ }
65
+
66
+ const IPHONE = {coarse: true, width: 393, height: 852, iosWebKit: true};
67
+ const IPAD = {coarse: true, width: 820, height: 1180, iosWebKit: true};
68
+ const ANDROID = {coarse: true, width: 412, height: 915, iosWebKit: false};
69
+ const DESKTOP = {coarse: false, width: 1280, height: 800, iosWebKit: false};
70
+ const MAC_SAFARI = {coarse: false, width: 1440, height: 900, iosWebKit: false};
71
+ /** A narrow window under a fine pointer: compact by size, never by touch. */
72
+ const FINE_NARROW = {coarse: false, width: 600, height: 800, iosWebKit: false};
73
+ /** A phone in landscape: compact by its height alone. */
74
+ const PHONE_LANDSCAPE = {
75
+ coarse: true,
76
+ width: 852,
77
+ height: 393,
78
+ iosWebKit: true,
79
+ };
80
+ /** The compact edges at the default md point (768 wide, 500 high). */
81
+ const COMPACT_EDGES = [
82
+ {coarse: true, width: 767, height: 800, iosWebKit: false},
83
+ {coarse: true, width: 768, height: 800, iosWebKit: false},
84
+ {coarse: true, width: 1024, height: 499, iosWebKit: false},
85
+ {coarse: true, width: 1024, height: 500, iosWebKit: false},
86
+ ];
87
+
88
+ beforeEach(() => {
89
+ setDevice(DESKTOP);
90
+ vi.stubGlobal('CSS', {
91
+ supports: (condition: string) =>
92
+ condition === IOS_SUPPORTS_QUERY && device.iosWebKit,
93
+ });
94
+ vi.stubGlobal(
95
+ 'matchMedia',
96
+ vi.fn((query: string) => ({
97
+ get matches() {
98
+ return answer(query);
99
+ },
100
+ media: query,
101
+ addEventListener: (_type: string, listener: () => void) =>
102
+ listeners.add(listener),
103
+ removeEventListener: (_type: string, listener: () => void) =>
104
+ listeners.delete(listener),
105
+ })),
106
+ );
107
+ });
108
+
109
+ afterEach(() => {
110
+ listeners.clear();
111
+ vi.unstubAllGlobals();
112
+ });
113
+
114
+ const resolve = (
115
+ presentation: AdaptivePresentation | undefined,
116
+ whenOmitted?: PresentationDefault,
117
+ ) =>
118
+ renderHook(() => usePresentation(presentation, {whenOmitted})).result.current;
119
+
120
+ /** A component whose omitted presentation is the touch default. */
121
+ const TOUCH_WHEN_OMITTED: UsePresentationOptions = {whenOmitted: 'touch'};
122
+
123
+ describe('presentation: a caller choice wins over touch defaults', () => {
124
+ it('pins the fine surface and input modes with an explicit popover, even on an iPhone', () => {
125
+ setDevice(IPHONE);
126
+ for (const whenOmitted of ['popover', 'touch'] as const) {
127
+ expect(resolve('popover', whenOmitted)).toEqual({
128
+ surface: 'popover',
129
+ touchModes: false,
130
+ });
131
+ }
132
+ });
133
+
134
+ it("tells an omitted value from an explicit popover: the component's touch default applies only when omitted", () => {
135
+ setDevice(IPHONE);
136
+ expect(resolve(undefined, 'touch')).toEqual({
137
+ surface: 'popover',
138
+ touchModes: true,
139
+ });
140
+ // Today's components: omitted is popover.
141
+ expect(resolve(undefined)).toEqual(resolve('popover'));
142
+ expect(resolve(undefined, 'popover')).toEqual(resolve('popover'));
143
+ });
144
+
145
+ it('turns touch modes on under a finger only, the same on every touch device', () => {
146
+ setDevice(ANDROID);
147
+ expect(resolve(undefined, 'touch')).toEqual({
148
+ surface: 'popover',
149
+ touchModes: true,
150
+ });
151
+ for (const desk of [DESKTOP, MAC_SAFARI]) {
152
+ setDevice(desk);
153
+ expect(resolve(undefined, 'touch')).toMatchObject({
154
+ touchModes: false,
155
+ });
156
+ }
157
+ });
158
+
159
+ it('keeps the sheet meaning of adaptive and bottom-sheet, with touch modes under a finger', () => {
160
+ setDevice(IPHONE);
161
+ expect(resolve('adaptive')).toEqual({
162
+ surface: 'bottom-sheet',
163
+ touchModes: true,
164
+ });
165
+ setDevice(IPAD);
166
+ expect(resolve('adaptive')).toMatchObject({
167
+ surface: 'popover',
168
+ touchModes: true,
169
+ });
170
+ setDevice(DESKTOP);
171
+ expect(resolve('adaptive')).toMatchObject({
172
+ surface: 'popover',
173
+ touchModes: false,
174
+ });
175
+ expect(resolve('bottom-sheet')).toMatchObject({
176
+ surface: 'bottom-sheet',
177
+ touchModes: false,
178
+ });
179
+ });
180
+
181
+ it("renders useAdaptivePresentation's surface for every value on every device", () => {
182
+ for (const next of [
183
+ IPHONE,
184
+ IPAD,
185
+ ANDROID,
186
+ DESKTOP,
187
+ MAC_SAFARI,
188
+ FINE_NARROW,
189
+ PHONE_LANDSCAPE,
190
+ ...COMPACT_EDGES,
191
+ ]) {
192
+ setDevice(next);
193
+ for (const value of ['popover', 'bottom-sheet', 'adaptive'] as const) {
194
+ const {result} = renderHook(() => ({
195
+ surface: usePresentation(value).surface,
196
+ adaptive: useAdaptivePresentation(value),
197
+ }));
198
+ expect(result.current.surface).toBe(result.current.adaptive);
199
+ }
200
+ }
201
+ });
202
+
203
+ it("answers 'adaptive' with the sheet only on a compact touch screen: a fine narrow window, a phone in landscape and each compact edge (spec:AST-094 FR13, spec:AST-012 FR9)", () => {
204
+ for (const [next, surface] of [
205
+ [FINE_NARROW, 'popover'],
206
+ [PHONE_LANDSCAPE, 'bottom-sheet'],
207
+ [COMPACT_EDGES[0], 'bottom-sheet'],
208
+ [COMPACT_EDGES[1], 'popover'],
209
+ [COMPACT_EDGES[2], 'bottom-sheet'],
210
+ [COMPACT_EDGES[3], 'popover'],
211
+ ] as const) {
212
+ setDevice(next);
213
+ expect({...next, surface: resolve('adaptive').surface}).toEqual({
214
+ ...next,
215
+ surface,
216
+ });
217
+ }
218
+ });
219
+
220
+ it("follows the active theme's md point for 'adaptive' (spec:AST-012 FR9)", () => {
221
+ const wideMd = defineTheme({
222
+ name: 'wide-md-presentation',
223
+ adaptations: {widthBreakpoints: {md: 1000, lg: 1100}},
224
+ });
225
+ const wrapper = ({children}: {children: ReactNode}) =>
226
+ createElement(
227
+ Theme,
228
+ {theme: wideMd} as ComponentProps<typeof Theme>,
229
+ children,
230
+ );
231
+ setDevice({coarse: true, width: 900, height: 1024});
232
+ // Under the default md point (768) this screen is not compact.
233
+ const plain = renderHook(() => usePresentation('adaptive'));
234
+ expect(plain.result.current.surface).toBe('popover');
235
+ plain.unmount();
236
+ // Under a theme whose md is 1000, the same screen is compact.
237
+ const themed = renderHook(() => usePresentation('adaptive'), {wrapper});
238
+ expect(themed.result.current).toEqual({
239
+ surface: 'bottom-sheet',
240
+ touchModes: true,
241
+ });
242
+ });
243
+
244
+ it('follows the pointer when it changes (an iPad docking a trackpad)', () => {
245
+ setDevice(IPAD);
246
+ const {result} = renderHook(() =>
247
+ usePresentation(undefined, {whenOmitted: 'touch'}),
248
+ );
249
+ expect(result.current.touchModes).toBe(true);
250
+ act(() => setDevice({coarse: false}));
251
+ // A fine pointer in this context, checked before the claim.
252
+ expect(window.matchMedia('(pointer: coarse)').matches).toBe(false);
253
+ expect(result.current).toMatchObject({touchModes: false});
254
+ act(() => setDevice({coarse: true}));
255
+ expect(result.current).toMatchObject({touchModes: true});
256
+ });
257
+
258
+ it('renders no touch surface or input mode on the server or while hydrating, and takes no hint that would (spec:AST-094 FR13)', () => {
259
+ // On a phone, where a live read would answer touch modes.
260
+ setDevice(IPHONE);
261
+ function Probe({
262
+ presentation,
263
+ options = TOUCH_WHEN_OMITTED,
264
+ }: {
265
+ presentation?: AdaptivePresentation;
266
+ options?: UsePresentationOptions;
267
+ }) {
268
+ const resolved = usePresentation(presentation, options);
269
+ return `${resolved.surface}/${String(resolved.touchModes)}`;
270
+ }
271
+ expect(renderToString(createElement(Probe))).toContain('popover/false');
272
+ expect(
273
+ renderToString(createElement(Probe, {presentation: 'adaptive'})),
274
+ ).toContain('popover/false');
275
+ expect(
276
+ renderToString(createElement(Probe, {presentation: 'bottom-sheet'})),
277
+ ).toContain('bottom-sheet/false');
278
+ // A server hint would put touch modes into the server render, which
279
+ // hydration then contradicts: usePresentation takes none.
280
+ const hinted = {
281
+ whenOmitted: 'touch',
282
+ // @ts-expect-error usePresentation takes no serverDefault.
283
+ serverDefault: {isTouch: true, isCompact: true},
284
+ } satisfies UsePresentationOptions;
285
+ expect(
286
+ renderToString(
287
+ createElement(Probe, {presentation: 'adaptive', options: hinted}),
288
+ ),
289
+ ).toContain('popover/false');
290
+ });
291
+
292
+ it('hydrates on a phone from the server render, then follows the device (spec:AST-094 FR13)', async () => {
293
+ setDevice(IPHONE);
294
+ const renders: string[] = [];
295
+ function Probe({presentation}: {presentation: AdaptivePresentation}) {
296
+ const resolved = usePresentation(presentation, TOUCH_WHEN_OMITTED);
297
+ const text = `${resolved.surface}/${String(resolved.touchModes)}`;
298
+ renders.push(text);
299
+ return text;
300
+ }
301
+ const html = renderToString(
302
+ createElement(Probe, {presentation: 'adaptive'}),
303
+ );
304
+ expect(html).toContain('popover/false');
305
+ const container = document.createElement('div');
306
+ container.innerHTML = html;
307
+ document.body.append(container);
308
+ renders.length = 0;
309
+ const recoverable = vi.fn();
310
+ let root: Root | null = null;
311
+ await act(async () => {
312
+ root = hydrateRoot(
313
+ container,
314
+ createElement(Probe, {presentation: 'adaptive'}),
315
+ {onRecoverableError: recoverable},
316
+ );
317
+ });
318
+ // The hydrating render is the server's; the phone's answer follows it.
319
+ expect(renders[0]).toBe('popover/false');
320
+ expect(renders.at(-1)).toBe('bottom-sheet/true');
321
+ expect(recoverable).not.toHaveBeenCalled();
322
+ act(() => root?.unmount());
323
+ container.remove();
324
+ });
325
+
326
+ it('subscribes only to the queries a request reads (architecture:react-component-runtime INV5)', () => {
327
+ setDevice(IPHONE);
328
+ for (const [presentation, whenOmitted, subscriptions] of [
329
+ ['popover', 'touch', 0],
330
+ [undefined, 'popover', 0],
331
+ ['bottom-sheet', 'popover', 1],
332
+ [undefined, 'touch', 1],
333
+ ['adaptive', 'popover', 2],
334
+ ] as const) {
335
+ listeners.clear();
336
+ const {unmount} = renderHook(() =>
337
+ usePresentation(presentation, {whenOmitted}),
338
+ );
339
+ expect({
340
+ presentation,
341
+ whenOmitted,
342
+ subscriptions: listeners.size,
343
+ }).toEqual({presentation, whenOmitted, subscriptions});
344
+ unmount();
345
+ expect(listeners.size).toBe(0);
346
+ }
347
+ });
348
+
349
+ it('answers at one moment as the hook does, and as the server would with no window', () => {
350
+ setDevice(IPHONE);
351
+ const compact = DEFAULT_COMPACT_DEFINITION;
352
+ expect(
353
+ readPresentation(undefined, {whenOmitted: 'touch', compact}),
354
+ ).toEqual(resolve(undefined, 'touch'));
355
+ expect(
356
+ readPresentation('popover', {whenOmitted: 'touch', compact}),
357
+ ).toEqual(resolve('popover', 'touch'));
358
+ expect(readPresentation('adaptive', {compact})).toEqual(
359
+ resolve('adaptive'),
360
+ );
361
+ expect(
362
+ readPresentation(undefined, {whenOmitted: 'touch', view: null, compact}),
363
+ ).toEqual({
364
+ surface: 'popover',
365
+ touchModes: false,
366
+ });
367
+ });
368
+
369
+ it('reads the compact definition it is given, so a theme that moves md moves the sheet (spec:AST-012 FR9)', () => {
370
+ setDevice({...IPHONE, width: 900, height: 1024});
371
+ expect(
372
+ readPresentation('adaptive', {compact: DEFAULT_COMPACT_DEFINITION})
373
+ .surface,
374
+ ).toBe('popover');
375
+ expect(
376
+ readPresentation('adaptive', {compact: compactDefinition({md: 1000})})
377
+ .surface,
378
+ ).toBe('bottom-sheet');
379
+ });
380
+ });
@@ -0,0 +1,180 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file presentation.ts
7
+ * @input A component's `presentation` prop (a caller's value, or omitted),
8
+ * what the component does when it is omitted, and the touch presentation
9
+ * (utils/touchPresentation.ts)
10
+ * @output usePresentation and readPresentation (and the pure
11
+ * resolvePresentation): the surface to render and whether the component's
12
+ * touch input modes apply
13
+ * @position Internal resolver for stable components that, under a finger,
14
+ * present a different surface or switch how an open surface follows input
15
+ * (architecture:public-component-api INV16)
16
+ *
17
+ * A caller's explicit `presentation` always wins over the touch default:
18
+ *
19
+ * - `'popover'`: the fine-pointer surface AND input modes, on every device;
20
+ * - `'bottom-sheet'`: the sheet everywhere, with touch input modes under a
21
+ * finger;
22
+ * - `'adaptive'`: the sheet on a compact touch screen and the popover
23
+ * elsewhere, with touch input modes under a finger;
24
+ * - omitted: the component's default. `whenOmitted: 'popover'` answers as
25
+ * `'popover'` (today's components). `whenOmitted: 'touch'` keeps the
26
+ * popover surface and turns the component's touch input modes on under a
27
+ * finger, as UIMenu tracks a finger in an anchored menu. Making `'touch'`
28
+ * a released component's default is a functional default change
29
+ * (`spec:AST-017` FR3) whose Changeset names `presentation="popover"`.
30
+ *
31
+ * One touch model on every touch device: the modes depend on the pointer,
32
+ * never on the platform. Touch styling (press paint, the text-size floor)
33
+ * and touch-only behavior that a component's own record defines (a row
34
+ * swipe, a slider's thumb) do not read this: they keep the touch
35
+ * presentation.
36
+ *
37
+ * CSS: a style that implements a touch surface or input mode is applied with
38
+ * `touchModes` (`stylex.props(touchModes && styles.touchMode)`), never as a
39
+ * bare media key on a base style, so a pinned `'popover'` keeps none of it.
40
+ * A rule inside it that needs the touch query uses the touch presentation's
41
+ * touch key; the iOS `@supports` gate is only for the one style iOS WebKit
42
+ * itself requires (the 16px text-size floor), never for a touch mode. On the
43
+ * server and while hydrating, `touchModes` is false and `'adaptive'` is the
44
+ * popover: a touch surface or mode opens only after hydration
45
+ * (`spec:AST-094` FR13), and no hint changes that. usePresentation listens
46
+ * only to the queries a request reads (`architecture:react-component-runtime`
47
+ * INV5): none for a pinned `'popover'`.
48
+ *
49
+ * SYNC: When modified, update presentation.test.ts.
50
+ */
51
+
52
+ import {useCallback, useMemo, useSyncExternalStore} from 'react';
53
+ import type {AdaptivePresentation} from '../hooks/useAdaptivePresentation';
54
+ import type {CompactDefinition} from '../theme/themeAdaptations';
55
+ import {
56
+ readTouchPresentation,
57
+ TOUCH_QUERY,
58
+ useCompactDefinition,
59
+ type TouchPresentation,
60
+ } from './touchPresentation';
61
+
62
+ /** What a component does when the caller omits `presentation`. */
63
+ export type PresentationDefault = 'popover' | 'touch';
64
+
65
+ export interface ResolvedPresentation {
66
+ /** The surface to render. */
67
+ readonly surface: 'popover' | 'bottom-sheet';
68
+ /**
69
+ * The component's touch input modes apply: a finger, and `presentation`
70
+ * not pinned to `'popover'`.
71
+ */
72
+ readonly touchModes: boolean;
73
+ }
74
+
75
+ export interface PresentationOptions {
76
+ /** What an omitted `presentation` means. @default 'popover' */
77
+ readonly whenOmitted?: PresentationDefault;
78
+ }
79
+
80
+ /**
81
+ * usePresentation's options. There is no server hint: the server and
82
+ * hydrating render never present touch modes (`spec:AST-094` FR13).
83
+ */
84
+ export type UsePresentationOptions = PresentationOptions;
85
+
86
+ export interface ReadPresentationOptions extends PresentationOptions {
87
+ /** The window to read; the global one by default. */
88
+ readonly view?: Window | null;
89
+ /**
90
+ * The active theme's compact definition (`useCompactDefinition()`, read
91
+ * while rendering), so a theme that moves `md` moves the sheet too
92
+ * (`spec:AST-012` FR9).
93
+ */
94
+ readonly compact: CompactDefinition;
95
+ }
96
+
97
+ /** The decision, from a presentation request and a touch presentation. */
98
+ export function resolvePresentation(
99
+ presentation: AdaptivePresentation | undefined,
100
+ touch: TouchPresentation,
101
+ whenOmitted: PresentationDefault = 'popover',
102
+ ): ResolvedPresentation {
103
+ const request = presentation ?? whenOmitted;
104
+ const surface =
105
+ request === 'bottom-sheet' ||
106
+ (request === 'adaptive' && touch.isTouch && touch.isCompact)
107
+ ? 'bottom-sheet'
108
+ : 'popover';
109
+ const touchModes = request !== 'popover' && touch.isTouch;
110
+ return {surface, touchModes};
111
+ }
112
+
113
+ const noSubscription = () => () => {};
114
+ /** The server and hydrating render match no query (`spec:AST-094` FR13). */
115
+ const matchesNothing = () => false;
116
+
117
+ /**
118
+ * A media query's match, subscribed only while `query` is given
119
+ * (`architecture:react-component-runtime` INV5); false on the server and
120
+ * while hydrating (`spec:AST-094` FR13).
121
+ */
122
+ function useMatchWhileRead(query: string | null): boolean {
123
+ const subscribe = useCallback(
124
+ (onStoreChange: () => void) => {
125
+ if (query == null) {
126
+ return noSubscription();
127
+ }
128
+ const mql = window.matchMedia(query);
129
+ mql.addEventListener('change', onStoreChange);
130
+ return () => mql.removeEventListener('change', onStoreChange);
131
+ },
132
+ [query],
133
+ );
134
+ const getSnapshot = useCallback(
135
+ () => query != null && window.matchMedia(query).matches,
136
+ [query],
137
+ );
138
+ return useSyncExternalStore(subscribe, getSnapshot, matchesNothing);
139
+ }
140
+
141
+ /**
142
+ * The presentation now, following the pointer and the size as they change
143
+ * (a tablet docking a trackpad). SSR-safe: renders the server default (not
144
+ * touch, not compact) until hydrated (`spec:AST-094` FR13). It subscribes
145
+ * only to what the request reads: a pinned `'popover'` to neither query,
146
+ * `'bottom-sheet'` and the omitted `'touch'` default to the pointer, and
147
+ * `'adaptive'` to the pointer and the compact size.
148
+ */
149
+ export function usePresentation(
150
+ presentation: AdaptivePresentation | undefined,
151
+ {whenOmitted = 'popover'}: UsePresentationOptions = {},
152
+ ): ResolvedPresentation {
153
+ const request = presentation ?? whenOmitted;
154
+ const compact = useCompactDefinition();
155
+ const isTouch = useMatchWhileRead(request === 'popover' ? null : TOUCH_QUERY);
156
+ const isCompact = useMatchWhileRead(
157
+ request === 'adaptive' ? compact.query : null,
158
+ );
159
+ return useMemo(
160
+ () => resolvePresentation(presentation, {isTouch, isCompact}, whenOmitted),
161
+ [presentation, isTouch, isCompact, whenOmitted],
162
+ );
163
+ }
164
+
165
+ /**
166
+ * The presentation at this moment, for a decision taken once (a menu
167
+ * deciding how it follows the finger that opened it).
168
+ */
169
+ export function readPresentation(
170
+ presentation: AdaptivePresentation | undefined,
171
+ {whenOmitted = 'popover', view, compact}: ReadPresentationOptions,
172
+ ): ResolvedPresentation {
173
+ const target =
174
+ view === undefined ? (typeof window === 'undefined' ? null : window) : view;
175
+ return resolvePresentation(
176
+ presentation,
177
+ readTouchPresentation(target, compact),
178
+ whenOmitted,
179
+ );
180
+ }