@dxos/react-ui 0.8.4-main.69d29f4 → 0.8.4-main.6fa680abb7

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 (271) hide show
  1. package/dist/lib/browser/{chunk-CEKVHJ27.mjs → chunk-2FKSMWNY.mjs} +117 -117
  2. package/dist/lib/browser/chunk-2FKSMWNY.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +2804 -1957
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +56 -32
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/{chunk-2NHEX4AD.mjs → chunk-ZNBLTSHI.mjs} +117 -117
  9. package/dist/lib/node-esm/chunk-ZNBLTSHI.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +2804 -1957
  11. package/dist/lib/node-esm/index.mjs.map +4 -4
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +56 -32
  14. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  15. package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
  16. package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
  18. package/dist/types/src/components/Button/Button.d.ts.map +1 -1
  19. package/dist/types/src/components/Card/Card.d.ts +107 -0
  20. package/dist/types/src/components/Card/Card.d.ts.map +1 -0
  21. package/dist/types/src/components/Card/Card.stories.d.ts +21 -0
  22. package/dist/types/src/components/Card/Card.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/Card/index.d.ts +2 -0
  24. package/dist/types/src/components/Card/index.d.ts.map +1 -0
  25. package/dist/types/src/components/Dialog/AlertDialog.d.ts +12 -3
  26. package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -1
  27. package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/Dialog/Dialog.d.ts +11 -4
  29. package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -1
  30. package/dist/types/src/components/Dialog/Dialog.stories.d.ts +3 -6
  31. package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/ErrorFallback/ErrorFallback.d.ts +11 -0
  33. package/dist/types/src/components/ErrorFallback/ErrorFallback.d.ts.map +1 -0
  34. package/dist/types/src/components/ErrorFallback/ErrorFallback.stories.d.ts +7 -0
  35. package/dist/types/src/components/ErrorFallback/ErrorFallback.stories.d.ts.map +1 -0
  36. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts +8 -0
  37. package/dist/types/src/components/ErrorFallback/ErrorStack.d.ts.map +1 -0
  38. package/dist/types/src/components/ErrorFallback/ThrowError.d.ts +9 -0
  39. package/dist/types/src/components/ErrorFallback/ThrowError.d.ts.map +1 -0
  40. package/dist/types/src/components/ErrorFallback/index.d.ts +5 -0
  41. package/dist/types/src/components/ErrorFallback/index.d.ts.map +1 -0
  42. package/dist/types/src/components/Image/Image.d.ts +14 -0
  43. package/dist/types/src/components/Image/Image.d.ts.map +1 -0
  44. package/dist/types/src/components/Image/Image.stories.d.ts +33 -0
  45. package/dist/types/src/components/Image/Image.stories.d.ts.map +1 -0
  46. package/dist/types/src/components/Image/index.d.ts +2 -0
  47. package/dist/types/src/components/Image/index.d.ts.map +1 -0
  48. package/dist/types/src/components/Input/Input.d.ts +2 -5
  49. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  50. package/dist/types/src/components/Input/Input.stories.d.ts +7 -7
  51. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  52. package/dist/types/src/components/List/List.d.ts.map +1 -1
  53. package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/List/Treegrid.d.ts.map +1 -1
  55. package/dist/types/src/components/Main/Main.d.ts +9 -10
  56. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  57. package/dist/types/src/components/Main/Main.stories.d.ts +0 -3
  58. package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
  59. package/dist/types/src/components/Menu/ContextMenu.d.ts.map +1 -1
  60. package/dist/types/src/components/Message/Message.d.ts.map +1 -1
  61. package/dist/types/src/components/Message/Message.stories.d.ts +2 -3
  62. package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
  63. package/dist/types/src/components/Popover/Popover.d.ts +1 -0
  64. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  65. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +21 -26
  66. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
  67. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +41 -9
  68. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
  69. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
  70. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +6 -1
  71. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -1
  72. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  73. package/dist/types/src/components/Skeleton/Skeleton.d.ts +12 -0
  74. package/dist/types/src/components/Skeleton/Skeleton.d.ts.map +1 -0
  75. package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts +17 -0
  76. package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts.map +1 -0
  77. package/dist/types/src/components/Skeleton/index.d.ts +2 -0
  78. package/dist/types/src/components/Skeleton/index.d.ts.map +1 -0
  79. package/dist/types/src/components/Splitter/Splitter.d.ts +32 -0
  80. package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -0
  81. package/dist/types/src/components/Splitter/Splitter.stories.d.ts +7 -0
  82. package/dist/types/src/components/Splitter/Splitter.stories.d.ts.map +1 -0
  83. package/dist/types/src/components/Splitter/index.d.ts +2 -0
  84. package/dist/types/src/components/Splitter/index.d.ts.map +1 -0
  85. package/dist/types/src/components/Status/Status.stories.d.ts +4 -2
  86. package/dist/types/src/components/Status/Status.stories.d.ts.map +1 -1
  87. package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
  88. package/dist/types/src/components/Tag/Tag.stories.d.ts +0 -5
  89. package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
  90. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +1 -0
  91. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
  92. package/dist/types/src/components/ThemeProvider/ThemeProvider.stories.d.ts +12 -0
  93. package/dist/types/src/components/ThemeProvider/ThemeProvider.stories.d.ts.map +1 -0
  94. package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
  95. package/dist/types/src/components/Toolbar/Toolbar.d.ts +33 -7
  96. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  97. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  98. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/index.d.ts +8 -4
  100. package/dist/types/src/components/index.d.ts.map +1 -1
  101. package/dist/types/src/exemplars/generics.stories.d.ts +18 -0
  102. package/dist/types/src/exemplars/generics.stories.d.ts.map +1 -0
  103. package/dist/types/src/exemplars/slot.stories.d.ts +14 -0
  104. package/dist/types/src/exemplars/slot.stories.d.ts.map +1 -0
  105. package/dist/types/src/exemplars/tabster.stories.d.ts +8 -0
  106. package/dist/types/src/exemplars/tabster.stories.d.ts.map +1 -0
  107. package/dist/types/src/exemplars/virtualizer.stories.d.ts +11 -0
  108. package/dist/types/src/exemplars/virtualizer.stories.d.ts.map +1 -0
  109. package/dist/types/src/index.d.ts +1 -0
  110. package/dist/types/src/index.d.ts.map +1 -1
  111. package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
  112. package/dist/types/src/primitives/Column/Column.d.ts +26 -0
  113. package/dist/types/src/primitives/Column/Column.d.ts.map +1 -0
  114. package/dist/types/src/primitives/Column/Column.stories.d.ts +6 -0
  115. package/dist/types/src/primitives/Column/Column.stories.d.ts.map +1 -0
  116. package/dist/types/src/primitives/Column/index.d.ts +2 -0
  117. package/dist/types/src/primitives/Column/index.d.ts.map +1 -0
  118. package/dist/types/src/primitives/Container/Container.d.ts +8 -0
  119. package/dist/types/src/primitives/Container/Container.d.ts.map +1 -0
  120. package/dist/types/src/primitives/Container/Container.stories.d.ts +6 -0
  121. package/dist/types/src/primitives/Container/Container.stories.d.ts.map +1 -0
  122. package/dist/types/src/primitives/Container/index.d.ts +2 -0
  123. package/dist/types/src/primitives/Container/index.d.ts.map +1 -0
  124. package/dist/types/src/primitives/Flex/Flex.d.ts +13 -0
  125. package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -0
  126. package/dist/types/src/primitives/Flex/Flex.stories.d.ts +8 -0
  127. package/dist/types/src/primitives/Flex/Flex.stories.d.ts.map +1 -0
  128. package/dist/types/src/primitives/Flex/index.d.ts +2 -0
  129. package/dist/types/src/primitives/Flex/index.d.ts.map +1 -0
  130. package/dist/types/src/primitives/Grid/Grid.d.ts +15 -0
  131. package/dist/types/src/primitives/Grid/Grid.d.ts.map +1 -0
  132. package/dist/types/src/primitives/Grid/Grid.stories.d.ts +8 -0
  133. package/dist/types/src/primitives/Grid/Grid.stories.d.ts.map +1 -0
  134. package/dist/types/src/primitives/Grid/index.d.ts +2 -0
  135. package/dist/types/src/primitives/Grid/index.d.ts.map +1 -0
  136. package/dist/types/src/primitives/Panel/Panel.d.ts +26 -0
  137. package/dist/types/src/primitives/Panel/Panel.d.ts.map +1 -0
  138. package/dist/types/src/primitives/Panel/Panel.stories.d.ts +6 -0
  139. package/dist/types/src/primitives/Panel/Panel.stories.d.ts.map +1 -0
  140. package/dist/types/src/primitives/Panel/index.d.ts +2 -0
  141. package/dist/types/src/primitives/Panel/index.d.ts.map +1 -0
  142. package/dist/types/src/primitives/index.d.ts +6 -0
  143. package/dist/types/src/primitives/index.d.ts.map +1 -0
  144. package/dist/types/src/testing/Loading.d.ts +9 -0
  145. package/dist/types/src/testing/Loading.d.ts.map +1 -0
  146. package/dist/types/src/testing/decorators/withLayout.d.ts +1 -1
  147. package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -1
  148. package/dist/types/src/testing/decorators/withLayoutVariants.d.ts.map +1 -1
  149. package/dist/types/src/testing/decorators/withTheme.d.ts +3 -2
  150. package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
  151. package/dist/types/src/testing/index.d.ts +1 -0
  152. package/dist/types/src/testing/index.d.ts.map +1 -1
  153. package/dist/types/src/translations.d.ts +11 -0
  154. package/dist/types/src/translations.d.ts.map +1 -0
  155. package/dist/types/tsconfig.tsbuildinfo +1 -1
  156. package/package.json +25 -21
  157. package/src/components/Avatars/Avatar.stories.tsx +5 -6
  158. package/src/components/Avatars/Avatar.tsx +5 -12
  159. package/src/components/Avatars/AvatarGroup.stories.tsx +2 -2
  160. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +3 -3
  161. package/src/components/Breadcrumb/Breadcrumb.tsx +11 -37
  162. package/src/components/Button/Button.stories.tsx +3 -3
  163. package/src/components/Button/Button.tsx +6 -12
  164. package/src/components/Button/IconButton.stories.tsx +4 -4
  165. package/src/components/Button/IconButton.tsx +1 -1
  166. package/src/components/Button/Toggle.stories.tsx +2 -2
  167. package/src/components/Button/ToggleGroup.stories.tsx +2 -2
  168. package/src/components/Card/Card.stories.tsx +151 -0
  169. package/src/components/Card/Card.tsx +347 -0
  170. package/src/components/Card/index.ts +5 -0
  171. package/src/components/Clipboard/CopyButton.tsx +3 -3
  172. package/src/components/Dialog/AlertDialog.stories.tsx +15 -15
  173. package/src/components/Dialog/AlertDialog.tsx +116 -16
  174. package/src/components/Dialog/Dialog.stories.tsx +40 -15
  175. package/src/components/Dialog/Dialog.tsx +75 -45
  176. package/src/components/ErrorFallback/ErrorFallback.stories.tsx +50 -0
  177. package/src/components/ErrorFallback/ErrorFallback.tsx +70 -0
  178. package/src/components/ErrorFallback/ErrorStack.tsx +80 -0
  179. package/src/components/ErrorFallback/ThrowError.tsx +37 -0
  180. package/src/components/ErrorFallback/index.ts +9 -0
  181. package/src/components/Icon/Icon.stories.tsx +2 -2
  182. package/src/components/Icon/Icon.tsx +1 -1
  183. package/src/components/Image/Image.stories.tsx +86 -0
  184. package/src/components/Image/Image.tsx +223 -0
  185. package/src/components/Image/index.ts +5 -0
  186. package/src/components/Input/Input.stories.tsx +20 -39
  187. package/src/components/Input/Input.tsx +20 -65
  188. package/src/components/Link/Link.stories.tsx +2 -2
  189. package/src/components/Link/Link.tsx +2 -2
  190. package/src/components/List/List.stories.tsx +15 -22
  191. package/src/components/List/List.tsx +11 -16
  192. package/src/components/List/ListDropIndicator.tsx +7 -7
  193. package/src/components/List/Tree.stories.tsx +4 -4
  194. package/src/components/List/TreeDropIndicator.tsx +6 -6
  195. package/src/components/List/Treegrid.stories.tsx +3 -3
  196. package/src/components/List/Treegrid.tsx +10 -15
  197. package/src/components/Main/Main.stories.tsx +41 -23
  198. package/src/components/Main/Main.tsx +138 -81
  199. package/src/components/Menu/ContextMenu.stories.tsx +2 -2
  200. package/src/components/Menu/ContextMenu.tsx +9 -33
  201. package/src/components/Menu/DropdownMenu.stories.tsx +2 -2
  202. package/src/components/Menu/DropdownMenu.tsx +10 -10
  203. package/src/components/Message/Message.stories.tsx +25 -10
  204. package/src/components/Message/Message.tsx +17 -29
  205. package/src/components/Popover/Popover.stories.tsx +4 -4
  206. package/src/components/Popover/Popover.tsx +23 -20
  207. package/src/components/ScrollArea/ScrollArea.stories.tsx +152 -76
  208. package/src/components/ScrollArea/ScrollArea.tsx +72 -116
  209. package/src/components/ScrollArea/index.ts +1 -1
  210. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +41 -22
  211. package/src/components/ScrollContainer/ScrollContainer.tsx +18 -13
  212. package/src/components/Select/Select.stories.tsx +2 -2
  213. package/src/components/Select/Select.tsx +11 -27
  214. package/src/components/Separator/Separator.tsx +1 -1
  215. package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
  216. package/src/components/Skeleton/Skeleton.tsx +26 -0
  217. package/src/components/Skeleton/index.ts +5 -0
  218. package/src/components/Splitter/Splitter.stories.tsx +83 -0
  219. package/src/components/Splitter/Splitter.tsx +126 -0
  220. package/src/components/Splitter/index.ts +5 -0
  221. package/src/components/Status/Status.stories.tsx +21 -17
  222. package/src/components/Status/Status.tsx +2 -2
  223. package/src/components/Tag/Tag.stories.tsx +4 -9
  224. package/src/components/Tag/Tag.tsx +2 -7
  225. package/src/components/ThemeProvider/ThemeProvider.stories.tsx +32 -0
  226. package/src/components/ThemeProvider/ThemeProvider.tsx +4 -3
  227. package/src/components/Toast/Toast.stories.tsx +2 -2
  228. package/src/components/Toast/Toast.tsx +10 -14
  229. package/src/components/Toolbar/Toolbar.stories.tsx +2 -2
  230. package/src/components/Toolbar/Toolbar.tsx +174 -12
  231. package/src/components/Tooltip/Tooltip.stories.tsx +15 -13
  232. package/src/components/Tooltip/Tooltip.tsx +3 -2
  233. package/src/components/index.ts +9 -5
  234. package/src/exemplars/generics.stories.tsx +49 -0
  235. package/src/exemplars/slot.stories.tsx +107 -0
  236. package/src/exemplars/tabster.stories.tsx +127 -0
  237. package/src/exemplars/virtualizer.stories.tsx +137 -0
  238. package/src/index.ts +1 -0
  239. package/src/playground/Controls.stories.tsx +3 -10
  240. package/src/playground/Custom.stories.tsx +10 -10
  241. package/src/playground/Typography.stories.tsx +3 -3
  242. package/src/primitives/Column/Column.stories.tsx +78 -0
  243. package/src/primitives/Column/Column.tsx +134 -0
  244. package/src/primitives/Column/index.ts +5 -0
  245. package/src/primitives/Container/Container.stories.tsx +30 -0
  246. package/src/primitives/Container/Container.tsx +22 -0
  247. package/src/primitives/Container/index.ts +5 -0
  248. package/src/primitives/Flex/Flex.stories.tsx +58 -0
  249. package/src/primitives/Flex/Flex.tsx +29 -0
  250. package/src/primitives/Flex/index.ts +5 -0
  251. package/src/primitives/Grid/Grid.stories.tsx +57 -0
  252. package/src/primitives/Grid/Grid.tsx +35 -0
  253. package/src/primitives/Grid/index.ts +5 -0
  254. package/src/primitives/Panel/Panel.stories.tsx +67 -0
  255. package/src/primitives/Panel/Panel.tsx +119 -0
  256. package/src/primitives/Panel/index.ts +5 -0
  257. package/src/primitives/index.ts +9 -0
  258. package/src/testing/Loading.tsx +26 -0
  259. package/src/testing/decorators/withLayout.tsx +21 -7
  260. package/src/testing/decorators/withLayoutVariants.tsx +18 -21
  261. package/src/testing/decorators/withTheme.tsx +19 -17
  262. package/src/testing/index.ts +2 -0
  263. package/src/translations.ts +19 -0
  264. package/dist/lib/browser/chunk-CEKVHJ27.mjs.map +0 -7
  265. package/dist/lib/node-esm/chunk-2NHEX4AD.mjs.map +0 -7
  266. package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts +0 -15
  267. package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts.map +0 -1
  268. package/dist/types/src/components/AnchoredOverflow/index.d.ts +0 -2
  269. package/dist/types/src/components/AnchoredOverflow/index.d.ts.map +0 -1
  270. package/src/components/AnchoredOverflow/AnchoredOverflow.tsx +0 -59
  271. package/src/components/AnchoredOverflow/index.ts +0 -5
