@astryxdesign/core 0.4.4 → 0.4.5-canary.0472ec1

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 (522) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/AvatarGroup/AvatarGroupOverflow.js +1 -1
  3. package/dist/Blockquote/Blockquote.d.ts +4 -3
  4. package/dist/Blockquote/Blockquote.d.ts.map +1 -1
  5. package/dist/Blockquote/Blockquote.js +9 -10
  6. package/dist/BottomSheet/BottomSheet.d.ts +11 -2
  7. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheet.js +4 -0
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts +5 -2
  10. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  11. package/dist/BottomSheet/BottomSheetPanel.js +77 -18
  12. package/dist/BottomSheet/index.d.ts +1 -1
  13. package/dist/BottomSheet/index.d.ts.map +1 -1
  14. package/dist/BottomSheet/snapOffsets.d.ts +57 -20
  15. package/dist/BottomSheet/snapOffsets.d.ts.map +1 -1
  16. package/dist/BottomSheet/snapOffsets.js +106 -30
  17. package/dist/BottomSheet/useSheetGestures.d.ts +13 -8
  18. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  19. package/dist/BottomSheet/useSheetGestures.js +160 -64
  20. package/dist/Breadcrumbs/BreadcrumbItem.js +6 -6
  21. package/dist/Button/Button.js +5 -5
  22. package/dist/Calendar/Calendar.d.ts +17 -0
  23. package/dist/Calendar/Calendar.d.ts.map +1 -1
  24. package/dist/Calendar/Calendar.js +55 -15
  25. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts +14 -0
  26. package/dist/Calendar/getStandaloneShortWeekdayNames.d.ts.map +1 -0
  27. package/dist/Calendar/getStandaloneShortWeekdayNames.js +26 -0
  28. package/dist/Calendar/hooks/useCalendarConstraints.d.ts +22 -1
  29. package/dist/Calendar/hooks/useCalendarConstraints.d.ts.map +1 -1
  30. package/dist/Calendar/hooks/useCalendarConstraints.js +26 -4
  31. package/dist/Calendar/hooks/useCalendarDays.d.ts.map +1 -1
  32. package/dist/Calendar/hooks/useCalendarDays.js +14 -10
  33. package/dist/Calendar/hooks/useCalendarNavigation.d.ts.map +1 -1
  34. package/dist/Calendar/hooks/useCalendarNavigation.js +8 -4
  35. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts +47 -0
  36. package/dist/Calendar/standaloneShortWeekdayNames.generated.d.ts.map +1 -0
  37. package/dist/Calendar/standaloneShortWeekdayNames.generated.js +40 -0
  38. package/dist/Calendar/styles.js +3 -3
  39. package/dist/Center/Center.d.ts +23 -1
  40. package/dist/Center/Center.d.ts.map +1 -1
  41. package/dist/Center/Center.js +12 -6
  42. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposer.js +2 -2
  44. package/dist/Chat/ChatToolCalls.js +1 -1
  45. package/dist/Chat/useChatDictation.d.ts +1 -1
  46. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  47. package/dist/Chat/useSpeechRecognition.d.ts +1 -1
  48. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  49. package/dist/Chat/useSpeechRecognition.js +4 -3
  50. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  51. package/dist/CheckboxInput/CheckboxInput.js +9 -0
  52. package/dist/Citation/Citation.js +3 -3
  53. package/dist/ClickableCard/ClickableCard.js +2 -2
  54. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  56. package/dist/ComplexSelector/ComplexSelector.js +8 -3
  57. package/dist/DateInput/DateInput.d.ts +31 -2
  58. package/dist/DateInput/DateInput.d.ts.map +1 -1
  59. package/dist/DateInput/DateInput.js +92 -19
  60. package/dist/DateInput/MonthScroller.d.ts +35 -0
  61. package/dist/DateInput/MonthScroller.d.ts.map +1 -0
  62. package/dist/DateInput/MonthScroller.js +573 -0
  63. package/dist/DateInput/MonthYearWheels.d.ts +24 -0
  64. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -0
  65. package/dist/DateInput/MonthYearWheels.js +114 -0
  66. package/dist/DateInput/TouchDateField.d.ts +10 -0
  67. package/dist/DateInput/TouchDateField.d.ts.map +1 -0
  68. package/dist/DateInput/TouchDateField.js +810 -0
  69. package/dist/DateInput/Wheel.d.ts +41 -0
  70. package/dist/DateInput/Wheel.d.ts.map +1 -0
  71. package/dist/DateInput/Wheel.js +315 -0
  72. package/dist/DateInput/monthGeometry.d.ts +65 -0
  73. package/dist/DateInput/monthGeometry.d.ts.map +1 -0
  74. package/dist/DateInput/monthGeometry.js +98 -0
  75. package/dist/DateInput/tokens.stylex.d.ts +63 -0
  76. package/dist/DateInput/tokens.stylex.d.ts.map +1 -0
  77. package/dist/DateInput/tokens.stylex.js +74 -0
  78. package/dist/DateInput/useOwnScrollGesture.d.ts +41 -0
  79. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -0
  80. package/dist/DateInput/useOwnScrollGesture.js +202 -0
  81. package/dist/DateInput/usePointerDragScroll.d.ts +15 -0
  82. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -0
  83. package/dist/DateInput/usePointerDragScroll.js +216 -0
  84. package/dist/DateInput/useScrollSettle.d.ts +26 -0
  85. package/dist/DateInput/useScrollSettle.d.ts.map +1 -0
  86. package/dist/DateInput/useScrollSettle.js +149 -0
  87. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  88. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  89. package/dist/DateRangeInput/DateRangeInput.js +47 -9
  90. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  91. package/dist/DateTimeInput/DateTimeInput.js +18 -10
  92. package/dist/Dialog/Dialog.d.ts.map +1 -1
  93. package/dist/Dialog/Dialog.js +3 -3
  94. package/dist/Field/FieldLabel.d.ts +2 -1
  95. package/dist/Field/FieldLabel.d.ts.map +1 -1
  96. package/dist/Field/FieldLabel.js +21 -3
  97. package/dist/Field/InputClearButton.d.ts +2 -1
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +12 -1
  100. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  101. package/dist/Field/inputStyles.stylex.js +4 -4
  102. package/dist/FileInput/FileInput.d.ts.map +1 -1
  103. package/dist/FileInput/FileInput.js +2 -2
  104. package/dist/FormLayout/FormLayout.d.ts +24 -2
  105. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  106. package/dist/FormLayout/FormLayout.js +7 -2
  107. package/dist/FormLayout/FormLayoutContext.d.ts +15 -2
  108. package/dist/FormLayout/FormLayoutContext.d.ts.map +1 -1
  109. package/dist/FormLayout/FormLayoutContext.js +19 -4
  110. package/dist/FormLayout/index.d.ts +1 -1
  111. package/dist/FormLayout/index.d.ts.map +1 -1
  112. package/dist/HoverCard/HoverCard.d.ts +13 -3
  113. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  114. package/dist/HoverCard/HoverCard.js +2 -0
  115. package/dist/HoverCard/index.d.ts +1 -1
  116. package/dist/HoverCard/index.d.ts.map +1 -1
  117. package/dist/HoverCard/useHoverCard.d.ts +17 -2
  118. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  119. package/dist/HoverCard/useHoverCard.js +78 -9
  120. package/dist/Item/Item.d.ts +9 -1
  121. package/dist/Item/Item.d.ts.map +1 -1
  122. package/dist/Item/Item.js +46 -8
  123. package/dist/Layer/index.d.ts +4 -2
  124. package/dist/Layer/index.d.ts.map +1 -1
  125. package/dist/Layer/index.js +2 -0
  126. package/dist/Layer/useLayer.d.ts.map +1 -1
  127. package/dist/Layer/useLayer.js +3 -2
  128. package/dist/Layer/useTouchTrigger.d.ts +113 -0
  129. package/dist/Layer/useTouchTrigger.d.ts.map +1 -0
  130. package/dist/Layer/useTouchTrigger.js +241 -0
  131. package/dist/Layout/container.stylex.d.ts +4 -2
  132. package/dist/Layout/container.stylex.d.ts.map +1 -1
  133. package/dist/Layout/container.stylex.js +19 -11
  134. package/dist/Layout/index.d.ts +1 -0
  135. package/dist/Layout/index.d.ts.map +1 -1
  136. package/dist/Layout/index.js +4 -0
  137. package/dist/Layout/padding.stylex.d.ts +315 -12
  138. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  139. package/dist/Layout/padding.stylex.js +389 -12
  140. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  141. package/dist/Lightbox/Lightbox.js +2 -1
  142. package/dist/Link/Link.js +4 -4
  143. package/dist/Markdown/Markdown.d.ts +8 -0
  144. package/dist/Markdown/Markdown.d.ts.map +1 -1
  145. package/dist/Markdown/Markdown.js +31 -3
  146. package/dist/Markdown/parser.d.ts +14 -2
  147. package/dist/Markdown/parser.d.ts.map +1 -1
  148. package/dist/Markdown/parser.js +50 -2
  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 -3
  154. package/dist/NavItem/navItemStyles.stylex.d.ts +2 -2
  155. package/dist/NavItem/navItemStyles.stylex.js +2 -2
  156. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  157. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  158. package/dist/NumberInput/NumberInput.js +38 -5
  159. package/dist/Outline/Outline.js +2 -2
  160. package/dist/Outline/parseOutlineFromMarkdown.d.ts +2 -0
  161. package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
  162. package/dist/Outline/parseOutlineFromMarkdown.js +5 -31
  163. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  164. package/dist/PowerSearch/PowerSearch.js +11 -12
  165. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  166. package/dist/PowerSearch/PowerSearchToken.js +3 -1
  167. package/dist/PowerSearch/formatFilterValue.d.ts +11 -2
  168. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  169. package/dist/PowerSearch/formatFilterValue.js +23 -8
  170. package/dist/RadioList/RadioList.d.ts.map +1 -1
  171. package/dist/RadioList/RadioList.js +12 -1
  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 +68 -38
  175. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  176. package/dist/Resizable/ResizeHandle.js +3 -3
  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 +6 -1
  182. package/dist/SelectableCard/SelectableCard.js +1 -1
  183. package/dist/Selector/Selector.d.ts +26 -1
  184. package/dist/Selector/Selector.d.ts.map +1 -1
  185. package/dist/Selector/Selector.js +95 -17
  186. package/dist/Selector/SelectorOption.d.ts +14 -1
  187. package/dist/Selector/SelectorOption.d.ts.map +1 -1
  188. package/dist/Selector/SelectorOption.js +6 -0
  189. package/dist/Selector/SelectorRowLayoutContext.d.ts +8 -0
  190. package/dist/Selector/SelectorRowLayoutContext.d.ts.map +1 -0
  191. package/dist/Selector/SelectorRowLayoutContext.js +27 -0
  192. package/dist/Selector/types.d.ts +1 -0
  193. package/dist/Selector/types.d.ts.map +1 -1
  194. package/dist/SideNav/SideNavHeading.js +1 -1
  195. package/dist/SideNav/SideNavItem.js +1 -1
  196. package/dist/Slider/Slider.d.ts.map +1 -1
  197. package/dist/Slider/Slider.js +3 -3
  198. package/dist/Stack/Stack.d.ts +23 -1
  199. package/dist/Stack/Stack.d.ts.map +1 -1
  200. package/dist/Stack/Stack.js +12 -6
  201. package/dist/StatusDot/StatusDot.d.ts +43 -3
  202. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  203. package/dist/StatusDot/StatusDot.js +42 -5
  204. package/dist/Switch/Switch.d.ts.map +1 -1
  205. package/dist/Switch/Switch.js +9 -0
  206. package/dist/TabList/TabMenu.js +1 -1
  207. package/dist/Table/TableRow.js +4 -4
  208. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  209. package/dist/Table/plugins/columnResize/useTableColumnResize.js +1 -1
  210. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  211. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +1 -1
  212. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  213. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  214. package/dist/Table/plugins/sortable/useTableSortableState.d.ts +5 -3
  215. package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
  216. package/dist/Table/plugins/sortable/useTableSortableState.js +12 -9
  217. package/dist/Table/plugins/tree/useTableTreeData.js +2 -2
  218. package/dist/TextArea/TextArea.d.ts.map +1 -1
  219. package/dist/TextArea/TextArea.js +6 -1
  220. package/dist/TextInput/TextInput.d.ts.map +1 -1
  221. package/dist/TextInput/TextInput.js +6 -1
  222. package/dist/Thumbnail/Thumbnail.js +1 -1
  223. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  224. package/dist/TimeInput/TimeInput.js +6 -1
  225. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  226. package/dist/Timestamp/Timestamp.js +6 -4
  227. package/dist/Timestamp/formatInstant.d.ts +3 -2
  228. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  229. package/dist/Timestamp/formatInstant.js +8 -8
  230. package/dist/Timestamp/tooltipEntries.d.ts +2 -1
  231. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  232. package/dist/Timestamp/tooltipEntries.js +5 -3
  233. package/dist/Token/Token.js +1 -1
  234. package/dist/Tooltip/Tooltip.d.ts +16 -3
  235. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  236. package/dist/Tooltip/Tooltip.js +2 -0
  237. package/dist/Tooltip/index.d.ts +1 -1
  238. package/dist/Tooltip/index.d.ts.map +1 -1
  239. package/dist/Tooltip/useTooltip.d.ts +21 -2
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +71 -13
  242. package/dist/TopNav/TopNavHeading.js +2 -2
  243. package/dist/TopNav/TopNavItem.js +2 -2
  244. package/dist/TopNav/TopNavMegaMenu.js +1 -1
  245. package/dist/TopNav/TopNavMegaMenuItem.js +1 -1
  246. package/dist/TopNav/TopNavMenu.js +2 -2
  247. package/dist/TreeList/TreeListItem.js +1 -1
  248. package/dist/astryx.css +123 -60
  249. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -1
  250. package/dist/hooks/containerReveal.stylex.js +18 -18
  251. package/dist/hooks/scrollbarGutter.d.ts +28 -0
  252. package/dist/hooks/scrollbarGutter.d.ts.map +1 -0
  253. package/dist/hooks/scrollbarGutter.js +103 -0
  254. package/dist/hooks/useResolvedRequired.d.ts +19 -0
  255. package/dist/hooks/useResolvedRequired.d.ts.map +1 -0
  256. package/dist/hooks/useResolvedRequired.js +40 -0
  257. package/dist/hooks/useScrollLock.d.ts +4 -0
  258. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  259. package/dist/hooks/useScrollLock.js +13 -1
  260. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  261. package/dist/i18n/InternationalizationContext.js +2 -0
  262. package/dist/i18n/index.d.ts +6 -0
  263. package/dist/i18n/index.d.ts.map +1 -1
  264. package/dist/i18n/index.js +6 -0
  265. package/dist/i18n/useCollator.d.ts +12 -0
  266. package/dist/i18n/useCollator.d.ts.map +1 -0
  267. package/dist/i18n/useCollator.js +40 -0
  268. package/dist/i18n/useLocale.d.ts +3 -0
  269. package/dist/i18n/useLocale.d.ts.map +1 -0
  270. package/dist/i18n/useLocale.js +31 -0
  271. package/dist/theme/defineTheme.d.ts +5 -0
  272. package/dist/theme/defineTheme.d.ts.map +1 -1
  273. package/dist/theme/defineTheme.js +5 -0
  274. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  275. package/dist/theme/derivedVarRegistry.js +7 -0
  276. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  277. package/dist/theme/generateThemeRules.js +66 -5
  278. package/dist/theme/syntax/index.d.ts +6 -0
  279. package/dist/theme/syntax/index.d.ts.map +1 -1
  280. package/dist/theme/syntax/index.js +7 -2
  281. package/dist/utils/dateParser.d.ts +4 -3
  282. package/dist/utils/dateParser.d.ts.map +1 -1
  283. package/dist/utils/dateParser.js +8 -8
  284. package/dist/utils/index.d.ts +1 -1
  285. package/dist/utils/index.d.ts.map +1 -1
  286. package/dist/utils/index.js +1 -1
  287. package/dist/utils/plainDate.d.ts +11 -2
  288. package/dist/utils/plainDate.d.ts.map +1 -1
  289. package/dist/utils/plainDate.js +32 -4
  290. package/locales/af-ZA.json +958 -0
  291. package/locales/ar-SA.json +998 -0
  292. package/locales/ca-ES.json +974 -0
  293. package/locales/cs-CZ.json +990 -0
  294. package/locales/da-DK.json +966 -0
  295. package/locales/de-DE.json +966 -0
  296. package/locales/el-GR.json +990 -0
  297. package/locales/en.json +32 -0
  298. package/locales/es-ES.json +978 -0
  299. package/locales/fi-FI.json +990 -0
  300. package/locales/fr-FR.json +928 -0
  301. package/locales/he-IL.json +990 -0
  302. package/locales/hu-HU.json +990 -0
  303. package/locales/it-IT.json +978 -0
  304. package/locales/ja-JP.json +998 -0
  305. package/locales/ko-KR.json +990 -0
  306. package/locales/nl-NL.json +954 -0
  307. package/locales/no-NO.json +978 -0
  308. package/locales/pl-PL.json +982 -0
  309. package/locales/pseudo.json +24 -0
  310. package/locales/pt-BR.json +982 -0
  311. package/locales/pt-PT.json +982 -0
  312. package/locales/ro-RO.json +982 -0
  313. package/locales/ru-RU.json +990 -0
  314. package/locales/sr-SP.json +990 -0
  315. package/locales/sv-SE.json +986 -0
  316. package/locales/tr-TR.json +990 -0
  317. package/locales/uk-UA.json +990 -0
  318. package/locales/vi-VN.json +986 -0
  319. package/locales/zh-CN.json +998 -0
  320. package/locales/zh-TW.json +998 -0
  321. package/package.json +3 -3
  322. package/src/AvatarGroup/AvatarGroupOverflow.tsx +1 -1
  323. package/src/Banner/Banner.doc.mjs +2 -0
  324. package/src/Blockquote/Blockquote.doc.mjs +30 -7
  325. package/src/Blockquote/Blockquote.test.tsx +34 -3
  326. package/src/Blockquote/Blockquote.tsx +11 -9
  327. package/src/BottomSheet/BottomSheet.doc.mjs +36 -3
  328. package/src/BottomSheet/BottomSheet.test.tsx +177 -2
  329. package/src/BottomSheet/BottomSheet.tsx +16 -2
  330. package/src/BottomSheet/BottomSheetPanel.test.tsx +91 -0
  331. package/src/BottomSheet/BottomSheetPanel.tsx +121 -15
  332. package/src/BottomSheet/index.ts +5 -1
  333. package/src/BottomSheet/snapOffsets.test.ts +114 -47
  334. package/src/BottomSheet/snapOffsets.ts +119 -33
  335. package/src/BottomSheet/useSheetGestures.test.ts +113 -15
  336. package/src/BottomSheet/useSheetGestures.ts +196 -100
  337. package/src/Breadcrumbs/BreadcrumbItem.tsx +1 -1
  338. package/src/Button/Button.tsx +7 -7
  339. package/src/Calendar/Calendar.doc.mjs +16 -0
  340. package/src/Calendar/Calendar.test.tsx +137 -0
  341. package/src/Calendar/Calendar.tsx +80 -11
  342. package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +54 -0
  343. package/src/Calendar/getStandaloneShortWeekdayNames.ts +38 -0
  344. package/src/Calendar/hooks/useCalendarConstraints.ts +54 -3
  345. package/src/Calendar/hooks/useCalendarDays.ts +13 -10
  346. package/src/Calendar/hooks/useCalendarNavigation.ts +6 -4
  347. package/src/Calendar/standaloneShortWeekdayNames.generated.ts +50 -0
  348. package/src/Calendar/styles.ts +3 -3
  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 +7 -4
  353. package/src/Chat/ChatToolCalls.tsx +1 -1
  354. package/src/Chat/{useChatDictation.test.ts → useChatDictation.test.tsx} +29 -14
  355. package/src/Chat/useChatDictation.ts +1 -1
  356. package/src/Chat/useSpeechRecognition.ts +5 -3
  357. package/src/CheckboxInput/CheckboxInput.tsx +6 -0
  358. package/src/Citation/Citation.tsx +3 -3
  359. package/src/ClickableCard/ClickableCard.tsx +2 -2
  360. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  361. package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -0
  362. package/src/ComplexSelector/ComplexSelector.tsx +8 -5
  363. package/src/DateInput/DateInput.test.tsx +17 -1
  364. package/src/DateInput/DateInput.tsx +90 -19
  365. package/src/DateInput/DateInputTouch.test.tsx +2393 -0
  366. package/src/DateInput/MonthScroller.tsx +784 -0
  367. package/src/DateInput/MonthYearWheels.tsx +147 -0
  368. package/src/DateInput/TouchDateField.tsx +1214 -0
  369. package/src/DateInput/Wheel.tsx +470 -0
  370. package/src/DateInput/monthGeometry.ts +118 -0
  371. package/src/DateInput/tokens.stylex.ts +99 -0
  372. package/src/DateInput/useOwnScrollGesture.ts +227 -0
  373. package/src/DateInput/usePointerDragScroll.ts +220 -0
  374. package/src/DateInput/useScrollSettle.ts +164 -0
  375. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  376. package/src/DateRangeInput/DateRangeInput.test.tsx +81 -1
  377. package/src/DateRangeInput/DateRangeInput.tsx +88 -8
  378. package/src/DateTimeInput/DateTimeInput.tsx +23 -8
  379. package/src/Dialog/Dialog.test.tsx +24 -0
  380. package/src/Dialog/Dialog.tsx +3 -0
  381. package/src/Field/Field.doc.mjs +3 -0
  382. package/src/Field/FieldLabel.tsx +20 -4
  383. package/src/Field/InputClearButton.test.tsx +134 -2
  384. package/src/Field/InputClearButton.tsx +42 -1
  385. package/src/Field/inputStyles.stylex.ts +16 -12
  386. package/src/FileInput/FileInput.tsx +5 -4
  387. package/src/FormLayout/FormLayout.doc.mjs +13 -0
  388. package/src/FormLayout/FormLayout.test.tsx +181 -4
  389. package/src/FormLayout/FormLayout.tsx +33 -2
  390. package/src/FormLayout/FormLayoutContext.ts +22 -7
  391. package/src/FormLayout/index.ts +1 -1
  392. package/src/HoverCard/HoverCard.doc.mjs +18 -0
  393. package/src/HoverCard/HoverCard.test.tsx +189 -0
  394. package/src/HoverCard/HoverCard.tsx +22 -2
  395. package/src/HoverCard/index.ts +1 -0
  396. package/src/HoverCard/useHoverCard.doc.mjs +2 -0
  397. package/src/HoverCard/useHoverCard.tsx +103 -7
  398. package/src/Item/Item.doc.mjs +1 -0
  399. package/src/Item/Item.test.tsx +50 -0
  400. package/src/Item/Item.tsx +41 -2
  401. package/src/Layer/index.ts +10 -0
  402. package/src/Layer/useLayer.tsx +13 -2
  403. package/src/Layer/useTouchTrigger.test.ts +70 -0
  404. package/src/Layer/useTouchTrigger.ts +366 -0
  405. package/src/Layout/Layout.doc.mjs +18 -0
  406. package/src/Layout/container.stylex.ts +11 -3
  407. package/src/Layout/index.ts +4 -0
  408. package/src/Layout/overlayPaddingReset.test.tsx +222 -0
  409. package/src/Layout/padding.stylex.ts +192 -12
  410. package/src/Lightbox/Lightbox.tsx +2 -1
  411. package/src/Link/Link.tsx +4 -4
  412. package/src/Markdown/Markdown.doc.mjs +3 -0
  413. package/src/Markdown/Markdown.test.tsx +78 -0
  414. package/src/Markdown/Markdown.tsx +41 -1
  415. package/src/Markdown/parser.ts +60 -2
  416. package/src/MobileNav/MobileNav.tsx +61 -5
  417. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +212 -0
  418. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +109 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +1 -1
  420. package/src/MultiSelector/MultiSelector.tsx +8 -5
  421. package/src/NavItem/navItemStyles.stylex.ts +2 -2
  422. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  423. package/src/NumberInput/NumberInput.doc.mjs +24 -0
  424. package/src/NumberInput/NumberInput.test.tsx +138 -0
  425. package/src/NumberInput/NumberInput.tsx +44 -6
  426. package/src/Outline/Outline.doc.mjs +2 -0
  427. package/src/Outline/Outline.tsx +1 -1
  428. package/src/Outline/parseOutlineFromMarkdown.ts +10 -40
  429. package/src/PowerSearch/PowerSearch.tsx +17 -8
  430. package/src/PowerSearch/PowerSearchToken.tsx +3 -0
  431. package/src/PowerSearch/formatFilterValue.test.ts +22 -2
  432. package/src/PowerSearch/formatFilterValue.ts +31 -6
  433. package/src/RadioList/RadioList.doc.mjs +1 -1
  434. package/src/RadioList/RadioList.test.tsx +170 -2
  435. package/src/RadioList/RadioList.tsx +9 -1
  436. package/src/RadioList/RadioListItem.tsx +83 -51
  437. package/src/Resizable/ResizeHandle.test.tsx +22 -0
  438. package/src/Resizable/ResizeHandle.tsx +9 -3
  439. package/src/Section/Section.doc.mjs +55 -0
  440. package/src/Section/Section.test.tsx +201 -4
  441. package/src/Section/Section.tsx +69 -0
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -2
  444. package/src/SelectableCard/SelectableCard.tsx +1 -1
  445. package/src/Selector/Selector.doc.mjs +16 -2
  446. package/src/Selector/Selector.test.tsx +525 -1
  447. package/src/Selector/Selector.tsx +166 -21
  448. package/src/Selector/SelectorOption.doc.mjs +7 -0
  449. package/src/Selector/SelectorOption.tsx +21 -0
  450. package/src/Selector/SelectorRowLayoutContext.ts +32 -0
  451. package/src/Selector/types.ts +4 -4
  452. package/src/SideNav/SideNavHeading.tsx +2 -2
  453. package/src/SideNav/SideNavItem.tsx +1 -1
  454. package/src/Slider/Slider.tsx +11 -1
  455. package/src/Stack/Stack.doc.mjs +72 -0
  456. package/src/Stack/Stack.test.tsx +156 -0
  457. package/src/Stack/Stack.tsx +50 -8
  458. package/src/StatusDot/StatusDot.doc.mjs +13 -0
  459. package/src/StatusDot/StatusDot.test.tsx +115 -8
  460. package/src/StatusDot/StatusDot.tsx +74 -9
  461. package/src/Switch/Switch.tsx +6 -0
  462. package/src/TabList/TabMenu.tsx +1 -1
  463. package/src/Table/TableRow.tsx +4 -4
  464. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +2 -1
  465. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +2 -1
  466. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +22 -4
  467. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +34 -0
  468. package/src/Table/plugins/sortable/useTableSortableState.tsx +26 -23
  469. package/src/Table/plugins/tree/useTableTreeData.tsx +2 -2
  470. package/src/TextArea/TextArea.doc.mjs +1 -1
  471. package/src/TextArea/TextArea.tsx +3 -1
  472. package/src/TextInput/TextInput.tsx +3 -1
  473. package/src/Thumbnail/Thumbnail.tsx +1 -1
  474. package/src/TimeInput/TimeInput.tsx +3 -1
  475. package/src/Timestamp/Timestamp.test.tsx +27 -7
  476. package/src/Timestamp/Timestamp.tsx +8 -4
  477. package/src/Timestamp/formatInstant.ts +9 -7
  478. package/src/Timestamp/tooltipEntries.test.ts +11 -6
  479. package/src/Timestamp/tooltipEntries.ts +6 -2
  480. package/src/Token/Token.tsx +1 -1
  481. package/src/Tooltip/Tooltip.doc.mjs +18 -0
  482. package/src/Tooltip/Tooltip.test.tsx +254 -1
  483. package/src/Tooltip/Tooltip.tsx +22 -2
  484. package/src/Tooltip/index.ts +1 -0
  485. package/src/Tooltip/useTooltip.doc.mjs +2 -0
  486. package/src/Tooltip/useTooltip.tsx +99 -21
  487. package/src/TopNav/TopNavHeading.tsx +1 -1
  488. package/src/TopNav/TopNavItem.tsx +2 -2
  489. package/src/TopNav/TopNavMegaMenu.tsx +1 -1
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +1 -1
  491. package/src/TopNav/TopNavMenu.tsx +2 -2
  492. package/src/TreeList/TreeListItem.tsx +1 -1
  493. package/src/hooks/containerReveal.stylex.ts +54 -18
  494. package/src/hooks/scrollbarGutter.test.ts +167 -0
  495. package/src/hooks/scrollbarGutter.ts +121 -0
  496. package/src/hooks/useResolvedRequired.ts +42 -0
  497. package/src/hooks/useScrollLock.doc.mjs +4 -4
  498. package/src/hooks/useScrollLock.test.ts +73 -0
  499. package/src/hooks/useScrollLock.ts +14 -0
  500. package/src/i18n/InternationalizationContext.ts +2 -0
  501. package/src/i18n/__tests__/e2e-powersearch.test.tsx +37 -1
  502. package/src/i18n/__tests__/useCollator.test.tsx +115 -0
  503. package/src/i18n/__tests__/useLocale.test.tsx +52 -0
  504. package/src/i18n/index.ts +6 -0
  505. package/src/i18n/useCollator.doc.mjs +84 -0
  506. package/src/i18n/useCollator.ts +44 -0
  507. package/src/i18n/useLocale.doc.mjs +70 -0
  508. package/src/i18n/useLocale.ts +34 -0
  509. package/src/i18n/useTranslator.doc.mjs +1 -1
  510. package/src/theme/defineTheme.test.ts +9 -3
  511. package/src/theme/defineTheme.ts +5 -0
  512. package/src/theme/derivedVarRegistry.test.ts +19 -5
  513. package/src/theme/derivedVarRegistry.ts +4 -0
  514. package/src/theme/generateThemeRules.test.ts +118 -0
  515. package/src/theme/generateThemeRules.ts +83 -5
  516. package/src/theme/syntax/index.ts +6 -2
  517. package/src/theme/syntax/serverSafeSyntax.test.ts +68 -0
  518. package/src/utils/dateParser.test.ts +20 -0
  519. package/src/utils/dateParser.ts +14 -7
  520. package/src/utils/index.ts +2 -0
  521. package/src/utils/plainDate.test.ts +56 -0
  522. package/src/utils/plainDate.ts +33 -4
