@dxos/react-ui 0.9.0 → 0.9.1-staging.ee54ba693a

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 (263) hide show
  1. package/dist/lib/browser/{chunk-5SPBSIWS.mjs → chunk-CMHHVHX3.mjs} +14 -1
  2. package/dist/lib/browser/chunk-CMHHVHX3.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-7JFD5ZHZ.mjs → chunk-GTHV4WYK.mjs} +141 -254
  4. package/dist/lib/browser/chunk-GTHV4WYK.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +1553 -1274
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +5 -5
  9. package/dist/lib/browser/testing/index.mjs.map +2 -2
  10. package/dist/lib/browser/translations.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-TTEL2FP2.mjs → chunk-3VSKWNCQ.mjs} +141 -254
  12. package/dist/lib/node-esm/chunk-3VSKWNCQ.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-FPVTVXND.mjs → chunk-P4KFQGJI.mjs} +14 -1
  14. package/dist/lib/node-esm/chunk-P4KFQGJI.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +1553 -1274
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +5 -5
  19. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  20. package/dist/lib/node-esm/translations.mjs +1 -1
  21. package/dist/types/src/components/Avatars/Avatar.d.ts +1 -1
  22. package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
  23. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts +3 -3
  24. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
  25. package/dist/types/src/components/Button/Button.d.ts +4 -2
  26. package/dist/types/src/components/Button/Button.d.ts.map +1 -1
  27. package/dist/types/src/components/Button/IconButton.d.ts +0 -1
  28. package/dist/types/src/components/Button/IconButton.d.ts.map +1 -1
  29. package/dist/types/src/components/Button/IconButton.stories.d.ts +37 -0
  30. package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -1
  31. package/dist/types/src/components/Button/MicButton.d.ts +24 -0
  32. package/dist/types/src/components/Button/MicButton.d.ts.map +1 -0
  33. package/dist/types/src/components/Button/MicButton.stories.d.ts +57 -0
  34. package/dist/types/src/components/Button/MicButton.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/Button/SystemIconButton.d.ts +33 -0
  36. package/dist/types/src/components/Button/SystemIconButton.d.ts.map +1 -0
  37. package/dist/types/src/components/Button/SystemIconButton.stories.d.ts +51 -0
  38. package/dist/types/src/components/Button/SystemIconButton.stories.d.ts.map +1 -0
  39. package/dist/types/src/components/Button/ToggleGroup.d.ts +2 -2
  40. package/dist/types/src/components/Button/ToggleGroup.d.ts.map +1 -1
  41. package/dist/types/src/components/Button/ToggleIconButton.d.ts +15 -0
  42. package/dist/types/src/components/Button/ToggleIconButton.d.ts.map +1 -0
  43. package/dist/types/src/components/Button/index.d.ts +3 -0
  44. package/dist/types/src/components/Button/index.d.ts.map +1 -1
  45. package/dist/types/src/components/Calendar/Calendar.d.ts.map +1 -1
  46. package/dist/types/src/components/Calendar/Calendar.theme.d.ts.map +1 -1
  47. package/dist/types/src/components/Card/Card.d.ts +32 -26
  48. package/dist/types/src/components/Card/Card.d.ts.map +1 -1
  49. package/dist/types/src/components/Card/Card.stories.d.ts +3 -3
  50. package/dist/types/src/components/Card/Card.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/Card/Card.theme.d.ts.map +1 -1
  52. package/dist/types/src/components/Carousel/Carousel.d.ts +29 -6
  53. package/dist/types/src/components/Carousel/Carousel.d.ts.map +1 -1
  54. package/dist/types/src/components/Carousel/Carousel.stories.d.ts +48 -2
  55. package/dist/types/src/components/Carousel/Carousel.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Clipboard/index.d.ts +1 -0
  57. package/dist/types/src/components/Clipboard/index.d.ts.map +1 -1
  58. package/dist/types/src/components/Column/Column.d.ts +20 -2
  59. package/dist/types/src/components/Column/Column.d.ts.map +1 -1
  60. package/dist/types/src/components/Column/Column.theme.d.ts +8 -0
  61. package/dist/types/src/components/Column/Column.theme.d.ts.map +1 -1
  62. package/dist/types/src/components/Dialog/AlertDialog.d.ts +2 -2
  63. package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -1
  64. package/dist/types/src/components/Dialog/Dialog.d.ts +1 -1
  65. package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/types/src/components/Dialog/Dialog.stories.d.ts +2 -2
  67. package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -1
  68. package/dist/types/src/components/Icon/Icon.stories.d.ts +5 -0
  69. package/dist/types/src/components/Icon/Icon.stories.d.ts.map +1 -1
  70. package/dist/types/src/components/Input/Input.d.ts +4 -1
  71. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  72. package/dist/types/src/components/Input/Input.stories.d.ts +3 -3
  73. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/Input/Input.theme.d.ts +10 -10
  75. package/dist/types/src/components/Input/Input.theme.d.ts.map +1 -1
  76. package/dist/types/src/components/Input/SegmentedInput.d.ts +8 -8
  77. package/dist/types/src/components/Input/SegmentedInput.d.ts.map +1 -1
  78. package/dist/types/src/components/Input/index.d.ts +0 -1
  79. package/dist/types/src/components/Input/index.d.ts.map +1 -1
  80. package/dist/types/src/components/Main/Main.d.ts +3 -3
  81. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  82. package/dist/types/src/components/Menu/ContextMenu.d.ts +1 -1
  83. package/dist/types/src/components/Menu/ContextMenu.d.ts.map +1 -1
  84. package/dist/types/src/components/Menu/DropdownMenu.d.ts +2 -2
  85. package/dist/types/src/components/Menu/DropdownMenu.d.ts.map +1 -1
  86. package/dist/types/src/components/Message/Message.d.ts +1 -1
  87. package/dist/types/src/components/Message/Message.d.ts.map +1 -1
  88. package/dist/types/src/components/Message/Message.stories.d.ts +2 -2
  89. package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
  90. package/dist/types/src/components/Message/Message.theme.d.ts.map +1 -1
  91. package/dist/types/src/components/Panel/Panel.d.ts +1 -1
  92. package/dist/types/src/components/Panel/Panel.d.ts.map +1 -1
  93. package/dist/types/src/components/Panel/Panel.theme.d.ts.map +1 -1
  94. package/dist/types/src/components/Popover/Popover.d.ts +2 -2
  95. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  96. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +2 -0
  97. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
  98. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +1 -0
  99. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
  100. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +1 -1
  101. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
  102. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +2 -2
  103. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -1
  104. package/dist/types/src/components/Select/Select.d.ts +1 -1
  105. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  106. package/dist/types/src/components/Select/Select.stories.d.ts +2 -2
  107. package/dist/types/src/components/Select/Select.stories.d.ts.map +1 -1
  108. package/dist/types/src/components/Separator/Separator.d.ts.map +1 -1
  109. package/dist/types/src/components/Splitter/Splitter.d.ts +30 -16
  110. package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -1
  111. package/dist/types/src/components/Splitter/Splitter.stories.d.ts +6 -1
  112. package/dist/types/src/components/Splitter/Splitter.stories.d.ts.map +1 -1
  113. package/dist/types/src/components/Splitter/Splitter.theme.d.ts +3 -1
  114. package/dist/types/src/components/Splitter/Splitter.theme.d.ts.map +1 -1
  115. package/dist/types/src/components/Tag/Tag.d.ts +1 -1
  116. package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
  117. package/dist/types/src/components/Tag/Tag.theme.d.ts +1 -1
  118. package/dist/types/src/components/Tag/Tag.theme.d.ts.map +1 -1
  119. package/dist/types/src/components/Toast/Toast.d.ts +1 -1
  120. package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
  121. package/dist/types/src/components/Toast/Toast.theme.d.ts.map +1 -1
  122. package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -1
  123. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  124. package/dist/types/src/components/Tooltip/TextTooltip.d.ts +19 -0
  125. package/dist/types/src/components/Tooltip/TextTooltip.d.ts.map +1 -0
  126. package/dist/types/src/components/Tooltip/Tooltip.d.ts +3 -3
  127. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  128. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +2 -2
  129. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
  130. package/dist/types/src/components/Tooltip/index.d.ts +1 -0
  131. package/dist/types/src/components/Tooltip/index.d.ts.map +1 -1
  132. package/dist/types/src/components/index.d.ts +0 -1
  133. package/dist/types/src/components/index.d.ts.map +1 -1
  134. package/dist/types/src/index.d.ts +1 -1
  135. package/dist/types/src/index.d.ts.map +1 -1
  136. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts +1 -8
  137. package/dist/types/src/primitives/ThemeProvider/TranslationsProvider.d.ts.map +1 -1
  138. package/dist/types/src/testing/Loading.d.ts.map +1 -1
  139. package/dist/types/src/theme/defaultTheme.d.ts.map +1 -1
  140. package/dist/types/src/theme/index.d.ts +0 -2
  141. package/dist/types/src/theme/index.d.ts.map +1 -1
  142. package/dist/types/src/translations.d.ts +13 -0
  143. package/dist/types/src/translations.d.ts.map +1 -1
  144. package/dist/types/src/util/slots.d.ts +1 -1
  145. package/dist/types/src/util/slots.d.ts.map +1 -1
  146. package/dist/types/tsconfig.tsbuildinfo +1 -1
  147. package/package.json +22 -21
  148. package/src/components/Avatars/Avatar.stories.tsx +2 -2
  149. package/src/components/Avatars/Avatar.theme.ts +1 -1
  150. package/src/components/Avatars/Avatar.tsx +3 -3
  151. package/src/components/Breadcrumb/Breadcrumb.tsx +6 -6
  152. package/src/components/Button/Button.tsx +25 -4
  153. package/src/components/Button/IconButton.stories.tsx +81 -1
  154. package/src/components/Button/IconButton.tsx +2 -4
  155. package/src/components/Button/MicButton.stories.tsx +60 -0
  156. package/src/components/Button/MicButton.tsx +117 -0
  157. package/src/components/Button/SystemIconButton.stories.tsx +91 -0
  158. package/src/components/Button/SystemIconButton.tsx +264 -0
  159. package/src/components/Button/ToggleGroup.tsx +2 -2
  160. package/src/components/Button/ToggleIconButton.tsx +32 -0
  161. package/src/components/Button/index.ts +3 -0
  162. package/src/components/Calendar/Calendar.theme.ts +2 -0
  163. package/src/components/Calendar/Calendar.tsx +3 -3
  164. package/src/components/Card/Card.stories.tsx +71 -29
  165. package/src/components/Card/Card.theme.ts +27 -9
  166. package/src/components/Card/Card.tsx +176 -99
  167. package/src/components/Carousel/Carousel.stories.tsx +42 -18
  168. package/src/components/Carousel/Carousel.tsx +169 -33
  169. package/src/components/Column/Column.theme.ts +32 -4
  170. package/src/components/Column/Column.tsx +39 -7
  171. package/src/components/Dialog/AlertDialog.stories.tsx +2 -2
  172. package/src/components/Dialog/AlertDialog.tsx +13 -13
  173. package/src/components/Dialog/Dialog.stories.tsx +3 -4
  174. package/src/components/Dialog/Dialog.theme.ts +1 -1
  175. package/src/components/Dialog/Dialog.tsx +12 -12
  176. package/src/components/Icon/Icon.stories.tsx +23 -0
  177. package/src/components/Icon/Icon.theme.ts +1 -1
  178. package/src/components/Input/Input.stories.tsx +13 -5
  179. package/src/components/Input/Input.theme.ts +17 -39
  180. package/src/components/Input/Input.tsx +30 -13
  181. package/src/components/Input/SegmentedInput.tsx +50 -41
  182. package/src/components/Input/index.ts +0 -1
  183. package/src/components/Main/Main.tsx +3 -3
  184. package/src/components/MediaPlayer/MediaPlayer.tsx +4 -4
  185. package/src/components/Menu/ContextMenu.tsx +8 -8
  186. package/src/components/Menu/DropdownMenu.tsx +12 -13
  187. package/src/components/Message/Message.stories.tsx +2 -2
  188. package/src/components/Message/Message.theme.ts +1 -11
  189. package/src/components/Message/Message.tsx +6 -6
  190. package/src/components/Panel/Panel.theme.ts +2 -1
  191. package/src/components/Panel/Panel.tsx +1 -1
  192. package/src/components/Popover/Popover.theme.ts +1 -1
  193. package/src/components/Popover/Popover.tsx +9 -9
  194. package/src/components/ScrollArea/ScrollArea.tsx +7 -4
  195. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +2 -2
  196. package/src/components/ScrollContainer/ScrollContainer.tsx +2 -2
  197. package/src/components/Select/Select.stories.tsx +2 -2
  198. package/src/components/Select/Select.theme.ts +1 -1
  199. package/src/components/Select/Select.tsx +14 -14
  200. package/src/components/Separator/Separator.tsx +1 -0
  201. package/src/components/Splitter/Splitter.stories.tsx +84 -20
  202. package/src/components/Splitter/Splitter.theme.ts +20 -3
  203. package/src/components/Splitter/Splitter.tsx +320 -47
  204. package/src/components/Tag/Tag.stories.tsx +1 -1
  205. package/src/components/Tag/Tag.theme.ts +1 -1
  206. package/src/components/Tag/Tag.tsx +3 -5
  207. package/src/components/Toast/Toast.theme.ts +3 -8
  208. package/src/components/Toast/Toast.tsx +16 -15
  209. package/src/components/Toolbar/Toolbar.tsx +10 -10
  210. package/src/components/Tooltip/TextTooltip.tsx +54 -0
  211. package/src/components/Tooltip/Tooltip.stories.tsx +2 -2
  212. package/src/components/Tooltip/Tooltip.tsx +3 -3
  213. package/src/components/Tooltip/index.ts +1 -0
  214. package/src/components/index.ts +0 -1
  215. package/src/exemplars/tabster.stories.tsx +1 -1
  216. package/src/index.ts +1 -1
  217. package/src/primitives/ThemeProvider/TranslationsProvider.tsx +10 -30
  218. package/src/testing/Loading.tsx +2 -1
  219. package/src/testing/decorators/withLayout.tsx +2 -2
  220. package/src/theme/defaultTheme.ts +0 -4
  221. package/src/theme/index.ts +0 -2
  222. package/src/translations.ts +18 -0
  223. package/src/util/slots.ts +5 -5
  224. package/dist/lib/browser/chunk-5SPBSIWS.mjs.map +0 -7
  225. package/dist/lib/browser/chunk-7JFD5ZHZ.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-FPVTVXND.mjs.map +0 -7
  227. package/dist/lib/node-esm/chunk-TTEL2FP2.mjs.map +0 -7
  228. package/dist/types/src/components/Input/constants.d.ts +0 -2
  229. package/dist/types/src/components/Input/constants.d.ts.map +0 -1
  230. package/dist/types/src/components/List/List.d.ts +0 -29
  231. package/dist/types/src/components/List/List.d.ts.map +0 -1
  232. package/dist/types/src/components/List/List.stories.d.ts +0 -16
  233. package/dist/types/src/components/List/List.stories.d.ts.map +0 -1
  234. package/dist/types/src/components/List/List.theme.d.ts +0 -7
  235. package/dist/types/src/components/List/List.theme.d.ts.map +0 -1
  236. package/dist/types/src/components/List/ListDropIndicator.d.ts +0 -13
  237. package/dist/types/src/components/List/ListDropIndicator.d.ts.map +0 -1
  238. package/dist/types/src/components/List/Tree.d.ts +0 -23
  239. package/dist/types/src/components/List/Tree.d.ts.map +0 -1
  240. package/dist/types/src/components/List/Tree.stories.d.ts +0 -15
  241. package/dist/types/src/components/List/Tree.stories.d.ts.map +0 -1
  242. package/dist/types/src/components/List/TreeDropIndicator.d.ts +0 -8
  243. package/dist/types/src/components/List/TreeDropIndicator.d.ts.map +0 -1
  244. package/dist/types/src/components/List/Treegrid.d.ts +0 -33
  245. package/dist/types/src/components/List/Treegrid.d.ts.map +0 -1
  246. package/dist/types/src/components/List/Treegrid.stories.d.ts +0 -12
  247. package/dist/types/src/components/List/Treegrid.stories.d.ts.map +0 -1
  248. package/dist/types/src/components/List/Treegrid.theme.d.ts +0 -7
  249. package/dist/types/src/components/List/Treegrid.theme.d.ts.map +0 -1
  250. package/dist/types/src/components/List/index.d.ts +0 -4
  251. package/dist/types/src/components/List/index.d.ts.map +0 -1
  252. package/src/components/Input/constants.ts +0 -5
  253. package/src/components/List/List.stories.tsx +0 -234
  254. package/src/components/List/List.theme.ts +0 -47
  255. package/src/components/List/List.tsx +0 -171
  256. package/src/components/List/ListDropIndicator.tsx +0 -69
  257. package/src/components/List/Tree.stories.tsx +0 -87
  258. package/src/components/List/Tree.tsx +0 -74
  259. package/src/components/List/TreeDropIndicator.tsx +0 -70
  260. package/src/components/List/Treegrid.stories.tsx +0 -156
  261. package/src/components/List/Treegrid.theme.ts +0 -35
  262. package/src/components/List/Treegrid.tsx +0 -187
  263. package/src/components/List/index.ts +0 -7