@@ -1,138 +1,105 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import {
6
- Corner as ScrollAreaPrimitiveCorner,
7
- type ScrollAreaCornerProps as ScrollAreaPrimitiveCornerProps,
8
- Root as ScrollAreaPrimitiveRoot,
9
- type ScrollAreaProps as ScrollAreaPrimitiveRootProps,
10
- Scrollbar as ScrollAreaPrimitiveScrollbar,
11
- type ScrollAreaScrollbarProps as ScrollAreaPrimitiveScrollbarProps,
12
- Thumb as ScrollAreaPrimitiveThumb,
13
- type ScrollAreaThumbProps as ScrollAreaPrimitiveThumbProps,
14
- Viewport as ScrollAreaPrimitiveViewport,
15
- type ScrollAreaViewportProps as ScrollAreaPrimitiveViewportProps,
16
- } from '@radix-ui/react-scroll-area';
17
- import React, { type PropsWithChildren, forwardRef } from 'react';
18
-
19
- import { mx } from '@dxos/ui-theme';
5
+ import { createContext } from '@radix-ui/react-context';
6
+ import { Primitive } from '@radix-ui/react-primitive';
7
+ import { Slot } from '@radix-ui/react-slot';
8
+ import React, { type HTMLAttributes, forwardRef } from 'react';
20
9
 
