@astryxdesign/core 0.4.5 → 0.4.6-canary.cea9ecd

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 (538) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/dist/Avatar/Avatar.d.ts +10 -5
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +139 -30
  5. package/dist/Avatar/AvatarStatusDot.d.ts +2 -1
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/Avatar/AvatarStatusDot.js +30 -4
  8. package/dist/Avatar/AvatarStatusLabelContext.d.ts +39 -0
  9. package/dist/Avatar/AvatarStatusLabelContext.d.ts.map +1 -0
  10. package/dist/Avatar/AvatarStatusLabelContext.js +41 -0
  11. package/dist/AvatarGroup/AvatarGroup.d.ts +3 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +15 -9
  15. package/dist/Blockquote/Blockquote.d.ts +4 -3
  16. package/dist/Blockquote/Blockquote.d.ts.map +1 -1
  17. package/dist/Blockquote/Blockquote.js +9 -10
  18. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  19. package/dist/BottomSheet/BottomSheetPanel.js +21 -5
  20. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/BreadcrumbItem.js +47 -34
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +7 -7
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +21 -12
  26. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  27. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  28. package/dist/Calendar/styles.d.ts +2 -2
  29. package/dist/Calendar/styles.d.ts.map +1 -1
  30. package/dist/Calendar/styles.js +5 -5
  31. package/dist/Center/Center.d.ts +23 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +12 -6
  34. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  35. package/dist/Chat/ChatComposer.js +3 -3
  36. package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
  37. package/dist/Chat/ChatComposerDrawer.js +2 -2
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +2 -2
  40. package/dist/Chat/useChatDictation.d.ts +1 -1
  41. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  42. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  43. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  44. package/dist/Chat/useSpeechRecognition.js +4 -3
  45. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  46. package/dist/Chat/useTriggerMenu.js +2 -2
  47. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  48. package/dist/CheckboxInput/CheckboxInput.js +2 -2
  49. package/dist/Citation/Citation.d.ts.map +1 -1
  50. package/dist/Citation/Citation.js +5 -5
  51. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  52. package/dist/ClickableCard/ClickableCard.js +4 -4
  53. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  54. package/dist/CodeBlock/CodeBlock.js +1 -1
  55. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  56. package/dist/Collapsible/Collapsible.js +2 -2
  57. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  58. package/dist/CommandPalette/CommandPaletteItem.js +3 -3
  59. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  60. package/dist/ComplexSelector/ComplexSelector.js +6 -6
  61. package/dist/DateInput/DateInput.d.ts +31 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +90 -22
  64. package/dist/DateInput/MonthScroller.d.ts +35 -0
  65. package/dist/DateInput/MonthScroller.d.ts.map +1 -0
  66. package/dist/DateInput/MonthScroller.js +658 -0
  67. package/dist/DateInput/MonthYearWheels.d.ts +24 -0
  68. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
  69. package/dist/DateInput/MonthYearWheels.js +114 -0
  70. package/dist/DateInput/TouchDateField.d.ts +10 -0
  71. package/dist/DateInput/TouchDateField.d.ts.map +1 -0
  72. package/dist/DateInput/TouchDateField.js +810 -0
  73. package/dist/DateInput/Wheel.d.ts +41 -0
  74. package/dist/DateInput/Wheel.d.ts.map +1 -0
  75. package/dist/DateInput/Wheel.js +315 -0
  76. package/dist/DateInput/monthGeometry.d.ts +65 -0
  77. package/dist/DateInput/monthGeometry.d.ts.map +1 -0
  78. package/dist/DateInput/monthGeometry.js +98 -0
  79. package/dist/DateInput/tokens.stylex.d.ts +63 -0
  80. package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
  81. package/dist/DateInput/tokens.stylex.js +74 -0
  82. package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
  83. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
  84. package/dist/DateInput/useOwnScrollGesture.js +202 -0
  85. package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
  86. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
  87. package/dist/DateInput/usePointerDragScroll.js +216 -0
  88. package/dist/DateInput/useScrollSettle.d.ts +26 -0
  89. package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
  90. package/dist/DateInput/useScrollSettle.js +149 -0
  91. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  92. package/dist/DateRangeInput/DateRangeInput.js +17 -14
  93. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  94. package/dist/DateTimeInput/DateTimeInput.js +17 -14
  95. package/dist/Dialog/Dialog.d.ts.map +1 -1
  96. package/dist/Dialog/Dialog.js +3 -3
  97. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  98. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +2 -2
  99. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  100. package/dist/DropdownMenu/DropdownMenuItem.js +2 -2
  101. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  102. package/dist/DropdownMenu/DropdownMenuRadioItem.js +2 -2
  103. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  104. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -2
  105. package/dist/Field/FieldLabel.d.ts.map +1 -1
  106. package/dist/Field/FieldLabel.js +4 -4
  107. package/dist/Field/InputClearButton.d.ts.map +1 -1
  108. package/dist/Field/InputClearButton.js +6 -0
  109. package/dist/Field/inputStyles.stylex.d.ts +1 -1
  110. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  111. package/dist/Field/inputStyles.stylex.js +5 -5
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +6 -6
  114. package/dist/HoverCard/HoverCard.d.ts +13 -3
  115. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  116. package/dist/HoverCard/HoverCard.js +2 -0
  117. package/dist/HoverCard/index.d.ts +1 -1
  118. package/dist/HoverCard/index.d.ts.map +1 -1
  119. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  120. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  121. package/dist/HoverCard/useHoverCard.js +78 -9
  122. package/dist/InputGroup/InputGroup.js +1 -1
  123. package/dist/Item/Item.d.ts +9 -1
  124. package/dist/Item/Item.d.ts.map +1 -1
  125. package/dist/Item/Item.js +52 -14
  126. package/dist/Layer/index.d.ts +4 -2
  127. package/dist/Layer/index.d.ts.map +1 -1
  128. package/dist/Layer/index.js +2 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +3 -2
  131. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  132. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  133. package/dist/Layer/useTouchTrigger.js +241 -0
  134. package/dist/Layout/container.stylex.d.ts +4 -2
  135. package/dist/Layout/container.stylex.d.ts.map +1 -1
  136. package/dist/Layout/container.stylex.js +19 -11
  137. package/dist/Layout/index.d.ts +1 -0
  138. package/dist/Layout/index.d.ts.map +1 -1
  139. package/dist/Layout/index.js +4 -0
  140. package/dist/Layout/padding.stylex.d.ts +315 -12
  141. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  142. package/dist/Layout/padding.stylex.js +389 -12
  143. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  144. package/dist/Lightbox/Lightbox.js +5 -4
  145. package/dist/Link/Link.d.ts.map +1 -1
  146. package/dist/Link/Link.js +6 -6
  147. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  148. package/dist/MetadataList/MetadataList.js +1 -1
  149. package/dist/MobileNav/MobileNav.d.ts +1 -0
  150. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  151. package/dist/MobileNav/MobileNav.js +24 -6
  152. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  153. package/dist/MultiSelector/MultiSelector.js +8 -8
  154. package/dist/NavItem/navItemStyles.stylex.d.ts +4 -4
  155. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  156. package/dist/NavItem/navItemStyles.stylex.js +4 -4
  157. package/dist/NavMenu/NavHeadingMenuItem.d.ts.map +1 -1
  158. package/dist/NavMenu/NavHeadingMenuItem.js +3 -3
  159. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  160. package/dist/NumberInput/NumberInput.js +36 -8
  161. package/dist/Outline/Outline.d.ts.map +1 -1
  162. package/dist/Outline/Outline.js +3 -3
  163. package/dist/Pagination/Pagination.d.ts.map +1 -1
  164. package/dist/Pagination/Pagination.js +2 -2
  165. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  166. package/dist/PowerSearch/PowerSearch.js +11 -12
  167. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  169. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  170. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  171. package/dist/PowerSearch/formatFilterValue.js +23 -8
  172. package/dist/RadioList/RadioListItem.d.ts +2 -2
  173. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  174. package/dist/RadioList/RadioListItem.js +70 -40
  175. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  176. package/dist/Resizable/ResizeHandle.js +7 -7
  177. package/dist/Section/Section.d.ts +36 -1
  178. package/dist/Section/Section.d.ts.map +1 -1
  179. package/dist/Section/Section.js +12 -2
  180. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  181. package/dist/SegmentedControl/SegmentedControlItem.js +7 -2
  182. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  183. package/dist/SelectableCard/SelectableCard.js +3 -3
  184. package/dist/Selector/Selector.d.ts +26 -1
  185. package/dist/Selector/Selector.d.ts.map +1 -1
  186. package/dist/Selector/Selector.js +93 -20
  187. package/dist/Selector/SelectorOption.d.ts +14 -1
  188. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  189. package/dist/Selector/SelectorOption.js +6 -0
  190. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  191. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  192. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  193. package/dist/Selector/types.d.ts +1 -0
  194. package/dist/Selector/types.d.ts.map +1 -1
  195. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  196. package/dist/SideNav/SideNavHeading.js +4 -4
  197. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  198. package/dist/SideNav/SideNavItem.js +3 -3
  199. package/dist/Slider/Slider.d.ts.map +1 -1
  200. package/dist/Slider/Slider.js +7 -7
  201. package/dist/Stack/Stack.d.ts +23 -1
  202. package/dist/Stack/Stack.d.ts.map +1 -1
  203. package/dist/Stack/Stack.js +12 -6
  204. package/dist/Switch/Switch.d.ts.map +1 -1
  205. package/dist/Switch/Switch.js +2 -2
  206. package/dist/TabList/Tab.d.ts.map +1 -1
  207. package/dist/TabList/Tab.js +1 -1
  208. package/dist/TabList/TabMenu.d.ts.map +1 -1
  209. package/dist/TabList/TabMenu.js +3 -3
  210. package/dist/Table/TableRow.js +4 -4
  211. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  212. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  213. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  214. package/dist/Table/plugins/filtering/useTableFiltering.js +2 -2
  215. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  216. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +2 -2
  217. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  218. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +2 -2
  219. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  220. package/dist/Table/plugins/sortable/useTableSortable.js +1 -1
  221. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  222. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  223. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  224. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  225. package/dist/Table/plugins/tree/useTableTreeData.js +3 -3
  226. package/dist/TextArea/TextArea.js +1 -1
  227. package/dist/TextInput/TextInput.js +1 -1
  228. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  229. package/dist/Thumbnail/Thumbnail.js +3 -3
  230. package/dist/TimeInput/TimeInput.js +2 -2
  231. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  232. package/dist/Timestamp/Timestamp.js +6 -4
  233. package/dist/Timestamp/formatInstant.d.ts +3 -2
  234. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  235. package/dist/Timestamp/formatInstant.js +8 -8
  236. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  237. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  238. package/dist/Timestamp/tooltipEntries.js +5 -3
  239. package/dist/Toast/Toast.d.ts +4 -3
  240. package/dist/Toast/Toast.d.ts.map +1 -1
  241. package/dist/Toast/Toast.js +10 -7
  242. package/dist/Token/Token.d.ts.map +1 -1
  243. package/dist/Token/Token.js +6 -6
  244. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  245. package/dist/Tokenizer/Tokenizer.js +1 -1
  246. package/dist/Tooltip/Tooltip.d.ts +16 -3
  247. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  248. package/dist/Tooltip/Tooltip.js +2 -0
  249. package/dist/Tooltip/index.d.ts +1 -1
  250. package/dist/Tooltip/index.d.ts.map +1 -1
  251. package/dist/Tooltip/useTooltip.d.ts +21 -2
  252. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  253. package/dist/Tooltip/useTooltip.js +71 -13
  254. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  255. package/dist/TopNav/TopNavHeading.js +4 -4
  256. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavItem.js +3 -3
  258. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  259. package/dist/TopNav/TopNavMegaMenu.js +2 -2
  260. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  261. package/dist/TopNav/TopNavMegaMenuItem.js +2 -2
  262. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  263. package/dist/TopNav/TopNavMenu.js +4 -4
  264. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  265. package/dist/TreeList/TreeListItem.js +7 -7
  266. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  267. package/dist/Typeahead/BaseTypeahead.js +2 -2
  268. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  269. package/dist/Typeahead/Typeahead.js +1 -1
  270. package/dist/astryx.css +124 -62
  271. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  272. package/dist/hooks/containerReveal.stylex.js +18 -18
  273. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  274. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  275. package/dist/hooks/scrollbarGutter.js +103 -0
  276. package/dist/hooks/useAutoMediaMode.d.ts +31 -0
  277. package/dist/hooks/useAutoMediaMode.d.ts.map +1 -0
  278. package/dist/hooks/useAutoMediaMode.js +201 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  280. package/dist/hooks/useInputStatusIcon.js +1 -1
  281. package/dist/hooks/useListFocus.d.ts +5 -2
  282. package/dist/hooks/useListFocus.d.ts.map +1 -1
  283. package/dist/hooks/useListFocus.js +12 -6
  284. package/dist/hooks/useScrollLock.d.ts +4 -0
  285. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  286. package/dist/hooks/useScrollLock.js +13 -1
  287. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  288. package/dist/i18n/InternationalizationContext.js +2 -0
  289. package/dist/i18n/index.d.ts +6 -0
  290. package/dist/i18n/index.d.ts.map +1 -1
  291. package/dist/i18n/index.js +6 -0
  292. package/dist/i18n/useCollator.d.ts +12 -0
  293. package/dist/i18n/useCollator.d.ts.map +1 -0
  294. package/dist/i18n/useCollator.js +40 -0
  295. package/dist/i18n/useLocale.d.ts +3 -0
  296. package/dist/i18n/useLocale.d.ts.map +1 -0
  297. package/dist/i18n/useLocale.js +31 -0
  298. package/dist/theme/MediaTheme.d.ts +25 -5
  299. package/dist/theme/MediaTheme.d.ts.map +1 -1
  300. package/dist/theme/MediaTheme.js +31 -6
  301. package/dist/theme/defineTheme.d.ts +5 -0
  302. package/dist/theme/defineTheme.d.ts.map +1 -1
  303. package/dist/theme/defineTheme.js +5 -0
  304. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  305. package/dist/theme/derivedVarRegistry.js +7 -0
  306. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  307. package/dist/theme/generateThemeRules.js +66 -5
  308. package/dist/theme/index.d.ts +1 -1
  309. package/dist/theme/index.d.ts.map +1 -1
  310. package/dist/theme/syntax/index.d.ts +6 -0
  311. package/dist/theme/syntax/index.d.ts.map +1 -1
  312. package/dist/theme/syntax/index.js +7 -2
  313. package/dist/utils/dateParser.d.ts +4 -3
  314. package/dist/utils/dateParser.d.ts.map +1 -1
  315. package/dist/utils/dateParser.js +8 -8
  316. package/dist/utils/index.d.ts +1 -1
  317. package/dist/utils/index.d.ts.map +1 -1
  318. package/dist/utils/index.js +1 -1
  319. package/dist/utils/plainDate.d.ts +5 -2
  320. package/dist/utils/plainDate.d.ts.map +1 -1
  321. package/dist/utils/plainDate.js +20 -4
  322. package/locales/en.json +28 -0
  323. package/locales/pseudo.json +21 -0
  324. package/package.json +3 -3
  325. package/src/Avatar/Avatar.doc.mjs +10 -6
  326. package/src/Avatar/Avatar.test.tsx +219 -1
  327. package/src/Avatar/Avatar.tsx +179 -37
  328. package/src/Avatar/AvatarStatusDot.tsx +30 -4
  329. package/src/Avatar/AvatarStatusLabelContext.ts +47 -0
  330. package/src/AvatarGroup/AvatarGroup.doc.mjs +10 -6
  331. package/src/AvatarGroup/AvatarGroup.test.tsx +62 -0
  332. package/src/AvatarGroup/AvatarGroup.tsx +3 -1
  333. package/src/AvatarGroup/AvatarGroupOverflow.tsx +31 -8
  334. package/src/Banner/Banner.doc.mjs +2 -0
  335. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  336. package/src/Blockquote/Blockquote.test.tsx +34 -3
  337. package/src/Blockquote/Blockquote.tsx +11 -9
  338. package/src/BottomSheet/BottomSheet.doc.mjs +3 -3
  339. package/src/BottomSheet/BottomSheet.test.tsx +24 -0
  340. package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
  341. package/src/BottomSheet/BottomSheetPanel.tsx +58 -3
  342. package/src/Breadcrumbs/BreadcrumbItem.tsx +36 -23
  343. package/src/Breadcrumbs/Breadcrumbs.doc.mjs +26 -2
  344. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -0
  345. package/src/Button/Button.tsx +12 -9
  346. package/src/Calendar/Calendar.tsx +15 -9
  347. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  348. package/src/Calendar/styles.ts +8 -5
  349. package/src/Center/Center.doc.mjs +48 -0
  350. package/src/Center/Center.test.tsx +158 -0
  351. package/src/Center/Center.tsx +50 -9
  352. package/src/Chat/ChatComposer.tsx +11 -5
  353. package/src/Chat/ChatComposerDrawer.tsx +4 -1
  354. package/src/Chat/ChatToolCalls.tsx +13 -4
  355. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  356. package/src/Chat/useChatDictation.ts +1 -1
  357. package/src/Chat/useSpeechRecognition.ts +5 -3
  358. package/src/Chat/useTriggerMenu.tsx +4 -1
  359. package/src/CheckboxInput/CheckboxInput.tsx +5 -2
  360. package/src/Citation/Citation.test.tsx +6 -1
  361. package/src/Citation/Citation.tsx +11 -5
  362. package/src/ClickableCard/ClickableCard.tsx +7 -4
  363. package/src/CodeBlock/CodeBlock.tsx +4 -1
  364. package/src/Collapsible/Collapsible.tsx +5 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +6 -3
  366. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  367. package/src/ComplexSelector/ComplexSelector.tsx +14 -7
  368. package/src/DateInput/DateInput.test.tsx +17 -1
  369. package/src/DateInput/DateInput.tsx +93 -21
  370. package/src/DateInput/DateInputTouch.test.tsx +2582 -0
  371. package/src/DateInput/MonthScroller.tsx +878 -0
  372. package/src/DateInput/MonthYearWheels.tsx +147 -0
  373. package/src/DateInput/TouchDateField.tsx +1223 -0
  374. package/src/DateInput/Wheel.tsx +473 -0
  375. package/src/DateInput/monthGeometry.ts +118 -0
  376. package/src/DateInput/tokens.stylex.ts +99 -0
  377. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  378. package/src/DateInput/usePointerDragScroll.ts +220 -0
  379. package/src/DateInput/useScrollSettle.ts +164 -0
  380. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  381. package/src/DateRangeInput/DateRangeInput.tsx +31 -13
  382. package/src/DateTimeInput/DateTimeInput.tsx +25 -9
  383. package/src/Dialog/Dialog.test.tsx +24 -0
  384. package/src/Dialog/Dialog.tsx +3 -0
  385. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +5 -2
  386. package/src/DropdownMenu/DropdownMenuItem.tsx +5 -2
  387. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +5 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +5 -2
  389. package/src/Field/Field.doc.mjs +2 -0
  390. package/src/Field/FieldLabel.tsx +9 -3
  391. package/src/Field/InputClearButton.test.tsx +110 -3
  392. package/src/Field/InputClearButton.tsx +38 -0
  393. package/src/Field/inputStyles.stylex.ts +17 -13
  394. package/src/FileInput/FileInput.tsx +15 -8
  395. package/src/FormLayout/FormLayout.doc.mjs +1 -1
  396. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  397. package/src/HoverCard/HoverCard.test.tsx +189 -0
  398. package/src/HoverCard/HoverCard.tsx +22 -2
  399. package/src/HoverCard/index.ts +1 -0
  400. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  401. package/src/HoverCard/useHoverCard.tsx +103 -7
  402. package/src/InputGroup/InputGroup.tsx +1 -1
  403. package/src/Item/Item.doc.mjs +1 -0
  404. package/src/Item/Item.test.tsx +50 -0
  405. package/src/Item/Item.tsx +54 -6
  406. package/src/Layer/index.ts +10 -0
  407. package/src/Layer/useLayer.tsx +13 -2
  408. package/src/Layer/useTouchTrigger.test.ts +70 -0
  409. package/src/Layer/useTouchTrigger.ts +366 -0
  410. package/src/Layout/Layout.doc.mjs +18 -0
  411. package/src/Layout/container.stylex.ts +11 -3
  412. package/src/Layout/index.ts +4 -0
  413. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  414. package/src/Layout/padding.stylex.ts +192 -12
  415. package/src/Lightbox/Lightbox.tsx +11 -3
  416. package/src/Link/Link.tsx +9 -6
  417. package/src/MetadataList/MetadataList.tsx +4 -1
  418. package/src/MobileNav/MobileNav.tsx +61 -5
  419. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  420. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  421. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  422. package/src/MultiSelector/MultiSelector.tsx +26 -10
  423. package/src/NavItem/navItemStyles.stylex.ts +7 -4
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +6 -3
  425. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  426. package/src/NumberInput/NumberInput.test.tsx +138 -0
  427. package/src/NumberInput/NumberInput.tsx +47 -8
  428. package/src/Outline/Outline.tsx +5 -2
  429. package/src/Pagination/Pagination.tsx +5 -2
  430. package/src/Popover/Popover.test.tsx +27 -1
  431. package/src/PowerSearch/PowerSearch.tsx +17 -8
  432. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  433. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  434. package/src/PowerSearch/formatFilterValue.ts +31 -6
  435. package/src/RadioList/RadioList.doc.mjs +1 -1
  436. package/src/RadioList/RadioList.test.tsx +170 -2
  437. package/src/RadioList/RadioListItem.tsx +88 -53
  438. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  439. package/src/Resizable/ResizeHandle.tsx +25 -7
  440. package/src/Section/Section.doc.mjs +55 -0
  441. package/src/Section/Section.test.tsx +201 -4
  442. package/src/Section/Section.tsx +69 -0
  443. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  444. package/src/SegmentedControl/SegmentedControlItem.tsx +15 -3
  445. package/src/SelectableCard/SelectableCard.tsx +6 -3
  446. package/src/Selector/Selector.doc.mjs +16 -2
  447. package/src/Selector/Selector.test.tsx +525 -1
  448. package/src/Selector/Selector.tsx +180 -25
  449. package/src/Selector/SelectorOption.doc.mjs +7 -0
  450. package/src/Selector/SelectorOption.tsx +21 -0
  451. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  452. package/src/Selector/types.ts +4 -4
  453. package/src/SideNav/SideNavHeading.tsx +14 -5
  454. package/src/SideNav/SideNavItem.tsx +9 -3
  455. package/src/Slider/Slider.tsx +25 -6
  456. package/src/Stack/Stack.doc.mjs +72 -0
  457. package/src/Stack/Stack.test.tsx +156 -0
  458. package/src/Stack/Stack.tsx +50 -8
  459. package/src/Switch/Switch.tsx +5 -2
  460. package/src/TabList/Tab.tsx +4 -1
  461. package/src/TabList/TabMenu.tsx +9 -3
  462. package/src/Table/TableRow.tsx +4 -4
  463. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +6 -2
  464. package/src/Table/plugins/filtering/useTableFiltering.tsx +4 -1
  465. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +10 -3
  466. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +6 -3
  467. package/src/Table/plugins/sortable/useTableSortable.tsx +4 -1
  468. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  469. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  470. package/src/Table/plugins/tree/useTableTreeData.tsx +10 -4
  471. package/src/TextArea/TextArea.doc.mjs +1 -1
  472. package/src/TextArea/TextArea.tsx +1 -1
  473. package/src/TextInput/TextInput.tsx +1 -1
  474. package/src/Thumbnail/Thumbnail.tsx +9 -3
  475. package/src/TimeInput/TimeInput.tsx +1 -1
  476. package/src/Timestamp/Timestamp.test.tsx +27 -7
  477. package/src/Timestamp/Timestamp.tsx +8 -4
  478. package/src/Timestamp/formatInstant.ts +9 -7
  479. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  480. package/src/Timestamp/tooltipEntries.ts +6 -2
  481. package/src/Toast/Toast.tsx +9 -7
  482. package/src/Token/Token.tsx +14 -5
  483. package/src/Tokenizer/Tokenizer.tsx +4 -1
  484. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  485. package/src/Tooltip/Tooltip.test.tsx +254 -1
  486. package/src/Tooltip/Tooltip.tsx +22 -2
  487. package/src/Tooltip/index.ts +1 -0
  488. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  489. package/src/Tooltip/useTooltip.tsx +99 -21
  490. package/src/TopNav/TopNavHeading.tsx +13 -4
  491. package/src/TopNav/TopNavItem.tsx +6 -3
  492. package/src/TopNav/TopNavMegaMenu.tsx +5 -2
  493. package/src/TopNav/TopNavMegaMenuItem.tsx +5 -2
  494. package/src/TopNav/TopNavMenu.tsx +10 -4
  495. package/src/TreeList/TreeListItem.tsx +22 -7
  496. package/src/Typeahead/BaseTypeahead.tsx +5 -2
  497. package/src/Typeahead/Typeahead.tsx +4 -1
  498. package/src/hooks/containerReveal.stylex.ts +54 -18
  499. package/src/hooks/scrollbarGutter.test.ts +167 -0
  500. package/src/hooks/scrollbarGutter.ts +121 -0
  501. package/src/hooks/useAutoMediaMode.test.ts +109 -0
  502. package/src/hooks/useAutoMediaMode.ts +233 -0
  503. package/src/hooks/useInputStatusIcon.tsx +4 -1
  504. package/src/hooks/useListFocus.doc.mjs +2 -2
  505. package/src/hooks/useListFocus.test.tsx +65 -3
  506. package/src/hooks/useListFocus.ts +15 -7
  507. package/src/hooks/useScrollLock.doc.mjs +4 -4
  508. package/src/hooks/useScrollLock.test.ts +73 -0
  509. package/src/hooks/useScrollLock.ts +14 -0
  510. package/src/i18n/InternationalizationContext.ts +2 -0
  511. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  512. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  513. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  514. package/src/i18n/index.ts +6 -0
  515. package/src/i18n/useCollator.doc.mjs +84 -0
  516. package/src/i18n/useCollator.ts +44 -0
  517. package/src/i18n/useLocale.doc.mjs +70 -0
  518. package/src/i18n/useLocale.ts +34 -0
  519. package/src/i18n/useTranslator.doc.mjs +1 -1
  520. package/src/reset.css +9 -2
  521. package/src/reset.test.ts +22 -0
  522. package/src/theme/MediaTheme.doc.mjs +23 -4
  523. package/src/theme/MediaTheme.dom.test.tsx +97 -0
  524. package/src/theme/MediaTheme.tsx +42 -7
  525. package/src/theme/defineTheme.test.ts +9 -3
  526. package/src/theme/defineTheme.ts +5 -0
  527. package/src/theme/derivedVarRegistry.test.ts +19 -5
  528. package/src/theme/derivedVarRegistry.ts +4 -0
  529. package/src/theme/generateThemeRules.test.ts +118 -0
  530. package/src/theme/generateThemeRules.ts +83 -5
  531. package/src/theme/index.ts +1 -1
  532. package/src/theme/syntax/index.ts +6 -2
  533. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  534. package/src/utils/dateParser.test.ts +20 -0
  535. package/src/utils/dateParser.ts +14 -7
  536. package/src/utils/index.ts +2 -0
  537. package/src/utils/plainDate.test.ts +6 -0
  538. package/src/utils/plainDate.ts +21 -4