@@ -0,0 +1,264 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { InputHTMLAttributes, forwardRef, useCallback, useRef } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import { translationKey } from '#translations';
9
+
10
+ import { IconButton, type IconButtonProps } from './IconButton';
11
+ import { ToggleIconButton, type ToggleIconButtonProps } from './ToggleIconButton';
12
+
13
+ // Static presets fix the icon and default the label; callers can still override `label`.
14
+ type StaticPresetProps = Omit<IconButtonProps, 'icon' | 'label'> & { label?: string };
15
+
16
+ // Stateful presets fix both icons so a call site cannot supply a conflicting glyph.
17
+ type TogglePresetProps = Omit<ToggleIconButtonProps, 'icon' | 'activeIcon' | 'label'> & { label?: string };
18
+
19
+ //
20
+ // Star
21
+ //
22
+
23
+ const StarIconButton = forwardRef<HTMLButtonElement, TogglePresetProps>(({ label, active, ...props }, forwardedRef) => {
24
+ const { t } = useTranslation(translationKey);
25
+ return (
26
+ <ToggleIconButton
27
+ {...props}
28
+ active={active}
29
+ icon='ph--star--regular'
30
+ activeIcon='ph--star--fill'
31
+ label={label ?? t(active ? 'system-button.unstar.label' : 'system-button.star.label')}
32
+ ref={forwardedRef}
33
+ />
34
+ );
35
+ });
36
+
37
+ StarIconButton.displayName = 'SystemIconButton.Star';
38
+
39
+ //
40
+ // Bookmark
41
+ //
42
+
43
+ const BookmarkIconButton = forwardRef<HTMLButtonElement, TogglePresetProps>(
44
+ ({ label, active, ...props }, forwardedRef) => {
45
+ const { t } = useTranslation(translationKey);
46
+ return (
47
+ <ToggleIconButton
48
+ {...props}
49
+ active={active}
50
+ icon='ph--bookmark-simple--regular'
51
+ activeIcon='ph--bookmark-simple--fill'
52
+ label={label ?? t(active ? 'system-button.unbookmark.label' : 'system-button.bookmark.label')}
53
+ ref={forwardedRef}
54
+ />
55
+ );
56
+ },
57
+ );
58
+
59
+ BookmarkIconButton.displayName = 'SystemIconButton.Bookmark';
60
+
61
+ //
62
+ // Expander
63
+ //
64
+
65
+ const ExpanderIconButton = forwardRef<HTMLButtonElement, TogglePresetProps>(
66
+ ({ label, active, ...props }, forwardedRef) => {
67
+ const { t } = useTranslation(translationKey);
68
+ return (
69
+ <ToggleIconButton
70
+ {...props}
71
+ active={active}
72
+ icon='ph--caret-right--regular'
73
+ label={label ?? t(active ? 'system-button.collapse.label' : 'system-button.expand.label')}
74
+ ref={forwardedRef}
75
+ />
76
+ );
77
+ },
78
+ );
79
+
80
+ ExpanderIconButton.displayName = 'SystemIconButton.Expander';
81
+
82
+ //
83
+ // Add
84
+ //
85
+
86
+ const AddIconButton = forwardRef<HTMLButtonElement, StaticPresetProps>(({ label, ...props }, forwardedRef) => {
87
+ const { t } = useTranslation(translationKey);
88
+ return (
89
+ <IconButton {...props} icon='ph--plus--regular' label={label ?? t('system-button.add.label')} ref={forwardedRef} />
90
+ );
91
+ });
92
+
93
+ AddIconButton.displayName = 'SystemIconButton.Add';
94
+
95
+ //
96
+ // Delete
97
+ //
98
+
99
+ const DeleteIconButton = forwardRef<HTMLButtonElement, StaticPresetProps>(({ label, ...props }, forwardedRef) => {
100
+ const { t } = useTranslation(translationKey);
101
+ return (
102
+ <IconButton
103
+ {...props}
104
+ icon='ph--trash--regular'
105
+ label={label ?? t('system-button.delete.label')}
106
+ ref={forwardedRef}
107
+ />
108
+ );
109
+ });
110
+
111
+ DeleteIconButton.displayName = 'SystemIconButton.Delete';
112
+
113
+ //
114
+ // Edit
115
+ //
116
+
117
+ const EditIconButton = forwardRef<HTMLButtonElement, StaticPresetProps>(({ label, ...props }, forwardedRef) => {
118
+ const { t } = useTranslation(translationKey);
119
+ return (
120
+ <IconButton {...props} icon='ph--pen--regular' label={label ?? t('system-button.edit.label')} ref={forwardedRef} />
121
+ );
122
+ });
123
+
124
+ EditIconButton.displayName = 'SystemIconButton.Edit';
125
+
126
+ //
127
+ // Close
128
+ //
129
+
130
+ const CloseIconButton = forwardRef<HTMLButtonElement, StaticPresetProps>(({ label, ...props }, forwardedRef) => {
131
+ const { t } = useTranslation(translationKey);
132
+ return (
133
+ <IconButton {...props} icon='ph--x--regular' label={label ?? t('system-button.close.label')} ref={forwardedRef} />
134
+ );
135
+ });
136
+
137
+ CloseIconButton.displayName = 'SystemIconButton.Close';
138
+
139
+ //
140
+ // Clipboard
141
+ //
142
+
143
+ type ClipboardIconButtonProps = StaticPresetProps & {
144
+ onCopy: () => string;
145
+ };
146
+
147
+ const ClipboardIconButton = forwardRef<HTMLButtonElement, ClipboardIconButtonProps>(
148
+ ({ label, onCopy, ...props }, forwardedRef) => {
149
+ const { t } = useTranslation(translationKey);
150
+ const handleCopy = useCallback(() => {
151
+ const text = onCopy();
152
+ if (text) {
153
+ void navigator.clipboard.writeText(text);
154
+ }
155
+ }, [onCopy]);
156
+ return (
157
+ <IconButton
158
+ {...props}
159
+ icon='ph--clipboard--regular'
160
+ label={label ?? t('system-button.clipboard.label')}
161
+ onClick={handleCopy}
162
+ ref={forwardedRef}
163
+ />
164
+ );
165
+ },
166
+ );
167
+
168
+ ClipboardIconButton.displayName = 'SystemIconButton.Clipboard';
169
+
170
+ //
171
+ // Upload
172
+ //
173
+
174
+ type UploadIconButtonProps = StaticPresetProps &
175
+ Pick<InputHTMLAttributes<HTMLInputElement>, 'accept'> & {
176
+ onFileChange?: InputHTMLAttributes<HTMLInputElement>['onChange'];
177
+ };
178
+
179
+ const UploadIconButton = forwardRef<HTMLButtonElement, UploadIconButtonProps>(
180
+ ({ accept, onFileChange, label, ...props }, forwardedRef) => {
181
+ const { t } = useTranslation(translationKey);
182
+ const fileInputRef = useRef<HTMLInputElement>(null);
183
+ return (
184
+ <>
185
+ <input className='sr-only' type='file' accept={accept} onChange={onFileChange} ref={fileInputRef} />
186
+ <IconButton
187
+ icon='ph--upload-simple--regular'
188
+ label={label ?? t('system-button.upload.label')}
189
+ {...props}
190
+ onClick={() => fileInputRef.current?.click()}
191
+ ref={forwardedRef}
192
+ />
193
+ </>
194
+ );
195
+ },
196
+ );
197
+
198
+ UploadIconButton.displayName = 'SystemIconButton.Upload';
199
+
200
+ //
201
+ // Download
202
+ //
203
+
204
+ type DownloadIconButtonProps = StaticPresetProps & {
205
+ filename: string;
206
+ onDownload: () => Blob | null;
207
+ };
208
+
209
+ const DownloadIconButton = forwardRef<HTMLButtonElement, DownloadIconButtonProps>(
210
+ ({ filename, onDownload, label, ...props }, forwardedRef) => {
211
+ const { t } = useTranslation(translationKey);
212
+ const handleDownload = useCallback(() => {
213
+ const blob = onDownload();
214
+ if (!blob) {
215
+ return;
216
+ }
217
+
218
+ const url = URL.createObjectURL(blob);
219
+
220
+ const a = document.createElement('a');
221
+ a.href = url;
222
+ a.download = filename;
223
+ a.click();
224
+
225
+ URL.revokeObjectURL(url);
226
+ }, [onDownload]);
227
+ return (
228
+ <IconButton
229
+ icon='ph--download-simple--regular'
230
+ label={label ?? t('system-button.download.label')}
231
+ {...props}
232
+ onClick={handleDownload}
233
+ ref={forwardedRef}
234
+ />
235
+ );
236
+ },
237
+ );
238
+
239
+ DownloadIconButton.displayName = 'SystemIconButton.Download';
240
+
241
+ //
242
+ // Namespace
243
+ //
244
+
245
+ export const SystemIconButton = {
246
+ Star: StarIconButton,
247
+ Bookmark: BookmarkIconButton,
248
+ Expander: ExpanderIconButton,
249
+ Add: AddIconButton,
250
+ Delete: DeleteIconButton,
251
+ Edit: EditIconButton,
252
+ Close: CloseIconButton,
253
+ Clipboard: ClipboardIconButton,
254
+ Upload: UploadIconButton,
255
+ Download: DownloadIconButton,
256
+ };
257
+
258
+ export type {
259
+ ClipboardIconButtonProps,
260
+ DownloadIconButtonProps,
261
+ StaticPresetProps,
262
+ TogglePresetProps,
263
+ UploadIconButtonProps,
264
+ };
@@ -46,5 +46,5 @@ const ToggleGroupIconItem = forwardRef<HTMLButtonElement, ToggleGroupIconItemPro
46
46
  },
