@dxos/react-ui 0.10.0 → 0.11.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 (354) hide show
  1. package/dist/lib/chunk-parse-stack.mjs +1566 -0
  2. package/dist/lib/chunk-parse-stack.mjs.map +1 -0
  3. package/dist/lib/index.mjs +5220 -0
  4. package/dist/lib/index.mjs.map +1 -0
  5. package/dist/lib/testing.mjs +144 -0
  6. package/dist/lib/testing.mjs.map +1 -0
  7. package/dist/lib/translations.mjs +38 -0
  8. package/dist/lib/translations.mjs.map +1 -0
  9. package/dist/types/src/components/Avatars/Avatar.d.ts +1 -6
  10. package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
  11. package/dist/types/src/components/Avatars/AvatarContext.d.ts +9 -0
  12. package/dist/types/src/components/Avatars/AvatarContext.d.ts.map +1 -0
  13. package/dist/types/src/components/Avatars/index.d.ts +1 -0
  14. package/dist/types/src/components/Avatars/index.d.ts.map +1 -1
  15. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts +1 -1
  16. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
  17. package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
  18. package/dist/types/src/components/Breadcrumb/Breadcrumb.theme.d.ts.map +1 -1
  19. package/dist/types/src/components/Button/Button.d.ts +1 -6
  20. package/dist/types/src/components/Button/Button.d.ts.map +1 -1
  21. package/dist/types/src/components/Button/ButtonGroupContext.d.ts +9 -0
  22. package/dist/types/src/components/Button/ButtonGroupContext.d.ts.map +1 -0
  23. package/dist/types/src/components/Button/IconButton.d.ts.map +1 -1
  24. package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Button/IconButton.theme.d.ts +1 -0
  26. package/dist/types/src/components/Button/IconButton.theme.d.ts.map +1 -1
  27. package/dist/types/src/components/Button/SystemIconButton.d.ts +1 -1
  28. package/dist/types/src/components/Button/SystemIconButton.d.ts.map +1 -1
  29. package/dist/types/src/components/Button/index.d.ts +1 -0
  30. package/dist/types/src/components/Button/index.d.ts.map +1 -1
  31. package/dist/types/src/components/Calendar/Calendar.d.ts +0 -2
  32. package/dist/types/src/components/Calendar/Calendar.d.ts.map +1 -1
  33. package/dist/types/src/components/Calendar/index.d.ts +1 -0
  34. package/dist/types/src/components/Calendar/index.d.ts.map +1 -1
  35. package/dist/types/src/components/Calendar/parse-calendar-date.d.ts +3 -0
  36. package/dist/types/src/components/Calendar/parse-calendar-date.d.ts.map +1 -0
  37. package/dist/types/src/components/Card/Card.d.ts +42 -15
  38. package/dist/types/src/components/Card/Card.d.ts.map +1 -1
  39. package/dist/types/src/components/Card/Card.theme.d.ts +0 -1
  40. package/dist/types/src/components/Card/Card.theme.d.ts.map +1 -1
  41. package/dist/types/src/components/Carousel/Carousel.d.ts +2 -4
  42. package/dist/types/src/components/Carousel/Carousel.d.ts.map +1 -1
  43. package/dist/types/src/components/Carousel/CarouselContext.d.ts +8 -0
  44. package/dist/types/src/components/Carousel/CarouselContext.d.ts.map +1 -0
  45. package/dist/types/src/components/Carousel/index.d.ts +1 -0
  46. package/dist/types/src/components/Carousel/index.d.ts.map +1 -1
  47. package/dist/types/src/components/Clipboard/ClipboardContext.d.ts +7 -0
  48. package/dist/types/src/components/Clipboard/ClipboardContext.d.ts.map +1 -0
  49. package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts +0 -6
  50. package/dist/types/src/components/Clipboard/ClipboardProvider.d.ts.map +1 -1
  51. package/dist/types/src/components/Clipboard/index.d.ts +1 -1
  52. package/dist/types/src/components/Clipboard/index.d.ts.map +1 -1
  53. package/dist/types/src/components/Column/Column.d.ts +7 -12
  54. package/dist/types/src/components/Column/Column.d.ts.map +1 -1
  55. package/dist/types/src/components/Column/Column.stories.d.ts +2 -2
  56. package/dist/types/src/components/Column/Column.theme.d.ts +0 -1
  57. package/dist/types/src/components/Column/Column.theme.d.ts.map +1 -1
  58. package/dist/types/src/components/Column/ColumnContext.d.ts +9 -0
  59. package/dist/types/src/components/Column/ColumnContext.d.ts.map +1 -0
  60. package/dist/types/src/components/Column/index.d.ts +1 -0
  61. package/dist/types/src/components/Column/index.d.ts.map +1 -1
  62. package/dist/types/src/components/Column/withColumn.d.ts +40 -4
  63. package/dist/types/src/components/Column/withColumn.d.ts.map +1 -1
  64. package/dist/types/src/components/DatePicker/DatePicker.d.ts +0 -10
  65. package/dist/types/src/components/DatePicker/DatePicker.d.ts.map +1 -1
  66. package/dist/types/src/components/DatePicker/DatePickerContext.d.ts +13 -0
  67. package/dist/types/src/components/DatePicker/DatePickerContext.d.ts.map +1 -0
  68. package/dist/types/src/components/DatePicker/index.d.ts +1 -0
  69. package/dist/types/src/components/DatePicker/index.d.ts.map +1 -1
  70. package/dist/types/src/components/Dialog/AlertDialog.d.ts +3 -3
  71. package/dist/types/src/components/Dialog/Dialog.d.ts +3 -3
  72. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts +5 -10
  73. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts.map +1 -1
  74. package/dist/types/src/components/ErrorFallback/index.d.ts +1 -0
  75. package/dist/types/src/components/ErrorFallback/index.d.ts.map +1 -1
  76. package/dist/types/src/components/ErrorFallback/parse-stack.d.ts +8 -0
  77. package/dist/types/src/components/ErrorFallback/parse-stack.d.ts.map +1 -0
  78. package/dist/types/src/components/Focus/Focus.d.ts +2 -10
  79. package/dist/types/src/components/Focus/Focus.d.ts.map +1 -1
  80. package/dist/types/src/components/Focus/FocusContext.d.ts +10 -0
  81. package/dist/types/src/components/Focus/FocusContext.d.ts.map +1 -0
  82. package/dist/types/src/components/Focus/index.d.ts +1 -0
  83. package/dist/types/src/components/Focus/index.d.ts.map +1 -1
  84. package/dist/types/src/components/Input/Input.d.ts +9 -10
  85. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  86. package/dist/types/src/components/Input/Input.stories.d.ts +7 -2
  87. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  88. package/dist/types/src/components/Input/Input.theme.d.ts +13 -0
  89. package/dist/types/src/components/Input/Input.theme.d.ts.map +1 -1
  90. package/dist/types/src/components/Input/InputTriggerContext.d.ts +15 -0
  91. package/dist/types/src/components/Input/InputTriggerContext.d.ts.map +1 -0
  92. package/dist/types/src/components/Input/SegmentedInput.d.ts.map +1 -1
  93. package/dist/types/src/components/Input/index.d.ts +1 -0
  94. package/dist/types/src/components/Input/index.d.ts.map +1 -1
  95. package/dist/types/src/components/Main/Main.d.ts +2 -35
  96. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  97. package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/Main/Main.theme.d.ts.map +1 -1
  99. package/dist/types/src/components/Main/MainContext.d.ts +38 -0
  100. package/dist/types/src/components/Main/MainContext.d.ts.map +1 -0
  101. package/dist/types/src/components/Main/index.d.ts +1 -0
  102. package/dist/types/src/components/Main/index.d.ts.map +1 -1
  103. package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts +1 -17
  104. package/dist/types/src/components/MediaPlayer/MediaPlayer.d.ts.map +1 -1
  105. package/dist/types/src/components/MediaPlayer/index.d.ts +1 -0
  106. package/dist/types/src/components/MediaPlayer/index.d.ts.map +1 -1
  107. package/dist/types/src/components/MediaPlayer/media-kind.d.ts +18 -0
  108. package/dist/types/src/components/MediaPlayer/media-kind.d.ts.map +1 -0
  109. package/dist/types/src/components/Menu/DropdownMenu.d.ts +6 -22
  110. package/dist/types/src/components/Menu/DropdownMenu.d.ts.map +1 -1
  111. package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts +1 -11
  112. package/dist/types/src/components/Menu/DropdownMenu.stories.d.ts.map +1 -1
  113. package/dist/types/src/components/Menu/DropdownMenuContext.d.ts +27 -0
  114. package/dist/types/src/components/Menu/DropdownMenuContext.d.ts.map +1 -0
  115. package/dist/types/src/components/Menu/index.d.ts +1 -0
  116. package/dist/types/src/components/Menu/index.d.ts.map +1 -1
  117. package/dist/types/src/components/Message/Message.d.ts +11 -6
  118. package/dist/types/src/components/Message/Message.d.ts.map +1 -1
  119. package/dist/types/src/components/Message/Message.stories.d.ts +4 -1
  120. package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
  121. package/dist/types/src/components/Message/Message.theme.d.ts.map +1 -1
  122. package/dist/types/src/components/Message/index.d.ts +1 -0
  123. package/dist/types/src/components/Message/index.d.ts.map +1 -1
  124. package/dist/types/src/components/Message/message-icons.d.ts +3 -0
  125. package/dist/types/src/components/Message/message-icons.d.ts.map +1 -0
  126. package/dist/types/src/components/Panel/Panel.d.ts +4 -4
  127. package/dist/types/src/components/Panel/Panel.stories.d.ts.map +1 -1
  128. package/dist/types/src/components/Panel/Panel.theme.d.ts +1 -1
  129. package/dist/types/src/components/Panel/Panel.theme.d.ts.map +1 -1
  130. package/dist/types/src/components/Popover/Popover.d.ts +6 -10
  131. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  132. package/dist/types/src/components/Popover/PopoverContext.d.ts +29 -0
  133. package/dist/types/src/components/Popover/PopoverContext.d.ts.map +1 -0
  134. package/dist/types/src/components/Popover/index.d.ts +1 -0
  135. package/dist/types/src/components/Popover/index.d.ts.map +1 -1
  136. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +2 -2
  137. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +2 -2
  138. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +3 -16
  139. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
  140. package/dist/types/src/components/ScrollContainer/ScrollContainerContext.d.ts +16 -0
  141. package/dist/types/src/components/ScrollContainer/ScrollContainerContext.d.ts.map +1 -0
  142. package/dist/types/src/components/ScrollContainer/index.d.ts +1 -0
  143. package/dist/types/src/components/ScrollContainer/index.d.ts.map +1 -1
  144. package/dist/types/src/components/Slider/Slider.d.ts +25 -0
  145. package/dist/types/src/components/Slider/Slider.d.ts.map +1 -0
  146. package/dist/types/src/components/Slider/Slider.stories.d.ts +29 -0
  147. package/dist/types/src/components/Slider/Slider.stories.d.ts.map +1 -0
  148. package/dist/types/src/components/Slider/Slider.test.d.ts +2 -0
  149. package/dist/types/src/components/Slider/Slider.test.d.ts.map +1 -0
  150. package/dist/types/src/components/Slider/Slider.theme.d.ts +7 -0
  151. package/dist/types/src/components/Slider/Slider.theme.d.ts.map +1 -0
  152. package/dist/types/src/components/Slider/index.d.ts +2 -0
  153. package/dist/types/src/components/Slider/index.d.ts.map +1 -0
  154. package/dist/types/src/components/Splitter/Splitter.d.ts +3 -3
  155. package/dist/types/src/components/Status/Status.d.ts.map +1 -1
  156. package/dist/types/src/components/Toolbar/Toolbar.d.ts +4 -2
  157. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  158. package/dist/types/src/components/Toolbar/Toolbar.theme.d.ts.map +1 -1
  159. package/dist/types/src/components/Tooltip/TextTooltip.d.ts.map +1 -1
  160. package/dist/types/src/components/Tooltip/Tooltip.d.ts +2 -23
  161. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  162. package/dist/types/src/components/Tooltip/TooltipContext.d.ts +35 -0
  163. package/dist/types/src/components/Tooltip/TooltipContext.d.ts.map +1 -0
  164. package/dist/types/src/components/Tooltip/index.d.ts +1 -0
  165. package/dist/types/src/components/Tooltip/index.d.ts.map +1 -1
  166. package/dist/types/src/components/index.d.ts +1 -0
  167. package/dist/types/src/components/index.d.ts.map +1 -1
  168. package/dist/types/src/exemplars/generics.stories.d.ts +1 -1
  169. package/dist/types/src/hooks/useIconHref.d.ts +8 -0
  170. package/dist/types/src/hooks/useIconHref.d.ts.map +1 -1
  171. package/dist/types/src/hooks/useThemeContext.d.ts +2 -1
  172. package/dist/types/src/hooks/useThemeContext.d.ts.map +1 -1
  173. package/dist/types/src/hooks/useThemeContext.test.d.ts +2 -0
  174. package/dist/types/src/hooks/useThemeContext.test.d.ts.map +1 -0
  175. package/dist/types/src/playground/Density.stories.d.ts +11 -0
  176. package/dist/types/src/playground/Density.stories.d.ts.map +1 -0
  177. package/dist/types/src/playground/Elevation.stories.d.ts +14 -0
  178. package/dist/types/src/playground/Elevation.stories.d.ts.map +1 -0
  179. package/dist/types/src/primitives/Container/Container.d.ts +1 -1
  180. package/dist/types/src/primitives/Container/Container.d.ts.map +1 -1
  181. package/dist/types/src/primitives/DensityProvider/DensityContext.d.ts +6 -0
  182. package/dist/types/src/primitives/DensityProvider/DensityContext.d.ts.map +1 -0
  183. package/dist/types/src/primitives/DensityProvider/DensityProvider.d.ts +10 -4
  184. package/dist/types/src/primitives/DensityProvider/DensityProvider.d.ts.map +1 -1
  185. package/dist/types/src/primitives/DensityProvider/index.d.ts +1 -0
  186. package/dist/types/src/primitives/DensityProvider/index.d.ts.map +1 -1
  187. package/dist/types/src/primitives/Flex/Flex.d.ts +1 -1
  188. package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -1
  189. package/dist/types/src/primitives/Grid/Grid.d.ts +1 -1
  190. package/dist/types/src/primitives/Grid/Grid.d.ts.map +1 -1
  191. package/dist/types/src/primitives/ThemeProvider/IconRegistry.d.ts +15 -0
  192. package/dist/types/src/primitives/ThemeProvider/IconRegistry.d.ts.map +1 -0
  193. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.d.ts +1 -2
  194. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.d.ts.map +1 -1
  195. package/dist/types/src/primitives/ThemeProvider/ThemeProvider.stories.d.ts +1 -1
  196. package/dist/types/src/primitives/ThemeProvider/TranslationsContext.d.ts +112 -0
  197. package/dist/types/src/primitives/ThemeProvider/TranslationsContext.d.ts.map +1 -0
  198. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts +0 -109
  199. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts.map +1 -1
  200. package/dist/types/src/primitives/ThemeProvider/icon-registry.d.ts +34 -0
  201. package/dist/types/src/primitives/ThemeProvider/icon-registry.d.ts.map +1 -0
  202. package/dist/types/src/primitives/ThemeProvider/index.d.ts +3 -1
  203. package/dist/types/src/primitives/ThemeProvider/index.d.ts.map +1 -1
  204. package/dist/types/src/testing/decorators/withTheme.d.ts +1 -1
  205. package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
  206. package/dist/types/src/theme/defaultTheme.d.ts.map +1 -1
  207. package/dist/types/src/theme/index.d.ts +1 -0
  208. package/dist/types/src/theme/index.d.ts.map +1 -1
  209. package/dist/types/src/util/slots.d.ts +6 -3
  210. package/dist/types/src/util/slots.d.ts.map +1 -1
  211. package/dist/types/src/util/slots.stories.d.ts +14 -0
  212. package/dist/types/src/util/slots.stories.d.ts.map +1 -0
  213. package/dist/types/tsconfig.tsbuildinfo +1 -1
  214. package/package.json +25 -27
  215. package/src/components/Avatars/Avatar.tsx +1 -11
  216. package/src/components/Avatars/AvatarContext.ts +16 -0
  217. package/src/components/Avatars/index.ts +2 -0
  218. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +27 -25
  219. package/src/components/Breadcrumb/Breadcrumb.theme.ts +7 -5
  220. package/src/components/Breadcrumb/Breadcrumb.tsx +2 -2
  221. package/src/components/Button/Button.tsx +2 -11
  222. package/src/components/Button/ButtonGroupContext.ts +17 -0
  223. package/src/components/Button/IconButton.stories.tsx +2 -3
  224. package/src/components/Button/IconButton.theme.ts +15 -2
  225. package/src/components/Button/IconButton.tsx +6 -1
  226. package/src/components/Button/SystemIconButton.tsx +23 -16
  227. package/src/components/Button/index.ts +2 -0
  228. package/src/components/Calendar/Calendar.theme.ts +5 -5
  229. package/src/components/Calendar/Calendar.tsx +1 -3
  230. package/src/components/Calendar/index.ts +2 -0
  231. package/src/components/Calendar/parse-calendar-date.ts +9 -0
  232. package/src/components/Card/Card.theme.ts +48 -38
  233. package/src/components/Card/Card.tsx +77 -16
  234. package/src/components/Carousel/Carousel.tsx +4 -11
  235. package/src/components/Carousel/CarouselContext.ts +17 -0
  236. package/src/components/Carousel/index.ts +2 -0
  237. package/src/components/Clipboard/ClipboardContext.ts +20 -0
  238. package/src/components/Clipboard/ClipboardProvider.tsx +2 -12
  239. package/src/components/Clipboard/CopyButton.tsx +1 -1
  240. package/src/components/Clipboard/index.ts +1 -1
  241. package/src/components/Column/Column.stories.tsx +2 -2
  242. package/src/components/Column/Column.theme.ts +2 -11
  243. package/src/components/Column/Column.tsx +35 -57
  244. package/src/components/Column/ColumnContext.ts +22 -0
  245. package/src/components/Column/index.ts +2 -0
  246. package/src/components/Column/withColumn.ts +41 -4
  247. package/src/components/DatePicker/DatePicker.theme.ts +2 -2
  248. package/src/components/DatePicker/DatePicker.tsx +1 -14
  249. package/src/components/DatePicker/DatePickerContext.ts +21 -0
  250. package/src/components/DatePicker/index.ts +2 -0
  251. package/src/components/ErrorFallback/ErrorStack.tsx +8 -28
  252. package/src/components/ErrorFallback/index.ts +1 -0
  253. package/src/components/ErrorFallback/parse-stack.ts +33 -0
  254. package/src/components/Focus/Focus.stories.tsx +6 -6
  255. package/src/components/Focus/Focus.tsx +2 -30
  256. package/src/components/Focus/FocusContext.ts +22 -0
  257. package/src/components/Focus/index.ts +2 -0
  258. package/src/components/Image/Image.tsx +1 -1
  259. package/src/components/Input/Input.stories.tsx +63 -48
  260. package/src/components/Input/Input.theme.ts +33 -27
  261. package/src/components/Input/Input.tsx +46 -43
  262. package/src/components/Input/InputTriggerContext.ts +45 -0
  263. package/src/components/Input/SegmentedInput.tsx +2 -1
  264. package/src/components/Input/index.ts +2 -0
  265. package/src/components/Main/Main.stories.tsx +2 -1
  266. package/src/components/Main/Main.theme.ts +10 -1
  267. package/src/components/Main/Main.tsx +1 -115
  268. package/src/components/Main/MainContext.ts +126 -0
  269. package/src/components/Main/index.ts +2 -0
  270. package/src/components/MediaPlayer/MediaPlayer.tsx +16 -47
  271. package/src/components/MediaPlayer/index.ts +2 -0
  272. package/src/components/MediaPlayer/media-kind.ts +41 -0
  273. package/src/components/Menu/DropdownMenu.tsx +27 -44
  274. package/src/components/Menu/DropdownMenuContext.ts +35 -0
  275. package/src/components/Menu/Menu.theme.ts +3 -3
  276. package/src/components/Menu/index.ts +2 -0
  277. package/src/components/Message/Message.stories.tsx +32 -15
  278. package/src/components/Message/Message.theme.ts +8 -2
  279. package/src/components/Message/Message.tsx +47 -23
  280. package/src/components/Message/index.ts +2 -0
  281. package/src/components/Message/message-icons.ts +16 -0
  282. package/src/components/Panel/Panel.stories.tsx +15 -2
  283. package/src/components/Panel/Panel.theme.ts +15 -6
  284. package/src/components/Popover/Popover.theme.ts +1 -1
  285. package/src/components/Popover/Popover.tsx +22 -37
  286. package/src/components/Popover/PopoverContext.ts +32 -0
  287. package/src/components/Popover/index.ts +2 -0
  288. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +1 -1
  289. package/src/components/ScrollContainer/ScrollContainer.tsx +1 -18
  290. package/src/components/ScrollContainer/ScrollContainerContext.ts +25 -0
  291. package/src/components/ScrollContainer/index.ts +2 -0
  292. package/src/components/Select/Select.theme.ts +2 -2
  293. package/src/components/Slider/Slider.stories.tsx +107 -0
  294. package/src/components/Slider/Slider.test.tsx +105 -0
  295. package/src/components/Slider/Slider.theme.ts +38 -0
  296. package/src/components/Slider/Slider.tsx +64 -0
  297. package/src/components/Slider/index.ts +5 -0
  298. package/src/components/Status/Status.theme.ts +1 -1
  299. package/src/components/Status/Status.tsx +0 -1
  300. package/src/components/Tag/Tag.stories.tsx +1 -1
  301. package/src/components/Toolbar/Toolbar.theme.ts +14 -5
  302. package/src/components/Toolbar/Toolbar.tsx +5 -1
  303. package/src/components/Tooltip/TextTooltip.tsx +2 -1
  304. package/src/components/Tooltip/Tooltip.tsx +12 -35
  305. package/src/components/Tooltip/TooltipContext.ts +41 -0
  306. package/src/components/Tooltip/index.ts +2 -0
  307. package/src/components/index.ts +1 -0
  308. package/src/hooks/useIconHref.ts +26 -5
  309. package/src/hooks/useThemeContext.test.tsx +33 -0
  310. package/src/hooks/useThemeContext.ts +28 -3
  311. package/src/playground/Density.stories.tsx +41 -0
  312. package/src/playground/Elevation.stories.tsx +348 -0
  313. package/src/primitives/DensityProvider/DensityContext.ts +15 -0
  314. package/src/primitives/DensityProvider/DensityProvider.tsx +12 -6
  315. package/src/primitives/DensityProvider/index.ts +1 -0
  316. package/src/primitives/Flex/Flex.stories.tsx +1 -1
  317. package/src/primitives/Grid/Grid.stories.tsx +1 -1
  318. package/src/primitives/ThemeProvider/IconRegistry.tsx +42 -0
  319. package/src/primitives/ThemeProvider/ThemeProvider.tsx +16 -15
  320. package/src/primitives/ThemeProvider/TranslationsContext.ts +24 -0
  321. package/src/primitives/ThemeProvider/TranslationsProvider.tsx +4 -16
  322. package/src/primitives/ThemeProvider/icon-registry.ts +272 -0
  323. package/src/primitives/ThemeProvider/index.ts +9 -1
  324. package/src/testing/decorators/withLayout.tsx +4 -4
  325. package/src/testing/decorators/withLayoutVariants.tsx +3 -3
  326. package/src/testing/decorators/withTheme.tsx +1 -2
  327. package/src/theme/defaultTheme.ts +2 -0
  328. package/src/theme/index.ts +1 -0
  329. package/src/util/slots.stories.tsx +80 -0
  330. package/src/util/slots.ts +14 -5
  331. package/dist/lib/browser/chunk-CMHHVHX3.mjs +0 -46
  332. package/dist/lib/browser/chunk-CMHHVHX3.mjs.map +0 -7
  333. package/dist/lib/browser/chunk-GTHV4WYK.mjs +0 -1499
  334. package/dist/lib/browser/chunk-GTHV4WYK.mjs.map +0 -7
  335. package/dist/lib/browser/index.mjs +0 -5632
  336. package/dist/lib/browser/index.mjs.map +0 -7
  337. package/dist/lib/browser/meta.json +0 -1
  338. package/dist/lib/browser/testing/index.mjs +0 -133
  339. package/dist/lib/browser/testing/index.mjs.map +0 -7
  340. package/dist/lib/browser/translations.mjs +0 -9
  341. package/dist/lib/browser/translations.mjs.map +0 -7
  342. package/dist/lib/node-esm/chunk-3VSKWNCQ.mjs +0 -1501
  343. package/dist/lib/node-esm/chunk-3VSKWNCQ.mjs.map +0 -7
  344. package/dist/lib/node-esm/chunk-P4KFQGJI.mjs +0 -48
  345. package/dist/lib/node-esm/chunk-P4KFQGJI.mjs.map +0 -7
  346. package/dist/lib/node-esm/index.mjs +0 -5633
  347. package/dist/lib/node-esm/index.mjs.map +0 -7
  348. package/dist/lib/node-esm/meta.json +0 -1
  349. package/dist/lib/node-esm/testing/index.mjs +0 -134
  350. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  351. package/dist/lib/node-esm/translations.mjs +0 -10
  352. package/dist/lib/node-esm/translations.mjs.map +0 -7
  353. package/src/components/Column/AUDIT.md +0 -148
  354. package/src/components/Focus/AUDIT.md +0 -43
