@astryxdesign/core 0.4.4 → 0.4.5-canary.013e728

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 (469) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -1
  3. package/dist/BottomSheet/BottomSheet.d.ts +11 -2
  4. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  5. package/dist/BottomSheet/BottomSheet.js +4 -0
  6. package/dist/BottomSheet/BottomSheetPanel.d.ts +5 -2
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +65 -15
  9. package/dist/BottomSheet/index.d.ts +1 -1
  10. package/dist/BottomSheet/index.d.ts.map +1 -1
  11. package/dist/BottomSheet/snapOffsets.d.ts +57 -20
  12. package/dist/BottomSheet/snapOffsets.d.ts.map +1 -1
  13. package/dist/BottomSheet/snapOffsets.js +106 -30
  14. package/dist/BottomSheet/useSheetGestures.d.ts +13 -8
  15. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  16. package/dist/BottomSheet/useSheetGestures.js +160 -64
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +6 -6
  18. package/dist/Button/Button.js +5 -5
  19. package/dist/Calendar/Calendar.d.ts +17 -0
  20. package/dist/Calendar/Calendar.d.ts.map +1 -1
  21. package/dist/Calendar/Calendar.js +55 -15
  22. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts +14 -0
  23. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts.map +1 -0
  24. package/dist/Calendar/getStandaloneShortWeekdayNames.js +26 -0
  25. package/dist/Calendar/hooks/useCalendarConstraints.d.ts +22 -1
  26. package/dist/Calendar/hooks/useCalendarConstraints.d.ts.map +1 -1
  27. package/dist/Calendar/hooks/useCalendarConstraints.js +26 -4
  28. package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
  29. package/dist/Calendar/hooks/useCalendarDays.js +14 -10
  30. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  31. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  32. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts +47 -0
  33. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts.map +1 -0
  34. package/dist/Calendar/standaloneShortWeekdayNames.generated.js +40 -0
  35. package/dist/Calendar/styles.js +3 -3
  36. package/dist/Center/Center.d.ts +23 -1
  37. package/dist/Center/Center.d.ts.map +1 -1
  38. package/dist/Center/Center.js +12 -6
  39. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  40. package/dist/Chat/ChatComposer.js +2 -2
  41. package/dist/Chat/ChatToolCalls.js +1 -1
  42. package/dist/Chat/useChatDictation.d.ts +1 -1
  43. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  44. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  45. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  46. package/dist/Chat/useSpeechRecognition.js +4 -3
  47. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  48. package/dist/CheckboxInput/CheckboxInput.js +9 -0
  49. package/dist/Citation/Citation.js +3 -3
  50. package/dist/ClickableCard/ClickableCard.js +2 -2
  51. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.js +8 -3
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +17 -9
  56. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  57. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  58. package/dist/DateRangeInput/DateRangeInput.js +47 -9
  59. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  60. package/dist/DateTimeInput/DateTimeInput.js +18 -10
  61. package/dist/Dialog/Dialog.d.ts.map +1 -1
  62. package/dist/Dialog/Dialog.js +3 -3
  63. package/dist/Field/FieldLabel.d.ts +2 -1
  64. package/dist/Field/FieldLabel.d.ts.map +1 -1
  65. package/dist/Field/FieldLabel.js +21 -3
  66. package/dist/Field/InputClearButton.d.ts +2 -1
  67. package/dist/Field/InputClearButton.d.ts.map +1 -1
  68. package/dist/Field/InputClearButton.js +12 -1
  69. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  70. package/dist/Field/inputStyles.stylex.js +4 -4
  71. package/dist/FileInput/FileInput.d.ts.map +1 -1
  72. package/dist/FileInput/FileInput.js +2 -2
  73. package/dist/FormLayout/FormLayout.d.ts +24 -2
  74. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  75. package/dist/FormLayout/FormLayout.js +7 -2
  76. package/dist/FormLayout/FormLayoutContext.d.ts +15 -2
  77. package/dist/FormLayout/FormLayoutContext.d.ts.map +1 -1
  78. package/dist/FormLayout/FormLayoutContext.js +19 -4
  79. package/dist/FormLayout/index.d.ts +1 -1
  80. package/dist/FormLayout/index.d.ts.map +1 -1
  81. package/dist/HoverCard/HoverCard.d.ts +13 -3
  82. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  83. package/dist/HoverCard/HoverCard.js +2 -0
  84. package/dist/HoverCard/index.d.ts +1 -1
  85. package/dist/HoverCard/index.d.ts.map +1 -1
  86. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +78 -9
  89. package/dist/Item/Item.d.ts +9 -1
  90. package/dist/Item/Item.d.ts.map +1 -1
  91. package/dist/Item/Item.js +46 -8
  92. package/dist/Layer/index.d.ts +4 -2
  93. package/dist/Layer/index.d.ts.map +1 -1
  94. package/dist/Layer/index.js +2 -0
  95. package/dist/Layer/useLayer.d.ts.map +1 -1
  96. package/dist/Layer/useLayer.js +3 -2
  97. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  98. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  99. package/dist/Layer/useTouchTrigger.js +241 -0
  100. package/dist/Layout/container.stylex.d.ts +4 -2
  101. package/dist/Layout/container.stylex.d.ts.map +1 -1
  102. package/dist/Layout/container.stylex.js +19 -11
  103. package/dist/Layout/index.d.ts +1 -0
  104. package/dist/Layout/index.d.ts.map +1 -1
  105. package/dist/Layout/index.js +4 -0
  106. package/dist/Layout/padding.stylex.d.ts +315 -12
  107. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  108. package/dist/Layout/padding.stylex.js +389 -12
  109. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  110. package/dist/Lightbox/Lightbox.js +2 -1
  111. package/dist/Link/Link.js +4 -4
  112. package/dist/Markdown/Markdown.d.ts +8 -0
  113. package/dist/Markdown/Markdown.d.ts.map +1 -1
  114. package/dist/Markdown/Markdown.js +31 -3
  115. package/dist/Markdown/parser.d.ts +14 -2
  116. package/dist/Markdown/parser.d.ts.map +1 -1
  117. package/dist/Markdown/parser.js +50 -2
  118. package/dist/MobileNav/MobileNav.d.ts +1 -0
  119. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  120. package/dist/MobileNav/MobileNav.js +24 -6
  121. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  122. package/dist/MultiSelector/MultiSelector.js +8 -3
  123. package/dist/NavItem/navItemStyles.stylex.d.ts +2 -2
  124. package/dist/NavItem/navItemStyles.stylex.js +2 -2
  125. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  126. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  127. package/dist/NumberInput/NumberInput.js +38 -5
  128. package/dist/Outline/Outline.js +2 -2
  129. package/dist/Outline/parseOutlineFromMarkdown.d.ts +2 -0
  130. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  131. package/dist/Outline/parseOutlineFromMarkdown.js +5 -31
  132. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  133. package/dist/PowerSearch/PowerSearch.js +11 -12
  134. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  135. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  136. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  137. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  138. package/dist/PowerSearch/formatFilterValue.js +23 -8
  139. package/dist/RadioList/RadioList.d.ts.map +1 -1
  140. package/dist/RadioList/RadioList.js +12 -1
  141. package/dist/RadioList/RadioListItem.d.ts +2 -2
  142. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  143. package/dist/RadioList/RadioListItem.js +68 -38
  144. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  145. package/dist/Resizable/ResizeHandle.js +3 -3
  146. package/dist/Section/Section.d.ts +36 -1
  147. package/dist/Section/Section.d.ts.map +1 -1
  148. package/dist/Section/Section.js +12 -2
  149. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  150. package/dist/SegmentedControl/SegmentedControlItem.js +6 -1
  151. package/dist/SelectableCard/SelectableCard.js +1 -1
  152. package/dist/Selector/Selector.d.ts +26 -1
  153. package/dist/Selector/Selector.d.ts.map +1 -1
  154. package/dist/Selector/Selector.js +95 -17
  155. package/dist/Selector/SelectorOption.d.ts +14 -1
  156. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  157. package/dist/Selector/SelectorOption.js +6 -0
  158. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  159. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  160. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  161. package/dist/Selector/types.d.ts +1 -0
  162. package/dist/Selector/types.d.ts.map +1 -1
  163. package/dist/SideNav/SideNavHeading.js +1 -1
  164. package/dist/SideNav/SideNavItem.js +1 -1
  165. package/dist/Slider/Slider.d.ts.map +1 -1
  166. package/dist/Slider/Slider.js +3 -3
  167. package/dist/Stack/Stack.d.ts +23 -1
  168. package/dist/Stack/Stack.d.ts.map +1 -1
  169. package/dist/Stack/Stack.js +12 -6
  170. package/dist/StatusDot/StatusDot.d.ts +43 -3
  171. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  172. package/dist/StatusDot/StatusDot.js +42 -5
  173. package/dist/Switch/Switch.d.ts.map +1 -1
  174. package/dist/Switch/Switch.js +9 -0
  175. package/dist/TabList/TabMenu.js +1 -1
  176. package/dist/Table/TableRow.js +4 -4
  177. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  178. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  179. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  180. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +1 -1
  181. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  182. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  183. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  184. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  185. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  186. package/dist/Table/plugins/tree/useTableTreeData.js +2 -2
  187. package/dist/TextArea/TextArea.d.ts.map +1 -1
  188. package/dist/TextArea/TextArea.js +6 -1
  189. package/dist/TextInput/TextInput.d.ts.map +1 -1
  190. package/dist/TextInput/TextInput.js +6 -1
  191. package/dist/Thumbnail/Thumbnail.js +1 -1
  192. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  193. package/dist/TimeInput/TimeInput.js +6 -1
  194. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  195. package/dist/Timestamp/Timestamp.js +6 -4
  196. package/dist/Timestamp/formatInstant.d.ts +3 -2
  197. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  198. package/dist/Timestamp/formatInstant.js +8 -8
  199. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  200. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  201. package/dist/Timestamp/tooltipEntries.js +5 -3
  202. package/dist/Token/Token.js +1 -1
  203. package/dist/Tooltip/Tooltip.d.ts +16 -3
  204. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  205. package/dist/Tooltip/Tooltip.js +2 -0
  206. package/dist/Tooltip/index.d.ts +1 -1
  207. package/dist/Tooltip/index.d.ts.map +1 -1
  208. package/dist/Tooltip/useTooltip.d.ts +21 -2
  209. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  210. package/dist/Tooltip/useTooltip.js +71 -13
  211. package/dist/TopNav/TopNavHeading.js +2 -2
  212. package/dist/TopNav/TopNavItem.js +2 -2
  213. package/dist/TopNav/TopNavMegaMenu.js +1 -1
  214. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  215. package/dist/TopNav/TopNavMenu.js +2 -2
  216. package/dist/TreeList/TreeListItem.js +1 -1
  217. package/dist/astryx.css +85 -60
  218. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  219. package/dist/hooks/containerReveal.stylex.js +18 -18
  220. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  221. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  222. package/dist/hooks/scrollbarGutter.js +103 -0
  223. package/dist/hooks/useResolvedRequired.d.ts +19 -0
  224. package/dist/hooks/useResolvedRequired.d.ts.map +1 -0
  225. package/dist/hooks/useResolvedRequired.js +40 -0
  226. package/dist/hooks/useScrollLock.d.ts +4 -0
  227. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  228. package/dist/hooks/useScrollLock.js +13 -1
  229. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  230. package/dist/i18n/InternationalizationContext.js +2 -0
  231. package/dist/i18n/index.d.ts +6 -0
  232. package/dist/i18n/index.d.ts.map +1 -1
  233. package/dist/i18n/index.js +6 -0
  234. package/dist/i18n/useCollator.d.ts +12 -0
  235. package/dist/i18n/useCollator.d.ts.map +1 -0
  236. package/dist/i18n/useCollator.js +40 -0
  237. package/dist/i18n/useLocale.d.ts +3 -0
  238. package/dist/i18n/useLocale.d.ts.map +1 -0
  239. package/dist/i18n/useLocale.js +31 -0
  240. package/dist/theme/defineTheme.d.ts +5 -0
  241. package/dist/theme/defineTheme.d.ts.map +1 -1
  242. package/dist/theme/defineTheme.js +5 -0
  243. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  244. package/dist/theme/derivedVarRegistry.js +7 -0
  245. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  246. package/dist/theme/generateThemeRules.js +66 -5
  247. package/dist/utils/dateParser.d.ts +4 -3
  248. package/dist/utils/dateParser.d.ts.map +1 -1
  249. package/dist/utils/dateParser.js +8 -8
  250. package/dist/utils/plainDate.d.ts +9 -2
  251. package/dist/utils/plainDate.d.ts.map +1 -1
  252. package/dist/utils/plainDate.js +16 -4
  253. package/locales/af-ZA.json +958 -0
  254. package/locales/ar-SA.json +998 -0
  255. package/locales/ca-ES.json +974 -0
  256. package/locales/cs-CZ.json +990 -0
  257. package/locales/da-DK.json +966 -0
  258. package/locales/de-DE.json +966 -0
  259. package/locales/el-GR.json +990 -0
  260. package/locales/en.json +8 -0
  261. package/locales/es-ES.json +978 -0
  262. package/locales/fi-FI.json +990 -0
  263. package/locales/fr-FR.json +928 -0
  264. package/locales/he-IL.json +990 -0
  265. package/locales/hu-HU.json +990 -0
  266. package/locales/it-IT.json +978 -0
  267. package/locales/ja-JP.json +998 -0
  268. package/locales/ko-KR.json +990 -0
  269. package/locales/nl-NL.json +954 -0
  270. package/locales/no-NO.json +978 -0
  271. package/locales/pl-PL.json +982 -0
  272. package/locales/pseudo.json +6 -0
  273. package/locales/pt-BR.json +982 -0
  274. package/locales/pt-PT.json +982 -0
  275. package/locales/ro-RO.json +982 -0
  276. package/locales/ru-RU.json +990 -0
  277. package/locales/sr-SP.json +990 -0
  278. package/locales/sv-SE.json +986 -0
  279. package/locales/tr-TR.json +990 -0
  280. package/locales/uk-UA.json +990 -0
  281. package/locales/vi-VN.json +986 -0
  282. package/locales/zh-CN.json +998 -0
  283. package/locales/zh-TW.json +998 -0
  284. package/package.json +3 -3
  285. package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -1
  286. package/src/Banner/Banner.doc.mjs +2 -0
  287. package/src/BottomSheet/BottomSheet.doc.mjs +36 -3
  288. package/src/BottomSheet/BottomSheet.test.tsx +177 -2
  289. package/src/BottomSheet/BottomSheet.tsx +16 -2
  290. package/src/BottomSheet/BottomSheetPanel.test.tsx +41 -0
  291. package/src/BottomSheet/BottomSheetPanel.tsx +95 -15
  292. package/src/BottomSheet/index.ts +5 -1
  293. package/src/BottomSheet/snapOffsets.test.ts +114 -47
  294. package/src/BottomSheet/snapOffsets.ts +119 -33
  295. package/src/BottomSheet/useSheetGestures.test.ts +113 -15
  296. package/src/BottomSheet/useSheetGestures.ts +196 -100
  297. package/src/Breadcrumbs/BreadcrumbItem.tsx +1 -1
  298. package/src/Button/Button.tsx +7 -7
  299. package/src/Calendar/Calendar.doc.mjs +16 -0
  300. package/src/Calendar/Calendar.test.tsx +137 -0
  301. package/src/Calendar/Calendar.tsx +80 -11
  302. package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +54 -0
  303. package/src/Calendar/getStandaloneShortWeekdayNames.ts +38 -0
  304. package/src/Calendar/hooks/useCalendarConstraints.ts +54 -3
  305. package/src/Calendar/hooks/useCalendarDays.ts +13 -10
  306. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  307. package/src/Calendar/standaloneShortWeekdayNames.generated.ts +50 -0
  308. package/src/Calendar/styles.ts +3 -3
  309. package/src/Center/Center.doc.mjs +48 -0
  310. package/src/Center/Center.test.tsx +158 -0
  311. package/src/Center/Center.tsx +50 -9
  312. package/src/Chat/ChatComposer.tsx +7 -4
  313. package/src/Chat/ChatToolCalls.tsx +1 -1
  314. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  315. package/src/Chat/useChatDictation.ts +1 -1
  316. package/src/Chat/useSpeechRecognition.ts +5 -3
  317. package/src/CheckboxInput/CheckboxInput.tsx +6 -0
  318. package/src/Citation/Citation.tsx +3 -3
  319. package/src/ClickableCard/ClickableCard.tsx +2 -2
  320. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  321. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  322. package/src/ComplexSelector/ComplexSelector.tsx +8 -5
  323. package/src/DateInput/DateInput.test.tsx +17 -1
  324. package/src/DateInput/DateInput.tsx +13 -9
  325. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  326. package/src/DateRangeInput/DateRangeInput.test.tsx +81 -1
  327. package/src/DateRangeInput/DateRangeInput.tsx +88 -8
  328. package/src/DateTimeInput/DateTimeInput.tsx +23 -8
  329. package/src/Dialog/Dialog.test.tsx +24 -0
  330. package/src/Dialog/Dialog.tsx +3 -0
  331. package/src/Field/Field.doc.mjs +3 -0
  332. package/src/Field/FieldLabel.tsx +20 -4
  333. package/src/Field/InputClearButton.test.tsx +134 -2
  334. package/src/Field/InputClearButton.tsx +42 -1
  335. package/src/Field/inputStyles.stylex.ts +16 -12
  336. package/src/FileInput/FileInput.tsx +5 -4
  337. package/src/FormLayout/FormLayout.doc.mjs +13 -0
  338. package/src/FormLayout/FormLayout.test.tsx +181 -4
  339. package/src/FormLayout/FormLayout.tsx +33 -2
  340. package/src/FormLayout/FormLayoutContext.ts +22 -7
  341. package/src/FormLayout/index.ts +1 -1
  342. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  343. package/src/HoverCard/HoverCard.test.tsx +189 -0
  344. package/src/HoverCard/HoverCard.tsx +22 -2
  345. package/src/HoverCard/index.ts +1 -0
  346. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  347. package/src/HoverCard/useHoverCard.tsx +103 -7
  348. package/src/Item/Item.doc.mjs +1 -0
  349. package/src/Item/Item.test.tsx +50 -0
  350. package/src/Item/Item.tsx +41 -2
  351. package/src/Layer/index.ts +10 -0
  352. package/src/Layer/useLayer.tsx +13 -2
  353. package/src/Layer/useTouchTrigger.test.ts +70 -0
  354. package/src/Layer/useTouchTrigger.ts +366 -0
  355. package/src/Layout/Layout.doc.mjs +18 -0
  356. package/src/Layout/container.stylex.ts +11 -3
  357. package/src/Layout/index.ts +4 -0
  358. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  359. package/src/Layout/padding.stylex.ts +192 -12
  360. package/src/Lightbox/Lightbox.tsx +2 -1
  361. package/src/Link/Link.tsx +4 -4
  362. package/src/Markdown/Markdown.doc.mjs +3 -0
  363. package/src/Markdown/Markdown.test.tsx +78 -0
  364. package/src/Markdown/Markdown.tsx +41 -1
  365. package/src/Markdown/parser.ts +60 -2
  366. package/src/MobileNav/MobileNav.tsx +61 -5
  367. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  368. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  369. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  370. package/src/MultiSelector/MultiSelector.tsx +8 -5
  371. package/src/NavItem/navItemStyles.stylex.ts +2 -2
  372. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  373. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  374. package/src/NumberInput/NumberInput.test.tsx +138 -0
  375. package/src/NumberInput/NumberInput.tsx +44 -6
  376. package/src/Outline/Outline.doc.mjs +2 -0
  377. package/src/Outline/Outline.tsx +1 -1
  378. package/src/Outline/parseOutlineFromMarkdown.ts +10 -40
  379. package/src/PowerSearch/PowerSearch.tsx +17 -8
  380. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  381. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  382. package/src/PowerSearch/formatFilterValue.ts +31 -6
  383. package/src/RadioList/RadioList.doc.mjs +1 -1
  384. package/src/RadioList/RadioList.test.tsx +170 -2
  385. package/src/RadioList/RadioList.tsx +9 -1
  386. package/src/RadioList/RadioListItem.tsx +83 -51
  387. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  388. package/src/Resizable/ResizeHandle.tsx +9 -3
  389. package/src/Section/Section.doc.mjs +55 -0
  390. package/src/Section/Section.test.tsx +201 -4
  391. package/src/Section/Section.tsx +69 -0
  392. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  393. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -2
  394. package/src/SelectableCard/SelectableCard.tsx +1 -1
  395. package/src/Selector/Selector.doc.mjs +16 -2
  396. package/src/Selector/Selector.test.tsx +525 -1
  397. package/src/Selector/Selector.tsx +166 -21
  398. package/src/Selector/SelectorOption.doc.mjs +7 -0
  399. package/src/Selector/SelectorOption.tsx +21 -0
  400. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  401. package/src/Selector/types.ts +4 -4
  402. package/src/SideNav/SideNavHeading.tsx +2 -2
  403. package/src/SideNav/SideNavItem.tsx +1 -1
  404. package/src/Slider/Slider.tsx +11 -1
  405. package/src/Stack/Stack.doc.mjs +72 -0
  406. package/src/Stack/Stack.test.tsx +156 -0
  407. package/src/Stack/Stack.tsx +50 -8
  408. package/src/StatusDot/StatusDot.doc.mjs +13 -0
  409. package/src/StatusDot/StatusDot.test.tsx +115 -8
  410. package/src/StatusDot/StatusDot.tsx +74 -9
  411. package/src/Switch/Switch.tsx +6 -0
  412. package/src/TabList/TabMenu.tsx +1 -1
  413. package/src/Table/TableRow.tsx +4 -4
  414. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +2 -1
  415. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +2 -1
  416. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +22 -4
  417. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  418. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  419. package/src/Table/plugins/tree/useTableTreeData.tsx +2 -2
  420. package/src/TextArea/TextArea.doc.mjs +1 -1
  421. package/src/TextArea/TextArea.tsx +3 -1
  422. package/src/TextInput/TextInput.tsx +3 -1
  423. package/src/Thumbnail/Thumbnail.tsx +1 -1
  424. package/src/TimeInput/TimeInput.tsx +3 -1
  425. package/src/Timestamp/Timestamp.test.tsx +27 -7
  426. package/src/Timestamp/Timestamp.tsx +8 -4
  427. package/src/Timestamp/formatInstant.ts +9 -7
  428. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  429. package/src/Timestamp/tooltipEntries.ts +6 -2
  430. package/src/Token/Token.tsx +1 -1
  431. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  432. package/src/Tooltip/Tooltip.test.tsx +254 -1
  433. package/src/Tooltip/Tooltip.tsx +22 -2
  434. package/src/Tooltip/index.ts +1 -0
  435. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  436. package/src/Tooltip/useTooltip.tsx +99 -21
  437. package/src/TopNav/TopNavHeading.tsx +1 -1
  438. package/src/TopNav/TopNavItem.tsx +2 -2
  439. package/src/TopNav/TopNavMegaMenu.tsx +1 -1
  440. package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
  441. package/src/TopNav/TopNavMenu.tsx +2 -2
  442. package/src/TreeList/TreeListItem.tsx +1 -1
  443. package/src/hooks/containerReveal.stylex.ts +54 -18
  444. package/src/hooks/scrollbarGutter.test.ts +167 -0
  445. package/src/hooks/scrollbarGutter.ts +121 -0
  446. package/src/hooks/useResolvedRequired.ts +42 -0
  447. package/src/hooks/useScrollLock.doc.mjs +4 -4
  448. package/src/hooks/useScrollLock.test.ts +73 -0
  449. package/src/hooks/useScrollLock.ts +14 -0
  450. package/src/i18n/InternationalizationContext.ts +2 -0
  451. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  452. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  453. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  454. package/src/i18n/index.ts +6 -0
  455. package/src/i18n/useCollator.doc.mjs +84 -0
  456. package/src/i18n/useCollator.ts +44 -0
  457. package/src/i18n/useLocale.doc.mjs +70 -0
  458. package/src/i18n/useLocale.ts +34 -0
  459. package/src/i18n/useTranslator.doc.mjs +1 -1
  460. package/src/theme/defineTheme.test.ts +9 -3
  461. package/src/theme/defineTheme.ts +5 -0
  462. package/src/theme/derivedVarRegistry.test.ts +19 -5
  463. package/src/theme/derivedVarRegistry.ts +4 -0
  464. package/src/theme/generateThemeRules.test.ts +118 -0
  465. package/src/theme/generateThemeRules.ts +83 -5
  466. package/src/utils/dateParser.test.ts +20 -0
  467. package/src/utils/dateParser.ts +14 -7
  468. package/src/utils/plainDate.test.ts +56 -0
  469. package/src/utils/plainDate.ts +17 -4