47
47
  );
48
48
 
49
- export { ToggleGroup, ToggleGroupItem, ToggleGroupIconItem };
50
- export type { ToggleGroupProps, ToggleGroupItemProps };
49
+ export { ToggleGroup, ToggleGroupIconItem, ToggleGroupItem };
50
+ export type { ToggleGroupItemProps, ToggleGroupProps };
@@ -0,0 +1,32 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { forwardRef } from 'react';
6
+
7
+ import { mx } from '@dxos/ui-theme';
8
+
9
+ import { IconButton, type IconButtonProps } from './IconButton';
10
+
11
+ export type ToggleIconButtonProps = Omit<IconButtonProps, 'icon'> & {
12
+ active?: boolean;
13
+ /** Icon for the inactive state; rotated 90° in the active state when `activeIcon` is omitted. */
14
+ icon: NonNullable<IconButtonProps['icon']>;
15
+ /** Icon for the active state. When omitted, `icon` rotates 90° (disclosure-style). */
16
+ activeIcon?: NonNullable<IconButtonProps['icon']>;
17
+ };
18
+
19
+ /**
20
+ * Icon button bound to a boolean toggle. With `activeIcon` it swaps icons between states;
21
+ * otherwise it rotates `icon` 90° to signal the active (e.g. expanded) state.
22
+ */
23
+ export const ToggleIconButton = forwardRef<HTMLButtonElement, ToggleIconButtonProps>(
24
+ ({ active, icon, activeIcon, iconClassNames, ...props }, forwardedRef) => (
25
+ <IconButton
26
+ {...props}
27
+ ref={forwardedRef}
28
+ icon={active && activeIcon ? activeIcon : icon}
29
+ iconClassNames={activeIcon ? iconClassNames : mx('transition-transform', active && 'rotate-90', iconClassNames)}
30
+ />
31
+ ),
32
+ );
@@ -4,5 +4,8 @@
4
4
 