@@ -0,0 +1,366 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useTouchTrigger.ts
7
+ * @input Touch trigger mode, layer open state, trigger ref, show/hide callbacks
8
+ * @output Exports useTouchTrigger hook, isActionTrigger, LayerTouchTrigger type
9
+ * @position Layer hook; shared touch behavior for useTooltip and useHoverCard
10
+ *
11
+ * SYNC: When modified, update:
12
+ * - /packages/core/src/Layer/index.ts
13
+ *
14
+ * Hover is the one trigger a touch screen cannot express. A tap synthesizes
15
+ * `mouseenter`, so an untreated hover layer either opens on every tap and
16
+ * lingers with nothing to close it, or swallows the tap the user aimed at the
17
+ * control underneath.
18
+ *
19
+ * What the trigger DOES decides which of those the tap deserves. A trigger
20
+ * that performs an action — a button, a link, a form control — keeps its tap:
21
+ * the layer stays shut, because the tap already has somewhere to go and a hint
22
+ * about a control the user just operated is noise. A trigger that performs no
23
+ * action — an info icon, an abbreviation, a truncated label — has nothing to
24
+ * lose, so the tap opens the layer and the next tap outside dismisses it. That
25
+ * is `auto`; `tap` and `none` state the choice outright, which is what an
26
+ * icon-button whose only job is to reveal the layer needs.
27
+ */
28
+
29
+ import {useCallback, useEffect, useRef, type RefObject} from 'react';
30
+ import {
31
+ getInteractionModality,
32
+ trackInteractionModality,
33
+ } from '../utils/interactionModality';
34
+
35
+ /**
36
+ * How a hover layer behaves on a touch pointer.
37
+ *
38
+ * - `auto`: tap-to-open, unless the trigger performs an action of its own
39
+ * - `tap`: always tap-to-open, even on a trigger that performs an action
40
+ * - `none`: never open on touch
41
+ */
42
+ export type LayerTouchTrigger = 'auto' | 'tap' | 'none';
43
+
44
+ /**
45
+ * Pointer types whose *press* is a tap rather than a click.
46
+ *
47
+ * A pen belongs here but not in the arrival path: in detection range it hovers
48
+ * — firing `pointerenter`/`pointermove` with no contact, exactly as a mouse
49
+ * does, on a device where `(hover: hover)` matches — and only becomes a tap
50
+ * once it lands. See `handlePointerEnter`.
51
+ */
52
+ const TOUCH_POINTER_TYPES = new Set(['touch', 'pen']);
53
+
54
+ /**
55
+ * ARIA roles that make an element do something when activated. An explicit
56
+ * role wins over the tag: `<button role="presentation">` is scenery, and a
57
+ * `<span role="button">` is a real control.
58
+ */
59
+ const ACTION_ROLES = new Set([
60
+ 'button',
61
+ 'checkbox',
62
+ 'combobox',
63
+ 'link',
64
+ 'menuitem',
65
+ 'menuitemcheckbox',
66
+ 'menuitemradio',
67
+ 'option',
68
+ 'radio',
69
+ 'searchbox',
70
+ 'slider',
71
+ 'spinbutton',
72
+ 'switch',
73
+ 'tab',
74
+ 'textbox',
75
+ ]);
76
+
77
+ /**
78
+ * Whether activating this element does something other than reveal the layer.
79
+ *
80
+ * Deliberately narrower than "focusable": the wrapper a text-only Tooltip
81
+ * renders carries `tabindex=0` so keyboard users can reach the hint, and it
82
+ * still performs no action.
83
+ *
84
+ * True only decides that the layer stays shut — the tap itself is never
85
+ * swallowed. Nothing here calls `preventDefault` or `stopPropagation`, so an
86
+ * inert trigger that happens to carry its own `onClick` (a `<div onClick>`
87
+ * with no role) gets both: the layer opens and the handler runs.
88
+ */
89
+ export function isActionTrigger(element: HTMLElement): boolean {
90
+ const role = element.getAttribute('role');
91
+ if (role != null && role !== '') {
92
+ return ACTION_ROLES.has(role);
93
+ }
94
+
95
+ switch (element.tagName) {
96
+ case 'BUTTON':
97
+ case 'INPUT':
98
+ case 'LABEL':
99
+ case 'SELECT':
100
+ case 'SUMMARY':
101
+ case 'TEXTAREA':
102
+ return true;
103
+ case 'A':
104
+ case 'AREA':
105
+ return element.hasAttribute('href');
106
+ default:
107
+ return isEditable(element);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Whether typing into this element edits it. Reads the attribute as well as
113
+ * the property: jsdom does not implement `isContentEditable`, and an editor
114
+ * that is an action in the browser must not be inert under test.
115
+ */
116
+ function isEditable(element: HTMLElement): boolean {
117
+ if (element.isContentEditable === true) {
118
+ return true;
119
+ }
120
+ const attribute = element.getAttribute('contenteditable');
121
+ return attribute != null && attribute !== 'false';
122
+ }
123
+
124
+ export interface UseTouchTriggerOptions {
125
+ /** How the layer should behave on a touch pointer. */
126
+ touchTrigger: LayerTouchTrigger;
127
+
128
+ /** Whether the layer's triggers are live at all. */
129
+ isEnabled: boolean;
130
+
131
+ /**
132
+ * Whether the consumer controls visibility. A controlled layer is never
133
+ * toggled by a tap — its visibility is the consumer's to own.
134
+ */
135
+ isControlled: boolean;
136
+
137
+ /** Whether the layer is currently open. */
138
+ isOpen: boolean;
139
+
140
+ /** Element id of the layer surface, so taps inside it count as inside. */
141
+ layerId: string;
142
+
143
+ /** The trigger element the layer is anchored to. */
144
+ triggerRef: RefObject<HTMLElement | null>;
145
+
146
+ /** Open the layer immediately, with no hover delay. */
147
+ show: () => void;
148
+
149
+ /** Close the layer immediately. */
150
+ hide: () => void;
151
+ }
152
+
153
+ export interface UseTouchTriggerReturn {
154
+ /**
155
+ * Whether the pointer in play on this trigger has no hover of its own: a
156
+ * finger, or a pen that has landed. A hovering pen reads as false, because
157
+ * it hovers.
158
+ */
159
+ isTouchPointerRef: RefObject<boolean>;
160
+
161
+ /**
162
+ * Whether the interaction in flight is a touch one. Unlike the raw ref this
163
+ * goes false again as soon as the user reaches for the keyboard, so
164
+ * focus-driven triggers stay available after a tap.
165
+ */
166
+ isTouchInteraction: () => boolean;
167
+
168
+ /**
169
+ * Attach to the trigger: records pointer type ahead of synthesized hover.
170
+ * Arrival alone only marks a finger — a pen hovers, so it is left to the
171
+ * hover path until it presses.
172
+ */
173
+ handlePointerEnter: (event: PointerEvent) => void;
174
+
175
+ /**
176
+ * Attach to the trigger. Returns true when the press was a touch one and
177
+ * this hook has dealt with it, meaning the caller's own pointer-down
178
+ * behavior must not also run.
179
+ */
180
+ handlePointerDown: (event: PointerEvent) => boolean;
181
+
182
+ /** Forget a tap-open. Call from every other close path (Escape, controlled). */
183
+ clearTapOpen: () => void;
184
+ }
185
+
186
+ /**
187
+ * Touch behavior shared by the hover layers.
188
+ *
189
+ * @example
190
+ * ```
191
+ * const touch = useTouchTrigger({
192
+ * touchTrigger,
193
+ * isEnabled,
194
+ * isControlled: isOpen !== undefined,
195
+ * isOpen: layer.isOpen,
196
+ * layerId: layer.id,
197
+ * triggerRef,
198
+ * show: showNow,
199
+ * hide: hideNow,
200
+ * });
201
+ * ```
202
+ */
203
+ export function useTouchTrigger(
204
+ options: UseTouchTriggerOptions,
205
+ ): UseTouchTriggerReturn {
206
+ const {
207
+ touchTrigger,
208
+ isEnabled,
209
+ isControlled,
210
+ isOpen,
211
+ layerId,
212
+ triggerRef,
213
+ show,
214
+ hide,
215
+ } = options;
216
+
217
+ const isTouchPointerRef = useRef(false);
218
+ // Mirrors `isOpen` for synchronous reads inside the pointer handler, which
219
+ // runs outside React's render and would otherwise close over a stale value.
220
+ const isOpenRef = useRef(isOpen);
221
+ isOpenRef.current = isOpen;
222
+
223
+ // Latest values for the document listener, which outlives the render that
224
+ // armed it.
225
+ const hideRef = useRef(hide);
226
+ hideRef.current = hide;
227
+ const layerIdRef = useRef(layerId);
228
+ layerIdRef.current = layerId;
229
+
230
+ // Whether a tap is what opened the layer. Only a tap-open owes the user an
231
+ // outside-tap dismissal: a hover-opened layer already closes on
232
+ // pointer-leave, and a controlled one is not ours to close.
233
+ const isTapOpenRef = useRef(false);
234
+ const outsideListenerRef = useRef<((event: PointerEvent) => void) | null>(
235
+ null,
236
+ );
237
+
238
+ useEffect(() => {
239
+ trackInteractionModality();
240
+ }, []);
241
+
242
+ const disarmOutsideDismiss = useCallback(() => {
243
+ isTapOpenRef.current = false;
244
+ const listener = outsideListenerRef.current;
245
+ if (listener == null) {
246
+ return;
247
+ }
248
+ outsideListenerRef.current = null;
249
+ document.removeEventListener('pointerdown', listener, true);
250
+ }, []);
251
+
252
+ // Capture phase, so a trigger that stops propagation cannot strand an open
253
+ // layer. The opening tap's own capture phase at the document has already
254
+ // passed by the time this runs, so the listener never sees it.
255
+ const armOutsideDismiss = useCallback(() => {
256
+ isTapOpenRef.current = true;
257
+ if (outsideListenerRef.current != null) {
258
+ return;
259
+ }
260
+
261
+ const handleOutsidePointerDown = (event: PointerEvent) => {
262
+ const target = event.target as Node | null;
263
+ if (target != null) {
264
+ // The trigger's own handler owns the toggle.
265
+ if (triggerRef.current?.contains(target) === true) {
266
+ return;
267
+ }
268
+ // Hover card content is interactive; a tap inside it is not a dismiss.
269
+ if (
270
+ document.getElementById(layerIdRef.current)?.contains(target) === true
271
+ ) {
272
+ return;
273
+ }
274
+ }
275
+ disarmOutsideDismiss();
276
+ hideRef.current();
277
+ };
278
+
279
+ outsideListenerRef.current = handleOutsidePointerDown;
280
+ document.addEventListener('pointerdown', handleOutsidePointerDown, true);
281
+ }, [triggerRef, disarmOutsideDismiss]);
282
+
283
+ useEffect(() => disarmOutsideDismiss, [disarmOutsideDismiss]);
284
+
285
+ const isTouchInteraction = useCallback(
286
+ () => isTouchPointerRef.current && getInteractionModality() === 'pointer',
287
+ [],
288
+ );
289
+
290
+ const handlePointerEnter = useCallback((event: PointerEvent) => {
291
+ // Only a finger is hoverless on arrival. A pen in detection range hovers
292
+ // like a mouse, and treating its arrival as touch would bail out of the
293
+ // hover path and leave a stylus user with no tooltip at all. A pen that
294
+ // presses is a tap, and `handlePointerDown` still reads it as one.
295
+ isTouchPointerRef.current = event.pointerType === 'touch';
296
+ }, []);
297
+
298
+ const handlePointerDown = useCallback(
299
+ (event: PointerEvent): boolean => {
300
+ const isTouch = TOUCH_POINTER_TYPES.has(event.pointerType);
301
+ isTouchPointerRef.current = isTouch;
302
+
303
+ if (!isTouch || isControlled) {
304
+ return false;
305
+ }
306
+
307
+ const trigger = triggerRef.current;
308
+ const mode =
309
+ touchTrigger === 'auto'
310
+ ? trigger != null && isActionTrigger(trigger)
311
+ ? 'none'
312
+ : 'tap'
313
+ : touchTrigger;
314
+
315
+ if (mode === 'none' || !isEnabled) {
316
+ // A layer left open by an earlier tap must not survive the next one.
317
+ disarmOutsideDismiss();
318
+ hide();
319
+ return true;
320
+ }
321
+
322
+ // A lazily mounted layer reports `isOpen` a tick after `show()`, so the
323
+ // tap's own bookkeeping is what makes the second tap a close.
324
+ if (isOpenRef.current || isTapOpenRef.current) {
325
+ disarmOutsideDismiss();
326
+ hide();
327
+ return true;
328
+ }
329
+
330
+ armOutsideDismiss();
331
+ show();
332
+ return true;
333
+ },
334
+ [
335
+ touchTrigger,
336
+ isEnabled,
337
+ isControlled,
338
+ triggerRef,
339
+ show,
340
+ hide,
341
+ armOutsideDismiss,
342
+ disarmOutsideDismiss,
343
+ ],
344
+ );
345
+
346
+ // A layer closed by any other path (Escape, a consumer, the browser) leaves
347
+ // no tap-open to dismiss. Only a true open→closed transition counts: a
348
+ // lazily mounted layer still reports `false` for a tick after the tap asked
349
+ // it to open, and reading that as a close would disarm the dismissal the
350
+ // tap-open just armed.
351
+ const wasOpenRef = useRef(isOpen);
352
+ useEffect(() => {
353
+ if (wasOpenRef.current && !isOpen) {
354
+ disarmOutsideDismiss();
355
+ }
356
+ wasOpenRef.current = isOpen;
357
+ }, [isOpen, disarmOutsideDismiss]);
358
+
359
+ return {
360
+ isTouchPointerRef,
361
+ isTouchInteraction,
362
+ handlePointerEnter,
363
+ handlePointerDown,
364
+ clearTapOpen: disarmOutsideDismiss,
365
+ };
366
+ }
@@ -93,6 +93,24 @@ export const docs = {
93
93
  description: 'Height behavior: fill the container or grow with content.',
94
94
  default: "'fill'",
95
95
  },
96
+ {
97
+ name: 'contentWidth',
98
+ type: 'number | string',
99
+ description:
100
+ 'Maximum width of the content within each slot (header, content, footer, panels), centered when narrower than the available space. Dividers stay full-bleed. Numbers are pixels, strings are used as-is (e.g. `60ch`). Common page widths: 640 for forms, settings, and text-focused pages; 960 for content pages and wider layouts.',
101
+ },
102
+ {
103
+ name: 'padding',
104
+ type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
105
+ description:
106
+ "Padding at the layout's outer edges using the spacing scale.",
107
+ },
108
+ {
109
+ name: 'defaultHasDividers',
110
+ type: 'boolean',
111
+ description:
112
+ "Default divider visibility for LayoutHeader and LayoutFooter children. Headers and footers that don't pass `hasDivider` use this value; when unset, nested layouts inherit from their parent context.",
113
+ },
96
114
  ],
97
115
  components: [
98
116
  {name: 'LayoutHeader'},
@@ -28,8 +28,10 @@
28
28
  * }
29
29
  * ```
30
30
  *
31
- * Internal variables (`--layout-padding-inner-x`, `--container-padding-*`)
32
- * are implementation details and must not be referenced by themes.
31
+ * Internal variables (`--layout-padding-inner-x`, `--container-padding-*`,
32
+ * `--_section-padding-propagated`) are implementation details and must not be
33
+ * referenced by themes. They also do not cross an overlay boundary — see
34
+ * `overlayPaddingReset` in padding.stylex.ts.
33
35
  *
34
36
  * SYNC: When modified, update /packages/core/src/Layout/Layout.doc.mjs
35
37
  */
@@ -105,7 +107,13 @@ const cardBlockEnd = `var(--astryx-card-padding-block-end, ${cardShorthand})`;
105
107
  // Section padding chains: --astryx-* then the next specificity level, terminating
106
108
  // at --spacing-4. Built as chained const strings (no function calls) so StyleX
107
109
  // can statically analyze them; see naming.ts for the prefix policy.
108
- const sectionShorthand = `var(--astryx-section-padding, ${SP4})`;
110
+ // `--_section-padding-propagated` (set by an ancestor Section with explicit
111
+ // padding) is read AHEAD of the public theme token, so a propagated value
112
+ // still wins over the theme for nested sections. Splitting the two names is
113
+ // what lets an overlay drop the inherited value at its boundary while keeping
114
+ // the theme's — see `overlayPaddingReset` in padding.stylex.ts.
115
+ const sectionThemeShorthand = `var(--astryx-section-padding, ${SP4})`;
116
+ const sectionShorthand = `var(--_section-padding-propagated, ${sectionThemeShorthand})`;
109
117
  const sectionInline = `var(--astryx-section-padding-inline, ${sectionShorthand})`;
110
118
  const sectionInlineStart = `var(--astryx-section-padding-inline-start, ${sectionInline})`;
111
119
  const sectionInlineEnd = `var(--astryx-section-padding-inline-end, ${sectionInline})`;
@@ -19,6 +19,10 @@ export type {
19
19
  SpacingToken,
20
20
  } from './container.stylex';
21
21
 
22
+ // Overlay padding-variable reset (applied on overlay roots so the container
23
+ // padding system does not cross a fixed/top-layer boundary)
24
+ export {overlayPaddingReset} from './padding.stylex';
25
+
22
26
  // Edge compensation utility
23
27
  export {edgeCompSlot, EDGE_COMP_ATTR} from './edgeCompensation.stylex';
24
28
 
@@ -0,0 +1,222 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file overlayPaddingReset.test.tsx
5
+ * @input Uses vitest, @testing-library/react, overlay components from core
6
+ * @output Tests that overlay roots stop the container padding system at their
7
+ * boundary, and that the public theme token still crosses it
8
+ * @position Layout tests; validates overlayPaddingReset in padding.stylex.ts
9
+ *
10
+ * The container padding system talks to descendants through INHERITED custom
11
+ * properties, but an overlay leaves its parent's visual box while staying a DOM
12
+ * descendant of it. Every overlay root therefore has to stop those values (see
13
+ * #5208: a Section in a 640px sheet rendered 672px wide).
14
+ *
15
+ * jsdom does no layout, so these assert on the custom properties themselves —
16
+ * the definitions the layout is computed from. The widths they stand in for are
17
+ * verified in a browser; see the PR description.
18
+ *
19
+ * SYNC: When an overlay is added, add it to OVERLAYS below.
20
+ */
21
+
22
+ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
23
+ import {readFileSync} from 'node:fs';
24
+ import {join} from 'node:path';
25
+ import {render, screen} from '@testing-library/react';
26
+ import type {ReactNode} from 'react';
27
+ import {Dialog} from '../Dialog';
28
+ import {BottomSheet} from '../BottomSheet';
29
+ import {MobileNav} from '../MobileNav';
30
+ import {Lightbox} from '../Lightbox';
31
+ import {Popover} from '../Popover';
32
+ import {Section} from '../Section';
33
+
34
+ // jsdom implements neither the <dialog> methods nor matchMedia.
35
+ beforeEach(() => {
36
+ HTMLDialogElement.prototype.showModal = vi.fn(function (
37
+ this: HTMLDialogElement,
38
+ ) {
39
+ this.setAttribute('open', '');
40
+ });
41
+ HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
42
+ this.setAttribute('open', '');
43
+ });
44
+ HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
45
+ this.removeAttribute('open');
46
+ });
47
+ vi.stubGlobal(
48
+ 'matchMedia',
49
+ vi.fn().mockReturnValue({
50
+ matches: false,
51
+ media: '',
52
+ onchange: null,
53
+ addEventListener: vi.fn(),
54
+ removeEventListener: vi.fn(),
55
+ addListener: vi.fn(),
56
+ removeListener: vi.fn(),
57
+ dispatchEvent: vi.fn(),
58
+ }),
59
+ );
60
+ });
61
+
62
+ afterEach(() => {
63
+ vi.unstubAllGlobals();
64
+ });
65
+
66
+ /**
67
+ * Values descendants SUBTRACT (bleed margins). The overlay root has no padding
68
+ * of its own to escape, so these must read a literal zero.
69
+ */
70
+ const SUBTRACTED = [
71
+ '--container-padding-inline-start',
72
+ '--container-padding-inline-end',
73
+ '--container-padding-block-start',
74
+ '--container-padding-block-end',
75
+ ];
76
+
77
+ /**
78
+ * Values descendants ADD. These must be guaranteed-invalid (`initial`) rather
79
+ * than zero, so readers fall through to their own default instead of losing
80
+ * their padding — a computed empty string is what `initial` looks like here.
81
+ */
82
+ const CLEARED = [
83
+ '--layout-padding-outer-x',
84
+ '--layout-padding-outer-y',
85
+ '--layout-padding-inner-x',
86
+ '--layout-padding-inner-y',
87
+ '--_section-padding-propagated',
88
+ ];
89
+
90
+ /** What a cleared custom property reads back as. See the assertion below. */
91
+ const CLEARED_READBACK = ['', 'initial'];
92
+
93
+ /** Each overlay, rendered open inside a page Section that leaks 40px. */
94
+ const OVERLAYS: {
95
+ name: string;
96
+ render: (child: ReactNode) => ReactNode;
97
+ root: () => HTMLElement;
98
+ }[] = [
99
+ {
100
+ name: 'Dialog',
101
+ render: child => (
102
+ <Dialog isOpen onOpenChange={() => {}}>
103
+ {child}
104
+ </Dialog>
105
+ ),
106
+ root: () => screen.getByRole('dialog'),
107
+ },
108
+ {
109
+ name: 'BottomSheet',
110
+ render: child => (
111
+ <BottomSheet isOpen onOpenChange={() => {}} label="S">
112
+ {child}
113
+ </BottomSheet>
114
+ ),
115
+ root: () =>
116
+ document.querySelector<HTMLElement>(
117
+ '.astryx-bottom-sheet',
118
+ ) as HTMLElement,
119
+ },
120
+ {
121
+ name: 'MobileNav',
122
+ render: child => (
123
+ <MobileNav isOpen onOpenChange={() => {}} label="N">
124
+ {child}
125
+ </MobileNav>
126
+ ),
127
+ root: () => screen.getByRole('dialog'),
128
+ },
129
+ {
130
+ name: 'Lightbox',
131
+ render: () => (
132
+ <Lightbox
133
+ isOpen
134
+ onOpenChange={() => {}}
135
+ media={{src: 'a.png', alt: 'a'}}
136
+ />
137
+ ),
138
+ root: () => screen.getByRole('dialog'),
139
+ },
140
+ {
141
+ name: 'Popover (useLayer surface)',
142
+ render: child => (
143
+ <Popover isOpen onOpenChange={() => {}} label="P" content={child}>
144
+ <button type="button">t</button>
145
+ </Popover>
146
+ ),
147
+ root: () => document.querySelector<HTMLElement>('[popover]') as HTMLElement,
148
+ },
149
+ ];
150
+
151
+ describe('overlayPaddingReset', () => {
152
+ describe.each(OVERLAYS)('$name', ({render: renderOverlay, root}) => {
153
+ it('zeroes the values descendants subtract', () => {
154
+ render(<Section padding={10}>{renderOverlay(<div>c</div>)}</Section>);
155
+ const computed = getComputedStyle(root());
156
+ for (const name of SUBTRACTED) {
157
+ expect(computed.getPropertyValue(name), name).toBe('0px');
158
+ }
159
+ });
160
+
161
+ it('clears the values descendants add, so they fall to their default', () => {
162
+ render(<Section padding={10}>{renderOverlay(<div>c</div>)}</Section>);
163
+ const computed = getComputedStyle(root());
164
+ for (const name of CLEARED) {
165
+ // A browser resolves `initial` on a custom property to the
166
+ // guaranteed-invalid value and reports '' here; jsdom does not
167
+ // implement that and echoes the keyword. Either proves the
168
+ // declaration landed — and neither is the leaked '40px'.
169
+ expect(CLEARED_READBACK, name).toContain(
170
+ computed.getPropertyValue(name),
171
+ );
172
+ }
173
+ });
174
+ });
175
+
176
+ it('does not clear the public theme token', () => {
177
+ // The reset clears the PRIVATE propagation var only. The public
178
+ // `--astryx-section-padding` is theme surface, set once at the theme root,
179
+ // so it has to keep reaching inside every overlay — clearing it would
180
+ // blank a theme's section padding in every dialog. That is the whole
181
+ // reason the two names were split, and it is the mistake a later
182
+ // "simplification" would most plausibly make.
183
+ //
184
+ // jsdom does not inherit custom properties, so the cascade cannot show
185
+ // this; assert it where it is decided instead. (Verified in a browser: a
186
+ // theme's 20px still reaches a Section inside a Dialog nested under a
187
+ // 40px page Section.)
188
+ const source = readFileSync(join(__dirname, 'padding.stylex.ts'), 'utf8');
189
+ const reset = source.slice(
190
+ source.indexOf('export const overlayPaddingReset'),
191
+ );
192
+ expect(reset).toContain("'--_section-padding-propagated': 'initial'");
193
+ expect(reset).not.toContain('--astryx-section-padding');
194
+ });
195
+
196
+ it("stops an ancestor Section's propagated padding at the boundary", () => {
197
+ // The page Section propagates 40px. Without the reset it would reach the
198
+ // Section inside the overlay, which would pad itself 40px instead of the
199
+ // theme default — the second half of #5208.
200
+ render(
201
+ <Section padding={10}>
202
+ <Dialog isOpen onOpenChange={() => {}}>
203
+ <div data-testid="content">c</div>
204
+ </Dialog>
205
+ </Section>,
206
+ );
207
+ const dialog = screen.getByRole('dialog');
208
+ expect(CLEARED_READBACK).toContain(
209
+ getComputedStyle(dialog).getPropertyValue(
210
+ '--_section-padding-propagated',
211
+ ),
212
+ );
213
+ // ...while the page Section outside the overlay still propagates it.
214
+ // (jsdom does not resolve the token reference to its 40px value.)
215
+ const pageSection = document.querySelector<HTMLElement>('.astryx-section');
216
+ expect(
217
+ getComputedStyle(pageSection as HTMLElement).getPropertyValue(
218
+ '--_section-padding-propagated',
219
+ ),
220
+ ).toBe('var(--spacing-10)');
221
+ });
222
+ });