@@ -4,8 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file useTooltip.tsx
7
- * @input Uses useLayer, React hooks
8
- * @output Exports useTooltip hook for hover/focus triggered tooltips
7
+ * @input Uses useLayer, useTouchTrigger, React hooks
8
+ * @output Exports useTooltip hook for hover/focus/tap triggered tooltips
9
9
  * @position Layer hook; builds on useLayer for tooltip behavior
10
10
  *
11
11
  * SYNC: When modified, update:
@@ -26,6 +26,10 @@ import {
26
26
  type LayerAlignment,
27
27
  type LayerPlacement,
28
28
  } from '../Layer/useLayer';
29
+ import {
30
+ useTouchTrigger,
31
+ type LayerTouchTrigger,
32
+ } from '../Layer/useTouchTrigger';
29
33
  import {layerAnimations} from '../Layer/layerAnimations.stylex';
30
34
  import {themeProps} from '../utils/themeProps';
31
35
  import {isImeKeyEvent} from '../utils/ime';
@@ -73,6 +77,11 @@ const styles = stylex.create({
73
77
  */
74
78
  export type TooltipFocusTrigger = 'auto' | 'always' | 'never';
75
79
 
80
+ /**
81
+ * Touch trigger behavior for tooltips
82
+ */
83
+ export type TooltipTouchTrigger = LayerTouchTrigger;
84
+
76
85
  export interface TooltipOptions {
77
86
  /**
78
87
  * Position placement relative to anchor
@@ -108,6 +117,20 @@ export interface TooltipOptions {
108
117
  */
109
118
  focusTrigger?: TooltipFocusTrigger;
110
119
 
120
+ /**
121
+ * What a tap does on a touch pointer, where there is no hover:
122
+ * - `auto`: tap opens the tooltip, unless the trigger performs an action of
123
+ * its own (a button, a link, a form control) — that tap belongs to the
124
+ * control, and a hint about a control the user just operated is noise
125
+ * - `tap`: tap always opens the tooltip. This is what an info icon rendered
126
+ * as a button wants: it looks like an action to the DOM, but revealing the
127
+ * tooltip is the only thing it does
128
+ * - `none`: touch never opens the tooltip
129
+ *
130
+ * @default 'auto'
131
+ */
132
+ touchTrigger?: TooltipTouchTrigger;
133
+
111
134
  /**
112
135
  * Whether the tooltip is enabled.
113
136
  * When false, hover/focus triggers are disabled.
@@ -218,6 +241,7 @@ function isFocusable(element: HTMLElement): boolean {
218
241
  * Builds on useLayer to add:
219
242
  * - Hover triggers with configurable delay
220
243
  * - Focus triggers with auto-detection for focusable elements
244
+ * - Tap triggers on touch, where there is no hover (see useTouchTrigger)
221
245
  * - Inverted color palette for high contrast
222
246
  *
223
247
  * Unlike HoverCard, tooltips:
@@ -242,6 +266,7 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
242
266
  delay = 200,
243
267
  hideDelay = 0,
244
268
  focusTrigger = 'auto',
269
+ touchTrigger = 'auto',
245
270
  isEnabled = true,
246
271
  isOpen,
247
272
  isDefaultOpen = false,
@@ -273,6 +298,29 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
273
298
  }
274
299
  }, []);
275
300
 
301
+ // Touch resolves immediately: the hover delays exist to filter out a pointer
302
+ // passing across the trigger, and a tap is never that.
303
+ const showNow = useCallback(() => {
304
+ clearTimeouts();
305
+ layer.show();
306
+ }, [clearTimeouts, layer]);
307
+
308
+ const hideNow = useCallback(() => {
309
+ clearTimeouts();
310
+ layer.hide();
311
+ }, [clearTimeouts, layer]);
312
+
313
+ const touch = useTouchTrigger({
314
+ touchTrigger,
315
+ isEnabled,
316
+ isControlled: isOpen !== undefined,
317
+ isOpen: layer.isOpen,
318
+ layerId: layer.id,
319
+ triggerRef,
320
+ show: showNow,
321
+ hide: hideNow,
322
+ });
323
+
276
324
  // Schedule show with delay (suppressed when isOpen is false)
277
325
  const scheduleShow = useCallback(() => {
278
326
  if (!isEnabled || isOpen === false) {
@@ -309,26 +357,38 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
309
357
 
310
358
  // Event handlers
311
359
  const handleMouseEnter = useCallback(() => {
312
- // Suppress tooltips on touch devices — hover is simulated and eats a tap
313
- if (
314
- typeof window !== 'undefined' &&
315
- typeof window.matchMedia === 'function' &&
316
- window.matchMedia('(hover: none)').matches
317
- ) {
360
+ // A tap synthesizes mouseenter. On touch the tap path owns the decision,
361
+ // so hover must not also fire — including the tap that just opened the
362
+ // tooltip, which would otherwise be double-handled.
363
+ if (touch.isTouchPointerRef.current) {
318
364
  return;
319
365
  }
320
366
  scheduleShow();
321
- }, [scheduleShow]);
367
+ }, [touch, scheduleShow]);
322
368
 
323
369
  const handleMouseLeave = useCallback(() => {
370
+ // On touch the synthesized mouseleave arrives with the next tap elsewhere,
371
+ // which the outside-tap dismissal already handles — and handling it here
372
+ // too would close the tooltip behind the tap-open bookkeeping's back.
373
+ if (touch.isTouchPointerRef.current) {
374
+ return;
375
+ }
324
376
  scheduleHide();
325
- }, [scheduleHide]);
377
+ }, [touch, scheduleHide]);
326
378
 
327
379
  const handleFocusIn = useCallback(
328
380
  (e: Event) => {
329
381
  if (!isEnabled) {
330
382
  return;
331
383
  }
384
+ // A tap focuses the trigger it activates, and `:focus-visible` does not
385
+ // filter that out on every element: a tapped `<input>` or contenteditable
386
+ // matches it. Those are exactly the action triggers `auto` just decided
387
+ // to keep shut, so without this the focus reopens what the tap
388
+ // suppressed, over the field the user is trying to type into.
389
+ if (touch.isTouchInteraction()) {
390
+ return;
391
+ }
332
392
  // Only show tooltip for keyboard focus (:focus-visible),
333
393
  // not programmatic focus (e.g. dialog auto-focus, touch tap)
334
394
  const target = e.target as HTMLElement;
@@ -338,7 +398,7 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
338
398
  clearTimeouts();
339
399
  layer.show();
340
400
  },
341
- [isEnabled, clearTimeouts, layer],
401
+ [isEnabled, touch, clearTimeouts, layer],
342
402
  );
343
403
 
344
404
  const handleFocusOut = useCallback(() => {
@@ -350,15 +410,24 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
350
410
  // just-pressed control reads as stale. Fires on pointerdown so it feels
351
411
  // immediate. Uncontrolled tooltips only — a controlled tooltip's visibility
352
412
  // is owned by the consumer. `layer.hide()` self-guards when already closed.
353
- const handlePointerDown = useCallback(() => {
354
- if (isOpen !== undefined) {
355
- return;
356
- }
357
- clearTimeouts();
358
- layer.hide();
359
- }, [isOpen, clearTimeouts, layer]);
413
+ // A touch press is a different gesture (it may be the only way to open the
414
+ // tooltip at all), so the touch path answers it first.
415
+ const handlePointerDown = useCallback(
416
+ (event: PointerEvent) => {
417
+ if (touch.handlePointerDown(event)) {
418
+ return;
419
+ }
420
+ if (isOpen !== undefined) {
421
+ return;
422
+ }
423
+ clearTimeouts();
424
+ layer.hide();
425
+ },
426
+ [touch, isOpen, clearTimeouts, layer],
427
+ );
360
428
 
361
429
  // Interaction ref that handles event listeners only
430
+ const {handlePointerEnter, clearTapOpen} = touch;
362
431
  const interactionRef: RefCallback<HTMLElement> = useCallback(
363
432
  (el: HTMLElement | null) => {
364
433
  // Cleanup previous element
@@ -367,6 +436,10 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
367
436
  triggerRef.current.removeEventListener('mouseleave', handleMouseLeave);
368
437
  triggerRef.current.removeEventListener('focusin', handleFocusIn);
369
438
  triggerRef.current.removeEventListener('focusout', handleFocusOut);
439
+ triggerRef.current.removeEventListener(
440
+ 'pointerenter',
441
+ handlePointerEnter,
442
+ );
370
443
  triggerRef.current.removeEventListener(
371
444
  'pointerdown',
372
445
  handlePointerDown,
@@ -374,10 +447,13 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
374
447
  }
375
448
 
376
449
  if (el) {
377
- // Attach hover listeners
450
+ // Attach hover listeners. `pointerenter` runs before the synthesized
451
+ // `mouseenter` a tap produces, which is what lets the hover path know
452
+ // it is looking at a finger.
453
+ el.addEventListener('pointerenter', handlePointerEnter);
378
454
  el.addEventListener('mouseenter', handleMouseEnter);
379
455
  el.addEventListener('mouseleave', handleMouseLeave);
380
- // Press-to-dismiss: activating the trigger hides its own tooltip.
456
+ // Press-to-dismiss on a mouse; tap-to-open on a touch pointer.
381
457
  el.addEventListener('pointerdown', handlePointerDown);
382
458
 
383
459
  // Attach focus listeners based on focusTrigger option
@@ -399,6 +475,7 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
399
475
  handleMouseLeave,
400
476
  handleFocusIn,
401
477
  handleFocusOut,
478
+ handlePointerEnter,
402
479
  handlePointerDown,
403
480
  ],
404
481
  );
@@ -461,13 +538,14 @@ export function useTooltip(options: TooltipOptions = {}): TooltipReturn {
461
538
  return;
462
539
  }
463
540
  clearTimeouts();
541
+ clearTapOpen();
464
542
  layer.hide();
465
543
  };
466
544
  document.addEventListener('keydown', handleKeyDown);
467
545
  return () => {
468
546
  document.removeEventListener('keydown', handleKeyDown);
469
547
  };
470
- }, [isOpen, layer, clearTimeouts]);
548
+ }, [isOpen, layer, clearTimeouts, clearTapOpen]);
471
549
 
472
550
  // Render function that wraps layer.render with tooltip styling
473
551
  const renderTooltip = useCallback(
@@ -72,7 +72,7 @@ const styles = stylex.create({
72
72
  fontSize: 'inherit',
73
73
  fontWeight: fontWeightVars['--font-weight-normal'],
74
74
  textAlign: 'start',
75
- ':hover': {
75
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
76
76
  '@media (hover: hover)': {
77
77
  backgroundColor: colorVars['--color-overlay-hover'],
78
78
  },
@@ -59,7 +59,7 @@ const styles = stylex.create({
59
59
  transitionTimingFunction: easeVars['--ease-standard'],
60
60
  backgroundColor: {
61
61
  default: 'transparent',
62
- ':hover': {
62
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
63
63
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
64
64
  },
65
65
  ':active': colorVars['--color-overlay-pressed'],
@@ -70,7 +70,7 @@ const styles = stylex.create({
70
70
  fontWeight: fontWeightVars['--font-weight-semibold'],
71
71
  backgroundColor: {
72
72
  default: colorVars['--color-neutral'],
73
- ':hover': {
73
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
74
74
  '@media (hover: hover)': colorVars['--color-neutral'],
75
75
  },
76
76
  ':active': colorVars['--color-neutral'],
@@ -84,7 +84,7 @@ const styles = stylex.create({
84
84
  transitionTimingFunction: easeVars['--ease-standard'],
85
85
  backgroundColor: {
86
86
  default: 'transparent',
87
- ':hover': {
87
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
88
88
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
89
89
  },
90
90
  },
@@ -56,7 +56,7 @@ const styles = stylex.create({
56
56
  transitionTimingFunction: easeVars['--ease-standard'],
57
57
  backgroundColor: {
58
58
  default: 'transparent',
59
- ':hover': {
59
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
60
60
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
61
61
  },
62
62
  ':active': colorVars['--color-overlay-pressed'],
@@ -71,7 +71,7 @@ const styles = stylex.create({
71
71
  transitionTimingFunction: easeVars['--ease-standard'],
72
72
  backgroundColor: {
73
73
  default: 'transparent',
74
- ':hover': {
74
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
75
75
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
76
76
  },
77
77
  },
@@ -121,7 +121,7 @@ const styles = stylex.create({
121
121
  transitionTimingFunction: easeVars['--ease-standard'],
122
122
  backgroundColor: {
123
123
  default: 'transparent',
124
- ':hover': {
124
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
125
125
  '@media (hover: hover)': colorVars['--color-overlay-hover'],
126
126
  },
127
127
  },
@@ -101,7 +101,7 @@ const styles = stylex.create({
101
101
  transitionTimingFunction: easeVars['--ease-standard'],
102
102
  backgroundImage: {
103
103
  default: null,
104
- ':hover': {
104
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
105
105
  '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
106
106
  },
107
107
  ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
@@ -32,13 +32,17 @@ export const styles = stylex.create({
32
32
  '--_hover-delay': '0s',
33
33
  '--_reveal-opacity': {
34
34
  default: 0,
35
- ':hover': {'@media (hover: hover)': 1},
35
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
36
+ '@media (hover: hover)': 1,
37
+ },
36
38
  ':focus-within': 1,
37
39
  '@media (any-pointer: coarse)': 1,
38
40
  },
39
41
  '--_reveal-position': {
40
42
  default: 'absolute',
41
- ':hover': {'@media (hover: hover)': 'static'},
43
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
44
+ '@media (hover: hover)': 'static',
45
+ },
42
46
  ':focus-within': 'static',
43
47
  '@media (any-pointer: coarse)': 'static',
44
48
  },
@@ -49,7 +53,9 @@ export const styles = stylex.create({
49
53
  // content would snap out of flow at full opacity and flicker.
50
54
  '--_reveal-delay': {
51
55
  default: REST_DELAY,
52
- ':hover': {'@media (hover: hover)': HOVER_DELAY + ', ' + HOVER_DELAY},
56
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
57
+ '@media (hover: hover)': HOVER_DELAY + ', ' + HOVER_DELAY,
58
+ },
53
59
  ':focus-within': '0s, 0s',
54
60
  '@media (any-pointer: coarse)': '0s, 0s',
55
61
  },
@@ -57,7 +63,9 @@ export const styles = stylex.create({
57
63
  // for) but keeps the dwell: an intent gate is timing, not motion.
58
64
  '--_reveal-delay-reduced': {
59
65
  default: '0s, 0s',
60
- ':hover': {'@media (hover: hover)': HOVER_DELAY + ', ' + HOVER_DELAY},
66
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
67
+ '@media (hover: hover)': HOVER_DELAY + ', ' + HOVER_DELAY,
68
+ },
61
69
  ':focus-within': '0s, 0s',
62
70
  '@media (any-pointer: coarse)': '0s, 0s',
63
71
  },
@@ -66,13 +74,17 @@ export const styles = stylex.create({
66
74
  // coarse-pointer branch (it stays visible on touch).
67
75
  '--_conceal-opacity': {
68
76
  default: 1,
69
- ':hover': {'@media (hover: hover)': 0},
77
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
78
+ '@media (hover: hover)': 0,
79
+ },
70
80
  },
71
81
  // Single-value dwell for the opacity-only blocks, which have no discrete
72
82
  // position to sequence.
73
83
  '--_fade-delay': {
74
84
  default: '0s',
75
- ':hover': {'@media (hover: hover)': HOVER_DELAY},
85
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
86
+ '@media (hover: hover)': HOVER_DELAY,
87
+ },
76
88
  ':focus-within': '0s',
77
89
  '@media (any-pointer: coarse)': '0s',
78
90
  },
@@ -87,35 +99,47 @@ export const styles = stylex.create({
87
99
  stateInactive: {
88
100
  '--_reveal-opacity': {
89
101
  default: 0,
90
- ':hover': {'@media (hover: hover)': 0},
102
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
103
+ '@media (hover: hover)': 0,
104
+ },
91
105
  ':focus-within': 1,
92
106
  '@media (any-pointer: coarse)': 1,
93
107
  },
94
108
  '--_reveal-position': {
95
109
  default: 'absolute',
96
- ':hover': {'@media (hover: hover)': 'absolute'},
110
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
111
+ '@media (hover: hover)': 'absolute',
112
+ },
97
113
  ':focus-within': 'static',
98
114
  '@media (any-pointer: coarse)': 'static',
99
115
  },
100
116
  '--_reveal-delay': {
101
117
  default: REST_DELAY,
102
- ':hover': {'@media (hover: hover)': REST_DELAY},
118
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
119
+ '@media (hover: hover)': REST_DELAY,
120
+ },
103
121
  ':focus-within': '0s, 0s',
104
122
  '@media (any-pointer: coarse)': '0s, 0s',
105
123
  },
106
124
  '--_reveal-delay-reduced': {
107
125
  default: '0s, 0s',
108
- ':hover': {'@media (hover: hover)': '0s, 0s'},
126
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
127
+ '@media (hover: hover)': '0s, 0s',
128
+ },
109
129
  ':focus-within': '0s, 0s',
110
130
  '@media (any-pointer: coarse)': '0s, 0s',
111
131
  },
112
132
  '--_conceal-opacity': {
113
133
  default: 1,
114
- ':hover': {'@media (hover: hover)': 1},
134
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
135
+ '@media (hover: hover)': 1,
136
+ },
115
137
  },
116
138
  '--_fade-delay': {
117
139
  default: '0s',
118
- ':hover': {'@media (hover: hover)': '0s'},
140
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
141
+ '@media (hover: hover)': '0s',
142
+ },
119
143
  ':focus-within': '0s',
120
144
  '@media (any-pointer: coarse)': '0s',
121
145
  },
@@ -126,35 +150,47 @@ export const styles = stylex.create({
126
150
  stateActive: {
127
151
  '--_reveal-opacity': {
128
152
  default: 1,
129
- ':hover': {'@media (hover: hover)': 1},
153
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
154
+ '@media (hover: hover)': 1,
155
+ },
130
156
  ':focus-within': 1,
131
157
  '@media (any-pointer: coarse)': 1,
132
158
  },
133
159
  '--_reveal-position': {
134
160
  default: 'static',
135
- ':hover': {'@media (hover: hover)': 'static'},
161
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
162
+ '@media (hover: hover)': 'static',
163
+ },
136
164
  ':focus-within': 'static',
137
165
  '@media (any-pointer: coarse)': 'static',
138
166
  },
139
167
  '--_reveal-delay': {
140
168
  default: '0s, 0s',
141
- ':hover': {'@media (hover: hover)': '0s, 0s'},
169
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
170
+ '@media (hover: hover)': '0s, 0s',
171
+ },
142
172
  ':focus-within': '0s, 0s',
143
173
  '@media (any-pointer: coarse)': '0s, 0s',
144
174
  },
145
175
  '--_reveal-delay-reduced': {
146
176
  default: '0s, 0s',
147
- ':hover': {'@media (hover: hover)': '0s, 0s'},
177
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
178
+ '@media (hover: hover)': '0s, 0s',
179
+ },
148
180
  ':focus-within': '0s, 0s',
149
181
  '@media (any-pointer: coarse)': '0s, 0s',
150
182
  },
151
183
  '--_conceal-opacity': {
152
184
  default: 0,
153
- ':hover': {'@media (hover: hover)': 0},
185
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
186
+ '@media (hover: hover)': 0,
187
+ },
154
188
  },
155
189
  '--_fade-delay': {
156
190
  default: '0s',
157
- ':hover': {'@media (hover: hover)': '0s'},
191
+ ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
192
+ '@media (hover: hover)': '0s',
193
+ },
158
194
  ':focus-within': '0s',
159
195
  '@media (any-pointer: coarse)': '0s',
160
196
  },
@@ -0,0 +1,167 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file scrollbarGutter.test.ts
5
+ * @input Uses vitest and a stubbed viewport + element box (jsdom does no layout)
6
+ * @output Unit tests for holdScrollbarGutter
7
+ * @position Testing; validates scrollbarGutter.ts implementation
8
+ *
9
+ * SYNC: When scrollbarGutter.ts changes, update tests to match new behavior
10
+ */
11
+
12
+ import {afterEach, describe, expect, it} from 'vitest';
13
+ import {holdScrollbarGutter} from './scrollbarGutter';
14
+
15
+ /**
16
+ * jsdom does no layout, so both halves of the measurement are stubbed: the
17
+ * viewport (`innerWidth` vs `documentElement.clientWidth`) and the element's
18
+ * own box, which `widths` reads from and a test flips to simulate the lock
19
+ * widening it.
20
+ */
21
+ function stub({
22
+ innerWidth,
23
+ clientWidth,
24
+ widths,
25
+ }: {
26
+ innerWidth: number;
27
+ clientWidth: number;
28
+ widths?: () => number;
29
+ }) {
30
+ Object.defineProperty(window, 'innerWidth', {
31
+ value: innerWidth,
32
+ configurable: true,
33
+ writable: true,
34
+ });
35
+ Object.defineProperty(document.documentElement, 'clientWidth', {
36
+ value: clientWidth,
37
+ configurable: true,
38
+ });
39
+ if (widths) {
40
+ document.body.getBoundingClientRect = () => ({width: widths()}) as DOMRect;
41
+ }
42
+ }
43
+
44
+ const rootGutter = () => document.documentElement.style.scrollbarGutter;
45
+
46
+ describe('holdScrollbarGutter', () => {
47
+ afterEach(() => {
48
+ document.body.style.cssText = '';
49
+ document.documentElement.style.cssText = '';
50
+ // @ts-expect-error -- drop the stubs so jsdom's own values come back
51
+ delete document.documentElement.clientWidth;
52
+ // @ts-expect-error -- restore the prototype's implementation
53
+ delete document.body.getBoundingClientRect;
54
+ });
55
+
56
+ it('holds the gutter open when a space-taking scrollbar is about to be hidden', () => {
57
+ // A 1024px window over a 1009px layout viewport = a 15px classic scrollbar.
58
+ stub({innerWidth: 1024, clientWidth: 1009});
59
+
60
+ const hold = holdScrollbarGutter(document.body);
61
+
62
+ expect(rootGutter()).toBe('stable');
63
+
64
+ hold.settle();
65
+ hold.release();
66
+
67
+ expect(rootGutter()).toBe('');
68
+ });
69
+
70
+ it('leaves an overlay scrollbar alone — there is no gutter to hold', () => {
71
+ stub({innerWidth: 1024, clientWidth: 1024});
72
+
73
+ const hold = holdScrollbarGutter(document.body);
74
+ hold.settle();
75
+
76
+ expect(rootGutter()).toBe('');
77
+ expect(document.body.style.paddingRight).toBe('');
78
+
79
+ hold.release();
80
+ });
81
+
82
+ it('does not pad when holding the gutter kept the element still', () => {
83
+ stub({innerWidth: 1024, clientWidth: 1009, widths: () => 1009});
84
+
85
+ const hold = holdScrollbarGutter(document.body);
86
+ hold.settle();
87
+
88
+ expect(rootGutter()).toBe('stable');
89
+ expect(document.body.style.paddingRight).toBe('');
90
+
91
+ hold.release();
92
+ });
93
+
94
+ it('falls back to padding when the element grew anyway', () => {
95
+ // What an engine without scrollbar-gutter support does: the gutter request
96
+ // is ignored, the scrollbar goes away, and the element widens by 15px.
97
+ let width = 1009;
98
+ stub({innerWidth: 1024, clientWidth: 1009, widths: () => width});
99
+
100
+ const hold = holdScrollbarGutter(document.body);
101
+ width = 1024;
102
+ hold.settle();
103
+
104
+ expect(document.body.style.paddingRight).toBe('15px');
105
+
106
+ hold.release();
107
+
108
+ expect(document.body.style.paddingRight).toBe('');
109
+ });
110
+
111
+ it("adds to the page's own padding instead of replacing it", () => {
112
+ let width = 1009;
113
+ stub({innerWidth: 1024, clientWidth: 1009, widths: () => width});
114
+ document.body.style.paddingRight = '24px';
115
+
116
+ const hold = holdScrollbarGutter(document.body);
117
+ width = 1024;
118
+ hold.settle();
119
+
120
+ expect(document.body.style.paddingRight).toBe('39px');
121
+
122
+ hold.release();
123
+
124
+ expect(document.body.style.paddingRight).toBe('24px');
125
+ });
126
+
127
+ it('restores the page\u2019s own scrollbar-gutter rather than clearing it', () => {
128
+ stub({innerWidth: 1024, clientWidth: 1009});
129
+ document.documentElement.style.scrollbarGutter = 'stable both-edges';
130
+
131
+ const hold = holdScrollbarGutter(document.body);
132
+
133
+ expect(rootGutter()).toBe('stable');
134
+
135
+ hold.settle();
136
+ hold.release();
137
+
138
+ expect(rootGutter()).toBe('stable both-edges');
139
+ });
140
+
141
+ it('settles once, however many times it is called', () => {
142
+ let width = 1009;
143
+ stub({innerWidth: 1024, clientWidth: 1009, widths: () => width});
144
+
145
+ const hold = holdScrollbarGutter(document.body);
146
+ width = 1024;
147
+ hold.settle();
148
+ hold.settle();
149
+ hold.settle();
150
+
151
+ expect(document.body.style.paddingRight).toBe('15px');
152
+
153
+ hold.release();
154
+ });
155
+
156
+ it('does nothing where there is no layout to measure', () => {
157
+ stub({innerWidth: 1024, clientWidth: 0});
158
+
159
+ const hold = holdScrollbarGutter(document.body);
160
+ hold.settle();
161
+
162
+ expect(rootGutter()).toBe('');
163
+ expect(document.body.style.paddingRight).toBe('');
164
+
165
+ hold.release();
166
+ });
167
+ });