5
5
  export * from './Button';
6
6
  export * from './IconButton';
7
+ export * from './MicButton';
8
+ export * from './SystemIconButton';
7
9
  export * from './Toggle';
8
10
  export * from './ToggleGroup';
11
+ export * from './ToggleIconButton';
@@ -51,6 +51,8 @@ const day: ComponentFunction<CalendarStyleProps> = (_p, ...etc) =>
51
51
  'outline-none',
52
52
  'data-[hovered]:bg-hover-surface',
53
53
  'data-[focus-visible]:outline-2 data-[focus-visible]:outline-primary-500',
54
+ // Highlight today (overridden by the selected style below when today is also the selected day).
55
+ 'data-[today]:text-orange-500 data-[today]:font-medium',
54
56
  'data-[selected]:bg-primary-500 data-[selected]:text-primary-fg data-[selected]:hover:bg-primary-500',
55
57
  'data-[outside-month]:text-description/40',
56
58
  'data-[disabled]:text-description/40 data-[disabled]:cursor-not-allowed data-[disabled]:hover:bg-transparent',
@@ -3,20 +3,20 @@
3
3
  //
4
4
 
5
5
  import { CalendarDate, parseDate } from '@internationalized/date';
6
- import React, { forwardRef, type ComponentPropsWithoutRef, type ReactNode } from 'react';
6
+ import React, { type ComponentPropsWithoutRef, type ReactNode, forwardRef } from 'react';
7
7
  import {
8
+ type DateValue,
8
9
  Button as RACButton,
9
10
  Calendar as RACCalendar,
10
11
  CalendarCell as RACCalendarCell,
11
- type CalendarProps as RACCalendarProps,
12
12
  CalendarGrid as RACCalendarGrid,
13
13
  CalendarGridBody as RACCalendarGridBody,
14
14
  CalendarGridHeader as RACCalendarGridHeader,
15
15
  CalendarHeaderCell as RACCalendarHeaderCell,
16
+ type CalendarProps as RACCalendarProps,
16
17
  Heading as RACHeading,
17
18
  RangeCalendar as RACRangeCalendar,
18
19
  type RangeCalendarProps as RACRangeCalendarProps,
19
- type DateValue,
20
20
  } from 'react-aria-components';