21
- import { useThemeContext } from '../../hooks';
22
- import { type ThemedClassName } from '../../util';
10
+ import { composableProps } from '@dxos/ui-theme';
11
+ import { type AllowedAxis, type SlottableProps, type ThemedClassName } from '@dxos/ui-types';
23
12
 
24
- type ScrollAreaVariant = 'coarse' | 'fine';
13
+ import { useThemeContext } from '../../hooks';
25
14
 
26
15
  //
27
- // Root
16
+ // Context
28
17
  //
29
18
 
30
- type ScrollAreaRootProps = ThemedClassName<ScrollAreaPrimitiveRootProps>;
31
-
32
- const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(({ classNames, ...props }, forwardedRef) => {
33
- const { tx } = useThemeContext();
34
- return (
35
- <ScrollAreaPrimitiveRoot
36
- {...props}
37
- className={tx('scrollArea.root', 'scroll-area', {}, classNames)}
38
- ref={forwardedRef}
39
- />
40
- );
41
- });
42
-
43
- //
44
- // Viewport
45
- //
19
+ const SCROLLAREA_NAME = 'ScrollArea';
46
20
 
47
- type ScrollAreaViewportProps = ThemedClassName<ScrollAreaPrimitiveViewportProps>;
21
+ type ScrollAreaContextType = {
22
+ /** Orientation of scrollbars. */
23
+ orientation: AllowedAxis;
24
+ /** Hide scrollbars when not scrolling. */
25
+ autoHide: boolean;
26
+ /** Apply padding to opposite side of scrollbar. */
27
+ margin?: boolean;
28
+ /** Apply padding. */
29
+ padding: boolean;
30
+ /** Use thin scrollbars. */
31
+ thin: boolean;
32
+ /** Enable snap scrolling. */
33
+ snap: boolean;
34
+ };
48
35
 
