@astryxdesign/core 0.4.7 → 0.5.0-canary.32a62fd

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 (554) hide show
  1. package/CHANGELOG.md +161 -1
  2. package/dist/AppShell/AppShell.d.ts.map +1 -1
  3. package/dist/AppShell/AppShell.js +3 -2
  4. package/dist/AspectRatio/AspectRatio.d.ts +24 -0
  5. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  6. package/dist/AspectRatio/AspectRatio.js +23 -4
  7. package/dist/Avatar/Avatar.d.ts +25 -1
  8. package/dist/Avatar/Avatar.d.ts.map +1 -1
  9. package/dist/Avatar/Avatar.js +58 -19
  10. package/dist/Avatar/index.d.ts +2 -2
  11. package/dist/Avatar/index.d.ts.map +1 -1
  12. package/dist/Avatar/index.js +1 -1
  13. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  14. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  15. package/dist/AvatarGroup/AvatarGroup.js +8 -4
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  17. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  19. package/dist/AvatarGroup/AvatarGroupOverflow.js +10 -6
  20. package/dist/Banner/Banner.d.ts +40 -15
  21. package/dist/Banner/Banner.d.ts.map +1 -1
  22. package/dist/Banner/Banner.js +51 -18
  23. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  24. package/dist/BottomSheet/BottomSheet.js +17 -4
  25. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  26. package/dist/BottomSheet/BottomSheetSwitcher.js +3 -2
  27. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  28. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -10
  29. package/dist/Button/Button.d.ts.map +1 -1
  30. package/dist/Button/Button.js +5 -4
  31. package/dist/ButtonGroup/ButtonGroup.d.ts +5 -1
  32. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  33. package/dist/ButtonGroup/ButtonGroup.js +26 -7
  34. package/dist/Calendar/Calendar.d.ts +3 -1
  35. package/dist/Calendar/Calendar.d.ts.map +1 -1
  36. package/dist/Calendar/Calendar.js +13 -14
  37. package/dist/Calendar/getInitialFocusDate.d.ts +46 -0
  38. package/dist/Calendar/getInitialFocusDate.d.ts.map +1 -0
  39. package/dist/Calendar/getInitialFocusDate.js +62 -0
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +3 -3
  42. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  43. package/dist/Chat/ChatComposerInput.js +2 -3
  44. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  45. package/dist/Chat/ChatLayout.js +3 -3
  46. package/dist/Chat/ChatMessageList.d.ts +22 -3
  47. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  48. package/dist/Chat/ChatMessageList.js +6 -3
  49. package/dist/Chat/ChatTokenizedText.d.ts +2 -0
  50. package/dist/Chat/ChatTokenizedText.d.ts.map +1 -1
  51. package/dist/Chat/ChatTokenizedText.js +11 -6
  52. package/dist/Chat/useChatComposerTokens.js +1 -1
  53. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  54. package/dist/CheckboxInput/CheckboxInput.js +10 -3
  55. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  56. package/dist/ClickableCard/ClickableCard.js +3 -3
  57. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  58. package/dist/CodeBlock/CodeBlock.js +15 -4
  59. package/dist/CodeBlock/highlightRanges.js +1 -1
  60. package/dist/CodeBlock/highlightStyles.js +13 -13
  61. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  62. package/dist/CommandPalette/CommandPaletteInput.js +3 -2
  63. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  64. package/dist/CommandPalette/CommandPaletteItem.js +3 -2
  65. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  66. package/dist/ContextMenu/ContextMenu.js +4 -3
  67. package/dist/DateInput/DateInput.d.ts +43 -0
  68. package/dist/DateInput/DateInput.d.ts.map +1 -1
  69. package/dist/DateInput/DateInput.js +22 -2
  70. package/dist/DateInput/NativeDateField.d.ts +10 -0
  71. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  72. package/dist/DateInput/NativeDateField.js +477 -0
  73. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  74. package/dist/DateInput/TouchDateField.js +38 -4
  75. package/dist/DateInput/index.d.ts +1 -1
  76. package/dist/DateInput/index.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.d.ts +21 -1
  78. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  79. package/dist/DateTimeInput/DateTimeInput.js +406 -12
  80. package/dist/DateTimeInput/index.d.ts +1 -1
  81. package/dist/DateTimeInput/index.d.ts.map +1 -1
  82. package/dist/Dialog/Dialog.d.ts +7 -1
  83. package/dist/Dialog/Dialog.d.ts.map +1 -1
  84. package/dist/Dialog/Dialog.js +89 -58
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.js +6 -5
  87. package/dist/Field/FieldLabel.d.ts.map +1 -1
  88. package/dist/Field/FieldLabel.js +9 -1
  89. package/dist/FileInput/FileInput.d.ts.map +1 -1
  90. package/dist/FileInput/FileInput.js +3 -2
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +6 -2
  93. package/dist/HoverCard/HoverCard.d.ts +6 -0
  94. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  95. package/dist/HoverCard/HoverCard.js +52 -3
  96. package/dist/HoverCard/useHoverCard.d.ts +16 -2
  97. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  98. package/dist/HoverCard/useHoverCard.js +74 -26
  99. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  100. package/dist/Indicator/CheckboxIndicator.js +6 -2
  101. package/dist/Item/Item.d.ts.map +1 -1
  102. package/dist/Item/Item.js +4 -2
  103. package/dist/Kbd/Kbd.d.ts.map +1 -1
  104. package/dist/Kbd/Kbd.js +2 -19
  105. package/dist/Layer/LayerDepthContext.d.ts +52 -0
  106. package/dist/Layer/LayerDepthContext.d.ts.map +1 -0
  107. package/dist/Layer/LayerDepthContext.js +66 -0
  108. package/dist/Layer/index.d.ts +3 -0
  109. package/dist/Layer/index.d.ts.map +1 -1
  110. package/dist/Layer/index.js +4 -0
  111. package/dist/Layer/layerStack.d.ts +98 -0
  112. package/dist/Layer/layerStack.d.ts.map +1 -0
  113. package/dist/Layer/layerStack.js +306 -0
  114. package/dist/Layer/useLayerDismissal.d.ts +93 -0
  115. package/dist/Layer/useLayerDismissal.d.ts.map +1 -0
  116. package/dist/Layer/useLayerDismissal.js +91 -0
  117. package/dist/Lightbox/Lightbox.d.ts +1 -0
  118. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  119. package/dist/Lightbox/Lightbox.js +103 -81
  120. package/dist/Markdown/index.d.ts +1 -1
  121. package/dist/Markdown/index.d.ts.map +1 -1
  122. package/dist/Markdown/parser.d.ts +38 -1
  123. package/dist/Markdown/parser.d.ts.map +1 -1
  124. package/dist/Markdown/parser.js +177 -29
  125. package/dist/MobileNav/MobileNav.d.ts +6 -1
  126. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  127. package/dist/MobileNav/MobileNav.js +61 -36
  128. package/dist/MultiSelector/MultiSelector.d.ts +15 -1
  129. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  130. package/dist/MultiSelector/MultiSelector.js +17 -6
  131. package/dist/MultiSelector/index.d.ts +1 -1
  132. package/dist/MultiSelector/index.d.ts.map +1 -1
  133. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  134. package/dist/NavIcon/NavIcon.js +5 -1
  135. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  136. package/dist/NavMenu/NavHeadingMenu.js +3 -2
  137. package/dist/NumberInput/NumberInput.d.ts +1 -0
  138. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  139. package/dist/NumberInput/NumberInput.js +27 -37
  140. package/dist/NumberInput/numberParser.d.ts +43 -0
  141. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  142. package/dist/NumberInput/numberParser.js +293 -0
  143. package/dist/Outline/Outline.d.ts.map +1 -1
  144. package/dist/Outline/Outline.js +3 -2
  145. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  146. package/dist/OverflowList/OverflowList.js +3 -2
  147. package/dist/Overlay/Overlay.d.ts.map +1 -1
  148. package/dist/Overlay/Overlay.js +3 -2
  149. package/dist/Popover/usePopover.d.ts.map +1 -1
  150. package/dist/Popover/usePopover.js +20 -17
  151. package/dist/PowerSearch/PowerSearch.d.ts +8 -2
  152. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearch.js +19 -6
  154. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +3 -1
  155. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  156. package/dist/PowerSearch/PowerSearchEditPopover.js +28 -6
  157. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +3 -1
  158. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  159. package/dist/PowerSearch/PowerSearchValueEditor.js +19 -9
  160. package/dist/PowerSearch/types.d.ts +2 -0
  161. package/dist/PowerSearch/types.d.ts.map +1 -1
  162. package/dist/PowerSearch/usePowerSearchSource.d.ts +6 -1
  163. package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
  164. package/dist/PowerSearch/usePowerSearchSource.js +14 -5
  165. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  166. package/dist/ProgressBar/ProgressBar.js +18 -5
  167. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  168. package/dist/Resizable/ResizeHandle.js +3 -2
  169. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  170. package/dist/SegmentedControl/SegmentedControl.js +3 -2
  171. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  172. package/dist/SelectableCard/SelectableCard.js +3 -3
  173. package/dist/Selector/Selector.d.ts.map +1 -1
  174. package/dist/Selector/Selector.js +5 -0
  175. package/dist/SideNav/SideNav.d.ts.map +1 -1
  176. package/dist/SideNav/SideNav.js +4 -3
  177. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  178. package/dist/SideNav/SideNavHeading.js +8 -7
  179. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  180. package/dist/SideNav/SideNavItem.js +4 -3
  181. package/dist/Slider/Slider.d.ts.map +1 -1
  182. package/dist/Slider/Slider.js +3 -3
  183. package/dist/Spinner/Spinner.d.ts +3 -3
  184. package/dist/Spinner/Spinner.d.ts.map +1 -1
  185. package/dist/Spinner/Spinner.js +130 -78
  186. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  187. package/dist/StatusDot/StatusDot.js +6 -1
  188. package/dist/Stepper/Step.d.ts +140 -0
  189. package/dist/Stepper/Step.d.ts.map +1 -0
  190. package/dist/Stepper/Step.js +1045 -0
  191. package/dist/Stepper/StepStatus.d.ts +24 -0
  192. package/dist/Stepper/StepStatus.d.ts.map +1 -0
  193. package/dist/Stepper/StepStatus.js +1 -0
  194. package/dist/Stepper/Stepper.d.ts +93 -0
  195. package/dist/Stepper/Stepper.d.ts.map +1 -0
  196. package/dist/Stepper/Stepper.js +184 -0
  197. package/dist/Stepper/StepperContext.d.ts +41 -0
  198. package/dist/Stepper/StepperContext.d.ts.map +1 -0
  199. package/dist/Stepper/StepperContext.js +34 -0
  200. package/dist/Stepper/index.d.ts +8 -0
  201. package/dist/Stepper/index.d.ts.map +1 -0
  202. package/dist/Stepper/index.js +7 -0
  203. package/dist/Stepper/stepper.stylex.d.ts +15 -0
  204. package/dist/Stepper/stepper.stylex.d.ts.map +1 -0
  205. package/dist/Stepper/stepper.stylex.js +20 -0
  206. package/dist/Switch/Switch.d.ts.map +1 -1
  207. package/dist/Switch/Switch.js +8 -3
  208. package/dist/TabList/Tab.d.ts +15 -2
  209. package/dist/TabList/Tab.d.ts.map +1 -1
  210. package/dist/TabList/Tab.js +32 -7
  211. package/dist/TabList/TabList.d.ts +46 -7
  212. package/dist/TabList/TabList.d.ts.map +1 -1
  213. package/dist/TabList/TabList.js +353 -23
  214. package/dist/TabList/TabListContext.d.ts +11 -1
  215. package/dist/TabList/TabListContext.d.ts.map +1 -1
  216. package/dist/TabList/TabListContext.js +11 -2
  217. package/dist/TabList/TabMenu.d.ts.map +1 -1
  218. package/dist/TabList/TabMenu.js +3 -2
  219. package/dist/TabList/index.d.ts +2 -2
  220. package/dist/TabList/index.d.ts.map +1 -1
  221. package/dist/Table/BaseTable.d.ts.map +1 -1
  222. package/dist/Table/BaseTable.js +10 -2
  223. package/dist/Table/Table.d.ts.map +1 -1
  224. package/dist/Table/Table.js +3 -6
  225. package/dist/Table/TableRow.d.ts +16 -5
  226. package/dist/Table/TableRow.d.ts.map +1 -1
  227. package/dist/Table/TableRow.js +16 -5
  228. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  229. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +20 -8
  230. package/dist/Table/plugins/pagination/useTablePagination.d.ts +3 -0
  231. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  232. package/dist/Table/plugins/pagination/useTablePagination.js +21 -4
  233. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  234. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  235. package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -1
  236. package/dist/Table/plugins/rowStatus/useTableRowStatus.js +10 -3
  237. package/dist/Table/plugins/selection/useTableSelection.d.ts +16 -0
  238. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  239. package/dist/Table/plugins/selection/useTableSelection.js +19 -5
  240. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  241. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  242. package/dist/Table/types.d.ts +22 -4
  243. package/dist/Table/types.d.ts.map +1 -1
  244. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  245. package/dist/Table/useBaseTablePlugins.js +5 -0
  246. package/dist/Text/Text.d.ts.map +1 -1
  247. package/dist/Text/Text.js +6 -2
  248. package/dist/TextArea/TextArea.d.ts.map +1 -1
  249. package/dist/TextArea/TextArea.js +9 -3
  250. package/dist/TextInput/TextInput.d.ts.map +1 -1
  251. package/dist/TextInput/TextInput.js +3 -2
  252. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  253. package/dist/TimeInput/TimeInput.js +3 -2
  254. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  255. package/dist/Timestamp/Timestamp.js +5 -3
  256. package/dist/Token/TokenLink.d.ts.map +1 -1
  257. package/dist/Token/TokenLink.js +2 -2
  258. package/dist/Tokenizer/Tokenizer.d.ts +3 -1
  259. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  260. package/dist/Tokenizer/Tokenizer.js +2 -0
  261. package/dist/Tooltip/Tooltip.d.ts +5 -0
  262. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  263. package/dist/Tooltip/useTooltip.d.ts +5 -0
  264. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  265. package/dist/Tooltip/useTooltip.js +39 -24
  266. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  267. package/dist/TopNav/TopNavHeading.js +5 -4
  268. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  269. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  270. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  271. package/dist/TopNav/TopNavMenu.js +4 -3
  272. package/dist/Typeahead/BaseTypeahead.d.ts +2 -0
  273. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  274. package/dist/Typeahead/BaseTypeahead.js +68 -29
  275. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  276. package/dist/Typeahead/Typeahead.js +3 -2
  277. package/dist/astryx.css +94 -7
  278. package/dist/hooks/index.d.ts +1 -0
  279. package/dist/hooks/index.d.ts.map +1 -1
  280. package/dist/hooks/index.js +1 -0
  281. package/dist/hooks/useFocusTrap.d.ts +7 -4
  282. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  283. package/dist/hooks/useFocusTrap.js +60 -95
  284. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  285. package/dist/hooks/useHotkeys.js +3 -18
  286. package/dist/hooks/useMergedRefs.d.ts +18 -0
  287. package/dist/hooks/useMergedRefs.d.ts.map +1 -0
  288. package/dist/hooks/useMergedRefs.js +25 -0
  289. package/dist/index.d.ts +1 -0
  290. package/dist/index.d.ts.map +1 -1
  291. package/dist/index.js +1 -0
  292. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  293. package/dist/theme/derivedVarRegistry.js +24 -4
  294. package/dist/theme/syntax/tokens.d.ts +1 -1
  295. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  296. package/dist/theme/syntax/tokens.js +5 -2
  297. package/dist/utils/groupItems.d.ts +5 -1
  298. package/dist/utils/groupItems.d.ts.map +1 -1
  299. package/dist/utils/groupItems.js +9 -9
  300. package/dist/utils/index.d.ts +1 -1
  301. package/dist/utils/index.d.ts.map +1 -1
  302. package/dist/utils/isApplePlatform.d.ts +18 -0
  303. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  304. package/dist/utils/isApplePlatform.js +37 -0
  305. package/dist/utils/sharedResizeObserver.d.ts.map +1 -1
  306. package/dist/utils/sharedResizeObserver.js +10 -1
  307. package/dist/utils/timeParser.d.ts +1 -1
  308. package/dist/utils/timeParser.d.ts.map +1 -1
  309. package/dist/utils/timeParser.js +26 -12
  310. package/locales/en.json +31 -7
  311. package/locales/pseudo.json +18 -0
  312. package/package.json +8 -3
  313. package/src/AppShell/AppShell.tsx +3 -2
  314. package/src/AspectRatio/AspectRatio.doc.mjs +6 -3
  315. package/src/AspectRatio/AspectRatio.test.tsx +57 -10
  316. package/src/AspectRatio/AspectRatio.tsx +40 -1
  317. package/src/Avatar/Avatar.doc.mjs +14 -3
  318. package/src/Avatar/Avatar.test.tsx +97 -0
  319. package/src/Avatar/Avatar.tsx +125 -52
  320. package/src/Avatar/index.ts +2 -2
  321. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  322. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  323. package/src/AvatarGroup/AvatarGroup.tsx +15 -6
  324. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +10 -4
  326. package/src/Banner/Banner.doc.mjs +13 -11
  327. package/src/Banner/Banner.test.tsx +102 -15
  328. package/src/Banner/Banner.tsx +72 -25
  329. package/src/BottomSheet/BottomSheet.test.tsx +123 -0
  330. package/src/BottomSheet/BottomSheet.tsx +17 -4
  331. package/src/BottomSheet/BottomSheetSwitcher.tsx +3 -2
  332. package/src/Breadcrumbs/BreadcrumbItem.tsx +9 -5
  333. package/src/Button/Button.doc.mjs +22 -0
  334. package/src/Button/Button.test.tsx +13 -0
  335. package/src/Button/Button.tsx +5 -4
  336. package/src/ButtonGroup/ButtonGroup.doc.mjs +10 -1
  337. package/src/ButtonGroup/ButtonGroup.test.tsx +391 -1
  338. package/src/ButtonGroup/ButtonGroup.tsx +23 -5
  339. package/src/Calendar/Calendar.doc.mjs +4 -3
  340. package/src/Calendar/Calendar.test.tsx +52 -0
  341. package/src/Calendar/Calendar.tsx +18 -15
  342. package/src/Calendar/getInitialFocusDate.test.ts +94 -0
  343. package/src/Calendar/getInitialFocusDate.ts +93 -0
  344. package/src/Carousel/Carousel.tsx +3 -2
  345. package/src/Chat/ChatComposerInput.tsx +5 -1
  346. package/src/Chat/ChatLayout.tsx +3 -2
  347. package/src/Chat/ChatMessageList.doc.mjs +9 -1
  348. package/src/Chat/ChatMessageList.test.tsx +46 -0
  349. package/src/Chat/ChatMessageList.tsx +28 -4
  350. package/src/Chat/ChatTokenizedText.tsx +17 -10
  351. package/src/Chat/useChatComposerTokens.ts +1 -1
  352. package/src/CheckboxInput/CheckboxInput.doc.mjs +2 -0
  353. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  354. package/src/CheckboxInput/CheckboxInput.tsx +8 -2
  355. package/src/ClickableCard/ClickableCard.tsx +3 -2
  356. package/src/CodeBlock/CodeBlock.doc.mjs +16 -4
  357. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  358. package/src/CodeBlock/CodeBlock.tsx +20 -4
  359. package/src/CodeBlock/highlightRanges.ts +1 -1
  360. package/src/CodeBlock/highlightStyles.ts +13 -13
  361. package/src/CommandPalette/CommandPaletteInput.tsx +3 -2
  362. package/src/CommandPalette/CommandPaletteItem.tsx +3 -2
  363. package/src/ContextMenu/ContextMenu.doc.mjs +5 -0
  364. package/src/ContextMenu/ContextMenu.tsx +4 -3
  365. package/src/DateInput/DateInput.doc.mjs +16 -0
  366. package/src/DateInput/DateInput.tsx +55 -2
  367. package/src/DateInput/DateInputTouch.test.tsx +68 -9
  368. package/src/DateInput/NativeDateField.test.tsx +565 -0
  369. package/src/DateInput/NativeDateField.tsx +623 -0
  370. package/src/DateInput/TouchDateField.tsx +38 -4
  371. package/src/DateInput/index.ts +1 -0
  372. package/src/DateTimeInput/DateTimeInput.doc.mjs +18 -0
  373. package/src/DateTimeInput/DateTimeInput.test.tsx +1088 -0
  374. package/src/DateTimeInput/DateTimeInput.tsx +556 -24
  375. package/src/DateTimeInput/index.ts +1 -0
  376. package/src/Dialog/Dialog.doc.mjs +8 -3
  377. package/src/Dialog/Dialog.test.tsx +261 -1
  378. package/src/Dialog/Dialog.tsx +109 -58
  379. package/src/DropdownMenu/DropdownMenu.doc.mjs +6 -0
  380. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +8 -5
  381. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +8 -11
  382. package/src/Field/Field.doc.mjs +13 -2
  383. package/src/Field/FieldLabel.test.tsx +29 -0
  384. package/src/Field/FieldLabel.tsx +7 -0
  385. package/src/FileInput/FileInput.tsx +3 -2
  386. package/src/Heading/Heading.test.tsx +23 -1
  387. package/src/Heading/Heading.tsx +6 -2
  388. package/src/HoverCard/HoverCard.doc.mjs +13 -2
  389. package/src/HoverCard/HoverCard.test.tsx +101 -4
  390. package/src/HoverCard/HoverCard.tsx +71 -3
  391. package/src/HoverCard/useHoverCard.tsx +101 -33
  392. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  393. package/src/Indicator/Indicator.doc.mjs +2 -0
  394. package/src/Item/Item.tsx +5 -4
  395. package/src/Kbd/Kbd.tsx +2 -19
  396. package/src/Layer/LayerDepthContext.tsx +62 -0
  397. package/src/Layer/index.ts +9 -0
  398. package/src/Layer/layerDismissalFamilies.test.tsx +359 -0
  399. package/src/Layer/layerDismissalInvariants.test.tsx +417 -0
  400. package/src/Layer/layerStack.ts +362 -0
  401. package/src/Layer/useLayerDismissal.test.tsx +469 -0
  402. package/src/Layer/useLayerDismissal.ts +173 -0
  403. package/src/Layout/Layout.doc.mjs +2 -2
  404. package/src/Lightbox/Lightbox.doc.mjs +12 -0
  405. package/src/Lightbox/Lightbox.tsx +124 -102
  406. package/src/Link/Link.doc.mjs +39 -0
  407. package/src/Markdown/Markdown.doc.mjs +5 -0
  408. package/src/Markdown/incremental.test.ts +16 -2
  409. package/src/Markdown/index.ts +1 -0
  410. package/src/Markdown/parser.perf.test.ts +96 -1
  411. package/src/Markdown/parser.test.ts +145 -2
  412. package/src/Markdown/parser.ts +244 -29
  413. package/src/MobileNav/MobileNav.tsx +62 -36
  414. package/src/MoreMenu/MoreMenu.doc.mjs +7 -1
  415. package/src/MultiSelector/MultiSelector.doc.mjs +27 -0
  416. package/src/MultiSelector/MultiSelector.test.tsx +106 -0
  417. package/src/MultiSelector/MultiSelector.tsx +50 -6
  418. package/src/MultiSelector/index.ts +1 -0
  419. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  420. package/src/NavIcon/NavIcon.test.tsx +9 -0
  421. package/src/NavIcon/NavIcon.tsx +5 -1
  422. package/src/NavMenu/NavHeadingMenu.tsx +3 -2
  423. package/src/NumberInput/NumberInput.doc.mjs +20 -0
  424. package/src/NumberInput/NumberInput.tsx +28 -29
  425. package/src/NumberInput/numberParser.test.ts +137 -0
  426. package/src/NumberInput/numberParser.ts +346 -0
  427. package/src/Outline/Outline.tsx +3 -2
  428. package/src/OverflowList/OverflowList.tsx +3 -2
  429. package/src/Overlay/Overlay.tsx +3 -2
  430. package/src/Popover/usePopover.tsx +31 -28
  431. package/src/PowerSearch/PowerSearch.doc.mjs +160 -28
  432. package/src/PowerSearch/PowerSearch.test.tsx +240 -1
  433. package/src/PowerSearch/PowerSearch.tsx +34 -5
  434. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +201 -0
  435. package/src/PowerSearch/PowerSearchEditPopover.tsx +29 -2
  436. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +47 -1
  437. package/src/PowerSearch/PowerSearchValueEditor.tsx +15 -0
  438. package/src/PowerSearch/types.ts +2 -0
  439. package/src/PowerSearch/usePowerSearchSource.test.ts +82 -2
  440. package/src/PowerSearch/usePowerSearchSource.ts +13 -3
  441. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  442. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  443. package/src/ProgressBar/ProgressBar.tsx +28 -8
  444. package/src/Resizable/ResizeHandle.tsx +3 -2
  445. package/src/SegmentedControl/SegmentedControl.tsx +3 -2
  446. package/src/SelectableCard/SelectableCard.tsx +3 -2
  447. package/src/Selector/Selector.doc.mjs +12 -1
  448. package/src/Selector/Selector.test.tsx +45 -1
  449. package/src/Selector/Selector.tsx +11 -1
  450. package/src/SideNav/SideNav.doc.mjs +6 -6
  451. package/src/SideNav/SideNav.tsx +4 -2
  452. package/src/SideNav/SideNavHeading.tsx +12 -12
  453. package/src/SideNav/SideNavItem.tsx +4 -2
  454. package/src/Slider/Slider.tsx +3 -2
  455. package/src/Spinner/Spinner.test.tsx +147 -1
  456. package/src/Spinner/Spinner.tsx +120 -101
  457. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  458. package/src/StatusDot/StatusDot.test.tsx +9 -0
  459. package/src/StatusDot/StatusDot.tsx +7 -1
  460. package/src/Stepper/Step.doc.mjs +98 -0
  461. package/src/Stepper/Step.tsx +1638 -0
  462. package/src/Stepper/StepStatus.ts +26 -0
  463. package/src/Stepper/Stepper.doc.mjs +370 -0
  464. package/src/Stepper/Stepper.test.tsx +1024 -0
  465. package/src/Stepper/Stepper.tsx +258 -0
  466. package/src/Stepper/StepperContext.ts +71 -0
  467. package/src/Stepper/index.ts +18 -0
  468. package/src/Stepper/stepper.stylex.ts +19 -0
  469. package/src/Switch/Switch.doc.mjs +2 -0
  470. package/src/Switch/Switch.test.tsx +10 -0
  471. package/src/Switch/Switch.tsx +6 -2
  472. package/src/TabList/Tab.doc.mjs +13 -1
  473. package/src/TabList/Tab.tsx +67 -7
  474. package/src/TabList/TabList.doc.mjs +22 -6
  475. package/src/TabList/TabList.test.tsx +559 -0
  476. package/src/TabList/TabList.tsx +490 -22
  477. package/src/TabList/TabListContext.ts +13 -2
  478. package/src/TabList/TabMenu.tsx +3 -2
  479. package/src/TabList/index.ts +6 -2
  480. package/src/Table/BaseTable.tsx +12 -4
  481. package/src/Table/Table.doc.mjs +9 -2
  482. package/src/Table/Table.test.tsx +132 -14
  483. package/src/Table/Table.tsx +3 -8
  484. package/src/Table/TableBody.doc.mjs +40 -0
  485. package/src/Table/TableFooter.doc.mjs +40 -0
  486. package/src/Table/TableHeader.doc.mjs +40 -0
  487. package/src/Table/TableRow.tsx +16 -5
  488. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +112 -0
  489. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +100 -0
  490. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +17 -8
  491. package/src/Table/plugins/pagination/useTablePagination.test.tsx +34 -4
  492. package/src/Table/plugins/pagination/useTablePagination.tsx +35 -3
  493. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +9 -2
  494. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +13 -7
  495. package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +12 -3
  496. package/src/Table/plugins/selection/useTableSelection.test.tsx +76 -0
  497. package/src/Table/plugins/selection/useTableSelection.tsx +40 -7
  498. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  499. package/src/Table/types.ts +22 -4
  500. package/src/Table/useBaseTablePlugins.ts +5 -0
  501. package/src/Table/useTableGroupedRows.doc.mjs +5 -4
  502. package/src/Table/useTablePagination.doc.mjs +7 -0
  503. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  504. package/src/Table/useTableSelection.doc.mjs +31 -0
  505. package/src/Text/Text.test.tsx +23 -1
  506. package/src/Text/Text.tsx +6 -2
  507. package/src/TextArea/TextArea.doc.mjs +8 -2
  508. package/src/TextArea/TextArea.test.tsx +11 -0
  509. package/src/TextArea/TextArea.tsx +15 -8
  510. package/src/TextInput/TextInput.doc.mjs +10 -0
  511. package/src/TextInput/TextInput.tsx +3 -2
  512. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  513. package/src/TimeInput/TimeInput.tsx +2 -2
  514. package/src/Timestamp/Timestamp.tsx +4 -2
  515. package/src/Toast/Toast.doc.mjs +6 -0
  516. package/src/Token/TokenLink.tsx +2 -2
  517. package/src/Tokenizer/Tokenizer.doc.mjs +27 -0
  518. package/src/Tokenizer/Tokenizer.tsx +4 -0
  519. package/src/Toolbar/Toolbar.doc.mjs +5 -0
  520. package/src/Tooltip/Tooltip.doc.mjs +5 -0
  521. package/src/Tooltip/Tooltip.test.tsx +32 -0
  522. package/src/Tooltip/Tooltip.tsx +5 -0
  523. package/src/Tooltip/useTooltip.tsx +47 -24
  524. package/src/TopNav/TopNavHeading.tsx +5 -4
  525. package/src/TopNav/TopNavMegaMenu.tsx +3 -2
  526. package/src/TopNav/TopNavMenu.tsx +4 -3
  527. package/src/Typeahead/BaseTypeahead.doc.mjs +11 -0
  528. package/src/Typeahead/BaseTypeahead.tsx +85 -33
  529. package/src/Typeahead/Typeahead.doc.mjs +5 -0
  530. package/src/Typeahead/Typeahead.test.tsx +49 -0
  531. package/src/Typeahead/Typeahead.tsx +3 -2
  532. package/src/hooks/index.ts +2 -0
  533. package/src/hooks/useContainerReveal.doc.mjs +2 -2
  534. package/src/hooks/useFocusTrap.test.tsx +14 -0
  535. package/src/hooks/useFocusTrap.ts +62 -111
  536. package/src/hooks/useFocusTrapEscapeShim.test.tsx +165 -0
  537. package/src/hooks/useHotkeys.ts +3 -18
  538. package/src/hooks/useMergedRefs.doc.mjs +79 -0
  539. package/src/hooks/useMergedRefs.test.tsx +62 -0
  540. package/src/hooks/useMergedRefs.ts +36 -0
  541. package/src/index.ts +1 -0
  542. package/src/theme/MediaTheme.doc.mjs +5 -5
  543. package/src/theme/derivedVarRegistry.test.ts +19 -3
  544. package/src/theme/derivedVarRegistry.ts +23 -4
  545. package/src/theme/generateThemeRules.test.ts +37 -0
  546. package/src/theme/syntax/tokens.ts +5 -2
  547. package/src/utils/groupItems.ts +12 -6
  548. package/src/utils/index.ts +1 -1
  549. package/src/utils/isApplePlatform.test.ts +61 -0
  550. package/src/utils/isApplePlatform.ts +37 -0
  551. package/src/utils/sharedResizeObserver.ts +10 -2
  552. package/src/utils/timeParser.test.ts +6 -1
  553. package/src/utils/timeParser.ts +35 -13
  554. package/src/__tests__/inputWidthContract.test.tsx +0 -69