21
21
 
22
22
  import { type ClassNameValue } from '@dxos/ui-types';
@@ -6,21 +6,21 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useRef } from 'react';
7
7
 
8
8
  import { random } from '@dxos/random';
9
- import { Icon } from '@dxos/react-ui';
9
+ import { Icon, IconButton } from '@dxos/react-ui';
10
10
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
 
12
12
  import { Card } from './Card';
13
13
 
14
14
  random.seed(0);
15
15
 
16
- type DefaultStoryProps = {
16
+ type StoryArgs = {
17
17
  title: string;
18
18
  description?: string;
19
19
  image?: string;
20
20
  fullWidth?: boolean;
21
21
  };
22
22
 
23
- const DefaultStory = ({ title, description, image, fullWidth }: DefaultStoryProps) => {
23
+ const DefaultStory = ({ title, description, image, fullWidth }: StoryArgs) => {
24
24
  const handleRef = useRef<HTMLButtonElement>(null);
25
25
  console.log(title);
26
26
  return (
@@ -32,10 +32,16 @@ const DefaultStory = ({ title, description, image, fullWidth }: DefaultStoryProp
32
32
  </Card.Header>
33
33
  <Card.Body>
34
34
  <Card.Poster alt='Card.Poster' image={image} />
35
- <Card.Row icon='ph--dot-outline--regular'>
35
+ <Card.Row>
36
+ <Card.Block>
37
+ <Icon icon='ph--dot-outline--regular' />
38
+ </Card.Block>
36
39
  <Card.Text>Card.Text (default)</Card.Text>
37
40
  </Card.Row>
38
- <Card.Row icon='ph--dot-outline--regular'>
41
+ <Card.Row>
42
+ <Card.Block>
43
+ <Icon icon='ph--dot-outline--regular' />
44
+ </Card.Block>
39
45
  <Card.Text variant='description'>
40
46
  Card.Text (description)
41
47
  <br />
@@ -52,7 +58,13 @@ const DefaultStory = ({ title, description, image, fullWidth }: DefaultStoryProp
52
58
  const meta = {
53
59
  title: 'ui/react-ui-core/components/Card',
54
60
  render: DefaultStory,
55
- decorators: [withTheme(), withLayout({ layout: 'centered', classNames: 'grid w-[30rem] place-items-center' })],
61
+ decorators: [
62
+ withTheme(),
63
+ withLayout({
64
+ layout: 'centered',
65
+ classNames: 'grid w-[30rem] place-items-center bg-transparent',
66
+ }),
67
+ ],
56
68
  } satisfies Meta<typeof DefaultStory>;
57
69
 
58
70
  export default meta;
@@ -147,26 +159,56 @@ export const Description: Story = {
147
159
  },
148
160
  };
149
161
 
150
- export const Mock = () => (
151
- <div className='grid grid-cols-[2rem_1fr_2rem] w-full dx-card-min-width dx-card-max-width border border-separator rounded-xs'>
152
- <div className='grid grid-cols-subgrid col-span-full'>
153
- <div className='grid h-[var(--dx-rail-item)] w-[var(--dx-rail-item)] place-items-center'>
154
- <Icon icon='ph--dots-six-vertical--regular' />
155
- </div>
156
- <div className='p-1 whitespace-normal break-words text-description items-center'>
157
- This line is very very long and it should wrap.
158
- </div>
159
- <div className='grid h-[var(--dx-rail-item)] w-[var(--dx-rail-item)] place-items-center'>
160
- <Icon icon='ph--x--regular' />
161
- </div>
162
- </div>
163
- <div className='grid grid-cols-subgrid col-span-3'>
164
- <div className='grid h-[var(--dx-rail-item)] w-[var(--dx-rail-item)] place-items-center'>
165
- <Icon icon='ph--dots-six-vertical--regular' />
166
- </div>
167
- <div className='p-1 text-description items-center col-span-2'>
168
- This line is very very long and it should wrap.
169
- </div>
170
- </div>
171
- </div>
172
- );
162
+ export const Slots: Story = {
163
+ args: {
164
+ title: random.commerce.productName(),
165
+ },
166
+ render: ({ title }) => {
167
+ const showLeading = true;
168
+ return (
169
+ <Card.Root>
170
+ <Card.Header>
171
+ <Card.Title>{title}</Card.Title>
172
+ <Card.ActionIconButton action='close' onClick={() => console.log('close')} />
173
+ </Card.Header>
174
+ <Card.Body>
175
+ {/* Leading passive icon. */}
176
+ <Card.Row>
177
+ <Card.Block>
178
+ <Icon icon='ph--user--regular' />
179
+ </Card.Block>
180
+ <Card.Text>Start slot passive Icon</Card.Text>
181
+ </Card.Row>
182
+ {/* Passive icon + trailing IconButton: the two gutters align to the pixel. */}
183
+ <Card.Row>
184
+ <Card.Block>
185
+ <Icon icon='ph--at--regular' />
186
+ </Card.Block>
187
+ <Card.Text>Start Icon + end IconButton (aligned)</Card.Text>
188
+ <Card.Block end>
189
+ <IconButton iconOnly variant='ghost' icon='ph--x--regular' label='Remove' onClick={() => {}} />
190
+ </Card.Block>
191
+ </Card.Row>
192
+ {/* Conditional leading slot: when falsy, content stays in the center track. */}
193
+ <Card.Row>
194
+ {showLeading && (
195
+ <Card.Block>
196
+ <Icon icon='ph--calendar--regular' />
197
+ </Card.Block>
198
+ )}
199
+ <Card.Text>Conditional start slot</Card.Text>
200
+ </Card.Row>
201
+ {/* asChild: the anchor itself becomes the trailing gutter cell. */}
202
+ <Card.Row>
203
+ <Card.Text>Trailing link via asChild</Card.Text>
204
+ <Card.Block end asChild>
205
+ <a href='https://dxos.org' target='_blank' rel='noreferrer'>
206
+ <Icon icon='ph--arrow-square-out--regular' />
207
+ </a>
208
+ </Card.Block>
209
+ </Card.Row>
210
+ </Card.Body>
211
+ </Card.Root>
212
+ );
213
+ },
214
+ };
@@ -17,8 +17,7 @@ export type CardStyleProps = {
17
17
 
18
18
  const root: ComponentFunction<CardStyleProps> = ({ border, fullWidth }, ...etc) =>
19
19
  mx(
20
- 'dx-card dx-card-min-width dx-card-max-width min-h-(--dx-rail-item) group/card relative overflow-hidden',
21
- 'bg-card-surface',
20
+ 'dx-card dx-card-surface dx-card-min-width dx-card-max-width min-h-(--dx-rail-item) group/card relative overflow-hidden',
22
21
  border && 'border border-subdued-separator rounded-sm dx-focus-ring-group-y-indicator',
23
22
  fullWidth && 'max-w-none!',
24
23
  ...etc,
@@ -26,13 +25,16 @@ const root: ComponentFunction<CardStyleProps> = ({ border, fullWidth }, ...etc)
26
25
 
27
26
  const header: ComponentFunction<CardStyleProps> = (_, ...etc) =>
28
27
  mx(
29
- 'dx-card__header dx-density-md bg-transparent p-0! gap-0! col-span-3 grid! grid-cols-subgrid! shadow-none [contain:none]',
28
+ 'dx-card__header col-span-3 grid grid-cols-subgrid items-center',
29
+ '[&>*:not(.dx-gutter)]:col-start-2 [&>*:not(.dx-gutter)>*]:col-start-2',
30
30
  ...etc,
31
31
  );
32
32
 
33
33
  const title: ComponentFunction<CardStyleProps> = (_props, ...etc) => mx('dx-card__title grow truncate', ...etc);
34
34
 
35
- const body: ComponentFunction<CardStyleProps> = (_props, ...etc) => mx('dx-card__body contents pb-1 last:pb-0', ...etc);
35
+ const body: ComponentFunction<CardStyleProps> = (_props, ...etc) => mx('dx-card__body contents', ...etc);
36
+
37
+ const block: ComponentFunction<CardStyleProps> = (_props, ...etc) => mx('dx-card__block', ...etc);
36
38
 
37
39
  const text: ComponentFunction<CardStyleProps> = ({ variant = 'default', truncate: _truncate }, ...etc) =>
38
40
  mx(
@@ -46,25 +48,40 @@ const textSpan: ComponentFunction<CardStyleProps> = ({ variant = 'default', trun
46
48
  mx(variant === 'description' && 'text-sm text-description line-clamp-3', truncate && 'truncate', ...etc);
47
49
 
48
50
  const poster: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
49
- mx('dx-card__poster col-span-3 max-h-[200px]', ...etc);
51
+ mx('dx-card__poster col-span-3 max-h-[200px] select-none pointer-events-none', ...etc);
50
52
 
51
53
  const posterIcon: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
52
54
  mx('dx-card__poster-icon col-span-3 grid place-items-center bg-input-surface text-subdued max-h-[200px]', ...etc);
53
55
 
54
56
  const action: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
55
- mx('dx-card__acztion col-span-3 !grid grid-cols-subgrid p-0! w-full text-start overflow-hidden', ...etc);
57
+ mx(
58
+ 'dx-card__action col-span-3 grid grid-cols-subgrid [&>*:not(.dx-gutter)]:col-start-2 [&>*:not(.dx-gutter)>*]:col-start-2 p-0! gap-0! w-full text-start overflow-hidden',
59
+ ...etc,
60
+ );
56
61
 
57
62
  const actionLabel: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
58
63
  mx('dx-card__action-label min-w-0 flex-1 truncate', ...etc);
59
64
 
60
65
  const link: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
61
- mx('dx-card__link col-span-3 !grid grid-cols-subgrid group p-0! dx-button dx-focus-ring min-h-1!', ...etc);
66
+ mx(
67
+ 'dx-card__link col-span-3 grid grid-cols-subgrid [&>*:not(.dx-gutter)]:col-start-2 [&>*:not(.dx-gutter)>*]:col-start-2 group p-0! dx-button dx-focus-ring min-h-1!',
68
+ ...etc,
69
+ );
62
70
 
63
71
  const linkLabel: ComponentFunction<CardStyleProps> = (_props, ...etc) =>
64
- mx('dx-card__link-label min-w-0 flex-1 truncate', ...etc);
72
+ mx('dx-card__link-label min-w-0 flex-1 truncate text-sm!', ...etc);
65
73
 
66
74
  const row: ComponentFunction<CardStyleProps> = ({ fullWidth }, ...etc) =>
67
- mx('dx-card__row', fullWidth ? 'col-span-full' : 'col-span-3 grid grid-cols-subgrid', ...etc);
75
+ mx(
76
+ 'dx-card__row overflow-hidden',
77
+ fullWidth
78
+ ? 'col-span-full'
79
+ : // The `>*` selector reaches the real grid item when a content child is `display: contents`
80
+ // (e.g. `dx-avatar`), which the direct-child selector cannot target. It is inert for normal
81
+ // block children, whose inner nodes are not grid items of this row.
82
+ 'col-span-3 grid grid-cols-subgrid [&>*:not(.dx-gutter)]:col-start-2 [&>*:not(.dx-gutter)>*]:col-start-2',
83
+ ...etc,
84
+ );
68
85
 
69
86
  // NOTE: Direct children that lack an explicit `col-*` utility default to the
70
87
  // Column.Root center track (via `--dx-col`); see `ui-theme`'s `css/components/card.css`.
@@ -79,6 +96,7 @@ export const cardTheme: Theme<CardStyleProps> = {
79
96
  header,
80
97
  title,
81
98
  body,
99
+ block,
82
100
  row,
83
101
  section,
84
102
  'section-title': sectionTitle,