rift-ds 1.13.0 → 1.15.0

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 (382) hide show
  1. package/README.md +14 -6
  2. package/behaviors/LinkProvider.d.ts +3 -0
  3. package/behaviors/LocaleProvider.d.ts +27 -0
  4. package/behaviors/LocaleProvider.js +16 -0
  5. package/behaviors/ScrollRegion.d.ts +8 -0
  6. package/behaviors/ScrollRegion.js +51 -0
  7. package/behaviors/devWarning.d.ts +9 -0
  8. package/behaviors/devWarning.js +16 -0
  9. package/behaviors/direction.css +19 -0
  10. package/behaviors/direction.d.ts +40 -0
  11. package/behaviors/direction.js +56 -0
  12. package/behaviors/focusable.d.ts +2 -1
  13. package/behaviors/locale.d.ts +47 -0
  14. package/behaviors/locale.js +87 -0
  15. package/behaviors/shortcut-keys.d.ts +49 -0
  16. package/behaviors/shortcut-keys.js +129 -0
  17. package/behaviors/shortcuts.d.ts +75 -0
  18. package/behaviors/shortcuts.js +160 -0
  19. package/behaviors/useAnchoredPosition.d.ts +124 -0
  20. package/behaviors/useAnchoredPosition.js +158 -0
  21. package/behaviors/useFocusScope.d.ts +15 -2
  22. package/behaviors/useFocusScope.js +7 -1
  23. package/behaviors/useLayer.d.ts +9 -0
  24. package/behaviors/useLayer.js +4 -0
  25. package/behaviors/useScrollLock.d.ts +4 -0
  26. package/bin/rift-ds.mjs +184 -25
  27. package/bin/templates/app/components/rift/ColorModeScript.tsx +10 -0
  28. package/bin/templates/app/components/rift/RiftRoot.tsx +13 -0
  29. package/bin/templates/app/components/rift/font.css +7 -0
  30. package/bin/templates/app/components/rift/font.ts +14 -0
  31. package/bin/templates/blocks/app/dashboard/page.module.css +90 -0
  32. package/bin/templates/blocks/app/dashboard/page.tsx +141 -0
  33. package/bin/templates/blocks/app/sign-in/page.module.css +91 -0
  34. package/bin/templates/blocks/app/sign-in/page.tsx +95 -0
  35. package/bin/templates/blocks/app/starter/page.module.css +7 -0
  36. package/bin/templates/blocks/app/starter/page.tsx +31 -0
  37. package/bin/templates/blocks/components/blocks/AppShell.module.css +160 -0
  38. package/bin/templates/blocks/components/blocks/AppShell.tsx +113 -0
  39. package/bin/templates/blocks/registry.json +39 -0
  40. package/chat/AssistantTurn.js +4 -2
  41. package/chat/ChatWidget.css +24 -24
  42. package/chat/ChatWidget.js +4 -2
  43. package/chat/suggestions.d.ts +12 -0
  44. package/chat/types.d.ts +8 -7
  45. package/chat/useChat.js +2 -2
  46. package/components/Accordion/Accordion.css +1 -1
  47. package/components/AgentPlan/AgentPlan.css +2 -2
  48. package/components/AgentRail/AgentRail.css +4 -4
  49. package/components/AgentRail/AgentRail.js +1 -1
  50. package/components/AgentStatus/AgentRing.d.ts +27 -0
  51. package/components/AgentStatus/AgentRing.js +9 -0
  52. package/components/AgentStatus/AgentStatus.css +638 -10
  53. package/components/AgentStatus/AgentStatus.d.ts +31 -3
  54. package/components/AgentStatus/AgentStatus.js +22 -4
  55. package/components/AgentStatus/AgentStatusPatterns.d.ts +1 -1
  56. package/components/AgentStatus/AgentStatusPatterns.js +2 -1
  57. package/components/AiButton/AiButton.css +214 -41
  58. package/components/AiButton/AiButton.d.ts +34 -0
  59. package/components/AiButton/AiButton.js +130 -9
  60. package/components/AlertDialog/AlertDialog.css +1 -1
  61. package/components/AnchorNav/AnchorNav.css +6 -6
  62. package/components/AnchorNav/AnchorNav.js +4 -2
  63. package/components/AnimatedNumber/AnimatedNumber.d.ts +6 -0
  64. package/components/AnimatedNumber/AnimatedNumber.js +12 -5
  65. package/components/AppLayout/AppLayout.css +3 -3
  66. package/components/AppSidebar/AppSidebar.css +20 -20
  67. package/components/AppSidebar/AppSidebar.js +6 -4
  68. package/components/ArtifactCard/ArtifactCard.css +1 -1
  69. package/components/ArtifactCard/ArtifactCard.js +2 -1
  70. package/components/ArtifactPanel/ArtifactPanel.js +2 -1
  71. package/components/Attachment/Attachment.css +4 -4
  72. package/components/Attachment/AttachmentDropZone.js +2 -1
  73. package/components/Attachment/AttachmentGroup.d.ts +2 -0
  74. package/components/Attachment/AttachmentGroup.js +4 -1
  75. package/components/Attachment/AttachmentViewer.d.ts +2 -0
  76. package/components/Attachment/AttachmentViewer.js +8 -13
  77. package/components/Attachment/fileTypes.d.ts +5 -3
  78. package/components/Attachment/fileTypes.js +8 -3
  79. package/components/Avatar/Avatar.css +55 -1
  80. package/components/Avatar/Avatar.d.ts +9 -1
  81. package/components/Avatar/Avatar.js +5 -1
  82. package/components/Avatar/avatarFill.d.ts +9 -0
  83. package/components/Avatar/avatarFill.js +13 -0
  84. package/components/AvatarGroup/AvatarGroup.css +1 -1
  85. package/components/AvatarGroup/AvatarGroup.js +13 -1
  86. package/components/BentoTile/BentoTile.css +15 -3
  87. package/components/BottomNav/BottomNav.css +8 -8
  88. package/components/BottomNav/BottomNav.js +3 -1
  89. package/components/Breadcrumb/Breadcrumb.js +2 -2
  90. package/components/Button/Button.css +8 -1
  91. package/components/Button/Button.d.ts +8 -1
  92. package/components/Button/Button.js +14 -3
  93. package/components/Card/Card.css +3 -3
  94. package/components/CardStack/CardStack.js +4 -2
  95. package/components/Carousel/Carousel.css +5 -2
  96. package/components/Carousel/Carousel.js +11 -7
  97. package/components/Chart/AreaChart.d.ts +2 -0
  98. package/components/Chart/AreaChart.js +15 -6
  99. package/components/Chart/BarChart.d.ts +2 -0
  100. package/components/Chart/BarChart.js +15 -5
  101. package/components/Chart/Chart.css +6 -6
  102. package/components/Chart/ComboChart.d.ts +2 -0
  103. package/components/Chart/ComboChart.js +16 -6
  104. package/components/Chart/LineChart.d.ts +2 -0
  105. package/components/Chart/LineChart.js +15 -6
  106. package/components/Chart/PieChart.d.ts +2 -0
  107. package/components/Chart/PieChart.js +9 -5
  108. package/components/Chart/RadarChart.d.ts +2 -0
  109. package/components/Chart/RadarChart.js +15 -6
  110. package/components/Chart/RadialChart.d.ts +2 -0
  111. package/components/Chart/RadialChart.js +9 -5
  112. package/components/Chart/ScatterChart.d.ts +2 -0
  113. package/components/Chart/ScatterChart.js +17 -7
  114. package/components/Chart/StackedBarChart.d.ts +2 -0
  115. package/components/Chart/StackedBarChart.js +15 -7
  116. package/components/Chart/Treemap.d.ts +2 -0
  117. package/components/Chart/Treemap.js +9 -5
  118. package/components/ChatMarker/ChatMarker.js +2 -1
  119. package/components/ChatMessage/ChatMessage.css +2 -2
  120. package/components/ChatThread/ChatThread.css +1 -1
  121. package/components/Checkbox/Checkbox.js +1 -1
  122. package/components/CircularButton/CircularButton.css +8 -7
  123. package/components/CircularButton/CircularButton.d.ts +2 -2
  124. package/components/CircularButton/CircularButton.js +2 -1
  125. package/components/CodeBlock/CodeBlock.d.ts +2 -0
  126. package/components/CodeBlock/CodeBlock.js +2 -0
  127. package/components/CodeDiff/CodeDiff.css +2 -2
  128. package/components/ColorPicker/ColorPicker.css +8 -8
  129. package/components/ColorPicker/ColorPicker.js +11 -0
  130. package/components/ColourSwatch/ColourSwatch.css +1 -1
  131. package/components/Combobox/Combobox.css +20 -5
  132. package/components/Combobox/Combobox.js +75 -55
  133. package/components/CommandPalette/CommandPalette.d.ts +13 -1
  134. package/components/CommandPalette/CommandPalette.js +12 -13
  135. package/components/CommandPaletteTrigger/CommandPaletteTrigger.css +88 -0
  136. package/components/CommandPaletteTrigger/CommandPaletteTrigger.d.ts +41 -0
  137. package/components/CommandPaletteTrigger/CommandPaletteTrigger.js +58 -0
  138. package/components/Composer/Composer.css +7 -7
  139. package/components/Composer/Composer.d.ts +2 -0
  140. package/components/Composer/Composer.js +6 -2
  141. package/components/ContactCard/ContactCard.css +1 -1
  142. package/components/ContactCard/ContactCard.js +4 -3
  143. package/components/ContextMenu/ContextMenu.css +10 -7
  144. package/components/ContextMenu/ContextMenu.d.ts +3 -0
  145. package/components/ContextMenu/ContextMenu.js +15 -9
  146. package/components/ContributionGraph/ContributionGraph.css +2 -2
  147. package/components/ContributionGraph/ContributionGraph.d.ts +7 -1
  148. package/components/ContributionGraph/ContributionGraph.js +6 -4
  149. package/components/DataTable/CellEditor.d.ts +37 -0
  150. package/components/DataTable/CellEditor.js +153 -0
  151. package/components/DataTable/DataTable.css +1181 -1
  152. package/components/DataTable/DataTable.d.ts +506 -29
  153. package/components/DataTable/DataTable.js +2374 -190
  154. package/components/DataTable/FilterEditor.d.ts +10 -0
  155. package/components/DataTable/FilterEditor.js +181 -0
  156. package/components/DataTable/TypedCell.d.ts +22 -0
  157. package/components/DataTable/TypedCell.js +80 -0
  158. package/components/DataTable/VirtualRows.d.ts +21 -0
  159. package/components/DataTable/VirtualRows.js +38 -0
  160. package/components/DataTable/aggregates.d.ts +29 -0
  161. package/components/DataTable/aggregates.js +52 -0
  162. package/components/DataTable/columnTypes.d.ts +49 -0
  163. package/components/DataTable/columnTypes.js +96 -0
  164. package/components/DataTable/dragGesture.d.ts +24 -0
  165. package/components/DataTable/dragGesture.js +62 -0
  166. package/components/DataTable/editorKinds.d.ts +24 -0
  167. package/components/DataTable/editorKinds.js +20 -0
  168. package/components/DataTable/filterModel.d.ts +84 -0
  169. package/components/DataTable/filterModel.js +159 -0
  170. package/components/DataTable/tableContext.d.ts +82 -0
  171. package/components/DataTable/tableContext.js +7 -0
  172. package/components/DataTable/views.d.ts +50 -0
  173. package/components/DataTable/views.js +39 -0
  174. package/components/DateInput/DateInput.css +10 -4
  175. package/components/DatePicker/DatePicker.d.ts +23 -7
  176. package/components/DatePicker/DatePicker.js +41 -34
  177. package/components/Dialog/Dialog.css +1 -1
  178. package/components/DocumentChip/DocumentChip.css +1 -1
  179. package/components/DocumentChip/DocumentChip.js +2 -1
  180. package/components/Drawer/Drawer.css +46 -12
  181. package/components/Drawer/Drawer.d.ts +6 -2
  182. package/components/Drawer/Drawer.js +3 -1
  183. package/components/Dropdown/ChosenMark.css +1 -1
  184. package/components/Dropdown/Dropdown.css +23 -7
  185. package/components/Dropdown/Dropdown.d.ts +6 -0
  186. package/components/Dropdown/Dropdown.js +41 -10
  187. package/components/DropdownMenu/DropdownMenu.css +38 -17
  188. package/components/DropdownMenu/DropdownMenu.d.ts +10 -1
  189. package/components/DropdownMenu/DropdownMenu.js +80 -23
  190. package/components/DropdownMenu/ShortcutHint.d.ts +12 -0
  191. package/components/DropdownMenu/ShortcutHint.js +17 -0
  192. package/components/EmptyState/EmptyState.js +2 -1
  193. package/components/EntityCard/EntityCard.js +2 -1
  194. package/components/EventCalendar/EventCalendar.css +1 -1
  195. package/components/EventCalendar/EventCalendar.d.ts +21 -6
  196. package/components/EventCalendar/EventCalendar.js +33 -31
  197. package/components/FileInput/FileInput.d.ts +2 -0
  198. package/components/FileInput/FileInput.js +4 -1
  199. package/components/FilterBar/FilterBar.css +10 -6
  200. package/components/FilterBar/FilterBar.js +69 -44
  201. package/components/FunnelChart/FunnelChart.d.ts +2 -0
  202. package/components/FunnelChart/FunnelChart.js +11 -7
  203. package/components/GanttChart/GanttChart.css +1 -0
  204. package/components/GanttChart/GanttChart.js +2 -1
  205. package/components/Gauge/Gauge.css +1 -1
  206. package/components/Gauge/Gauge.d.ts +2 -0
  207. package/components/Gauge/Gauge.js +3 -1
  208. package/components/Globe/Globe.css +1 -0
  209. package/components/HoverCard/HoverCard.css +25 -44
  210. package/components/HoverCard/HoverCard.d.ts +2 -2
  211. package/components/HoverCard/HoverCard.js +18 -2
  212. package/components/ImageCompare/ImageCompare.css +1 -0
  213. package/components/Input/Input.css +14 -8
  214. package/components/Input/Input.js +3 -2
  215. package/components/Inspector/Inspector.css +12 -11
  216. package/components/Inspector/InspectorSlider.d.ts +2 -0
  217. package/components/Inspector/InspectorSlider.js +8 -1
  218. package/components/Instructions/Instructions.css +1 -1
  219. package/components/Instructions/Instructions.js +2 -1
  220. package/components/InterruptCard/InterruptCard.css +9 -1
  221. package/components/InterruptCard/InterruptCard.d.ts +2 -0
  222. package/components/InterruptCard/InterruptCard.js +9 -2
  223. package/components/Kbd/Kbd.css +21 -0
  224. package/components/Kbd/Kbd.d.ts +14 -4
  225. package/components/Kbd/Kbd.js +10 -1
  226. package/components/LegendTile/LegendTile.d.ts +6 -1
  227. package/components/LegendTile/LegendTile.js +3 -2
  228. package/components/Lightbox/Lightbox.css +3 -3
  229. package/components/Lightbox/Lightbox.js +6 -14
  230. package/components/LinkList/LinkList.js +3 -2
  231. package/components/MapCallout/MapCallout.css +1 -0
  232. package/components/Mention/Mention.js +2 -1
  233. package/components/MessageActions/MessageActions.js +2 -1
  234. package/components/MessageCard/MessageCard.css +1 -1
  235. package/components/MessageCard/MessageCard.js +2 -1
  236. package/components/Meter/Meter.css +1 -1
  237. package/components/Meter/Meter.d.ts +2 -0
  238. package/components/Meter/Meter.js +3 -1
  239. package/components/ModelPicker/ModelPicker.css +14 -11
  240. package/components/ModelPicker/ModelPicker.d.ts +2 -0
  241. package/components/ModelPicker/ModelPicker.js +15 -1
  242. package/components/MotionSwatch/MotionSwatch.css +3 -3
  243. package/components/NavList/NavList.css +3 -3
  244. package/components/NotificationCenter/NotificationCenter.d.ts +2 -0
  245. package/components/NotificationCenter/NotificationCenter.js +7 -2
  246. package/components/Pagination/Pagination.css +8 -6
  247. package/components/Pagination/Pagination.d.ts +2 -0
  248. package/components/Pagination/Pagination.js +6 -2
  249. package/components/PinInput/PinInput.js +3 -2
  250. package/components/PixelAvatar/pixelInks.d.ts +6 -0
  251. package/components/PixelAvatar/pixelInks.js +7 -3
  252. package/components/Popover/Popover.css +32 -30
  253. package/components/Popover/Popover.d.ts +10 -2
  254. package/components/Popover/Popover.js +16 -2
  255. package/components/ProgressBar/ProgressBar.css +1 -1
  256. package/components/ProgressBar/ProgressBar.d.ts +2 -0
  257. package/components/ProgressBar/ProgressBar.js +3 -5
  258. package/components/PromptSuggestions/PromptSuggestions.css +10 -3
  259. package/components/PromptSuggestions/PromptSuggestions.js +3 -1
  260. package/components/Prose/Prose.css +50 -8
  261. package/components/Prose/Prose.d.ts +2 -0
  262. package/components/Prose/Prose.js +7 -2
  263. package/components/QuestionCard/QuestionCard.css +1 -1
  264. package/components/Quote/Quote.css +4 -4
  265. package/components/Rating/Rating.js +4 -3
  266. package/components/Reasoning/Reasoning.css +8 -8
  267. package/components/Reasoning/Reasoning.js +1 -0
  268. package/components/RichDropdown/RichDropdown.css +18 -5
  269. package/components/RichDropdown/RichDropdown.js +13 -0
  270. package/components/SegmentedControl/SegmentedControl.css +9 -1
  271. package/components/SegmentedControl/SegmentedControl.js +5 -3
  272. package/components/ShaderField/useShaderField.js +13 -1
  273. package/components/SidebarPanel/SidebarPanel.css +7 -7
  274. package/components/SidebarPanel/SidebarPanel.js +2 -2
  275. package/components/Skeleton/Skeleton.css +2 -2
  276. package/components/Skeleton/Skeleton.js +1 -0
  277. package/components/Slider/Slider.css +16 -1
  278. package/components/Slider/Slider.js +1 -1
  279. package/components/SourceChip/SourceChip.css +6 -12
  280. package/components/SourceChip/SourceChip.d.ts +2 -0
  281. package/components/SourceChip/SourceChip.js +16 -37
  282. package/components/SourceTrail/SourceTrail.css +1 -1
  283. package/components/SpacingSwatch/SpacingSwatch.css +3 -3
  284. package/components/SplitButton/SplitButton.css +2 -2
  285. package/components/SplitButton/SplitButton.js +1 -0
  286. package/components/SplitPane/SplitPane.js +3 -2
  287. package/components/Stat/Stat.css +1 -1
  288. package/components/Stat/Stat.d.ts +2 -0
  289. package/components/Stat/Stat.js +3 -1
  290. package/components/Stepper/Stepper.css +5 -5
  291. package/components/Stepper/Stepper.d.ts +2 -0
  292. package/components/Stepper/Stepper.js +5 -1
  293. package/components/StreamingText/StreamingText.css +1 -1
  294. package/components/Table/Table.css +63 -21
  295. package/components/Table/Table.d.ts +10 -2
  296. package/components/Table/Table.js +34 -12
  297. package/components/TableToolbar/TableToolbar.css +494 -0
  298. package/components/TableToolbar/TableToolbar.d.ts +281 -0
  299. package/components/TableToolbar/TableToolbar.js +1322 -0
  300. package/components/Tabs/Tabs.js +3 -2
  301. package/components/ThemePicker/ThemePicker.css +189 -0
  302. package/components/ThemePicker/ThemePicker.d.ts +67 -0
  303. package/components/ThemePicker/ThemePicker.js +165 -0
  304. package/components/ThemeToggle/ThemeToggle.css +82 -0
  305. package/components/ThemeToggle/ThemeToggle.d.ts +42 -0
  306. package/components/ThemeToggle/ThemeToggle.js +98 -0
  307. package/components/ThemeToggle/colorMode.d.ts +42 -0
  308. package/components/ThemeToggle/colorMode.js +26 -0
  309. package/components/ThemeToggle/useColorMode.d.ts +8 -0
  310. package/components/ThemeToggle/useColorMode.js +44 -0
  311. package/components/ThreadPanel/ThreadPanel.css +19 -5
  312. package/components/ThreadPanel/ThreadPanel.d.ts +6 -1
  313. package/components/ThreadPanel/ThreadPanel.js +11 -7
  314. package/components/ThreadTabs/ThreadTabs.css +8 -1
  315. package/components/ThreadTabs/ThreadTabs.js +4 -2
  316. package/components/TimePicker/TimePicker.css +12 -6
  317. package/components/TimePicker/TimePicker.d.ts +17 -4
  318. package/components/TimePicker/TimePicker.js +32 -3
  319. package/components/Timeline/Timeline.css +8 -8
  320. package/components/Timeline/Timeline.d.ts +2 -0
  321. package/components/Timeline/Timeline.js +5 -2
  322. package/components/Toast/Toast.css +5 -5
  323. package/components/Toast/Toast.js +3 -2
  324. package/components/ToggleGroup/ToggleGroup.css +11 -1
  325. package/components/ToggleSwitch/ToggleSwitch.css +5 -5
  326. package/components/ToggleSwitch/ToggleSwitch.js +1 -0
  327. package/components/ToolCall/ToolCall.css +3 -3
  328. package/components/Toolbar/Toolbar.js +4 -2
  329. package/components/Tooltip/Tooltip.css +25 -25
  330. package/components/Tooltip/Tooltip.d.ts +2 -2
  331. package/components/Tooltip/Tooltip.js +16 -60
  332. package/components/TopAppBar/TopAppBar.css +7 -5
  333. package/components/TopAppBar/TopAppBar.js +3 -1
  334. package/components/TreeView/TreeView.css +1 -1
  335. package/components/TreeView/TreeView.js +4 -3
  336. package/components/UsageCard/UsageCard.d.ts +2 -0
  337. package/components/UsageCard/UsageCard.js +3 -0
  338. package/components/Widget/Widget.css +1 -1
  339. package/components/Widget/Widget.d.ts +2 -0
  340. package/components/Widget/Widget.js +3 -2
  341. package/components/WorldMap/WorldMap.css +1 -0
  342. package/components/registry.json +37 -5
  343. package/components/registry.json.d.ts +37 -5
  344. package/components/registry.json.js +1 -1
  345. package/fonts/material-symbols.css +13 -1
  346. package/fonts/presets/anton-400-latin-ext.woff2 +0 -0
  347. package/fonts/presets/anton-400-latin.woff2 +0 -0
  348. package/fonts/presets/work-sans-300-700-latin-ext.woff2 +0 -0
  349. package/fonts/presets/work-sans-300-700-latin.woff2 +0 -0
  350. package/index.d.ts +12 -0
  351. package/index.js +34 -2
  352. package/package.json +13 -1
  353. package/tokens/base.css +84 -0
  354. package/tokens/motion.d.ts +4 -0
  355. package/tokens/motion.js +4 -0
  356. package/tokens/presets/contrast.css +6 -2
  357. package/tokens/presets/coral.css +4 -2
  358. package/tokens/presets/forest.css +6 -0
  359. package/tokens/presets/gold.css +10 -2
  360. package/tokens/presets/marquee.css +299 -0
  361. package/tokens/presets/mono.css +4 -2
  362. package/tokens/presets/pink.css +10 -0
  363. package/tokens/presets/presets.css +1 -0
  364. package/tokens/presets/terminal.css +8 -2
  365. package/tokens/presets/violet.css +10 -0
  366. package/tokens/presets/warm.css +4 -0
  367. package/tokens/presets/zest.css +6 -0
  368. package/tokens/registry.d.ts +1 -1
  369. package/tokens/registry.json +32 -1
  370. package/tokens/registry.json.d.ts +32 -1
  371. package/tokens/registry.json.js +1 -1
  372. package/tokens/tokens-dark.css +53 -0
  373. package/tokens/tokens-forced-colors.css +31 -0
  374. package/tokens/tokens-light.css +66 -0
  375. package/tokens/tokens-primitives.css +9 -0
  376. package/tokens/tokens.css +1 -0
  377. package/cli/templates/chat/app/api/chat/feedback/route.d.ts +0 -3
  378. package/cli/templates/chat/app/api/chat/followups/route.d.ts +0 -3
  379. package/cli/templates/chat/app/api/chat/guardrails.d.ts +0 -10
  380. package/cli/templates/chat/app/api/chat/route.d.ts +0 -4
  381. package/cli/templates/chat/components/chat/ChatMount.d.ts +0 -1
  382. package/cli/templates/chat/components/chat/models.d.ts +0 -11