@@ -15,6 +15,7 @@ export type {
15
15
  DateTimeInputSize,
16
16
  DateTimeInputHourFormat,
17
17
  DateTimeInputTimeIncrement,
18
+ DateTimeInputTimeOptionInterval,
18
19
  DateTimeInputStatus,
19
20
  DateTimeInputStatusType,
20
21
  ISODateTimeString,
@@ -62,14 +62,14 @@ export const docs = {
62
62
  {
63
63
  name: 'width',
64
64
  type: 'number | string',
65
- description: 'Width of the dialog in pixels or any CSS value.',
65
+ description: 'Preferred width of the dialog in pixels or any CSS value. Standard dialogs clamp to their container and the dynamic viewport with spacing-token gutters so narrow viewports keep content on screen.',
66
66
  default: '400',
67
67
  },
68
68
  {
69
69
  name: 'maxHeight',
70
70
  type: 'number | string',
71
- description: 'Maximum height of the dialog.',
72
- default: "'75vh'",
71
+ description: 'Maximum height of the dialog. Defaults to a dynamic viewport value so browser UI changes are reflected where supported.',
72
+ default: "'75dvh'",
73
73
  },
74
74
  {
75
75
  name: 'position',
@@ -90,6 +90,11 @@ export const docs = {
90
90
  description: 'Controls dismissal behavior: required disables Escape and backdrop click; form disables backdrop click after interaction; info allows both.',
91
91
  default: "'info'",
92
92
  },
93
+ {
94
+ name: 'padding',
95
+ type: '0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10',
96
+ description: 'Internal padding of the dialog using the spacing scale step.',
97
+ },
93
98
  {
94
99
  name: 'isInline',
95
100
  type: 'boolean',
@@ -9,8 +9,9 @@
9
9
  * SYNC: When Dialog.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
+ import {readFileSync} from 'node:fs';
12
13
  import {describe, it, expect, vi, beforeEach} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
14
+ import {render, screen, fireEvent} from '@testing-library/react';
14
15
  import {Dialog, resolveDialogPositionOffsets} from './Dialog';
15
16
  import {DialogHeader} from './DialogHeader';
16
17
 
@@ -144,6 +145,195 @@ describe('Dialog', () => {
144
145
  });
145
146
  });
146
147
 
148
+ describe('IME composition', () => {
149
+ // A CJK user presses Escape to cancel a half-formed character several times
150
+ // a sentence. jsdom models neither composition nor the close watcher, so
151
+ // these pin the wiring; the behaviour itself is measured in Chromium
152
+ // against the Layer Dismissal stories.
153
+ function DialogWithField({onOpenChange}: {onOpenChange: () => void}) {
154
+ return (
155
+ <Dialog isOpen onOpenChange={onOpenChange} aria-label="Filters">
156
+ <input aria-label="Search" />
157
+ </Dialog>
158
+ );
159
+ }
160
+
161
+ it('claims the composing Escape instead of letting the browser act', () => {
162
+ const onOpenChange = vi.fn();
163
+ render(<DialogWithField onOpenChange={onOpenChange} />);
164
+
165
+ const event = new KeyboardEvent('keydown', {
166
+ key: 'Escape',
167
+ bubbles: true,
168
+ cancelable: true,
169
+ });
170
+ Object.defineProperty(event, 'isComposing', {value: true});
171
+ screen.getByRole('textbox', {name: 'Search'}).dispatchEvent(event);
172
+
173
+ // Unclaimed, this press becomes a close request that arrives at
174
+ // handleCancel and closes the dialog on the same keystroke.
175
+ expect(event.defaultPrevented).toBe(true);
176
+ expect(onOpenChange).not.toHaveBeenCalled();
177
+ });
178
+
179
+ it('ignores a close request that arrives mid-composition', () => {
180
+ // The back gesture and the platform close watcher carry no composition
181
+ // state, so the dialog asks the stack rather than the event.
182
+ const onOpenChange = vi.fn();
183
+ render(<DialogWithField onOpenChange={onOpenChange} />);
184
+ const field = screen.getByRole('textbox', {name: 'Search'});
185
+
186
+ fireEvent.compositionStart(field);
187
+ const duringComposition = new Event('cancel', {cancelable: true});
188
+ screen.getByRole('dialog').dispatchEvent(duringComposition);
189
+
190
+ expect(duringComposition.defaultPrevented).toBe(true);
191
+ expect(onOpenChange).not.toHaveBeenCalled();
192
+
193
+ fireEvent.compositionEnd(field);
194
+ screen
195
+ .getByRole('dialog')
196
+ .dispatchEvent(new Event('cancel', {cancelable: true}));
197
+
198
+ expect(onOpenChange).toHaveBeenCalledTimes(1);
199
+ expect(onOpenChange).toHaveBeenCalledWith(false);
200
+ });
201
+ });
202
+
203
+ describe('nested-modal dismissal', () => {
204
+ function NestedModals({
205
+ isInnerOpen,
206
+ onOuterChange,
207
+ onInnerChange,
208
+ }: {
209
+ isInnerOpen: boolean;
210
+ onOuterChange: (isOpen: boolean) => void;
211
+ onInnerChange: (isOpen: boolean) => void;
212
+ }) {
213
+ return (
214
+ <Dialog
215
+ isOpen={true}
216
+ onOpenChange={onOuterChange}
217
+ purpose="info"
218
+ aria-label="Outer">
219
+ Outer content
220
+ <Dialog
221
+ isOpen={isInnerOpen}
222
+ onOpenChange={onInnerChange}
223
+ purpose="info"
224
+ aria-label="Inner">
225
+ Inner content
226
+ </Dialog>
227
+ </Dialog>
228
+ );
229
+ }
230
+
231
+ const getDialog = (label: string) =>
232
+ screen
233
+ .getAllByRole('dialog', {hidden: true})
234
+ .find(d => d.getAttribute('aria-label') === label)!;
235
+
236
+ const pressEscape = (target: Element) => {
237
+ target.dispatchEvent(
238
+ new KeyboardEvent('keydown', {
239
+ key: 'Escape',
240
+ bubbles: true,
241
+ cancelable: true,
242
+ }),
243
+ );
244
+ };
245
+
246
+ it('closes only the inner modal, not the outer, on Escape', () => {
247
+ const onOuterChange = vi.fn();
248
+ const onInnerChange = vi.fn();
249
+
250
+ render(
251
+ <NestedModals
252
+ isInnerOpen={true}
253
+ onOuterChange={onOuterChange}
254
+ onInnerChange={onInnerChange}
255
+ />,
256
+ );
257
+
258
+ const outer = getDialog('Outer');
259
+ const inner = getDialog('Inner');
260
+ expect(outer.contains(inner)).toBe(true);
261
+
262
+ pressEscape(inner);
263
+
264
+ expect(onInnerChange).toHaveBeenCalledTimes(1);
265
+ expect(onInnerChange).toHaveBeenCalledWith(false);
266
+ expect(onOuterChange).not.toHaveBeenCalled();
267
+ });
268
+
269
+ it('closes the outer modal on the next Escape once the inner one is gone', () => {
270
+ const onOuterChange = vi.fn();
271
+ const onInnerChange = vi.fn();
272
+
273
+ const {rerender} = render(
274
+ <NestedModals
275
+ isInnerOpen={true}
276
+ onOuterChange={onOuterChange}
277
+ onInnerChange={onInnerChange}
278
+ />,
279
+ );
280
+ rerender(
281
+ <NestedModals
282
+ isInnerOpen={false}
283
+ onOuterChange={onOuterChange}
284
+ onInnerChange={onInnerChange}
285
+ />,
286
+ );
287
+
288
+ pressEscape(getDialog('Outer'));
289
+
290
+ expect(onOuterChange).toHaveBeenCalledTimes(1);
291
+ expect(onOuterChange).toHaveBeenCalledWith(false);
292
+ expect(onInnerChange).not.toHaveBeenCalled();
293
+ });
294
+
295
+ it('closes the top-most modal on a browser-initiated cancel', () => {
296
+ const onOuterChange = vi.fn();
297
+ const onInnerChange = vi.fn();
298
+
299
+ render(
300
+ <NestedModals
301
+ isInnerOpen={true}
302
+ onOuterChange={onOuterChange}
303
+ onInnerChange={onInnerChange}
304
+ />,
305
+ );
306
+
307
+ const cancelEvent = new Event('cancel', {cancelable: true});
308
+ getDialog('Inner').dispatchEvent(cancelEvent);
309
+
310
+ expect(cancelEvent.defaultPrevented).toBe(true);
311
+ expect(onInnerChange).toHaveBeenCalledTimes(1);
312
+ expect(onInnerChange).toHaveBeenCalledWith(false);
313
+ expect(onOuterChange).not.toHaveBeenCalled();
314
+ });
315
+
316
+ it('leaves a modal that is not top-most open on a browser-initiated cancel', () => {
317
+ const onOuterChange = vi.fn();
318
+ const onInnerChange = vi.fn();
319
+
320
+ render(
321
+ <NestedModals
322
+ isInnerOpen={true}
323
+ onOuterChange={onOuterChange}
324
+ onInnerChange={onInnerChange}
325
+ />,
326
+ );
327
+
328
+ const cancelEvent = new Event('cancel', {cancelable: true});
329
+ getDialog('Outer').dispatchEvent(cancelEvent);
330
+
331
+ expect(cancelEvent.defaultPrevented).toBe(true);
332
+ expect(onOuterChange).not.toHaveBeenCalled();
333
+ expect(onInnerChange).not.toHaveBeenCalled();
334
+ });
335
+ });
336
+
147
337
  describe('variant: standard', () => {
148
338
  it('renders with default variant', () => {
149
339
  render(
@@ -184,6 +374,76 @@ describe('Dialog', () => {
184
374
  });
185
375
  });
186
376
 
377
+ describe('responsive sizing', () => {
378
+ it('keeps the requested width but clamps standard dialogs to container and dynamic viewport gutters', () => {
379
+ render(
380
+ <Dialog
381
+ isOpen={true}
382
+ onOpenChange={() => {}}
383
+ width={600}
384
+ maxHeight="70dvh"
385
+ aria-label="Sized dialog">
386
+ Content
387
+ </Dialog>,
388
+ );
389
+
390
+ const dialog = screen.getByRole('dialog');
391
+ const inlineStyle = dialog.getAttribute('style') ?? '';
392
+ expect(inlineStyle).toContain('--x-width: 600px');
393
+ expect(inlineStyle).toContain(
394
+ '--x-maxWidth: min(100%, calc(100dvw - var(--spacing-4) - var(--spacing-4)))',
395
+ );
396
+ expect(inlineStyle).toContain('--x-maxHeight: 70dvh');
397
+ });
398
+
399
+ it('uses a fullscreen-specific fade animation instead of centered dialog movement', () => {
400
+ const source = readFileSync(
401
+ 'packages/core/src/Dialog/Dialog.tsx',
402
+ 'utf8',
403
+ );
404
+ const standardOpen = source.slice(
405
+ source.indexOf(' open: {'),
406
+ source.indexOf(' // Backdrop using ::backdrop'),
407
+ );
408
+ const fullscreenOpen = source.slice(
409
+ source.indexOf(' fullscreenOpen: {'),
410
+ source.indexOf(' fullscreenSafeArea: {'),
411
+ );
412
+ const modalStyleOrder = source.slice(
413
+ source.indexOf('focusOutlineProps.focusVisible('),
414
+ source.indexOf(
415
+ ' xstyle,',
416
+ source.indexOf('focusOutlineProps.focusVisible('),
417
+ ),
418
+ );
419
+
420
+ expect(standardOpen).toContain('enterDirectional');
421
+ expect(fullscreenOpen).toContain('enterFullscreen');
422
+ expect(fullscreenOpen).not.toContain('enterDirectional');
423
+ expect(modalStyleOrder.indexOf('styles.open')).toBeLessThan(
424
+ modalStyleOrder.indexOf('styles.fullscreenOpen'),
425
+ );
426
+ });
427
+
428
+ it('protects fullscreen content with safe-area padding', () => {
429
+ render(
430
+ <Dialog
431
+ isOpen={true}
432
+ onOpenChange={() => {}}
433
+ variant="fullscreen"
434
+ aria-label="Fullscreen dialog">
435
+ <div data-testid="child">Content</div>
436
+ </Dialog>,
437
+ );
438
+
439
+ const wrapper = screen.getByTestId('child').parentElement!;
440
+ const computed = window.getComputedStyle(wrapper);
441
+ expect(computed.paddingInlineStart).toContain('safe-area-inset-left');
442
+ expect(computed.paddingInlineEnd).toContain('safe-area-inset-right');
443
+ expect(wrapper.parentElement!.tagName).toBe('DIALOG');
444
+ });
445
+ });
446
+
187
447
  describe('position prop', () => {
188
448
  it('accepts position configuration', () => {
189
449
  render(
@@ -8,6 +8,12 @@
8
8
  * @output Exports Dialog component, DialogProps, DialogVariant, DialogPurpose types
9
9
  * @position Core implementation; consumed by index.ts, tested by Dialog.test.tsx
10
10
  *
11
+ * The standard variant treats `width` as the preferred surface width, then
12
+ * clamps it to the dynamic viewport with spacing-token gutters so narrow
13
+ * viewports keep content and controls on screen without changing the public API.
14
+ * Fullscreen dialogs preserve the same padding floor while honoring safe-area
15
+ * insets, and fade in without the centered-dialog translate/scale motion.
16
+ *
11
17
  * SYNC: When modified, update these files to stay in sync:
12
18
  * - /packages/core/src/Dialog/Dialog.doc.mjs (props table, features, implementation notes)
13
19
  * - /packages/core/src/Dialog/Dialog.test.tsx (tests for new/changed behavior)
@@ -27,14 +33,15 @@ import {
27
33
  import type {BaseProps} from '../BaseProps';
28
34
  import * as stylex from '@stylexjs/stylex';
29
35
  import {useScrollLock} from '../hooks/useScrollLock';
30
- import {hasActiveFocusTrapEscape} from '../hooks/useFocusTrap';
31
- import {isImeKeyEvent} from '../utils/ime';
36
+ import {LayerDepthProvider} from '../Layer/LayerDepthContext';
37
+ import {useLayerDismissal} from '../Layer/useLayerDismissal';
32
38
  import {
33
39
  colorVars,
34
40
  radiusVars,
35
41
  durationVars,
36
42
  easeVars,
37
43
  shadowVars,
44
+ spacingVars,
38
45
  } from '../theme/tokens.stylex';
39
46
  import {container} from '../Layout/container.stylex';
40
47
  import type {SpacingToken} from '../Layout/container.stylex';
@@ -47,13 +54,14 @@ import {
47
54
  overlayPaddingReset,
48
55
  } from '../Layout/padding.stylex';
49
56
  import type {SpacingStep} from '../utils/types';
50
- import {mergeProps, mergeRefs} from '../utils';
57
+ import {mergeProps} from '../utils';
51
58
  import {devWarn} from '../utils/devWarning';
52
59
  import {DialogContext} from './DialogContext';
53
60
  import {themeProps} from '../utils/themeProps';
54
61
  import type {DialogVariantMap} from './index';
55
62
  import {focusOutlineProps} from '../utils/focusOutline.stylex';
56
63
 
64
+ import {useMergedRefs} from '../hooks/useMergedRefs';
57
65
  /**
58
66
  * Calculate a directional translate offset for dialog entry animation.
59
67
  * Returns a normalized vector from the trigger element toward the viewport
@@ -118,6 +126,11 @@ const enterDirectional = stylex.keyframes({
118
126
  to: {opacity: 1, transform: 'translate(0, 0) scale(1)'},
119
127
  });
120
128
 
129
+ const enterFullscreen = stylex.keyframes({
130
+ from: {opacity: 0},
131
+ to: {opacity: 1},
132
+ });
133
+
121
134
  /**
122
135
  * Dialog styles using native <dialog> element
123
136
  * Uses ::backdrop pseudo-element for overlay
@@ -171,6 +184,22 @@ const styles = stylex.create({
171
184
  margin: 0,
172
185
  inset: 0,
173
186
  },
187
+ fullscreenOpen: {
188
+ animationName: {
189
+ default: enterFullscreen,
190
+ '@media (prefers-reduced-motion: reduce)': 'none',
191
+ },
192
+ },
193
+ fullscreenSafeArea: {
194
+ paddingBlockStart:
195
+ 'max(var(--container-padding-block-start), env(safe-area-inset-top, 0px))',
196
+ paddingBlockEnd:
197
+ 'max(var(--container-padding-block-end), env(safe-area-inset-bottom, 0px))',
198
+ paddingInlineStart:
199
+ 'max(var(--container-padding-inline-start), env(safe-area-inset-left, 0px))',
200
+ paddingInlineEnd:
201
+ 'max(var(--container-padding-inline-end), env(safe-area-inset-right, 0px))',
202
+ },
174
203
  inner: {
175
204
  display: 'flex',
176
205
  flexDirection: 'column',
@@ -194,12 +223,30 @@ const styles = stylex.create({
194
223
  },
195
224
  });
196
225
 
197
- // Dynamic styles for width, maxHeight, and position
226
+ const STANDARD_DIALOG_VIEWPORT_GUTTER = spacingVars['--spacing-4'];
227
+ const STANDARD_DIALOG_VIEWPORT_MAX_WIDTH = `calc(100dvw - ${STANDARD_DIALOG_VIEWPORT_GUTTER} - ${STANDARD_DIALOG_VIEWPORT_GUTTER})`;
228
+ const STANDARD_DIALOG_MAX_WIDTH = `min(100%, ${STANDARD_DIALOG_VIEWPORT_MAX_WIDTH})`;
229
+
230
+ function formatSizeValue(value: number | string): string {
231
+ return typeof value === 'number' ? `${value}px` : value;
232
+ }
233
+
234
+ function resolveDialogSizing(
235
+ width: number | string,
236
+ maxHeight: number | string,
237
+ ) {
238
+ return {
239
+ width: formatSizeValue(width),
240
+ maxWidth: STANDARD_DIALOG_MAX_WIDTH,
241
+ maxHeight: formatSizeValue(maxHeight),
242
+ };
243
+ }
244
+
198
245
  const dynamicStyles = stylex.create({
199
- sizing: (width: number | string, maxHeight: number | string) => ({
200
- width: typeof width === 'number' ? `${width}px` : width,
201
- maxWidth: '90vw',
202
- maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,
246
+ sizing: (width: string, maxWidth: string, maxHeight: string) => ({
247
+ width,
248
+ maxWidth,
249
+ maxHeight,
203
250
  }),
204
251
  position: (
205
252
  top: string,
@@ -288,7 +335,7 @@ export interface DialogProps extends BaseProps<HTMLDialogElement> {
288
335
  * The actual height will be the height of its content.
289
336
  * Numbers are treated as pixels, strings are used as-is.
290
337
  * Ignored when variant is 'fullscreen'.
291
- * @default '75vh'
338
+ * @default '75dvh'
292
339
  */
293
340
  maxHeight?: number | string;
294
341
 
@@ -356,7 +403,7 @@ export function Dialog({
356
403
  isInline = false,
357
404
  onOpenChange,
358
405
  width = 400,
359
- maxHeight = '75vh',
406
+ maxHeight = '75dvh',
360
407
  position,
361
408
  variant = 'standard',
362
409
  purpose = 'info',
@@ -374,6 +421,9 @@ export function Dialog({
374
421
  const paddingToken = spacingStepToToken[effectivePadding] as SpacingToken;
375
422
 
376
423
  const isFullscreen = variant === 'fullscreen';
424
+ const standardSizing = isFullscreen
425
+ ? null
426
+ : resolveDialogSizing(width, maxHeight);
377
427
 
378
428
  // Default accessible name: publish a title id through DialogContext so a
379
429
  // DialogHeader applies it to its heading (mirrors AlertDialog's explicit
@@ -415,6 +465,8 @@ export function Dialog({
415
465
  [titleId, hasConsumerName],
416
466
  );
417
467
 
468
+ const mergedDialogRef = useMergedRefs(ref, attachDialog);
469
+
418
470
  // Capture the element that was focused when the dialog opened,
419
471
  // for directional animation origin and focus restoration on close.
420
472
  const triggerElementRef = useRef<HTMLElement | null>(null);
@@ -474,34 +526,24 @@ export function Dialog({
474
526
  // Skip for inline rendering — no modal overlay to compensate for.
475
527
  useScrollLock(isOpen && !isInline);
476
528
 
477
- // Handle Escape key — skip for inline rendering
478
- useEffect(() => {
479
- if (isInline) {
480
- return;
481
- }
482
- const dialog = dialogRef.current;
483
- if (!dialog || !isOpen) {
484
- return;
485
- }
486
-
487
- const handleKeyDown = (event: KeyboardEvent) => {
488
- if (event.key === 'Escape') {
489
- // Ignore IME composition-cancel, and defer to any popover/menu layered
490
- // on top of this dialog so a single Escape closes only the top-most
491
- // layer (the popover's own focus trap handles it and stops propagation).
492
- if (isImeKeyEvent(event) || hasActiveFocusTrapEscape()) {
493
- return;
494
- }
495
- event.preventDefault();
496
- if (allowEscape) {
497
- onOpenChange(false);
498
- }
499
- }
500
- };
501
-
502
- dialog.addEventListener('keydown', handleKeyDown);
503
- return () => dialog.removeEventListener('keydown', handleKeyDown);
504
- }, [isOpen, isInline, allowEscape, onOpenChange]);
529
+ // Join the shared layer dismissal stack. The stack owns the Escape listener
530
+ // and routes each press to the top-most layer, so this dialog does not listen
531
+ // itself — that is what stops a modal opened from inside another modal (or a
532
+ // popover opened inside this one) from closing two layers on one press.
533
+ //
534
+ // A `required` dialog registers as `block`: it must not be dismissed, and the
535
+ // press must not fall through and dismiss a layer behind it either.
536
+ //
537
+ // Inline mode opts out — it renders dialog content in normal flow, with
538
+ // nothing layered over anything.
539
+ const {shouldDismissOnCloseRequest} = useLayerDismissal({
540
+ isActive: isOpen,
541
+ isEnabled: !isInline,
542
+ escapeBehavior: allowEscape ? 'close' : 'block',
543
+ onDismiss: () => {
544
+ onOpenChange(false);
545
+ },
546
+ });
505
547
 
506
548
  // Dev-time guardrail: an open modal should always have an accessible name.
507
549
  // The header-title check reads the DOM, so this stays in an effect; the ref
@@ -537,12 +579,18 @@ export function Dialog({
537
579
  }
538
580
  };
539
581
 
540
- // Handle native cancel event (browser Escape handling)
582
+ // The native `cancel` event is the browser's own close-watcher firing: an
583
+ // Android back gesture, or a close request the stack never saw a press for.
584
+ // Escape presses the stack owns never arrive here — it preventDefault()s
585
+ // those, which suppresses the close watcher.
586
+ //
587
+ // Always preventDefault so the browser cannot close a controlled <dialog>
588
+ // behind React's back, then answer the request with the stack's own rules:
589
+ // only the top-most layer dismisses, a `required` dialog swallows it, and
590
+ // nothing dismisses while an IME composition is in progress.
541
591
  const handleCancel = (event: React.SyntheticEvent<HTMLDialogElement>) => {
542
592
  event.preventDefault();
543
- // Defer to a popover/menu layered on top of this dialog; it will dismiss
544
- // itself on the same Escape press.
545
- if (hasActiveFocusTrapEscape()) {
593
+ if (!shouldDismissOnCloseRequest()) {
546
594
  return;
547
595
  }
548
596
  if (allowEscape) {
@@ -550,7 +598,6 @@ export function Dialog({
550
598
  }
551
599
  };
552
600
 
553
- // Shared inner content wrapper
554
601
  const innerContent = (
555
602
  <div
556
603
  {...stylex.props(
@@ -559,22 +606,14 @@ export function Dialog({
559
606
  useThemeDefault
560
607
  ? {
561
608
  useThemeDefault: 'dialog',
562
- maxHeight: isFullscreen
563
- ? undefined
564
- : typeof maxHeight === 'number'
565
- ? `${maxHeight}px`
566
- : maxHeight,
609
+ maxHeight: standardSizing?.maxHeight,
567
610
  }
568
611
  : {
569
612
  paddingInnerX: paddingToken,
570
613
  paddingInnerY: paddingToken,
571
614
  paddingOuterX: paddingToken,
572
615
  paddingOuterY: paddingToken,
573
- maxHeight: isFullscreen
574
- ? undefined
575
- : typeof maxHeight === 'number'
576
- ? `${maxHeight}px`
577
- : maxHeight,
616
+ maxHeight: standardSizing?.maxHeight,
578
617
  },
579
618
  ),
580
619
  !useThemeDefault &&
@@ -589,6 +628,7 @@ export function Dialog({
589
628
  !useThemeDefault &&
590
629
  effectivePadding !== 4 &&
591
630
  containerPaddingBlockEndVarStyles[effectivePadding],
631
+ isFullscreen && styles.fullscreenSafeArea,
592
632
  )}>
593
633
  <DialogContext value={dialogContextValue}>{children}</DialogContext>
594
634
  </div>
@@ -612,7 +652,12 @@ export function Dialog({
612
652
  stylex.props(
613
653
  styles.inlineWrapper,
614
654
  overlayPaddingReset.reset,
615
- !isFullscreen && dynamicStyles.sizing(width, maxHeight),
655
+ standardSizing &&
656
+ dynamicStyles.sizing(
657
+ standardSizing.width,
658
+ standardSizing.maxWidth,
659
+ standardSizing.maxHeight,
660
+ ),
616
661
  isFullscreen && styles.fullscreen,
617
662
  xstyle,
618
663
  ),
@@ -623,7 +668,7 @@ export function Dialog({
623
668
  (props as Record<string, unknown>)['data-testid'] as
624
669
  string | undefined
625
670
  }>
626
- {innerContent}
671
+ <LayerDepthProvider>{innerContent}</LayerDepthProvider>
627
672
  </div>
628
673
  );
629
674
  }
@@ -632,7 +677,7 @@ export function Dialog({
632
677
 
633
678
  return (
634
679
  <dialog
635
- ref={mergeRefs(ref, attachDialog)}
680
+ ref={mergedDialogRef}
636
681
  {...safeProps}
637
682
  {...mergeProps(
638
683
  themeProps('dialog', {variant}),
@@ -641,7 +686,12 @@ export function Dialog({
641
686
  overlayPaddingReset.reset,
642
687
  isOpen && styles.open,
643
688
  styles.backdrop,
644
- !isFullscreen && dynamicStyles.sizing(width, maxHeight),
689
+ standardSizing &&
690
+ dynamicStyles.sizing(
691
+ standardSizing.width,
692
+ standardSizing.maxWidth,
693
+ standardSizing.maxHeight,
694
+ ),
645
695
  hasPosition &&
646
696
  (() => {
647
697
  const o = resolveDialogPositionOffsets(position);
@@ -653,6 +703,7 @@ export function Dialog({
653
703
  );
654
704
  })(),
655
705
  isFullscreen && styles.fullscreen,
706
+ isFullscreen && isOpen && styles.fullscreenOpen,
656
707
  xstyle,
657
708
  ),
658
709
  className,
@@ -665,7 +716,7 @@ export function Dialog({
665
716
  // imperatively in `attachDialog`; a consumer-provided aria-labelledby
666
717
  // flows through {...safeProps} above and wins.
667
718
  {...(purpose === 'required' ? {role: 'alertdialog'} : undefined)}>
668
- {innerContent}
719
+ <LayerDepthProvider>{innerContent}</LayerDepthProvider>
669
720
  </dialog>
670
721
  );
671
722
  }
@@ -89,6 +89,12 @@ export const docs = {
89
89
  type: '() => void',
90
90
  description: 'Callback fired when the trigger button is clicked.',
91
91
  },
92
+ {
93
+ name: 'placement',
94
+ type: "'above' | 'below' | 'start' | 'end'",
95
+ description: 'Position placement for the menu popover relative to the trigger button.',
96
+ default: "'below'",
97
+ },
92
98
  {
93
99
  name: 'hasChevron',
94
100
  type: 'boolean',
@@ -701,11 +701,14 @@ describe('DropdownMenuSubMenu theming slots', () => {
701
701
  name: /Move to/,
702
702
  hidden: true,
703
703
  });
704
- // The indicator-icon slot wraps the chevron affordance inside the trigger
705
- // row.
706
- expect(
707
- trigger.querySelector('.astryx-dropdown-menu-indicator-icon'),
708
- ).toBeInTheDocument();
704
+ // The indicator-icon slot sits on the chevron glyph itself (the element
705
+ // that carries the icon size), so a theme can restyle its size/color
706
+ // directly.
707
+ const indicator = trigger.querySelector(
708
+ '.astryx-dropdown-menu-indicator-icon',
709
+ );
710
+ expect(indicator).toBeInTheDocument();
711
+ expect(indicator).toHaveClass('astryx-icon');
709
712
  });
710
713
  });
711
714