@@ -237,4 +237,95 @@ describe('BottomSheetPanel', () => {
237
237
  expect(panelRef).toHaveBeenLastCalledWith(null);
238
238
  expect(panelRef).toHaveBeenCalledTimes(2);
239
239
  });
240
+
241
+ it('floats the handle bar over content that starts at the sheet top edge', () => {
242
+ renderPanel({kind: 'open', entering: false});
243
+
244
+ const bar = getPanel().querySelector<HTMLElement>(
245
+ 'div[aria-hidden="true"]',
246
+ );
247
+ if (bar == null) {
248
+ throw new Error('handle bar not found');
249
+ }
250
+ const body = bar.nextElementSibling as HTMLElement;
251
+
252
+ // Out of flow: the bar costs the content no layout space. That is the
253
+ // point of it -- the content sits closer to the sheet's top edge, and
254
+ // scrolled content passes beneath the pill rather than stopping at a
255
+ // hard edge.
256
+ expect(getComputedStyle(bar).position).toBe('absolute');
257
+
258
+ // So the body must not reserve room for it. A top padding here would
259
+ // push the content back down and undo the change.
260
+ expect(getComputedStyle(body).paddingTop).toBe('');
261
+ });
262
+
263
+ // The pill is only legible over the content riding up beneath it because
264
+ // the bar carries a backdrop; without it, text runs straight through the
265
+ // pill. jsdom's StyleX runtime does not inject the rule, so assert on the
266
+ // style definition itself (same approach as AspectRatio's reset.css tests).
267
+ it('backs the floating handle bar with a surface gradient', async () => {
268
+ const fs = await import('fs');
269
+ const path = await import('path');
270
+ const source = fs.readFileSync(
271
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
272
+ 'utf-8',
273
+ );
274
+
275
+ const handleBar = source.match(/handleBar: \{([\s\S]*?)\n {2}\},/);
276
+ expect(handleBar).not.toBeNull();
277
+ expect(handleBar![1]).toContain("position: 'absolute'");
278
+ expect(handleBar![1]).toContain('linear-gradient(to bottom');
279
+ expect(handleBar![1]).toContain("colorVars['--color-background-surface']");
280
+ });
281
+
282
+ // In dark mode the surface fill and the scrim sit a few RGB steps apart and
283
+ // the drop shadow is black on near-black, so the fill alone leaves the
284
+ // sheet's left and right edges invisible against the scrim. A hairline on
285
+ // the scrim-facing edges is what draws them. Asserted on the style
286
+ // definition for the same reason as the test above.
287
+ it('draws a hairline on the three edges that face the scrim', async () => {
288
+ const fs = await import('fs');
289
+ const path = await import('path');
290
+ const source = fs.readFileSync(
291
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
292
+ 'utf-8',
293
+ );
294
+
295
+ const sheet = source.match(/\n {2}sheet: \{([\s\S]*?)\n {2}\},/);
296
+ expect(sheet).not.toBeNull();
297
+ for (const edge of [
298
+ 'borderBlockStart',
299
+ 'borderInlineStart',
300
+ 'borderInlineEnd',
301
+ ]) {
302
+ expect(sheet![1]).toContain(`${edge}Width: borderVars['--border-width']`);
303
+ expect(sheet![1]).toContain(`${edge}Style: 'solid'`);
304
+ expect(sheet![1]).toContain(`${edge}Color: colorVars['--color-border']`);
305
+ }
306
+ // The block-end edge sits below the viewport, under the overscroll
307
+ // padding, so it carries no hairline to draw.
308
+ expect(sheet![1]).not.toContain('borderBlockEndWidth');
309
+ });
310
+
311
+ // A theme that packs an inset ring into --shadow-high (the bundled themes all
312
+ // add one in dark mode) draws it just inside the sheet, where an opaque
313
+ // content wrapper such as Section paints over it -- so it showed only in the
314
+ // gap below the content and the side edges appeared to change width partway
315
+ // down. The scrolling body paints the surface across the whole inner box to
316
+ // hide the ring evenly.
317
+ it('paints the surface across the scrolling body so the edge stays uniform', async () => {
318
+ const fs = await import('fs');
319
+ const path = await import('path');
320
+ const source = fs.readFileSync(
321
+ path.resolve(__dirname, './BottomSheetPanel.tsx'),
322
+ 'utf-8',
323
+ );
324
+
325
+ const body = source.match(/\n {2}body: \{([\s\S]*?)\n {2}\},/);
326
+ expect(body).not.toBeNull();
327
+ expect(body![1]).toContain(
328
+ "backgroundColor: colorVars['--color-background-surface']",
329
+ );
330
+ });
240
331
  });