@@ -0,0 +1,105 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { cleanup, render, screen } from '@testing-library/react';
6
+ import React, { type PropsWithChildren } from 'react';
7
+ import { afterEach, describe, test } from 'vitest';
8
+
9
+ import { ThemeProvider } from '../../primitives';
10
+ import { defaultTx } from '../../theme';
11
+ import { Slider } from './Slider';
12
+
13
+ describe('Slider', () => {
14
+ afterEach(() => {
15
+ cleanup();
16
+ });
17
+
18
+ test('renders one thumb per value with real (non-logical) size utility classes', ({ expect }) => {
19
+ render(<Slider defaultValue={[25, 75]} max={100} step={1} thumbLabels={['Minimum', 'Maximum']} />, {
20
+ wrapper: Wrapper,
21
+ });
22
+
23
+ const thumbs = screen.getAllByRole('slider');
24
+ expect(thumbs).toHaveLength(2);
25
+
26
+ // Regression guard for the invisible-thumb bug: `is-4`/`bs-4` are not real Tailwind utilities
27
+ // in this repo's config (no logical inline-/block-size plugin is configured), so they generated
28
+ // no CSS and the thumb collapsed to zero size. happy-dom has no layout engine, so a computed
29
+ // `getBoundingClientRect`/`getComputedStyle` size assertion cannot fail meaningfully here (it
30
+ // would pass or fail independent of the actual applied CSS) — asserting the class list carries
31
+ // real (non-logical) sizing utilities, and never the invalid `is-*`/`bs-*` ones, is the
32
+ // strongest check available in this environment. Matches any size value, not a specific one.
33
+ for (const thumb of thumbs) {
34
+ expect(thumb.className).toMatch(/\bh-\d+\b/);
35
+ expect(thumb.className).toMatch(/\bw-\d+\b/);
36
+ expect(thumb.className).not.toMatch(/\bis-\d+\b/);
37
+ expect(thumb.className).not.toMatch(/\bbs-\d+\b/);
38
+ }
39
+ });
40
+
41
+ test('keeps the thumb rendered (never removed) when disabled', ({ expect }) => {
42
+ render(<Slider defaultValue={[50]} max={100} step={1} disabled aria-label='Value' />, { wrapper: Wrapper });
43
+
44
+ // Disabled state is expressed via the root's `opacity-50` (visually muted), not by unmounting
45
+ // the thumb — assert it is still present with its sizing classes intact.
46
+ const thumb = screen.getByRole('slider');
47
+ expect(thumb.className).toMatch(/\bh-\d+\b/);
48
+ expect(thumb.className).toMatch(/\bw-\d+\b/);
49
+ });
50
+
51
+ test('gives each thumb an accessible name via thumbLabels', ({ expect }) => {
52
+ render(<Slider defaultValue={[25, 75]} max={100} step={1} thumbLabels={['Minimum', 'Maximum']} />, {
53
+ wrapper: Wrapper,
54
+ });
55
+
56
+ // `role="slider"` has no visible text an `Input.Label`'s `htmlFor` can reach; `thumbLabels`
57
+ // is the only way to give it a name exposed to assistive tech.
58
+ expect(screen.getByRole('slider', { name: 'Minimum' })).toBeTruthy();
59
+ expect(screen.getByRole('slider', { name: 'Maximum' })).toBeTruthy();
60
+ });
61
+
62
+ test('gives a single thumb an accessible name via a plain aria-label', ({ expect }) => {
63
+ render(<Slider defaultValue={[50]} max={100} step={1} aria-label='Volume' />, { wrapper: Wrapper });
64
+
65
+ // Ergonomic shorthand for the common one-thumb case: no need to wrap the label in `thumbLabels`.
66
+ expect(screen.getByRole('slider', { name: 'Volume' })).toBeTruthy();
67
+ });
68
+
69
+ test('every rendered thumb, across default and multi-thumb usage, exposes an accessible name', ({ expect }) => {
70
+ const { unmount } = render(<Slider defaultValue={[50]} max={100} step={1} aria-label='Value' />, {
71
+ wrapper: Wrapper,
72
+ });
73
+ for (const thumb of screen.getAllByRole('slider')) {
74
+ expect(thumb.getAttribute('aria-label')).toBeTruthy();
75
+ }
76
+ unmount();
77
+
78
+ render(<Slider defaultValue={[25, 75]} max={100} step={1} thumbLabels={['Minimum', 'Maximum']} />, {
79
+ wrapper: Wrapper,
80
+ });
81
+ for (const thumb of screen.getAllByRole('slider')) {
82
+ expect(thumb.getAttribute('aria-label')).toBeTruthy();
83
+ }
84
+ });
85
+
86
+ test('throws when thumbLabels covers only some of the rendered thumbs', ({ expect }) => {
87
+ // Matching the invariant's own message, not just "it threw": a bare `toThrow()` would also be
88
+ // satisfied by an unrelated render crash, which is exactly what this guard must not accept.
89
+ expect(() =>
90
+ render(<Slider defaultValue={[25, 75]} max={100} step={1} thumbLabels={['Minimum']} />, { wrapper: Wrapper }),
91
+ ).toThrow('Slider: thumbLabels has 1 entries but 2 thumb(s) are rendered.');
92
+ });
93
+
94
+ test('throws when a multi-thumb slider is given no labels at all', ({ expect }) => {
95
+ // The other half of the guard: `aria-label` names only a single thumb, so omitting `thumbLabels`
96
+ // on a two-thumb slider would silently leave one thumb unnamed.
97
+ expect(() =>
98
+ render(<Slider defaultValue={[25, 75]} max={100} step={1} aria-label='Range' />, { wrapper: Wrapper }),
99
+ ).toThrow('Slider: pass thumbLabels (2 entries) or, for a single thumb, aria-label.');
100
+ });
101
+ });
102
+
103
+ // `ThemeProvider`'s default `tx` is a no-op (`() => undefined`) — pass `defaultTx` explicitly so
104
+ // `tx('slider.thumb', ...)` actually resolves classes, matching `useThemeContext.test.tsx`'s pattern.
105
+ const Wrapper = ({ children }: PropsWithChildren) => <ThemeProvider tx={defaultTx}>{children}</ThemeProvider>;
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { mx } from '@dxos/ui-theme';
6
+ import { type ComponentFunction, type Theme } from '@dxos/ui-types';
7
+
8
+ export type SliderStyleProps = {
9
+ orientation?: 'horizontal' | 'vertical';
10
+ disabled?: boolean;
11
+ };
12
+
13
+ const root: ComponentFunction<SliderStyleProps> = ({ orientation, disabled }, ...etc) =>
14
+ mx(
15
+ 'relative flex touch-none select-none items-center',
16
+ orientation === 'vertical' ? 'h-full w-3 flex-col' : 'w-full h-3',
17
+ disabled && 'opacity-50 pointer-events-none',
18
+ ...etc,
19
+ );
20
+
21
+ const track: ComponentFunction<SliderStyleProps> = ({ orientation }, ...etc) =>
22
+ mx(
23
+ 'relative grow overflow-hidden rounded-full bg-input-surface',
24
+ orientation === 'vertical' ? 'w-1 h-full' : 'h-1 w-full',
25
+ ...etc,
26
+ );
27
+
28
+ const range: ComponentFunction<SliderStyleProps> = ({ orientation }, ...etc) =>
29
+ mx('absolute bg-accent-bg', orientation === 'vertical' ? 'w-full' : 'h-full', ...etc);
30
+
31
+ const thumb: ComponentFunction<SliderStyleProps> = (_props, ...etc) =>
32
+ mx(
33
+ 'block h-3 w-3 shrink-0 rounded-full bg-base-surface border-2 border-accent-bg shadow-sm transition-colors dx-focus-ring',
34
+ 'hover:bg-hover-surface',
35
+ ...etc,
36
+ );
37
+
38
+ export const sliderTheme: Theme<SliderStyleProps> = { root, track, range, thumb };
@@ -0,0 +1,64 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as SliderPrimitive from '@radix-ui/react-slider';
6
+ import React, { forwardRef } from 'react';
7
+
8
+ import { invariant } from '@dxos/invariant';
9
+
10
+ import { useThemeContext } from '../../hooks';
11
+ import { type ThemedClassName } from '../../util';
12
+
13
+ type SliderProps = ThemedClassName<SliderPrimitive.SliderProps> & {
14
+ /**
15
+ * Accessible name per thumb, by index — `role="slider"` has no visible text a label's `htmlFor`
16
+ * can reach, so this is the only way to name it for assistive tech. Required whenever there is
17
+ * more than one thumb; for the common single-thumb case, a plain `aria-label` works instead.
18
+ */
19
+ thumbLabels?: string[];
20
+ };
21
+
22
+ /** Range input built on the Radix slider primitive; supports one or more thumbs. */
23
+ const Slider = forwardRef<HTMLSpanElement, SliderProps>(
24
+ (
25
+ { classNames, orientation = 'horizontal', disabled, thumbLabels, 'aria-label': ariaLabel, ...props },
26
+ forwardedRef,
27
+ ) => {
28
+ const { tx } = useThemeContext();
29
+ const styleProps = { orientation, disabled };
30
+ const thumbCount = props.value?.length ?? props.defaultValue?.length ?? 1;
31
+
32
+ // Every thumb must expose an accessible name: either one label per thumb, or (for the common
33
+ // single-thumb case) a plain `aria-label` passthrough — never silently render an unnamed thumb.
34
+ invariant(
35
+ thumbLabels ? thumbLabels.length === thumbCount : thumbCount === 1 && ariaLabel !== undefined,
36
+ thumbLabels
37
+ ? `Slider: thumbLabels has ${thumbLabels.length} entries but ${thumbCount} thumb(s) are rendered.`
38
+ : `Slider: pass thumbLabels (${thumbCount} entries) or, for a single thumb, aria-label.`,
39
+ );
40
+
41
+ const labelFor = (index: number) => thumbLabels?.[index] ?? ariaLabel;
42
+
43
+ return (
44
+ <SliderPrimitive.Root
45
+ {...props}
46
+ orientation={orientation}
47
+ disabled={disabled}
48
+ className={tx('slider.root', styleProps, classNames)}
49
+ ref={forwardedRef}
50
+ >
51
+ <SliderPrimitive.Track className={tx('slider.track', styleProps)}>
52
+ <SliderPrimitive.Range className={tx('slider.range', styleProps)} />
53
+ </SliderPrimitive.Track>
54
+ {Array.from({ length: thumbCount }, (_unused, index) => (
55
+ <SliderPrimitive.Thumb key={index} className={tx('slider.thumb', styleProps)} aria-label={labelFor(index)} />
56
+ ))}
57
+ </SliderPrimitive.Root>
58
+ );
59
+ },
60
+ );
61
+
62
+ export type { SliderProps };
63
+
64
+ export { Slider };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './Slider';
@@ -13,7 +13,7 @@ export type StatusStyleProps = {
13
13
  const root: ComponentFunction<StatusStyleProps> = ({ variant = 'default' }, ...etc) =>
14
14
  mx(
15
15
  'h-1 relative rounded-full overflow-hidden',
16
- variant === 'main-bottom' ? 'w-full block' : 'inline-20 inline-block bg-base-surface',
16
+ variant === 'main-bottom' ? 'w-full block' : 'inline-block bg-base-surface',
17
17
  ...etc,
18
18
  );
19
19
 
@@ -26,7 +26,6 @@ const Status = forwardRef<HTMLSpanElement, StatusProps>(
26
26
  ref={forwardedRef}
27
27
  >
28
28
  <span
29
- role='none'
30
29
  className={tx('status.bar', { indeterminate, variant }, classNames)}
31
30
  {...(!indeterminate && { style: { width: `${Math.round(progress * 100)}%` } })}
32
31
  />
@@ -5,7 +5,7 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
- import { hues } from '@dxos/ui-theme';
8
+ import { hues } from '@dxos/ui-types';
9
9
  import { type ChromaticPalette, type MessageValence } from '@dxos/ui-types';
10
10
 
11
11
  import { withLayout, withTheme } from '../../testing';
@@ -14,12 +14,21 @@ export type ToolbarStyleProps = Partial<{
14
14
  const layout =
15
15
  'w-full shrink-0 flex flex-nowrap p-1 gap-1 items-center overflow-x-auto scrollbar-none dx-contain-layout';
16
16
 
17
+ // The bar declares its density so the shared rule (theme/spacing.css) resizes both the bar and
18
+ // every control inside it; the height then follows the same knob rather than a hard-coded step.
17
19
  const root: ComponentFunction<ToolbarStyleProps> = ({ density, disabled, layoutManaged }, ...etc) => {
18
20
  return mx(
19
- 'bg-toolbar-surface dx-toolbar shadow-sm',
20
- density === 'lg' && 'h-(--dx-rail-size) px-3!',
21
- density === 'sm' && 'h-7 px-2!',
22
- density === 'xs' && 'h-6 px-1!',
21
+ // No shadow here: `Panel.Toolbar` casts the bar's edge shadow, and a toolbar reaches that slot
22
+ // two ways as a descendant, or merged into it via `asChild`. Only the descendant form can be
23
+ // detected in CSS, so a shadow on this element would double up in the merged form and read as a
24
+ // floating bar. The slot owns it in both.
25
+ 'dx-toolbar-surface',
26
+ // As a descendant of a Panel slot the slot has already painted the surface full-width; painting
27
+ // again clamps it to this element, which a `classNames='dx-document'` toolbar renders as a strip
28
+ // floating inside the bar. (Merged via `asChild` there is one element, which must keep it.)
29
+ '[[data-slot=toolbar]_&]:bg-transparent',
30
+ '[[data-slot=statusbar]_&]:bg-transparent',
31
+ density && `dx-density-${density}`,
23
32
  disabled && '*:opacity-20',
24
33
  !layoutManaged && layout,
25
34
  ...etc,
@@ -27,7 +36,7 @@ const root: ComponentFunction<ToolbarStyleProps> = ({ density, disabled, layoutM
27
36
  };
28
37
 
29
38
  const text: ComponentFunction<ToolbarStyleProps> = (_, ...etc) => {
30
- return mx('px-2 grow truncate items-center', ...etc);
39
+ return mx('px-2 truncate items-center', ...etc);
31
40
  };
32
41
 
33
42
  export const toolbarTheme: Theme<ToolbarStyleProps> = {
@@ -27,6 +27,7 @@ import {
27
27
  type ToggleGroupItemProps,
28
28
  type ToggleProps,
29
29
  } from '../Button';
30
+ import { Icon } from '../Icon';
30
31
  import { Link, type LinkProps } from '../Link';
31
32
  import { DropdownMenu } from '../Menu';
32
33
  import { Separator, type SeparatorProps } from '../Separator';
@@ -303,6 +304,8 @@ ToolbarActionIconButton.displayName = 'Toolbar.ActionIconButton';
303
304
 
304
305
  type ToolbarMenuItem<T extends any | void = void> = {
305
306
  label: string;
307
+ /** Optional leading icon (e.g. `ph--trash--regular`). */
308
+ icon?: string;
306
309
  onClick: (context: T) => void;
307
310
  };
308
311
 
@@ -329,8 +332,9 @@ function ToolbarMenu<T extends any | void = void>({ context, items }: ToolbarMen
329
332
  <DropdownMenu.Portal>
330
333
  <DropdownMenu.Content>
331
334
  <DropdownMenu.Viewport>
332
- {items?.map(({ label, onClick: onSelect }, index) => (
335
+ {items?.map(({ label, icon, onClick: onSelect }, index) => (
333
336
  <DropdownMenu.Item key={index} onSelect={() => onSelect(context as T)}>
337
+ {icon && <Icon icon={icon} />}
334
338
  {label}
335
339
  </DropdownMenu.Item>
336
340
  ))}
@@ -12,7 +12,8 @@ import React, {
12
12
  useRef,
13
13
  } from 'react';
14
14
 
15
- import { Tooltip, type TooltipScopedProps, type TooltipTriggerProps } from './Tooltip';
15
+ import { Tooltip, type TooltipTriggerProps } from './Tooltip';
16
+ import { type TooltipScopedProps } from './TooltipContext';
16
17
 
17
18
  export type TextTooltipProps = PropsWithChildren<
18
19
  {
@@ -7,12 +7,10 @@
7
7
 
8
8
  import { composeEventHandlers } from '@radix-ui/primitive';
9
9
  import { useComposedRefs } from '@radix-ui/react-compose-refs';
10
- import { createContextScope } from '@radix-ui/react-context';
11
- import type { Scope } from '@radix-ui/react-context';
12
10
  import { DismissableLayer } from '@radix-ui/react-dismissable-layer';
13
11
  import { useId } from '@radix-ui/react-id';
14
12
  import * as PopperPrimitive from '@radix-ui/react-popper';
15
- import { type PopperAnchorProps, createPopperScope } from '@radix-ui/react-popper';
13
+ import { type PopperAnchorProps } from '@radix-ui/react-popper';
16
14
  import { Portal as PortalPrimitive } from '@radix-ui/react-portal';
17
15
  import { Presence } from '@radix-ui/react-presence';
18
16
  import { Primitive } from '@radix-ui/react-primitive';
@@ -36,35 +34,16 @@ import React, {
36
34
  } from 'react';
37
35
 
38
36
  import { useElevationContext, useThemeContext } from '../../hooks';
39
-
40
- type TooltipScopedProps<P = {}> = P & { __scopeTooltip?: Scope };
41
- const [createTooltipContext, createTooltipScope] = createContextScope('Tooltip', [createPopperScope]);
42
- const usePopperScope = createPopperScope();
43
-
44
- //
45
- // Tooltip
46
- //
47
-
48
- const DEFAULT_DELAY_DURATION = 700;
49
- const TOOLTIP_OPEN = 'tooltip.open';
50
- const TOOLTIP_NAME = 'Tooltip';
51
-
52
- type TooltipContextValue = {
53
- contentId: string;
54
- open: boolean;
55
- stateAttribute: 'closed' | 'delayed-open' | 'instant-open';
56
- trigger: TooltipTriggerElement | null;
57
- onTriggerChange(trigger: TooltipTriggerElement | null, content?: ReactNode, side?: TooltipSide): void;
58
- onTriggerEnter(): void;
59
- onTriggerLeave(): void;
60
- onOpen(): void;
61
- onClose(): void;
62
- onPointerInTransitChange(inTransit: boolean): void;
63
- isPointerInTransitRef: RefObject<boolean>;
64
- disableHoverableContent: boolean;
65
- };
66
-
67
- const [TooltipContextProvider, useTooltipContext] = createTooltipContext<TooltipContextValue>(TOOLTIP_NAME);
37
+ import {
38
+ DEFAULT_DELAY_DURATION,
39
+ TOOLTIP_NAME,
40
+ TOOLTIP_OPEN,
41
+ TooltipContextProvider,
42
+ type TooltipScopedProps,
43
+ createTooltipContext,
44
+ usePopperScope,
45
+ useTooltipContext,
46
+ } from './TooltipContext';
68
47
 
69
48
  type TooltipProviderProps = {
70
49
  children?: ReactNode;
@@ -763,6 +742,4 @@ export const Tooltip = {
763
742
  Trigger: TooltipTrigger,
764
743
  };
765
744
 
766
- export { createTooltipScope, useTooltipContext };
767
-
768
- export type { TooltipProviderProps, TooltipScopedProps, TooltipSide, TooltipTriggerProps };
745
+ export type { TooltipProviderProps, TooltipSide, TooltipTriggerElement, TooltipTriggerProps };
@@ -0,0 +1,41 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Scope, createContextScope } from '@radix-ui/react-context';
6
+ import { createPopperScope } from '@radix-ui/react-popper';
7
+ import { type ReactNode, type RefObject } from 'react';
8
+
9
+ import { type TooltipSide, type TooltipTriggerElement } from './Tooltip';
10
+
11
+ // Kept out of `Tooltip.tsx`: react-refresh only fast-refreshes a module whose exports are all
12
+ // components, so a context and its hook exported beside them force a full page reload on every edit.
13
+
14
+ export type TooltipScopedProps<P = {}> = P & { __scopeTooltip?: Scope };
15
+ export const [createTooltipContext, createTooltipScope] = createContextScope('Tooltip', [createPopperScope]);
16
+ export const usePopperScope = createPopperScope();
17
+
18
+ //
19
+ // Tooltip
20
+ //
21
+
22
+ export const DEFAULT_DELAY_DURATION = 700;
23
+ export const TOOLTIP_OPEN = 'tooltip.open';
24
+ export const TOOLTIP_NAME = 'Tooltip';
25
+
26
+ export type TooltipContextValue = {
27
+ contentId: string;
28
+ open: boolean;
29
+ stateAttribute: 'closed' | 'delayed-open' | 'instant-open';
30
+ trigger: TooltipTriggerElement | null;
31
+ onTriggerChange(trigger: TooltipTriggerElement | null, content?: ReactNode, side?: TooltipSide): void;
32
+ onTriggerEnter(): void;
33
+ onTriggerLeave(): void;
34
+ onOpen(): void;
35
+ onClose(): void;
36
+ onPointerInTransitChange(inTransit: boolean): void;
37
+ isPointerInTransitRef: RefObject<boolean>;
38
+ disableHoverableContent: boolean;
39
+ };
40
+
41
+ export const [TooltipContextProvider, useTooltipContext] = createTooltipContext<TooltipContextValue>(TOOLTIP_NAME);
@@ -3,5 +3,7 @@
3
3
  //
4
4
 
5
5
  export * from './TextTooltip';
6
+ export { type TooltipScopedProps, createTooltipScope, useTooltipContext } from './TooltipContext';
7
+
6
8
  export * from './Tooltip';
7
9
  export * from './tooltipContent';
@@ -29,6 +29,7 @@ export * from './ScrollContainer';
29
29
  export * from './Select';
30
30
  export * from './Separator';
31
31
  export * from './Skeleton';
32
+ export * from './Slider';
32
33
  export * from './Splitter';
33
34
  export * from './Status';
34
35
  export * from './Tag';
@@ -2,12 +2,33 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { useThemeContext } from './useThemeContext';
5
+ import { useCallback, useSyncExternalStore } from 'react';
6
6
 
7
- const ICONS_URL = '/icons.svg';
7
+ import { useIconRegistry } from '../primitives/ThemeProvider/icon-registry';
8
8
 
9
+ /**
10
+ * Resolves an icon name to a same-document `<use href>`.
11
+ *
12
+ * Returns `undefined` while the icon is not yet present in the in-DOM sprite — kicking off
13
+ * a runtime fetch as a side effect. Once the registry has the symbol (either because the
14
+ * static sprite finished ingesting or because the resolver injected it), the component
15
+ * re-renders via `useSyncExternalStore` and we return `#name`.
16
+ */
9
17
  export const useIconHref = (icon?: string) => {
10
- const { noCache } = useThemeContext();
11
- const url = noCache ? `${ICONS_URL}?nocache=${new Date().getMinutes()}` : ICONS_URL;
12
- return icon ? `${url}#${icon}` : undefined;
18
+ const registry = useIconRegistry();
19
+
20
+ const subscribe = useCallback((listener: () => void) => registry.subscribe(listener), [registry]);
21
+ const getSnapshot = useCallback(() => (icon ? registry.hasIcon(icon) : false), [icon, registry]);
22
+ const getServerSnapshot = useCallback(() => false, []);
23
+
24
+ const hasIcon = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
25
+
26
+ if (!icon) {
27
+ return undefined;
28
+ }
29
+ if (!hasIcon) {
30
+ registry.requestIcon(icon);
31
+ return undefined;
32
+ }
33
+ return `#${icon}`;
13
34
  };
@@ -0,0 +1,33 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { renderHook } from '@testing-library/react';
6
+ import React, { type PropsWithChildren } from 'react';
7
+ import { describe, expect, test } from 'vitest';
8
+
9
+ import { ThemeProvider } from '../primitives';
10
+ import { defaultTx } from '../theme';
11
+ import { useThemeContext } from './useThemeContext';
12
+
13
+ describe('useThemeContext', () => {
14
+ // Error-reporting surfaces (e.g. the fatal dialog) must be able to render even
15
+ // when the provider is missing — including the vite dev dual-module case where
16
+ // the provider supplied a different ThemeContext identity than the consumer's.
17
+ test('falls back to the default theme without a provider', () => {
18
+ const { result } = renderHook(() => useThemeContext());
19
+ expect(result.current.tx).toBe(defaultTx);
20
+ expect(result.current.themeMode).toBe('dark');
21
+ });
22
+
23
+ test('returns the provider value when present', () => {
24
+ const wrapper = ({ children }: PropsWithChildren) => (
25
+ <ThemeProvider tx={defaultTx} themeMode='light'>
26
+ {children}
27
+ </ThemeProvider>
28
+ );
29
+ const { result } = renderHook(() => useThemeContext(), { wrapper });
30
+ expect(result.current.tx).toBe(defaultTx);
31
+ expect(result.current.themeMode).toBe('light');
32
+ });
33
+ });
@@ -4,8 +4,33 @@
4
4
 
5
5
  import { useContext } from 'react';
6
6
 
7
- import { raise } from '@dxos/debug';
7
+ import { log } from '@dxos/log';
8
8
 
9
- import { ThemeContext } from '../primitives';
9
+ import { ThemeContext, type ThemeContextValue } from '../primitives';
10
+ import { defaultTx } from '../theme';
10
11
 
11
- export const useThemeContext = () => useContext(ThemeContext) ?? raise(new Error('Missing ThemeContext'));
12
+ // Failing soft (rather than throwing) keeps error-reporting surfaces renderable:
13
+ // the fatal dialog itself consumes this hook, and a missing provider — including
14
+ // the vite dev dual-module split, where the mounted ThemeProvider holds a
15
+ // different ThemeContext identity than this consumer — would otherwise crash the
16
+ // dialog meant to report the original error.
17
+ const fallbackContextValue: ThemeContextValue = {
18
+ tx: defaultTx,
19
+ themeMode: 'dark',
20
+ hasIosKeyboard: false,
21
+ };
22
+
23
+ let warned = false;
24
+
25
+ export const useThemeContext = (): ThemeContextValue => {
26
+ const contextValue = useContext(ThemeContext);
27
+ if (!contextValue) {
28
+ if (!warned) {
29
+ warned = true;
30
+ log.warn('Missing ThemeContext; falling back to the default theme.');
31
+ }
32
+ return fallbackContextValue;
33
+ }
34
+
35
+ return contextValue;
36
+ };
@@ -0,0 +1,41 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { IconButton } from '../components/Button';
9
+ import { Input } from '../components/Input';
10
+ import { withLayoutVariants, withTheme } from '../testing';
11
+
12
+ /**
13
+ * The three control sizes side by side, across control types — a density change must read the same
14
+ * on a checkbox, a text field and an icon button, since they share the `--dx-control*` knobs.
15
+ */
16
+ const DensityStory = () => (
17
+ <div className='flex flex-col gap-4'>
18
+ {(['lg', 'md', 'sm'] as const).map((density) => (
19
+ <Input.Root key={density}>
20
+ <Input.Label>{`density="${density}"`}</Input.Label>
21
+ <div className='flex items-center gap-2'>
22
+ <Input.Checkbox size={density === 'lg' ? 5 : 4} />
23
+ <Input.TextInput density={density} classNames='grow' placeholder={`This is a density:${density} input`} />
24
+ <IconButton density={density} iconOnly icon='ph--gear--regular' label='Settings' />
25
+ </div>
26
+ </Input.Root>
27
+ ))}
28
+ </div>
29
+ );
30
+
31
+ const meta = {
32
+ title: 'ui/react-ui-core/playground/Density',
33
+ render: DensityStory,
34
+ decorators: [withTheme(), withLayoutVariants()],
35
+ } satisfies Meta<typeof DensityStory>;
36
+
37
+ export default meta;
38
+
39
+ type Story = StoryObj<typeof meta>;
40
+
41
+ export const Default: Story = {};