@@ -0,0 +1,129 @@
1
+ const KEY_ALIASES = {
2
+ esc: "escape",
3
+ return: "enter",
4
+ up: "arrowup",
5
+ down: "arrowdown",
6
+ left: "arrowleft",
7
+ right: "arrowright",
8
+ del: "delete",
9
+ plus: "+",
10
+ " ": "space",
11
+ cmd: "meta",
12
+ command: "meta",
13
+ option: "alt",
14
+ control: "ctrl"
15
+ };
16
+ const MODIFIERS = /* @__PURE__ */ new Set(["mod", "ctrl", "meta", "alt", "shift"]);
17
+ const parseStep = (text) => {
18
+ const step = { key: "", mod: false, ctrl: false, meta: false, alt: false, shift: false };
19
+ for (const raw of text.toLowerCase().split("+")) {
20
+ const part = KEY_ALIASES[raw] ?? raw;
21
+ if (MODIFIERS.has(part)) step[part] = true;
22
+ else step.key = part;
23
+ }
24
+ return step;
25
+ };
26
+ const parseShortcut = (shortcut) => shortcut.trim().split(/\s+/).filter(Boolean).map(parseStep);
27
+ const detectPlatform = () => {
28
+ if (typeof navigator === "undefined") return "other";
29
+ const nav = navigator;
30
+ const name = nav.userAgentData?.platform || nav.platform || nav.userAgent || "";
31
+ return /mac|iphone|ipad|ipod/i.test(name) ? "apple" : "other";
32
+ };
33
+ const APPLE_LABELS = {
34
+ mod: "⌘",
35
+ meta: "⌘",
36
+ ctrl: "⌃",
37
+ alt: "⌥",
38
+ shift: "⇧",
39
+ enter: "↩",
40
+ escape: "Esc",
41
+ backspace: "⌫",
42
+ delete: "⌦",
43
+ tab: "⇥",
44
+ space: "Space"
45
+ };
46
+ const OTHER_LABELS = {
47
+ mod: "Ctrl",
48
+ meta: "Meta",
49
+ ctrl: "Ctrl",
50
+ alt: "Alt",
51
+ shift: "Shift",
52
+ enter: "Enter",
53
+ escape: "Esc",
54
+ backspace: "Backspace",
55
+ delete: "Del",
56
+ tab: "Tab",
57
+ space: "Space"
58
+ };
59
+ const SHARED_LABELS = {
60
+ arrowup: "↑",
61
+ arrowdown: "↓",
62
+ arrowleft: "←",
63
+ arrowright: "→",
64
+ home: "Home",
65
+ end: "End",
66
+ pageup: "PgUp",
67
+ pagedown: "PgDn"
68
+ };
69
+ const APPLE_ORDER = ["ctrl", "alt", "shift", "mod", "meta"];
70
+ const OTHER_ORDER = ["mod", "ctrl", "meta", "alt", "shift"];
71
+ function shortcutKeys(shortcut, platform, labels = {}) {
72
+ const table = { ...SHARED_LABELS, ...platform === "apple" ? APPLE_LABELS : OTHER_LABELS };
73
+ const legend = (name) => labels[name] ?? table[name] ?? (/^f\d+$/.test(name) ? name.toUpperCase() : name.length === 1 ? name.toUpperCase() : name);
74
+ return parseShortcut(shortcut).map((step) => {
75
+ const order = platform === "apple" ? APPLE_ORDER : OTHER_ORDER;
76
+ const mods = order.filter((mod) => step[mod]);
77
+ const unique = mods.filter((mod, i) => !mods.slice(0, i).some((m) => legend(m) === legend(mod)));
78
+ return [...unique.map(legend), legend(step.key)];
79
+ });
80
+ }
81
+ function formatShortcut(shortcut, platform, labels = {}) {
82
+ return shortcutKeys(shortcut, platform, labels).map((keys) => keys.join(platform === "apple" ? "" : "+")).join(" ");
83
+ }
84
+ const ARIA_KEYS = {
85
+ ctrl: "Control",
86
+ meta: "Meta",
87
+ alt: "Alt",
88
+ shift: "Shift",
89
+ escape: "Escape",
90
+ enter: "Enter",
91
+ space: "Space",
92
+ tab: "Tab",
93
+ backspace: "Backspace",
94
+ delete: "Delete",
95
+ arrowup: "ArrowUp",
96
+ arrowdown: "ArrowDown",
97
+ arrowleft: "ArrowLeft",
98
+ arrowright: "ArrowRight",
99
+ home: "Home",
100
+ end: "End",
101
+ pageup: "PageUp",
102
+ pagedown: "PageDown"
103
+ };
104
+ function shortcutAria(shortcut, platform) {
105
+ const steps = parseShortcut(shortcut);
106
+ if (steps.length !== 1) return void 0;
107
+ const [step] = steps;
108
+ const names = [
109
+ step.ctrl || step.mod && platform === "other" ? "Control" : null,
110
+ step.meta || step.mod && platform === "apple" ? "Meta" : null,
111
+ step.alt ? "Alt" : null,
112
+ step.shift ? "Shift" : null,
113
+ ARIA_KEYS[step.key] ?? (step.key.length === 1 ? step.key.toUpperCase() : step.key)
114
+ ].filter(Boolean);
115
+ return names.join("+");
116
+ }
117
+ function shortcutAriaAnyPlatform(shortcut) {
118
+ const forms = [shortcutAria(shortcut, "apple"), shortcutAria(shortcut, "other")].filter(Boolean);
119
+ return forms.length > 0 ? [...new Set(forms)].join(" ") : void 0;
120
+ }
121
+ export {
122
+ KEY_ALIASES,
123
+ detectPlatform,
124
+ formatShortcut,
125
+ parseShortcut,
126
+ shortcutAria,
127
+ shortcutAriaAnyPlatform,
128
+ shortcutKeys
129
+ };
@@ -0,0 +1,75 @@
1
+ import { Shortcut, ShortcutPlatform } from './shortcut-keys';
2
+ export { detectPlatform, formatShortcut, parseShortcut, shortcutAria, shortcutKeys, type Shortcut, type ShortcutKeyLabels, type ShortcutPlatform, } from './shortcut-keys';
3
+ /**
4
+ * Keyboard shortcuts: the one document keydown listener every app-level key
5
+ * binding goes through, the grammar a shortcut is written in, and the way it
6
+ * is drawn on each platform.
7
+ *
8
+ * A shortcut is a string: steps separated by spaces, each step modifiers and
9
+ * a key joined by `+`. `mod+k` is ⌘K on Apple platforms and Ctrl+K
10
+ * elsewhere; `shift+?`, `alt+arrowup`, `escape`; `g i` is G then I. Keys are
11
+ * a character or a name (enter, escape, space, tab, backspace, delete, the
12
+ * arrows, home, end, pageup, pagedown, f1 to f12, plus).
13
+ *
14
+ * Who hears a key, in order:
15
+ * - Nothing does while an IME is composing, or once a component has handled
16
+ * the key itself (`event.preventDefault()`), so a list's own arrow keys or
17
+ * an input's own Escape never also fire a shortcut. Escape is the layer
18
+ * stack's while any overlay is open (useLayer), so an `escape` shortcut
19
+ * is for closing something that is not a library overlay.
20
+ * - Nothing does while focus is in a text field, unless the shortcut opts in
21
+ * with `allowInInput` (a palette's Cmd+K should work from a search box).
22
+ * - A `global` shortcut always does.
23
+ * - A `scope`d shortcut does while focus is inside its element, or while
24
+ * nothing has focus and the element sits on the surface in front (the open
25
+ * modal, else the page). Lightbox's arrows are scoped to its panel.
26
+ * - Any other shortcut belongs to the page: it does only while no modal is
27
+ * open and focus is not inside an open overlay (a menu, a popover), the
28
+ * same topmost-only rule the layer stack applies to Escape.
29
+ * When several match, the longest sequence wins, then the shortcut
30
+ * registered last. A matched key's default is prevented.
31
+ *
32
+ * Exactly one document listener exists while any shortcut is registered, and
33
+ * none otherwise. scripts/validate-overlay-listeners.mjs holds every other
34
+ * document key listener in the library and the site to this module.
35
+ */
36
+ export interface ShortcutOptions {
37
+ /** What the shortcut does, in a few words, for a help list built from `useRegisteredShortcuts`. */
38
+ description?: string;
39
+ /** The heading a help list files the shortcut under, such as "Navigation". */
40
+ group?: string;
41
+ /** Whether the shortcut is live. Default true. */
42
+ enabled?: boolean;
43
+ /** Fires only while focus is inside this element (see the module's rules). */
44
+ scope?: React.RefObject<HTMLElement | null>;
45
+ /** Fires whatever is open or focused, outside text fields. Default false. */
46
+ global?: boolean;
47
+ /** Fires while focus is in a text field too. Default false. */
48
+ allowInInput?: boolean;
49
+ }
50
+ /**
51
+ * The platform shortcuts are drawn for. The server and the first client
52
+ * render both say `other`, then the client settles on its own, so drawing a
53
+ * shortcut never breaks hydration.
54
+ */
55
+ export declare function usePlatform(): ShortcutPlatform;
56
+ /**
57
+ * Binds a shortcut (or several, as an array) to `handler` while the component
58
+ * is mounted and `enabled` is not false. The handler and options are read at
59
+ * key time, so passing new ones each render never re-registers the shortcut.
60
+ */
61
+ export declare function useShortcut(shortcut: Shortcut | Shortcut[], handler: (event: KeyboardEvent) => void, options?: ShortcutOptions): void;
62
+ /** A live shortcut as a help list reads it. */
63
+ export interface RegisteredShortcut {
64
+ /** The shortcuts that run it, in the grammar. */
65
+ shortcuts: Shortcut[];
66
+ /** What it does, from the binding's `description`. */
67
+ description: string;
68
+ /** Its heading, from the binding's `group`. */
69
+ group?: string;
70
+ }
71
+ /**
72
+ * Every live shortcut that has a description, in registration order, for a
73
+ * "keyboard shortcuts" help dialog. Updates as shortcuts mount and unmount.
74
+ */
75
+ export declare function useRegisteredShortcuts(): RegisteredShortcut[];
@@ -0,0 +1,160 @@
1
+ "use client";
2
+ import { useSyncExternalStore, useRef, useEffect } from "react";
3
+ import { MOTION_SHORTCUT_SEQUENCE_MS } from "../tokens/motion.js";
4
+ import { warnOnce } from "./devWarning.js";
5
+ import { activeModalPanel } from "./useFocusScope.js";
6
+ import { hasOpenLayer, openLayerContains } from "./useLayer.js";
7
+ import { detectPlatform, parseShortcut, KEY_ALIASES } from "./shortcut-keys.js";
8
+ import { formatShortcut, shortcutAria, shortcutKeys } from "./shortcut-keys.js";
9
+ const noSubscription = () => () => {
10
+ };
11
+ function usePlatform() {
12
+ return useSyncExternalStore(noSubscription, detectPlatform, () => "other");
13
+ }
14
+ const eventKey = (event) => {
15
+ const key = event.key.toLowerCase();
16
+ return KEY_ALIASES[key] ?? key;
17
+ };
18
+ const codeKey = (event) => {
19
+ const match = /^(?:Key([A-Z])|Digit(\d))$/.exec(event.code ?? "");
20
+ return match ? (match[1] ?? match[2]).toLowerCase() : null;
21
+ };
22
+ const matchesStep = (step, event, platform) => {
23
+ const ctrl = step.ctrl || step.mod && platform === "other";
24
+ const meta = step.meta || step.mod && platform === "apple";
25
+ if (event.ctrlKey !== ctrl || event.metaKey !== meta || event.altKey !== step.alt) return false;
26
+ const symbol = step.key.length === 1 && !/[a-z0-9]/.test(step.key);
27
+ if (!symbol && event.shiftKey !== step.shift) return false;
28
+ return eventKey(event) === step.key || codeKey(event) === step.key;
29
+ };
30
+ const isTextField = (target) => {
31
+ if (!(target instanceof HTMLElement)) return false;
32
+ if (target.isContentEditable) return true;
33
+ if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) return true;
34
+ if (target instanceof HTMLInputElement) {
35
+ return !["checkbox", "radio", "button", "submit", "reset", "range", "color", "file", "image"].includes(target.type);
36
+ }
37
+ return false;
38
+ };
39
+ const registrations = [];
40
+ let version = 0;
41
+ const listeners = /* @__PURE__ */ new Set();
42
+ const notify = () => {
43
+ version += 1;
44
+ for (const listener of listeners) listener();
45
+ };
46
+ let recent = [];
47
+ const eligible = (registration, target) => {
48
+ const options = registration.optionsRef.current;
49
+ if (options.enabled === false) return false;
50
+ if (isTextField(target) && !options.allowInInput) return false;
51
+ if (options.global) return true;
52
+ const modal = activeModalPanel();
53
+ const node = target instanceof Node ? target : null;
54
+ const unfocused = !node || node === document.body || node === document.documentElement;
55
+ const scope = options.scope?.current;
56
+ if (scope) {
57
+ if (!unfocused) return scope.contains(node);
58
+ return modal ? modal.contains(scope) : !openLayerContains(scope);
59
+ }
60
+ if (modal) return false;
61
+ return unfocused || !openLayerContains(node);
62
+ };
63
+ const handleKeyDown = (event) => {
64
+ if (event.isComposing || event.defaultPrevented) return;
65
+ if (event.key === "Escape" && hasOpenLayer()) return;
66
+ if (["Shift", "Control", "Alt", "Meta"].includes(event.key)) return;
67
+ const now = event.timeStamp || performance.now();
68
+ const last = recent[recent.length - 1];
69
+ if (last && now - last.at > MOTION_SHORTCUT_SEQUENCE_MS) recent = [];
70
+ recent = [...recent.slice(-3), { event, at: now }];
71
+ const platform = detectPlatform();
72
+ let best = null;
73
+ registrations.forEach((registration2, index) => {
74
+ if (!eligible(registration2, event.target)) return;
75
+ for (const steps of registration2.steps) {
76
+ if (steps.length > recent.length) continue;
77
+ const tail = recent.slice(recent.length - steps.length);
78
+ if (!steps.every((step, i) => matchesStep(step, tail[i].event, platform))) continue;
79
+ if (!best || steps.length > best.length || steps.length === best.length && index > best.index) {
80
+ best = { registration: registration2, length: steps.length, index };
81
+ }
82
+ }
83
+ });
84
+ if (!best) return;
85
+ const { registration } = best;
86
+ event.preventDefault();
87
+ recent = [];
88
+ registration.handlerRef.current(event);
89
+ };
90
+ const register = (registration) => {
91
+ for (const shortcut of registration.shortcuts) {
92
+ const scope = registration.optionsRef.current.scope;
93
+ const clash = registrations.find(
94
+ (other) => other.optionsRef.current.enabled !== false && other.optionsRef.current.scope === scope && other.shortcuts.includes(shortcut)
95
+ );
96
+ if (clash && registration.optionsRef.current.enabled !== false) {
97
+ warnOnce(`The shortcut "${shortcut}" is registered twice in one scope; the one registered last wins.`);
98
+ }
99
+ }
100
+ registrations.push(registration);
101
+ if (registrations.length === 1) document.addEventListener("keydown", handleKeyDown);
102
+ notify();
103
+ return () => {
104
+ const index = registrations.indexOf(registration);
105
+ if (index !== -1) registrations.splice(index, 1);
106
+ if (registrations.length === 0) {
107
+ document.removeEventListener("keydown", handleKeyDown);
108
+ recent = [];
109
+ }
110
+ notify();
111
+ };
112
+ };
113
+ function useShortcut(shortcut, handler, options = {}) {
114
+ const handlerRef = useRef(handler);
115
+ const optionsRef = useRef(options);
116
+ useEffect(() => {
117
+ handlerRef.current = handler;
118
+ optionsRef.current = options;
119
+ });
120
+ const shortcuts = Array.isArray(shortcut) ? shortcut : [shortcut];
121
+ const key = shortcuts.join("\0");
122
+ const enabled = options.enabled !== false;
123
+ useEffect(() => {
124
+ if (!enabled) return;
125
+ const list = key.split("\0");
126
+ return register({ shortcuts: list, steps: list.map(parseShortcut), handlerRef, optionsRef });
127
+ }, [key, enabled]);
128
+ }
129
+ const subscribe = (listener) => {
130
+ listeners.add(listener);
131
+ return () => listeners.delete(listener);
132
+ };
133
+ let snapshot = { version: -1, list: [] };
134
+ const readRegistered = () => {
135
+ if (snapshot.version !== version) {
136
+ snapshot = {
137
+ version,
138
+ list: registrations.filter((r) => r.optionsRef.current.enabled !== false && r.optionsRef.current.description).map((r) => ({
139
+ shortcuts: r.shortcuts,
140
+ description: r.optionsRef.current.description ?? "",
141
+ group: r.optionsRef.current.group
142
+ }))
143
+ };
144
+ }
145
+ return snapshot.list;
146
+ };
147
+ const EMPTY = [];
148
+ function useRegisteredShortcuts() {
149
+ return useSyncExternalStore(subscribe, readRegistered, () => EMPTY);
150
+ }
151
+ export {
152
+ detectPlatform,
153
+ formatShortcut,
154
+ parseShortcut,
155
+ shortcutAria,
156
+ shortcutKeys,
157
+ usePlatform,
158
+ useRegisteredShortcuts,
159
+ useShortcut
160
+ };
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Anchored positioning: the one way every popup in the library places itself
3
+ * against the control that opened it (menus, listboxes, pickers, popovers,
4
+ * hover cards, tooltips).
5
+ *
6
+ * The panel stays where it is in the DOM, so it keeps the trigger's stacking
7
+ * context and any theme scoped above it, but is `position: fixed`, placed
8
+ * from the anchor's rectangle. A fixed panel is not clipped by an ancestor's
9
+ * `overflow`, which is what lets a menu inside a table's scroll wrapper, a
10
+ * rail or a card show whole. Portals would escape the same way but would
11
+ * leave the theme scope; floating-ui would solve it at the cost of a
12
+ * dependency. This is the approach Tooltip proved first.
13
+ *
14
+ * On every placement it:
15
+ * - puts the panel on the preferred side, flipping to the opposite side when
16
+ * the preferred one has too little room and the opposite has more;
17
+ * - shifts it along the edge to stay inside its box, a margin clear of the
18
+ * edge (`--gap-200` unless the component says otherwise);
19
+ * - optionally caps its height to the room on the side it took (a list then
20
+ * scrolls inside rather than running off the screen);
21
+ * - optionally sets its minimum width to the anchor's, so a listbox is never
22
+ * narrower than the control that opened it, and always publishes the
23
+ * anchor's width as `--ds-anchor-width` for a panel that should match it
24
+ * exactly;
25
+ * - marks the side it landed on as `data-side` (and `data-align`), so the
26
+ * entrance can grow from the anchor whichever way the panel flipped;
27
+ * - hides it (an inline `visibility: hidden`, flagged `data-anchor-hidden`)
28
+ * while the anchor is scrolled wholly out of view, so a panel never hangs
29
+ * at the screen's edge over nothing.
30
+ *
31
+ * The placement outlives the open state on purpose: a panel that eases out
32
+ * when it closes keeps its place for the exit instead of snapping back into
33
+ * flow mid-fade.
34
+ *
35
+ * It re-places on scroll (any scroll container, captured), on resize, and
36
+ * when the panel or the anchor changes size, once per animation frame, and
37
+ * writes straight to the DOM: placement never costs a React render, the same
38
+ * reasoning the gesture rule gives (design.md, the playground's resize
39
+ * record).
40
+ *
41
+ * The room a panel has is the viewport's. An ancestor with a transform,
42
+ * filter, backdrop filter, perspective or containment (a Dialog panel's
43
+ * entrance scale, the chat panel's blur) becomes the fixed panel's
44
+ * containing block, which moves the
45
+ * coordinates it is placed in; only when that ancestor also clips its
46
+ * overflow does the room narrow to its box.
47
+ */
48
+ export type AnchoredSide = 'top' | 'bottom' | 'left' | 'right';
49
+ /** A side a caller can ask for: a screen side, or `start` / `end` of the reading direction. */
50
+ export type AnchoredSideRequest = AnchoredSide | 'start' | 'end';
51
+ export type AnchoredAlign = 'start' | 'center' | 'end';
52
+ export type AnchoredPlacement = {
53
+ /** The side the panel took after flipping. */
54
+ side: AnchoredSide;
55
+ /** The panel's position in the box, in px from the box's top left. */
56
+ x: number;
57
+ y: number;
58
+ /** The panel's size as placed. */
59
+ width: number;
60
+ height: number;
61
+ /** The anchor's rectangle, in the same coordinates. */
62
+ anchor: {
63
+ left: number;
64
+ top: number;
65
+ width: number;
66
+ height: number;
67
+ };
68
+ };
69
+ export type AnchoredPositionOptions = {
70
+ /** Whether the panel is showing. Placement runs only while true. */
71
+ open: boolean;
72
+ /** The control the panel is anchored to. */
73
+ anchorRef: React.RefObject<HTMLElement | null>;
74
+ /** The panel being placed. */
75
+ panelRef: React.RefObject<HTMLElement | null>;
76
+ /**
77
+ * The preferred side. Default `bottom`. `start` and `end` resolve against
78
+ * the anchor's reading direction at placement time (`start` is the left
79
+ * in English, the right in Arabic); the placement reports the screen side
80
+ * it took.
81
+ */
82
+ side?: AnchoredSideRequest;
83
+ /**
84
+ * Where along that side the panel lines up with the anchor. Default
85
+ * `start`. On the top and bottom sides `start` and `end` follow the
86
+ * anchor's reading direction, so a menu lines up with its trigger's
87
+ * leading edge in either. The panel's `data-align` reports the screen
88
+ * alignment it took (`start` is the left edge), like `data-side`.
89
+ */
90
+ align?: AnchoredAlign;
91
+ /**
92
+ * The custom property that holds the gap between anchor and panel, read
93
+ * where it resolves so it is never restated as a number. Default
94
+ * `--gap-200`.
95
+ */
96
+ gapProperty?: string;
97
+ /**
98
+ * The custom property that holds the margin a panel keeps from the box's
99
+ * edge when it shifts to stay inside. Default `--gap-200`, so a panel never
100
+ * sits tighter to the screen's edge than that, whatever its trigger gap.
101
+ */
102
+ edgeProperty?: string;
103
+ /** Whether the panel may flip to the opposite side. Default true. */
104
+ flip?: boolean;
105
+ /** Cap the panel's height to the room on its side, for a list that scrolls inside. Default false. */
106
+ fitHeight?: boolean;
107
+ /** Set the panel's minimum width to the anchor's. Default false. */
108
+ matchWidth?: boolean;
109
+ /** Called after every placement, for a detail the panel draws itself (an arrow). */
110
+ onPlace?: (placement: AnchoredPlacement) => void;
111
+ /**
112
+ * Which of several anchors is open, for a component that shows one panel
113
+ * at a time against different controls (FilterBar's chips): a change
114
+ * re-reads the refs and places afresh. Leave undefined for one anchor.
115
+ */
116
+ target?: string | number | null;
117
+ };
118
+ /**
119
+ * The nearest ancestor a fixed-position descendant is placed from instead of
120
+ * the viewport. Null means the viewport.
121
+ */
122
+ export declare function fixedContainingBlock(element: HTMLElement): HTMLElement | null;
123
+ /** Places `panelRef` against `anchorRef` while `open`. See the module comment. */
124
+ export declare function useAnchoredPosition({ open, anchorRef, panelRef, side: sideRequest, align: alignRequest, gapProperty, edgeProperty, flip, fitHeight, matchWidth, onPlace, target, }: AnchoredPositionOptions): void;
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import { useRef, useLayoutEffect } from "react";
3
+ import { isRtl } from "./direction.js";
4
+ function fixedContainingBlock(element) {
5
+ for (let node = element.parentElement; node; node = node.parentElement) {
6
+ const style = getComputedStyle(node);
7
+ const read = (property) => style.getPropertyValue(property).trim();
8
+ if (read("transform") !== "none" || read("filter") !== "none" || read("backdrop-filter") !== "none" || read("perspective") !== "none" || /\b(layout|paint|strict|content)\b/.test(read("contain")) || /\b(transform|filter|perspective)\b/.test(read("will-change"))) {
9
+ return node;
10
+ }
11
+ }
12
+ return null;
13
+ }
14
+ const clamp = (value, min, max) => max < min ? min : Math.min(Math.max(value, min), max);
15
+ const OPPOSITE = {
16
+ top: "bottom",
17
+ bottom: "top",
18
+ left: "right",
19
+ right: "left"
20
+ };
21
+ function useAnchoredPosition({
22
+ open,
23
+ anchorRef,
24
+ panelRef,
25
+ side: sideRequest = "bottom",
26
+ align: alignRequest = "start",
27
+ gapProperty = "--gap-200",
28
+ edgeProperty = "--gap-200",
29
+ flip = true,
30
+ fitHeight = false,
31
+ matchWidth = false,
32
+ onPlace,
33
+ target
34
+ }) {
35
+ const onPlaceRef = useRef(onPlace);
36
+ useLayoutEffect(() => {
37
+ onPlaceRef.current = onPlace;
38
+ });
39
+ useLayoutEffect(() => {
40
+ if (!open) return;
41
+ const anchor = anchorRef.current;
42
+ const panel = panelRef.current;
43
+ if (!anchor || !panel) return;
44
+ panel.style.position = "fixed";
45
+ panel.style.inset = "auto";
46
+ panel.style.margin = "0";
47
+ const place = () => {
48
+ const block = fixedContainingBlock(panel);
49
+ const blockBox = block ? block.getBoundingClientRect() : null;
50
+ const origin = blockBox ? { left: blockBox.left + block.clientLeft, top: blockBox.top + block.clientTop } : { left: 0, top: 0 };
51
+ const bounds = {
52
+ left: 0,
53
+ top: 0,
54
+ right: document.documentElement.clientWidth,
55
+ bottom: document.documentElement.clientHeight
56
+ };
57
+ if (block && blockBox) {
58
+ const style = getComputedStyle(block);
59
+ if (style.overflowX !== "visible") {
60
+ bounds.left = Math.max(bounds.left, origin.left);
61
+ bounds.right = Math.min(bounds.right, origin.left + block.clientWidth);
62
+ }
63
+ if (style.overflowY !== "visible") {
64
+ bounds.top = Math.max(bounds.top, origin.top);
65
+ bounds.bottom = Math.min(bounds.bottom, origin.top + block.clientHeight);
66
+ }
67
+ }
68
+ const rtl = isRtl(anchor);
69
+ const side = sideRequest === "start" ? rtl ? "right" : "left" : sideRequest === "end" ? rtl ? "left" : "right" : sideRequest;
70
+ const flipsAlign = rtl && (side === "top" || side === "bottom") && alignRequest !== "center";
71
+ const align = flipsAlign ? alignRequest === "start" ? "end" : "start" : alignRequest;
72
+ const a = anchor.getBoundingClientRect();
73
+ const panelStyle = getComputedStyle(panel);
74
+ const gap = parseFloat(panelStyle.getPropertyValue(gapProperty)) || 0;
75
+ const edge = parseFloat(panelStyle.getPropertyValue(edgeProperty)) || 0;
76
+ panel.style.setProperty("--ds-anchor-width", `${a.width}px`);
77
+ if (matchWidth) panel.style.minWidth = `${a.width}px`;
78
+ if (fitHeight) panel.style.maxHeight = "";
79
+ const width = panel.offsetWidth;
80
+ let height = panel.offsetHeight;
81
+ const room = {
82
+ top: a.top - bounds.top - gap - edge,
83
+ bottom: bounds.bottom - a.bottom - gap - edge,
84
+ left: a.left - bounds.left - gap - edge,
85
+ right: bounds.right - a.right - gap - edge
86
+ };
87
+ const vertical = side === "top" || side === "bottom";
88
+ const need = vertical ? height : width;
89
+ let chosen = side;
90
+ if (flip && room[side] < need && room[OPPOSITE[side]] > room[side]) {
91
+ chosen = OPPOSITE[side];
92
+ }
93
+ if (fitHeight && vertical && height > room[chosen]) {
94
+ height = Math.max(room[chosen], 0);
95
+ panel.style.maxHeight = `${height}px`;
96
+ }
97
+ let x;
98
+ let y;
99
+ if (chosen === "top" || chosen === "bottom") {
100
+ y = chosen === "bottom" ? a.bottom + gap : a.top - gap - height;
101
+ x = align === "center" ? a.left + a.width / 2 - width / 2 : align === "end" ? a.right - width : a.left;
102
+ } else {
103
+ x = chosen === "right" ? a.right + gap : a.left - gap - width;
104
+ y = align === "center" ? a.top + a.height / 2 - height / 2 : align === "end" ? a.bottom - height : a.top;
105
+ }
106
+ x = clamp(x, bounds.left + edge, bounds.right - edge - width);
107
+ y = clamp(y, bounds.top + edge, bounds.bottom - edge - height);
108
+ panel.style.left = `${x - origin.left}px`;
109
+ panel.style.top = `${y - origin.top}px`;
110
+ panel.dataset.side = chosen;
111
+ panel.dataset.align = align;
112
+ const offscreen = a.bottom < bounds.top || a.top > bounds.bottom || a.right < bounds.left || a.left > bounds.right;
113
+ if (offscreen) {
114
+ panel.dataset.anchorHidden = "";
115
+ panel.style.visibility = "hidden";
116
+ } else if ("anchorHidden" in panel.dataset) {
117
+ delete panel.dataset.anchorHidden;
118
+ panel.style.removeProperty("visibility");
119
+ }
120
+ onPlaceRef.current?.({
121
+ side: chosen,
122
+ x: x - origin.left,
123
+ y: y - origin.top,
124
+ width,
125
+ height,
126
+ anchor: { left: a.left - origin.left, top: a.top - origin.top, width: a.width, height: a.height }
127
+ });
128
+ };
129
+ let frame = 0;
130
+ const schedule = () => {
131
+ if (frame) return;
132
+ frame = requestAnimationFrame(() => {
133
+ frame = 0;
134
+ place();
135
+ });
136
+ };
137
+ place();
138
+ window.addEventListener("scroll", schedule, true);
139
+ window.addEventListener("resize", schedule);
140
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
141
+ observer?.observe(panel);
142
+ observer?.observe(anchor);
143
+ return () => {
144
+ cancelAnimationFrame(frame);
145
+ window.removeEventListener("scroll", schedule, true);
146
+ window.removeEventListener("resize", schedule);
147
+ observer?.disconnect();
148
+ if ("anchorHidden" in panel.dataset) {
149
+ delete panel.dataset.anchorHidden;
150
+ panel.style.removeProperty("visibility");
151
+ }
152
+ };
153
+ }, [open, target, anchorRef, panelRef, sideRequest, alignRequest, gapProperty, edgeProperty, flip, fitHeight, matchWidth]);
154
+ }
155
+ export {
156
+ fixedContainingBlock,
157
+ useAnchoredPosition
158
+ };
@@ -30,8 +30,15 @@ type FocusScopeOptions = {
30
30
  active: boolean;
31
31
  /** Wrap Tab / Shift+Tab within the scope. Default true. */
32
32
  trap?: boolean;
33
- /** Restore focus to the previously focused element on deactivate. Default true. */
34
- restoreFocus?: boolean;
33
+ /**
34
+ * Restore focus on deactivate. Default true: the previously focused
35
+ * element, or its nearest visible stand-in. A function names the target
36
+ * instead, read at the moment of closing, for a host that knows better
37
+ * than the opener: a table whose row deleted itself from this dialog
38
+ * sends focus to the next row rather than the table's first control. A
39
+ * function that returns null falls back to the default.
40
+ */
41
+ restoreFocus?: boolean | (() => HTMLElement | null);
35
42
  /**
36
43
  * Where initial focus lands. Defaults to the first visible real control,
37
44
  * then the first focusable of any kind, then the container itself.
@@ -40,5 +47,11 @@ type FocusScopeOptions = {
40
47
  /** Make everything outside the scope inert while active. Default true. */
41
48
  inertOutside?: boolean;
42
49
  };
50
+ /**
51
+ * The panel of the topmost modal scope that isolates the page, or null when
52
+ * none is open. Keyboard shortcuts read it: while a modal is open, only the
53
+ * shortcuts that live inside it (or are global) may fire.
54
+ */
55
+ export declare const activeModalPanel: () => HTMLElement | null;
43
56
  export declare function useFocusScope(ref: React.RefObject<HTMLElement | null>, options: FocusScopeOptions): void;
44
57
  export {};