@@ -16,6 +16,7 @@
16
16
  * SYNC: When modified, update these files to stay in sync:
17
17
  * - /packages/core/src/BottomSheet/BottomSheet.tsx
18
18
  * - /packages/core/src/BottomSheet/BottomSheetPanel.test.tsx
19
+ * - /packages/core/src/BottomSheet/snapOffsets.ts
19
20
  * - /packages/core/src/BottomSheet/useMobileKeyboard.ts
20
21
  * - /packages/core/src/BottomSheet/useSheetGestures.ts
21
22
  */
@@ -25,12 +26,15 @@ import {
25
26
  useEffect,
26
27
  useImperativeHandle,
27
28
  useLayoutEffect,
29
+ useMemo,
28
30
  useRef,
29
31
  type ReactNode,
30
32
  } from 'react';
31
33
  import * as stylex from '@stylexjs/stylex';
32
34
  import type {BaseProps} from '../BaseProps';
35
+ import {useDevWarning} from '../hooks';
33
36
  import {
37
+ borderVars,
34
38
  colorVars,
35
39
  durationVars,
36
40
  easeVars,
@@ -40,11 +44,15 @@ import {
40
44
  spacingVars,
41
45
  } from '../theme/tokens.stylex';
42
46
  import {mergeProps, themeProps} from '../utils';
47
+ import {overlayPaddingReset} from '../Layout/padding.stylex';
48
+ import {
49
+ isValidSnapPoint,
50
+ resolveSnapPoints,
51
+ type BottomSheetSnapPoint,
52
+ } from './snapOffsets';
43
53
  import {useMobileKeyboard} from './useMobileKeyboard';
44
54
  import {useSheetGestures} from './useSheetGestures';
45
55
 
46
- const SNAP_FRACTIONS = [0.14, 0.5, 0.92];
47
-
48
56
  const HEIGHT_BUDGETS = {
49
57
  hug: '92dvh',
50
58
  capped: '62dvh',
@@ -52,35 +60,75 @@ const HEIGHT_BUDGETS = {
52
60
  } as const;
53
61
 
54
62
  export type BottomSheetHeight = keyof typeof HEIGHT_BUDGETS;
63
+ export type {BottomSheetSnapPoint};
55
64
 
56
65
  // SYNC: must match OVERSCROLL_MAX in useSheetGestures.ts.
57
66
  const OVERSCROLL_PADDING = 48;
58
67
  const MOBILE_KEYBOARD_BOTTOM_CLEARANCE = 48;
59
68
  const TRANSITION_BACKSTOP_BUFFER_MS = 50;
69
+ // The floating handle bar's height. The bar is out of flow, so this is not
70
+ // layout space the content pays for -- the pill (4px, centered) lands 10-14px
71
+ // from the sheet's top edge, inside the space a content wrapper's own top
72
+ // padding already provides.
73
+ const HANDLE_BAR_HEIGHT = spacingVars['--spacing-6'];
74
+
75
+ // Measure the same viewport the height budgets are written against. Those are
76
+ // `dvh`, which the virtual keyboard does not shrink, so reading
77
+ // `visualViewport` here would make the two disagree by exactly the keyboard's
78
+ // height: every detent would move while the sheet it measures did not. A
79
+ // keyboard is `useMobileKeyboard`'s business — it holds the sheet still and
80
+ // scrolls the body — and it does not redefine the sheet's detents.
81
+ function layoutViewportHeight(): number {
82
+ return typeof window === 'undefined' ? 0 : window.innerHeight;
83
+ }
60
84
 
61
- function defaultSnapHeights(): number[] {
62
- if (typeof window === 'undefined') {
63
- return [];
85
+ /**
86
+ * A stable identity for a set of snap points. Type-tagged, so the fraction
87
+ * `0.5` and the (invalid) string `'0.5'` cannot collide on one key.
88
+ *
89
+ * Recomputed every render — the panel re-renders on every frame of a drag — so
90
+ * it stays a single pass over a handful of values, and everything derived from
91
+ * the points hangs off it instead of being rebuilt per frame.
92
+ */
93
+ function snapPointsKeyFor(
94
+ points: ReadonlyArray<BottomSheetSnapPoint> | undefined,
95
+ ): string {
96
+ let key = '';
97
+ for (const point of points ?? []) {
98
+ key += `${typeof point}:${point}|`;
64
99
  }
65
- // Measure the same viewport the height budgets are written against. Those
66
- // are `dvh`, which the virtual keyboard does not shrink, so reading
67
- // `visualViewport` here made the two disagree by exactly the keyboard's
68
- // height: every detent moved while the sheet it was measuring did not. A
69
- // keyboard is `useMobileKeyboard`'s business — it holds the sheet still and
70
- // scrolls the body — and it does not redefine the sheet's detents.
71
- return SNAP_FRACTIONS.map(fraction => fraction * window.innerHeight);
100
+ return key;
72
101
  }
73
102
 
74
103
  const styles = stylex.create({
75
104
  sheet: {
76
105
  pointerEvents: 'auto',
77
106
  boxSizing: 'border-box',
107
+ // Containing block for the floating handle bar, which is lifted out of
108
+ // flow so the scrolling body reaches the sheet's top edge.
109
+ position: 'relative',
78
110
  display: 'flex',
79
111
  flexDirection: 'column',
80
112
  minHeight: 0,
81
113
  width: '100%',
82
114
  maxWidth: 640,
83
115
  backgroundColor: colorVars['--color-background-surface'],
116
+ // Hairline on the three edges that face the scrim. The surface fill alone
117
+ // separates sheet from scrim in light mode, but not in dark: there the two
118
+ // sit within a few RGB steps of each other and the drop shadow is black on
119
+ // near-black, so the sheet's left and right edges disappear. Same
120
+ // treatment MobileNav gives its scrim-facing edge. The block-end edge is
121
+ // deliberately left bare; it sits below the viewport, under the overscroll
122
+ // padding.
123
+ borderBlockStartWidth: borderVars['--border-width'],
124
+ borderBlockStartStyle: 'solid',
125
+ borderBlockStartColor: colorVars['--color-border'],
126
+ borderInlineStartWidth: borderVars['--border-width'],
127
+ borderInlineStartStyle: 'solid',
128
+ borderInlineStartColor: colorVars['--color-border'],
129
+ borderInlineEndWidth: borderVars['--border-width'],
130
+ borderInlineEndStyle: 'solid',
131
+ borderInlineEndColor: colorVars['--color-border'],
84
132
  borderStartStartRadius: radiusVars['--radius-container'],
85
133
  borderStartEndRadius: radiusVars['--radius-container'],
86
134
  boxShadow: shadowVars['--shadow-high'],
@@ -111,11 +159,23 @@ const styles = stylex.create({
111
159
  pointerEvents: 'none',
112
160
  },
113
161
  handleBar: {
114
- flexShrink: 0,
162
+ // Floats over the body rather than taking a row in the flex column. The
163
+ // content starts at the sheet's top edge and rides up under the pill --
164
+ // both moving it closer to the top and letting scrolled content pass
165
+ // beneath instead of stopping at a hard edge.
166
+ position: 'absolute',
167
+ insetBlockStart: 0,
168
+ insetInlineStart: 0,
169
+ insetInlineEnd: 0,
170
+ zIndex: 1,
115
171
  display: 'flex',
116
172
  alignItems: 'center',
117
173
  justifyContent: 'center',
118
- height: spacingVars['--spacing-12'],
174
+ height: HANDLE_BAR_HEIGHT,
175
+ // Keeps the pill legible over whatever sits or scrolls under it: opaque
176
+ // surface behind the pill itself, fading out across the lower half so the
177
+ // content emerging below has no visible cut line.
178
+ backgroundImage: `linear-gradient(to bottom, ${colorVars['--color-background-surface']} 60%, transparent)`,
119
179
  touchAction: 'none',
120
180
  cursor: 'grab',
121
181
  },
@@ -132,6 +192,19 @@ const styles = stylex.create({
132
192
  overflowY: 'auto',
133
193
  overscrollBehavior: 'none',
134
194
  touchAction: 'pan-y',
195
+ // The scrolling area paints the surface itself, covering the sheet's whole
196
+ // inner box. Without it the sheet's edge is not uniform: a theme that packs
197
+ // an inset ring into --shadow-high (the bundled themes all add one in dark
198
+ // mode) draws that ring just inside the sheet, where an opaque content
199
+ // wrapper such as Section paints over it -- so the ring shows only in the
200
+ // gap below where the content ends, and the sheet's side edges appear to
201
+ // change width partway down. Painting the surface here hides the ring
202
+ // evenly, leaving the border below as the sheet's one edge.
203
+ backgroundColor: colorVars['--color-background-surface'],
204
+ // No reserve for the handle bar: it floats, so the content starts at the
205
+ // sheet's top edge and rides up under the pill. The pill is 4px centered
206
+ // in a 24px band, so it occupies only 10-14px from the edge -- inside the
207
+ // space a content wrapper's own top padding already provides.
135
208
  paddingBlockEnd: 0,
136
209
  },
137
210
  tallKeyboardBody: {
@@ -171,6 +244,7 @@ interface BottomSheetPanelProps extends BaseProps<HTMLDivElement> {
171
244
  state: BottomSheetPanelState;
172
245
  height: BottomSheetHeight | number | string;
173
246
  children: ReactNode;
247
+ snapPoints?: ReadonlyArray<BottomSheetSnapPoint>;
174
248
  isSwipeDismissAllowed?: boolean;
175
249
  /** Whether the host has locked page scrolling (a modal, scrim-backed sheet). */
176
250
  isPageScrollLocked?: boolean;
@@ -296,6 +370,7 @@ export function BottomSheetPanel({
296
370
  state,
297
371
  height,
298
372
  children,
373
+ snapPoints,
299
374
  className,
300
375
  style,
301
376
  tabIndex,
@@ -340,6 +415,36 @@ export function BottomSheetPanel({
340
415
  const isFading = isRetained && state.motion === 'fading';
341
416
  const alignmentOffset = isRetained ? state.alignmentOffset : 0;
342
417
 
418
+ // Everything derived from the snap points keys off their identity, not the
419
+ // array's: the resolver's identity is the hook's signal that the stops
420
+ // changed, so it must not churn on a re-render the drag caused. The memo
421
+ // reads the points through a ref, so the resolver resolves them at call time
422
+ // — against whatever the viewport is then, not what it was at memo time.
423
+ const snapPointsKey = snapPointsKeyFor(snapPoints);
424
+ const snapPointsRef = useRef(snapPoints);
425
+ snapPointsRef.current = snapPoints;
426
+ const {snapHeights, ignoredSnapPointsMessage} = useMemo(() => {
427
+ if (snapPointsKey === '') {
428
+ return {snapHeights: undefined, ignoredSnapPointsMessage: ''};
429
+ }
430
+ const ignored = (snapPointsRef.current ?? []).filter(
431
+ point => !isValidSnapPoint(point),
432
+ );
433
+ return {
434
+ snapHeights: () =>
435
+ resolveSnapPoints(snapPointsRef.current ?? [], layoutViewportHeight()),
436
+ ignoredSnapPointsMessage:
437
+ ignored.length === 0
438
+ ? ''
439
+ : `snapPoints ignored ${JSON.stringify(ignored)}. A snap point is a viewport fraction above 0 and up to 1 (0.5 is half the screen), a px length ('320px'), or a percentage ('50%').`,
440
+ };
441
+ }, [snapPointsKey]);
442
+ useDevWarning(
443
+ 'BottomSheet',
444
+ ignoredSnapPointsMessage,
445
+ ignoredSnapPointsMessage !== '',
446
+ );
447
+
343
448
  const {
344
449
  contentProps,
345
450
  handleProps,
@@ -359,7 +464,7 @@ export function BottomSheetPanel({
359
464
  canDismiss: isSwipeDismissAllowed,
360
465
  offscreenBlockEndInset: OVERSCROLL_PADDING,
361
466
  onDismiss,
362
- snapHeights: defaultSnapHeights,
467
+ snapHeights,
363
468
  onScrimOpacity,
364
469
  });
365
470
 
@@ -502,6 +607,7 @@ export function BottomSheetPanel({
502
607
  themeProps('bottom-sheet'),
503
608
  stylex.props(
504
609
  styles.sheet,
610
+ overlayPaddingReset.reset,
505
611
  height === 'hug' ? styles.hugHeight : styles.budget,
506
612
  isClosing && styles.sheetClosing,
507
613
  isFading && styles.sheetFading,
@@ -10,6 +10,10 @@
10
10
  */
11
11
 
12
12
  export {BottomSheet} from './BottomSheet';
13
- export type {BottomSheetHeight, BottomSheetProps} from './BottomSheet';
13
+ export type {
14
+ BottomSheetHeight,
15
+ BottomSheetProps,
16
+ BottomSheetSnapPoint,
17
+ } from './BottomSheet';
14
18
  export {BottomSheetSwitcher} from './BottomSheetSwitcher';
15
19
  export type {BottomSheetSwitcherProps} from './BottomSheetSwitcher';
@@ -11,24 +11,73 @@
11
11
 
12
12
  import {describe, it, expect} from 'vitest';
13
13
  import {
14
- snapFractionsToHeights,
15
14
  computeDetentOffsets,
15
+ isValidSnapPoint,
16
16
  nearestOffset,
17
+ peekOffsetFor,
17
18
  resolveSettleOffset,
19
+ resolveSnapPoints,
18
20
  scrimOpacityForOffset,
19
- isPeekOffset,
20
21
  DETENT_DEDUP_PX,
21
22
  } from './snapOffsets';
22
23
 
23
- describe('snapFractionsToHeights', () => {
24
- it('scales in-range fractions to px', () => {
25
- expect(snapFractionsToHeights([0.5, 0.92], 800)).toEqual([400, 736]);
24
+ describe('resolveSnapPoints', () => {
25
+ it('reads a bare number as a fraction of the viewport', () => {
26
+ expect(resolveSnapPoints([0.5, 0.92], 800)).toEqual([400, 736]);
26
27
  });
27
- it('drops out-of-range fractions', () => {
28
- expect(snapFractionsToHeights([0, 0.5, 1, 1.2, -0.3], 1000)).toEqual([
28
+
29
+ it('reads a percentage as the same fraction', () => {
30
+ expect(resolveSnapPoints(['50%', '92%'], 800)).toEqual([400, 736]);
31
+ });
32
+
33
+ it('reads a px length as an absolute height', () => {
34
+ expect(resolveSnapPoints(['320px'], 800)).toEqual([320]);
35
+ });
36
+
37
+ it('keeps the caller order, mixing forms', () => {
38
+ expect(resolveSnapPoints(['120px', 0.5, '90%'], 800)).toEqual([
39
+ 120, 400, 720,
40
+ ]);
41
+ });
42
+
43
+ it('accepts fractional and unit-cased lengths, and surrounding space', () => {
44
+ expect(resolveSnapPoints(['12.5%', '0.5PX', ' 80px '], 800)).toEqual([
45
+ 100, 0.5, 80,
46
+ ]);
47
+ });
48
+
49
+ it('drops numbers outside the (0, 1] fraction range', () => {
50
+ // 200 is the px mistake — a bare number is never a length.
51
+ expect(resolveSnapPoints([0, 0.5, 1, 1.2, -0.3, 200], 1000)).toEqual([
29
52
  500, 1000,
30
53
  ]);
31
54
  });
55
+
56
+ it('drops lengths it cannot resolve by arithmetic on the viewport', () => {
57
+ expect(
58
+ resolveSnapPoints(
59
+ ['50', '30rem', '50vh', 'calc(50% - 20px)', '-10px', '', '0px'],
60
+ 800,
61
+ ),
62
+ ).toEqual([]);
63
+ });
64
+ });
65
+
66
+ describe('isValidSnapPoint', () => {
67
+ it('accepts the three supported forms', () => {
68
+ expect(isValidSnapPoint(0.5)).toBe(true);
69
+ expect(isValidSnapPoint(1)).toBe(true);
70
+ expect(isValidSnapPoint('50%')).toBe(true);
71
+ expect(isValidSnapPoint('320px')).toBe(true);
72
+ });
73
+
74
+ it('rejects what resolveSnapPoints would silently drop', () => {
75
+ expect(isValidSnapPoint(200)).toBe(false);
76
+ expect(isValidSnapPoint(0)).toBe(false);
77
+ expect(isValidSnapPoint('50')).toBe(false);
78
+ expect(isValidSnapPoint('50vh')).toBe(false);
79
+ expect(isValidSnapPoint('calc(50% - 20px)')).toBe(false);
80
+ });
32
81
  });
33
82
 
34
83
  describe('computeDetentOffsets', () => {
@@ -100,66 +149,84 @@ describe('resolveSettleOffset', () => {
100
149
  });
101
150
  });
102
151
 
103
- describe('isPeekOffset', () => {
104
- const offsets = [0, 100, 200]; // full, mid, peek
105
-
106
- it('is true only for the shortest detent', () => {
107
- expect(isPeekOffset(200, offsets)).toBe(true);
108
- expect(isPeekOffset(100, offsets)).toBe(false);
109
- expect(isPeekOffset(0, offsets)).toBe(false);
152
+ describe('peekOffsetFor', () => {
153
+ it('is the shortest stop when that stop is a sliver', () => {
154
+ // sheet 800; stops at 800, 400 and 100 visible px. The 100px stop is an
155
+ // eighth of the sheet, well inside the quarter that makes a peek.
156
+ expect(peekOffsetFor([0, 400, 700], 800)).toBe(700);
110
157
  });
111
158
 
112
- it('tolerates sub-pixel rounding around the peek', () => {
113
- expect(isPeekOffset(200.4, offsets)).toBe(true);
114
- expect(isPeekOffset(199.6, offsets)).toBe(true);
115
- expect(isPeekOffset(196, offsets)).toBe(false);
159
+ it('has no peek when the shortest stop is a working height', () => {
160
+ // A half-height stop lays its content out and keeps a full scrim; it is
161
+ // the sheet the caller asked for, not a glance at one.
162
+ expect(peekOffsetFor([0, 400], 800)).toBeNull();
116
163
  });
117
164
 
118
165
  it('has no peek when the sheet has no collapsed stop', () => {
119
- expect(isPeekOffset(200, [0])).toBe(false);
120
- expect(isPeekOffset(0, [0])).toBe(false);
166
+ expect(peekOffsetFor([0], 800)).toBeNull();
121
167
  });
122
168
 
123
- it('treats the only collapsed stop as the peek', () => {
124
- expect(isPeekOffset(200, [0, 200])).toBe(true);
169
+ it('takes the quarter mark itself as a peek', () => {
170
+ expect(peekOffsetFor([0, 600], 800)).toBe(600);
125
171
  });
126
172
 
127
- it('agrees with the detent scrimOpacityForOffset treats as the peek', () => {
128
- const dismissOffset = 280;
129
- const peek = offsets[offsets.length - 1];
130
- expect(isPeekOffset(peek, offsets)).toBe(true);
131
- expect(scrimOpacityForOffset(peek, offsets, dismissOffset)).toBeCloseTo(
132
- 0.3,
133
- );
173
+ it('has no peek without a measured sheet', () => {
174
+ expect(peekOffsetFor([0, 600], 0)).toBeNull();
134
175
  });
135
176
  });
136
177
 
137
178
  describe('scrimOpacityForOffset', () => {
138
- describe('with a peek detent (>= 2 detents)', () => {
179
+ describe('with a peek detent', () => {
139
180
  const offsets = [0, 100, 200]; // full, mid, peek
181
+ const peekOffset = 200;
140
182
  const dismissOffset = 280;
141
183
 
142
184
  it('is full at or above the mid detent', () => {
143
- expect(scrimOpacityForOffset(0, offsets, dismissOffset)).toBe(1);
144
- expect(scrimOpacityForOffset(100, offsets, dismissOffset)).toBe(1);
145
- expect(scrimOpacityForOffset(60, offsets, dismissOffset)).toBe(1);
185
+ expect(scrimOpacityForOffset(0, offsets, dismissOffset, peekOffset)).toBe(
186
+ 1,
187
+ );
188
+ expect(
189
+ scrimOpacityForOffset(100, offsets, dismissOffset, peekOffset),
190
+ ).toBe(1);
191
+ expect(
192
+ scrimOpacityForOffset(60, offsets, dismissOffset, peekOffset),
193
+ ).toBe(1);
146
194
  });
147
195
 
148
196
  it('fades linearly from the mid detent toward the peek floor', () => {
149
197
  // Halfway from mid (100) to peek (200), opacity is halfway from 1 to the
150
198
  // 0.3 floor => 0.65.
151
- expect(scrimOpacityForOffset(150, offsets, dismissOffset)).toBeCloseTo(
152
- 0.65,
153
- );
199
+ expect(
200
+ scrimOpacityForOffset(150, offsets, dismissOffset, peekOffset),
201
+ ).toBeCloseTo(0.65);
154
202
  });
155
203
 
156
204
  it('thins to the peek floor at or below the peek detent (still modal)', () => {
157
- expect(scrimOpacityForOffset(200, offsets, dismissOffset)).toBeCloseTo(
158
- 0.3,
159
- );
160
- expect(scrimOpacityForOffset(240, offsets, dismissOffset)).toBeCloseTo(
161
- 0.3,
162
- );
205
+ expect(
206
+ scrimOpacityForOffset(200, offsets, dismissOffset, peekOffset),
207
+ ).toBeCloseTo(0.3);
208
+ expect(
209
+ scrimOpacityForOffset(240, offsets, dismissOffset, peekOffset),
210
+ ).toBeCloseTo(0.3);
211
+ });
212
+ });
213
+
214
+ describe('with collapsed stops but no peek among them', () => {
215
+ // The shortest stop is a working height, so the scrim is full there and
216
+ // only fades once the sheet is being dragged out below it.
217
+ const offsets = [0, 100];
218
+ const dismissOffset = 160;
219
+
220
+ it('stays full at every stop, including the shortest', () => {
221
+ expect(scrimOpacityForOffset(0, offsets, dismissOffset, null)).toBe(1);
222
+ expect(scrimOpacityForOffset(100, offsets, dismissOffset, null)).toBe(1);
223
+ });
224
+
225
+ it('fades only across the dismiss overshoot below the shortest stop', () => {
226
+ expect(
227
+ scrimOpacityForOffset(130, offsets, dismissOffset, null),
228
+ ).toBeCloseTo(0.5);
229
+ expect(scrimOpacityForOffset(160, offsets, dismissOffset, null)).toBe(0);
163
230
  });
164
231
  });
165
232
 
@@ -168,14 +235,14 @@ describe('scrimOpacityForOffset', () => {
168
235
  const dismissOffset = 160;
169
236
 
170
237
  it('stays full until the drag begins collapsing', () => {
171
- expect(scrimOpacityForOffset(0, offsets, dismissOffset)).toBe(1);
238
+ expect(scrimOpacityForOffset(0, offsets, dismissOffset, null)).toBe(1);
172
239
  });
173
240
 
174
241
  it('fades across the dismiss overshoot toward the threshold', () => {
175
- expect(scrimOpacityForOffset(80, offsets, dismissOffset)).toBeCloseTo(
176
- 0.5,
177
- );
178
- expect(scrimOpacityForOffset(160, offsets, dismissOffset)).toBe(0);
242
+ expect(
243
+ scrimOpacityForOffset(80, offsets, dismissOffset, null),
244
+ ).toBeCloseTo(0.5);
245
+ expect(scrimOpacityForOffset(160, offsets, dismissOffset, null)).toBe(0);
179
246
  });
180
247
  });
181
248
  });