49
- const ScrollAreaViewport = forwardRef<HTMLDivElement, ScrollAreaViewportProps>(
50
- ({ classNames, ...props }, forwardedRef) => {
51
- const { tx } = useThemeContext();
52
- return (
53
- <ScrollAreaPrimitiveViewport
54
- {...props}
55
- className={tx('scrollArea.viewport', 'scroll-area', {}, classNames)}
56
- ref={forwardedRef}
57
- />
58
- );
59
- },
60
- );
36
+ const [ScrollAreaProvider, useScrollAreaContext] = createContext<ScrollAreaContextType>(SCROLLAREA_NAME);
61
37
 
62
38
  //
63
- // Scrollbar
39
+ // Root
64
40
  //
65
41
 
66
- type ScrollAreaScrollbarProps = ThemedClassName<ScrollAreaPrimitiveScrollbarProps> & { variant?: ScrollAreaVariant };
42
+ const SCROLLAREA_ROOT_NAME = 'ScrollArea.Root';
43
+
44
+ type ScrollAreaRootProps = SlottableProps<HTMLDivElement, Partial<ScrollAreaContextType>>;
67
45
 
68
- const ScrollAreaScrollbar = forwardRef<HTMLDivElement, ScrollAreaScrollbarProps>(
69
- ({ classNames, variant = 'fine', ...props }, forwardedRef) => {
46
+ /**
47
+ * ScrollArea provides native scrollbars with custom styling.
48
+ */
49
+ const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(
50
+ (
51
+ {
52
+ children,
53
+ asChild,
54
+ orientation = 'vertical',
55
+ autoHide = true,
56
+ margin = false,
57
+ padding = false,
58
+ thin = false,
59
+ snap = false,
60
+ ...props
61
+ },
62
+ forwardedRef,
63
+ ) => {
64
+ const { className, ...rest } = composableProps(props);
65
+ const Comp = asChild ? Slot : Primitive.div;
70
66
  const { tx } = useThemeContext();
67
+ const options = { orientation, autoHide, margin, padding, thin, snap };
68
+
71
69
  return (
72
- <ScrollAreaPrimitiveScrollbar
73
- data-variant={variant}
74
- {...props}
75
- className={tx('scrollArea.scrollbar', 'scroll-area__scrollbar', {}, classNames)}
76
- ref={forwardedRef}
77
- />
70
+ <ScrollAreaProvider {...options}>
71
+ <Comp {...rest} className={tx('scrollArea.root', options, className)} ref={forwardedRef}>
72
+ {children}
73
+ </Comp>
74
+ </ScrollAreaProvider>
78
75
  );
79
76
  },
80
77
  );
81
78
 
82
- //
83
- // Thumb
84
- //
85
-
86
- type ScrollAreaThumbProps = ThemedClassName<ScrollAreaPrimitiveThumbProps>;
87
-
88
- const ScrollAreaThumb = forwardRef<HTMLDivElement, ScrollAreaThumbProps>(({ classNames, ...props }, forwardedRef) => {
89
- const { tx } = useThemeContext();
90
- return (
91
- <ScrollAreaPrimitiveThumb
92
- {...props}
93
- className={tx('scrollArea.thumb', 'scroll-area__thumb', {}, classNames)}
94
- ref={forwardedRef}
95
- />
96
- );
97
- });
79
+ ScrollAreaRoot.displayName = SCROLLAREA_ROOT_NAME;
98
80
 
99
81
  //
100
- // Corner
82
+ // Viewport
101
83
  //
102
84
 
103
- type ScrollAreaCornerProps = ThemedClassName<ScrollAreaPrimitiveCornerProps>;
85
+ const SCROLLAREA_VIEWPORT_NAME = 'ScrollArea.Viewport';
104
86
 
105
- const ScrollAreaCorner = forwardRef<HTMLDivElement, ScrollAreaCornerProps>(({ classNames, ...props }, forwardedRef) => {
106
- const { tx } = useThemeContext();
107
- return (
108
- <ScrollAreaPrimitiveCorner
109
- {...props}
110
- className={tx('scrollArea.corner', 'scroll-area__corner', {}, classNames)}
111
- ref={forwardedRef}
112
- />
113
- );
114
- });
87
+ type ScrollAreaViewportProps = ThemedClassName<HTMLAttributes<HTMLDivElement>>;
115
88
 
116
- //
117
- // Expander
118
- //
119
-
120
- type ScrollAreaExpanderProps = ThemedClassName<PropsWithChildren>;
89
+ const ScrollAreaViewport = forwardRef<HTMLDivElement, ScrollAreaViewportProps>(
90
+ ({ classNames, children, ...props }, forwardedRef) => {
91
+ const { tx } = useThemeContext();
92
+ const options = useScrollAreaContext(SCROLLAREA_VIEWPORT_NAME);
121
93
 
122
- /**
123
- * Size-locking wrapper required for inner ScrollArea to function correctly.
124
- * NOTE: Radix ScrollArea.Viewport applies `display: table` to its immediate child,
125
- * causing the content to participate in table layout and escape the intended fixed-size viewport.
126
- */
127
- const ScrollAreaExpander = ({ classNames, children }: ScrollAreaExpanderProps) => {
128
- return (
129
- <div role='none' className={mx('relative bs-full is-full overflow-hidden', classNames)}>
130
- <div role='none' className='absolute inset-0 overflow-hidden'>
94
+ return (
95
+ <div {...props} className={tx('scrollArea.viewport', options, classNames)} ref={forwardedRef}>
131
96
  {children}
132
97
  </div>
133
- </div>
134
- );
135
- };
98
+ );
99
+ },
100
+ );
101
+
102
+ ScrollAreaViewport.displayName = SCROLLAREA_VIEWPORT_NAME;
136
103
 
137
104
  //
138
105
  // ScrollArea
@@ -141,17 +108,6 @@ const ScrollAreaExpander = ({ classNames, children }: ScrollAreaExpanderProps) =
141
108
  export const ScrollArea = {
142
109
  Root: ScrollAreaRoot,
143
110
  Viewport: ScrollAreaViewport,
144
- Scrollbar: ScrollAreaScrollbar,
145
- Thumb: ScrollAreaThumb,
146
- Corner: ScrollAreaCorner,
147
- Expander: ScrollAreaExpander,
148
111
  };
149
112
 
150
- export type {
151
- ScrollAreaRootProps,
152
- ScrollAreaViewportProps,
153
- ScrollAreaScrollbarProps,
154
- ScrollAreaThumbProps,
155
- ScrollAreaCornerProps,
156
- ScrollAreaExpanderProps,
157
- };
113
+ export type { ScrollAreaRootProps, ScrollAreaViewportProps };
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
5
  export * from './ScrollArea';
@@ -7,16 +7,22 @@ import React, { useEffect, useRef, useState } from 'react';
7
7
 
8
8
  import { faker } from '@dxos/random';
9
9
 
10
+ import { Panel } from '../../primitives';
10
11
  import { withLayout, withTheme } from '../../testing';
11
12
  import { Button } from '../Button';
12
13
  import { Toolbar } from '../Toolbar';
13
14
 
14
15
  import { ScrollContainer, type ScrollContainerRootProps, type ScrollController } from './ScrollContainer';
15
16
 
16
- const DefaultStory = (props: ScrollContainerRootProps) => {
17
+ type StoryProps = ScrollContainerRootProps & { running?: boolean; initialLines?: number };
18
+
19
+ const DefaultStory = ({ initialLines = 0, running: runningProp, ...props }: StoryProps) => {
17
20
  const [lines, setLines] = useState<string[]>([]);
18
- const [running, setRunning] = useState(true);
21
+ const [running, setRunning] = useState(runningProp);
19
22
  const scroller = useRef<ScrollController>(null);
23
+ useEffect(() => {
24
+ setLines(Array.from({ length: initialLines }, () => faker.lorem.paragraph()));
25
+ }, [initialLines]);
20
26
  useEffect(() => {
21
27
  if (!running) {
22
28
  return;
@@ -30,32 +36,36 @@ const DefaultStory = (props: ScrollContainerRootProps) => {
30
36
  }, [running]);
31
37
 
32
38
  return (
33
- <div className='flex flex-col bs-full overflow-hidden'>
34
- <Toolbar.Root>
35
- <Button onClick={() => setRunning((running) => !running)}>{running ? 'Stop' : 'Start'}</Button>
36
- <Button onClick={() => scroller.current?.scrollToBottom()}>Scroll to bottom</Button>
37
- <div className='flex-1' />
38
- <div>{lines.length}</div>
39
- </Toolbar.Root>
40
- <ScrollContainer.Root {...props} ref={scroller}>
41
- <ScrollContainer.Viewport>
42
- {lines.map((line, index) => (
43
- <div key={index} className='p-2'>
44
- {line}
45
- </div>
46
- ))}
47
- </ScrollContainer.Viewport>
48
- <ScrollContainer.ScrollDownButton />
49
- </ScrollContainer.Root>
50
- </div>
39
+ <Panel.Root className='dx-article'>
40
+ <Panel.Toolbar asChild>
41
+ <Toolbar.Root>
42
+ <Button onClick={() => setRunning((running) => !running)}>{running ? 'Stop' : 'Start'}</Button>
43
+ <Button onClick={() => scroller.current?.scrollToBottom()}>Scroll to bottom</Button>
44
+ <Toolbar.Separator />
45
+ <div className='px-1'>{lines.length}</div>
46
+ </Toolbar.Root>
47
+ </Panel.Toolbar>
48
+ <Panel.Content asChild>
49
+ <ScrollContainer.Root {...props} ref={scroller}>
50
+ <ScrollContainer.Viewport>
51
+ {lines.map((line, index) => (
52
+ <div key={index} className='p-2 text-description'>
53
+ {line}
54
+ </div>
55
+ ))}
56
+ </ScrollContainer.Viewport>
57
+ <ScrollContainer.ScrollDownButton />
58
+ </ScrollContainer.Root>
59
+ </Panel.Content>
60
+ </Panel.Root>
51
61
  );
52
62
  };
53
63
 
54
64
  const meta = {
55
- title: 'ui/react-ui-core/ScrollContainer',
65
+ title: 'ui/react-ui-core/components/ScrollContainer',
56
66
  component: ScrollContainer.Root,
57
67
  render: DefaultStory,
58
- decorators: [withTheme, withLayout({ layout: 'column', classNames: 'is-[30rem]' })],
68
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'w-[30rem]' })],
59
69
  } satisfies Meta<typeof DefaultStory>;
60
70
 
61
71
  export default meta;
@@ -66,5 +76,14 @@ export const Default: Story = {
66
76
  args: {
67
77
  pin: true,
68
78
  fade: true,
79
+ running: true,
80
+ },
81
+ };
82
+
83
+ export const Large: Story = {
84
+ args: {
85
+ pin: true,
86
+ fade: true,
87
+ initialLines: 100,
69
88
  },
70
89
  };
@@ -15,7 +15,6 @@ import React, {
15
15
  useState,
16
16
  } from 'react';
17
17
 
18
- // TODO(burdon): Move these deps to @dxos/dom-util.
19
18
  import { addEventListener, combine } from '@dxos/async';
20
19
  import { invariant } from '@dxos/invariant';
21
20
  import { useForwardedRef } from '@dxos/react-hooks';
@@ -23,6 +22,7 @@ import { mx } from '@dxos/ui-theme';
23
22
 
24
23
  import { type ThemedClassName } from '../../util';
25
24
  import { IconButton } from '../Button';
25
+ import { ScrollArea } from '../ScrollArea';
26
26
 
27
27
  const isBottom = (el: HTMLElement | null) => {
28
28
  return !!(el && el.scrollHeight - el.scrollTop === el.clientHeight);
@@ -68,7 +68,7 @@ const Root = forwardRef<ScrollController, RootProps>(
68
68
  const timeoutRef = useRef<NodeJS.Timeout>(undefined);
69
69
  const scrollToBottom = useCallback((behavior: ScrollBehavior = behaviorProp) => {
70
70
  if (scrollerRef.current) {
71
- // Temporarily hide scrollbar to prevent flicker.
71
+ // Temporarily hide scrollbar to prevent flickering.
72
72
  autoScrollRef.current = true;
73
73
  scrollerRef.current.classList.add('scrollbar-none');
74
74
  scrollerRef.current.scrollTo({
@@ -83,6 +83,7 @@ const Root = forwardRef<ScrollController, RootProps>(
83
83
  autoScrollRef.current = false;
84
84
  }, 500);
85
85
  }
86
+
86
87
  setPinned(true);
87
88
  }
88
89
  }, []);
@@ -125,22 +126,22 @@ const Root = forwardRef<ScrollController, RootProps>(
125
126
 
126
127
  return (
127
128
  <ScrollContainerProvider pinned={pinned} controller={controller} scrollToBottom={scrollToBottom}>
128
- <div className='relative grid flex-1 min-bs-0 overflow-hidden'>
129
+ <div className='relative grid dx-container overflow-hidden'>
129
130
  {fade && (
130
131
  <div
131
132
  role='none'
132
133
  data-visible={overflow}
133
134
  className={mx(
134
135
  // NOTE: Gradients may not be visible with dark reader extensions.
135
- 'z-10 absolute block-start-0 inset-inline-0 bs-24 is-full',
136
+ 'z-10 absolute top-0 inset-x-0 h-24 w-full',
136
137
  'opacity-0 duration-200 transition-opacity data-[visible="true"]:opacity-100',
137
- 'bg-gradient-to-b from-[--surface-bg] to-transparent pointer-events-none',
138
+ 'bg-gradient-to-b from-(--surface-bg) to-transparent pointer-events-none',
138
139
  )}
139
140
  />
140
141
  )}
141
- <div className={mx('flex flex-col min-bs-0 overflow-y-auto scrollbar-thin', classNames)} ref={scrollerRef}>
142
- {children}
143
- </div>
142
+ <ScrollArea.Root classNames={mx('min-h-0', classNames)} thin>
143
+ <ScrollArea.Viewport ref={scrollerRef}>{children}</ScrollArea.Viewport>
144
+ </ScrollArea.Root>
144
145
  </div>
145
146
  </ScrollContainerProvider>
146
147
  );
@@ -153,11 +154,13 @@ Root.displayName = 'ScrollContainer.Root';
153
154
  // Viewport
154
155
  //
155
156
 
157
+ const VIEWPORT_NAME = 'ScrollContainer.Viewport';
158
+
156
159
  type ViewportProps = ThemedClassName<PropsWithChildren<Omit<HTMLAttributes<HTMLDivElement>, 'className'>>>;
157
160
 
158
161
  const Viewport = forwardRef<HTMLDivElement, ViewportProps>(({ classNames, children, ...props }, forwardedRef) => {
159
162
  const contentRef = useForwardedRef(forwardedRef);
160
- const { pinned, scrollToBottom } = useScrollContainerContext(Viewport.displayName!);
163
+ const { pinned, scrollToBottom } = useScrollContainerContext(VIEWPORT_NAME);
161
164
 
162
165
  useEffect(() => {
163
166
  if (!pinned || !contentRef.current) {
@@ -174,22 +177,24 @@ const Viewport = forwardRef<HTMLDivElement, ViewportProps>(({ classNames, childr
174
177
  }, [pinned, scrollToBottom]);
175
178
 
176
179
  return (
177
- <div className={mx('is-full', classNames)} {...props} ref={contentRef}>
180
+ <div className={mx('w-full', classNames)} {...props} ref={contentRef}>
178
181
  {children}
179
182
  </div>
180
183
  );
181
184
  });
182
185
 
183
- Viewport.displayName = 'ScrollContainer.Viewport';
186
+ Viewport.displayName = VIEWPORT_NAME;
184
187
 
185
188
  //
186
189
  // ScrollDownButton
187
190
  //
188
191
 
192
+ const SCROLL_DOWN_BUTTON_NAME = 'ScrollContainer.ScrollDownButton';
193
+
189
194
  type ScrollDownButtonProps = ThemedClassName;
190
195
 
191
196
  const ScrollDownButton = ({ classNames }: ScrollDownButtonProps) => {
192
- const { pinned, scrollToBottom } = useScrollContainerContext(ScrollDownButton.displayName!);
197
+ const { pinned, scrollToBottom } = useScrollContainerContext(SCROLL_DOWN_BUTTON_NAME);
193
198
 
194
199
  return (
195
200
  <div
@@ -212,7 +217,7 @@ const ScrollDownButton = ({ classNames }: ScrollDownButtonProps) => {
212
217
  );
213
218
  };
214
219
 
215
- ScrollDownButton.displayName = 'ScrollContainer.ScrollDownButton';
220
+ ScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
216
221
 
217
222
  //
218
223
  // ScrollContainer
@@ -42,9 +42,9 @@ const DefaultStory = ({ items = [] }: StoryProps) => {
42
42
  };
43
43
 
44
44
  const meta = {
45
- title: 'ui/react-ui-core/Select',
45
+ title: 'ui/react-ui-core/components/Select',
46
46
  render: DefaultStory,
47
- decorators: [withTheme, withLayoutVariants()],
47
+ decorators: [withTheme(), withLayoutVariants()],
48
48
  } satisfies Meta<typeof DefaultStory>;
49
49
 
50
50
  export default meta;
@@ -39,7 +39,7 @@ const SelectTriggerButton = forwardRef<HTMLButtonElement, SelectTriggerButtonPro
39
39
  <SelectPrimitive.Trigger asChild ref={forwardedRef}>
40
40
  <Button {...props}>
41
41
  <SelectPrimitive.Value placeholder={placeholder}>{children}</SelectPrimitive.Value>
42
- <span className='is-1 flex-1' />
42
+ <span className='w-1 flex-1' />
43
43
  <SelectPrimitive.Icon asChild>
44
44
  <Icon size={3} icon='ph--caret-down--bold' />
45
45
  </SelectPrimitive.Icon>
@@ -61,7 +61,7 @@ const SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(
61
61
  {...props}
62
62
  data-arrow-keys='up down'
63
63
  collisionPadding={safeCollisionPadding}
64
- className={tx('select.content', 'select__content', { elevation }, classNames)}
64
+ className={tx('select.content', { elevation }, classNames)}
65
65
  position='popper'
66
66
  ref={forwardedRef}
67
67
  >
@@ -79,7 +79,7 @@ const SelectScrollUpButton = forwardRef<HTMLDivElement, SelectScrollUpButtonProp
79
79
  return (
80
80
  <SelectPrimitive.SelectScrollUpButton
81
81
  {...props}
82
- className={tx('select.scrollButton', 'select__scroll-button--up', {}, classNames)}
82
+ className={tx('select.scrollButton', {}, classNames)}
83
83
  ref={forwardedRef}
84
84
  >
85
85
  {children ?? <Icon size={3} icon='ph--caret-up--bold' />}
@@ -96,7 +96,7 @@ const SelectScrollDownButton = forwardRef<HTMLDivElement, SelectScrollDownButton
96
96
  return (
97
97
  <SelectPrimitive.SelectScrollDownButton
98
98
  {...props}
99
- className={tx('select.scrollButton', 'select__scroll-button--down', {}, classNames)}
99
+ className={tx('select.scrollButton', {}, classNames)}
100
100
  ref={forwardedRef}
101
101
  >
102
102
  {children ?? <Icon size={3} icon='ph--caret-down--bold' />}
@@ -111,11 +111,7 @@ const SelectViewport = forwardRef<HTMLDivElement, SelectViewportProps>(
111
111
  ({ classNames, children, ...props }, forwardedRef) => {
112
112
  const { tx } = useThemeContext();
113
113
  return (
114
- <SelectPrimitive.SelectViewport
115
- {...props}
116
- className={tx('select.viewport', 'select__viewport', {}, classNames)}
117
- ref={forwardedRef}
118
- >
114
+ <SelectPrimitive.SelectViewport {...props} className={tx('select.viewport', {}, classNames)} ref={forwardedRef}>
119
115
  {children}
120
116
  </SelectPrimitive.SelectViewport>
121
117
  );
@@ -126,7 +122,7 @@ type SelectItemProps = ThemedClassName<SelectPrimitive.SelectItemProps>;
126
122
 
127
123
  const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(({ classNames, ...props }, forwardedRef) => {
128
124
  const { tx } = useThemeContext();
129
- return <SelectPrimitive.Item {...props} className={tx('select.item', 'option', {}, classNames)} ref={forwardedRef} />;
125
+ return <SelectPrimitive.Item {...props} className={tx('select.item', {}, classNames)} ref={forwardedRef} />;
130
126
  });
131
127
 
132
128
  type SelectItemTextProps = SelectPrimitive.SelectItemTextProps;
@@ -141,7 +137,7 @@ const SelectItemIndicator = forwardRef<HTMLDivElement, SelectItemIndicatorProps>
141
137
  return (
142
138
  <SelectPrimitive.ItemIndicator
143
139
  {...props}
144
- className={tx('select.itemIndicator', 'option__indicator', {}, classNames)}
140
+ className={tx('select.itemIndicator', {}, classNames)}
145
141
  ref={forwardedRef}
146
142
  >
147
143
  {children}
@@ -156,9 +152,9 @@ type SelectOptionProps = SelectItemProps;
156
152
  const SelectOption = forwardRef<HTMLDivElement, SelectItemProps>(({ children, classNames, ...props }, forwardedRef) => {
157
153
  const { tx } = useThemeContext();
158
154
  return (
159
- <SelectPrimitive.Item {...props} className={tx('select.item', 'option', {}, classNames)} ref={forwardedRef}>
155
+ <SelectPrimitive.Item {...props} className={tx('select.item', {}, classNames)} ref={forwardedRef}>
160
156
  <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
161
- <span className='grow is-1' />
157
+ <span className='grow w-1' />
162
158
  {/* <SelectPrimitive.ItemIndicator className={tx('select.itemIndicator', 'option__indicator', {})}> */}
163
159
  <Icon icon='ph--check--regular' />
164
160
  {/* </SelectPrimitive.ItemIndicator> */}
@@ -178,26 +174,14 @@ type SelectSeparatorProps = ThemedClassName<SelectPrimitive.SelectSeparatorProps
178
174
 
179
175
  const SelectSeparator = forwardRef<HTMLDivElement, SelectSeparatorProps>(({ classNames, ...props }, forwardedRef) => {
180
176
  const { tx } = useThemeContext();
181
- return (
182
- <SelectPrimitive.Separator
183
- {...props}
184
- className={tx('select.separator', 'select__separator', {}, classNames)}
185
- ref={forwardedRef}
186
- />
187
- );
177
+ return <SelectPrimitive.Separator {...props} className={tx('select.separator', {}, classNames)} ref={forwardedRef} />;
188
178
  });
189
179
 
190
180
  type SelectArrowProps = ThemedClassName<SelectPrimitive.SelectArrowProps>;
191
181
 
192
182
  const SelectArrow = forwardRef<SVGSVGElement, SelectArrowProps>(({ classNames, ...props }, forwardedRef) => {
193
183
  const { tx } = useThemeContext();
194
- return (
195
- <SelectPrimitive.Arrow
196
- {...props}
197
- className={tx('select.arrow', 'select__arrow', {}, classNames)}
198
- ref={forwardedRef}
199
- />
200
- );
184
+ return <SelectPrimitive.Arrow {...props} className={tx('select.arrow', {}, classNames)} ref={forwardedRef} />;
201
185
  });
202
186
 
203
187
  export const Select = {
@@ -19,7 +19,7 @@ const Separator = forwardRef<HTMLDivElement, SeparatorProps>(
19
19
  <SeparatorPrimitive
20
20
  orientation={orientation}
21
21
  {...props}
22
- className={tx('separator.root', 'separator', { orientation, subdued }, classNames)}
22
+ className={tx('separator.root', { orientation, subdued }, classNames)}
23
23
  ref={forwardedRef}
24
24
  />
25
25
  );
@@ -0,0 +1,52 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { withTheme } from '../../testing';
8
+
9
+ import { Skeleton } from './Skeleton';
10
+
11
+ export default {
12
+ title: 'ui/react-ui-core/components/Skeleton',
13
+ component: Skeleton,
14
+ decorators: [withTheme()],
15
+ parameters: {
16
+ layout: 'centered',
17
+ },
18
+ };
19
+
20
+ export const Default = {
21
+ render: () => (
22
+ <div className='flex flex-col gap-4 p-4 border border-separator rounded-xs'>
23
+ <div className='flex w-fit items-center gap-4'>
24
+ <Skeleton classNames='size-10 shrink-0 rounded-full' />
25
+ <div className='grid gap-2'>
26
+ <Skeleton classNames='h-4 w-[150px]' />
27
+ <Skeleton classNames='h-4 w-[100px]' />
28
+ </div>
29
+ </div>
30
+ </div>
31
+ ),
32
+ };
33
+
34
+ export const Card = {
35
+ render: () => (
36
+ <div className='flex flex-col gap-3 w-96 p-4 border border-separator rounded-xs'>
37
+ <div className='flex items-center gap-3'>
38
+ <Skeleton variant='circle' classNames='h-12 w-12 rounded-full' />
39
+ <div className='flex flex-col gap-2 flex-1'>
40
+ <Skeleton classNames='h-4 w-24' />
41
+ <Skeleton classNames='h-3 w-32' />
42
+ </div>
43
+ </div>
44
+ <Skeleton classNames='h-32 w-full rounded-sm' />
45
+ <div className='flex flex-col gap-2'>
46
+ <Skeleton classNames='h-3 w-full' />
47
+ <Skeleton classNames='h-3 w-5/6' />
48
+ <Skeleton classNames='h-3 w-4/6' />
49
+ </div>
50
+ </div>
51
+ ),
52
+ };
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { type ComponentPropsWithRef, forwardRef } from 'react';
6
+
7
+ import { useThemeContext } from '../../hooks';
8
+ import { type ThemedClassName } from '../../util';
9
+
10
+ type SkeletonProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
11
+ variant?: 'default' | 'circle' | 'text';
12
+ };
13
+
14
+ /**
15
+ * A skeleton loading component that displays a placeholder while content is loading.
16
+ */
17
+ const Skeleton = forwardRef<HTMLDivElement, SkeletonProps>(
18
+ ({ classNames, variant = 'default', ...props }, forwardedRef) => {
19
+ const { tx } = useThemeContext();
20
+ return <div {...props} className={tx('skeleton.root', { variant }, classNames)} ref={forwardedRef} />;
21
+ },
22
+ );
23
+
24
+ export { Skeleton };
25
+
26
+ export type { SkeletonProps };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './Skeleton';