@dxos/react-ui 0.8.4-main.c85a9c8dae → 0.8.4-main.d05673bc65

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 (62) hide show
  1. package/dist/lib/browser/index.mjs +590 -603
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +18 -0
  5. package/dist/lib/browser/testing/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +590 -603
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/testing/index.mjs +18 -0
  10. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  11. package/dist/types/src/components/Card/Card.d.ts +15 -29
  12. package/dist/types/src/components/Card/Card.d.ts.map +1 -1
  13. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +4 -6
  14. package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
  15. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +5 -7
  16. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Splitter/Splitter.d.ts +7 -11
  18. package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -1
  19. package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -3
  20. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  21. package/dist/types/src/exemplars/slot.stories.d.ts.map +1 -1
  22. package/dist/types/src/primitives/Column/Column.d.ts +6 -12
  23. package/dist/types/src/primitives/Column/Column.d.ts.map +1 -1
  24. package/dist/types/src/primitives/Column/Column.stories.d.ts.map +1 -1
  25. package/dist/types/src/primitives/Container/Container.d.ts +8 -0
  26. package/dist/types/src/primitives/Container/Container.d.ts.map +1 -0
  27. package/dist/types/src/primitives/Container/Container.stories.d.ts +6 -0
  28. package/dist/types/src/primitives/Container/Container.stories.d.ts.map +1 -0
  29. package/dist/types/src/primitives/Container/index.d.ts +2 -0
  30. package/dist/types/src/primitives/Container/index.d.ts.map +1 -0
  31. package/dist/types/src/primitives/Flex/Flex.d.ts +9 -4
  32. package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -1
  33. package/dist/types/src/primitives/Grid/Grid.d.ts +10 -4
  34. package/dist/types/src/primitives/Grid/Grid.d.ts.map +1 -1
  35. package/dist/types/src/primitives/Panel/Panel.d.ts +6 -14
  36. package/dist/types/src/primitives/Panel/Panel.d.ts.map +1 -1
  37. package/dist/types/src/primitives/index.d.ts +1 -0
  38. package/dist/types/src/primitives/index.d.ts.map +1 -1
  39. package/dist/types/src/testing/Loading.d.ts +9 -0
  40. package/dist/types/src/testing/Loading.d.ts.map +1 -0
  41. package/dist/types/src/testing/index.d.ts +1 -0
  42. package/dist/types/src/testing/index.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +16 -16
  45. package/src/components/Card/Card.tsx +49 -54
  46. package/src/components/List/List.stories.tsx +2 -2
  47. package/src/components/List/TreeDropIndicator.tsx +1 -1
  48. package/src/components/ScrollArea/ScrollArea.tsx +6 -6
  49. package/src/components/Splitter/Splitter.tsx +17 -29
  50. package/src/components/Toolbar/Toolbar.tsx +18 -17
  51. package/src/exemplars/slot.stories.tsx +11 -23
  52. package/src/primitives/Column/Column.stories.tsx +6 -0
  53. package/src/primitives/Column/Column.tsx +26 -48
  54. package/src/primitives/Container/Container.stories.tsx +30 -0
  55. package/src/primitives/Container/Container.tsx +22 -0
  56. package/src/primitives/Container/index.ts +5 -0
  57. package/src/primitives/Flex/Flex.tsx +21 -19
  58. package/src/primitives/Grid/Grid.tsx +27 -35
  59. package/src/primitives/Panel/Panel.tsx +54 -72
  60. package/src/primitives/index.ts +1 -0
  61. package/src/testing/Loading.tsx +26 -0
  62. package/src/testing/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui",
3
- "version": "0.8.4-main.c85a9c8dae",
3
+ "version": "0.8.4-main.d05673bc65",
4
4
  "description": "Low-level React components for DXOS, applying a theme to a core group of primitives",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -80,17 +80,17 @@
80
80
  "react-error-boundary": "^4.0.13",
81
81
  "react-i18next": "^11.18.6",
82
82
  "react-remove-scroll": "^2.6.0",
83
- "@dxos/async": "0.8.4-main.c85a9c8dae",
84
- "@dxos/invariant": "0.8.4-main.c85a9c8dae",
85
- "@dxos/debug": "0.8.4-main.c85a9c8dae",
86
- "@dxos/lit-ui": "0.8.4-main.c85a9c8dae",
87
- "@dxos/log": "0.8.4-main.c85a9c8dae",
88
- "@dxos/react-hooks": "0.8.4-main.c85a9c8dae",
89
- "@dxos/react-error-boundary": "0.8.4-main.c85a9c8dae",
90
- "@dxos/react-input": "0.8.4-main.c85a9c8dae",
91
- "@dxos/react-list": "0.8.4-main.c85a9c8dae",
92
- "@dxos/ui-types": "0.8.4-main.c85a9c8dae",
93
- "@dxos/util": "0.8.4-main.c85a9c8dae"
83
+ "@dxos/debug": "0.8.4-main.d05673bc65",
84
+ "@dxos/invariant": "0.8.4-main.d05673bc65",
85
+ "@dxos/lit-ui": "0.8.4-main.d05673bc65",
86
+ "@dxos/react-error-boundary": "0.8.4-main.d05673bc65",
87
+ "@dxos/async": "0.8.4-main.d05673bc65",
88
+ "@dxos/log": "0.8.4-main.d05673bc65",
89
+ "@dxos/react-hooks": "0.8.4-main.d05673bc65",
90
+ "@dxos/react-input": "0.8.4-main.d05673bc65",
91
+ "@dxos/react-list": "0.8.4-main.d05673bc65",
92
+ "@dxos/ui-types": "0.8.4-main.d05673bc65",
93
+ "@dxos/util": "0.8.4-main.d05673bc65"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@dnd-kit/core": "^6.0.5",
@@ -104,14 +104,14 @@
104
104
  "react-dom": "~19.2.3",
105
105
  "tabster": "^8.5.5",
106
106
  "vite": "^7.1.11",
107
- "@dxos/ui-theme": "0.8.4-main.c85a9c8dae",
108
- "@dxos/util": "0.8.4-main.c85a9c8dae",
109
- "@dxos/random": "0.8.4-main.c85a9c8dae"
107
+ "@dxos/random": "0.8.4-main.d05673bc65",
108
+ "@dxos/util": "0.8.4-main.d05673bc65",
109
+ "@dxos/ui-theme": "0.8.4-main.d05673bc65"
110
110
  },
111
111
  "peerDependencies": {
112
112
  "react": "~19.2.3",
113
113
  "react-dom": "~19.2.3",
114
- "@dxos/ui-theme": "0.8.4-main.c85a9c8dae"
114
+ "@dxos/ui-theme": "0.8.4-main.d05673bc65"
115
115
  },
116
116
  "publishConfig": {
117
117
  "access": "public"
@@ -6,7 +6,7 @@ import { Primitive } from '@radix-ui/react-primitive';
6
6
  import { Slot } from '@radix-ui/react-slot';
7
7
  import React, { type HTMLAttributes, type PropsWithChildren, createContext, forwardRef, useContext } from 'react';
8
8
 
9
- import { mx } from '@dxos/ui-theme';
9
+ import { composableProps, mx } from '@dxos/ui-theme';
10
10
  import { type Density, type SlottableProps } from '@dxos/ui-types';
11
11
 
12
12
  import { useThemeContext } from '../../hooks';
@@ -32,23 +32,27 @@ const CardContext = createContext<CardContextValue>({});
32
32
  // Root
33
33
  //
34
34
 
35
- type CardRootProps = SlottableProps<HTMLDivElement> & {
36
- id?: string;
37
- border?: boolean;
38
- fullWidth?: boolean;
39
- };
35
+ type CardRootProps = SlottableProps<
36
+ HTMLDivElement,
37
+ {
38
+ id?: string;
39
+ border?: boolean;
40
+ fullWidth?: boolean;
41
+ }
42
+ >;
40
43
 
41
44
  const CardRoot = forwardRef<HTMLDivElement, CardRootProps>(
42
- ({ children, classNames, className, id, asChild, role, border = true, fullWidth, ...props }, forwardedRef) => {
43
- const { tx } = useThemeContext();
45
+ ({ children, id, asChild, role, border = true, fullWidth, ...props }, forwardedRef) => {
46
+ const { className, ...rest } = composableProps(props);
44
47
  const Comp = asChild ? Slot : Primitive.div;
48
+ const { tx } = useThemeContext();
45
49
 
46
50
  return (
47
51
  <Comp
48
- {...props}
52
+ {...rest}
49
53
  {...(id && { 'data-object-id': id })}
50
54
  role={role ?? 'group'}
51
- className={tx('card.root', { border, fullWidth }, [className, classNames])}
55
+ className={tx('card.root', { border, fullWidth }, className)}
52
56
  ref={forwardedRef}
53
57
  >
54
58
  <Column.Root gutter='rail'>{children}</Column.Root>
@@ -102,23 +106,17 @@ const CardMenu = <T extends any | void = void>({ context, items }: CardMenuProps
102
106
 
103
107
  type CardTitleProps = SlottableProps<HTMLDivElement>;
104
108
 
105
- const CardTitle = forwardRef<HTMLDivElement, CardTitleProps>(
106
- ({ children, classNames, className, asChild, role, ...props }, forwardedRef) => {
107
- const { tx } = useThemeContext();
108
- const Comp = asChild ? Slot : Primitive.div;
109
+ const CardTitle = forwardRef<HTMLDivElement, CardTitleProps>(({ children, asChild, role, ...props }, forwardedRef) => {
110
+ const { className, ...rest } = composableProps(props);
111
+ const Comp = asChild ? Slot : Primitive.div;
112
+ const { tx } = useThemeContext();
109
113
 
110
- return (
111
- <Comp
112
- {...props}
113
- role={role ?? 'heading'}
114
- className={tx('card.title', {}, [className, classNames])}
115
- ref={forwardedRef}
116
- >
117
- {children}
118
- </Comp>
119
- );
120
- },
121
- );
114
+ return (
115
+ <Comp {...rest} role={role ?? 'heading'} className={tx('card.title', {}, className)} ref={forwardedRef}>
116
+ {children}
117
+ </Comp>
118
+ );
119
+ });
122
120
 
123
121
  //
124
122
  // Content
@@ -140,19 +138,18 @@ const CardContent = forwardRef<HTMLDivElement, CardContentProps>(({ children, ro
140
138
  // Row
141
139
  //
142
140
 
143
- type CardRowProps = SlottableProps<HTMLDivElement> & { icon?: string };
141
+ type CardRowProps = SlottableProps<HTMLDivElement, { icon?: string }>;
144
142
 
145
- const CardRow = forwardRef<HTMLDivElement, CardRowProps>(
146
- ({ children, classNames, className, role, icon, ...props }, forwardedRef) => {
147
- return (
148
- <Column.Row {...props} role={role ?? 'none'} classNames={[classNames, className]} ref={forwardedRef}>
149
- {(icon && <CardIcon classNames='text-subdued' icon={icon} />) || <div />}
150
- {children}
151
- <div />
152
- </Column.Row>
153
- );
154
- },
155
- );
143
+ const CardRow = forwardRef<HTMLDivElement, CardRowProps>(({ children, role, icon, ...props }, forwardedRef) => {
144
+ const { className, ...rest } = composableProps(props);
145
+ return (
146
+ <Column.Row {...rest} role={role ?? 'none'} classNames={className} ref={forwardedRef}>
147
+ {(icon && <CardIcon classNames='text-subdued' icon={icon} />) || <div />}
148
+ {children}
149
+ <div />
150
+ </Column.Row>
151
+ );
152
+ });
156
153
 
157
154
  //
158
155
  // Section
@@ -161,11 +158,12 @@ const CardRow = forwardRef<HTMLDivElement, CardRowProps>(
161
158
  type CardSectionProps = SlottableProps<HTMLDivElement>;
162
159
 
163
160
  const CardSection = forwardRef<HTMLDivElement, CardSectionProps>(
164
- ({ children, classNames, className, asChild, role, ...props }, forwardedRef) => {
161
+ ({ children, asChild, role, ...props }, forwardedRef) => {
162
+ const { className, ...rest } = composableProps(props);
165
163
  const Comp = asChild ? Slot : Primitive.div;
166
164
 
167
165
  return (
168
- <Comp {...props} role={role ?? 'none'} className={mx(classNames, className, 'col-span-full')} ref={forwardedRef}>
166
+ <Comp {...rest} role={role ?? 'none'} className={mx('col-span-full', className)} ref={forwardedRef}>
169
167
  {children}
170
168
  </Comp>
171
169
  );
@@ -176,21 +174,22 @@ const CardSection = forwardRef<HTMLDivElement, CardSectionProps>(
176
174
  // Heading
177
175
  //
178
176
 
179
- type CardHeadingProps = SlottableProps<HTMLDivElement> & { variant?: 'default' | 'subtitle' };
177
+ type CardHeadingProps = SlottableProps<HTMLDivElement, { variant?: 'default' | 'subtitle' }>;
180
178
 
181
179
  /**
182
180
  * @deprecated Use typography.
183
181
  */
184
182
  const CardHeading = forwardRef<HTMLDivElement, CardHeadingProps>(
185
- ({ children, classNames, className, asChild, role, variant = 'default', ...props }, forwardedRef) => {
186
- const { tx } = useThemeContext();
183
+ ({ children, asChild, role, variant = 'default', ...props }, forwardedRef) => {
184
+ const { className, ...rest } = composableProps(props);
187
185
  const Comp = asChild ? Slot : Primitive.div;
186
+ const { tx } = useThemeContext();
188
187
 
189
188
  return (
190
189
  <Comp
191
- {...props}
190
+ {...rest}
192
191
  role={role ?? 'heading'}
193
- className={tx('card.heading', { variant }, [classNames, className])}
192
+ className={tx('card.heading', { variant }, className)}
194
193
  ref={forwardedRef}
195
194
  >
196
195
  {children}
@@ -203,20 +202,16 @@ const CardHeading = forwardRef<HTMLDivElement, CardHeadingProps>(
203
202
  // Text
204
203
  //
205
204
 
206
- type CardTextProps = SlottableProps<HTMLDivElement> & { truncate?: boolean; variant?: 'default' | 'description' };
205
+ type CardTextProps = SlottableProps<HTMLDivElement, { truncate?: boolean; variant?: 'default' | 'description' }>;
207
206
 
208
207
  const CardText = forwardRef<HTMLDivElement, CardTextProps>(
209
- ({ children, classNames, className, asChild, role, truncate, variant = 'default', ...props }, forwardedRef) => {
210
- const { tx } = useThemeContext();
208
+ ({ children, asChild, role, truncate, variant = 'default', ...props }, forwardedRef) => {
209
+ const { className, ...rest } = composableProps(props);
211
210
  const Comp = asChild ? Slot : Primitive.div;
211
+ const { tx } = useThemeContext();
212
212
 
213
213
  return (
214
- <Comp
215
- {...props}
216
- role={role ?? 'none'}
217
- className={tx('card.text', { variant }, [classNames, className])}
218
- ref={forwardedRef}
219
- >
214
+ <Comp {...rest} role={role ?? 'none'} className={tx('card.text', { variant }, className)} ref={forwardedRef}>
220
215
  <span className={tx('card.text-span', { variant, truncate })}>{children}</span>
221
216
  </Comp>
222
217
  );
@@ -9,7 +9,7 @@ import { useArrowNavigationGroup } from '@fluentui/react-tabster';
9
9
  import { type Meta, type StoryObj } from '@storybook/react-vite';
10
10
  import React, { type ReactNode, useCallback, useState } from 'react';
11
11
 
12
- import { getSize, ghostHover, ghostSelected, mx, surfaceShadow } from '@dxos/ui-theme';
12
+ import { getSize, ghostHover, mx, surfaceShadow } from '@dxos/ui-theme';
13
13
 
14
14
  import { withTheme } from '../../testing';
15
15
  import { Icon } from '../Icon';
@@ -222,7 +222,7 @@ export const SelectableListbox: Story = {
222
222
  key={id}
223
223
  tabIndex={0}
224
224
  selected={selectedId === id}
225
- classNames={mx(ghostHover, ghostSelected)}
225
+ classNames={mx(ghostHover, 'dx-selected')}
226
226
  onClick={() => setSelectedId(id)}
227
227
  onKeyUp={(event) => handleKeyUp(event, id)}
228
228
  >
@@ -22,7 +22,7 @@ const orientationStyles: Record<Orientation, HTMLAttributes<HTMLElement>['classN
22
22
  // TODO(wittjosiah): Stop using left/right here.
23
23
  sibling:
24
24
  'h-(--line-thickness) left-(--horizontal-indent) right-0 bg-accent-surface before:left-(--negative-terminal-size)',
25
- child: 'w-full top-0 bottom-0 border-[length:--line-thickness] before:invisible',
25
+ child: 'inset-0 border-[length:var(--line-thickness)] before:invisible',
26
26
  };
27
27
 
28
28
  const instructionStyles: Record<InstructionType, HTMLAttributes<HTMLElement>['className']> = {
@@ -7,6 +7,7 @@ import { Primitive } from '@radix-ui/react-primitive';
7
7
  import { Slot } from '@radix-ui/react-slot';
8
8
  import React, { type HTMLAttributes, forwardRef } from 'react';
9
9
 
10
+ import { composableProps } from '@dxos/ui-theme';
10
11
  import { type AllowedAxis, type SlottableProps, type ThemedClassName } from '@dxos/ui-types';
11
12
 
12
13
  import { useThemeContext } from '../../hooks';
@@ -40,7 +41,7 @@ const [ScrollAreaProvider, useScrollAreaContext] = createContext<ScrollAreaConte
40
41
 
41
42
  const SCROLLAREA_ROOT_NAME = 'ScrollArea.Root';
42
43
 
43
- type ScrollAreaRootProps = SlottableProps<HTMLDivElement> & Partial<ScrollAreaContextType>;
44
+ type ScrollAreaRootProps = SlottableProps<HTMLDivElement, Partial<ScrollAreaContextType>>;
44
45
 
45
46
  /**
46
47
  * ScrollArea provides native scrollbars with custom styling.
@@ -48,10 +49,8 @@ type ScrollAreaRootProps = SlottableProps<HTMLDivElement> & Partial<ScrollAreaCo
48
49
  const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(
49
50
  (
50
51
  {
51
- classNames,
52
- className,
53
- asChild,
54
52
  children,
53
+ asChild,
55
54
  orientation = 'vertical',
56
55
  autoHide = true,
57
56
  margin = false,
@@ -62,13 +61,14 @@ const ScrollAreaRoot = forwardRef<HTMLDivElement, ScrollAreaRootProps>(
62
61
  },
63
62
  forwardedRef,
64
63
  ) => {
65
- const { tx } = useThemeContext();
64
+ const { className, ...rest } = composableProps(props);
66
65
  const Comp = asChild ? Slot : Primitive.div;
66
+ const { tx } = useThemeContext();
67
67
  const options = { orientation, autoHide, margin, padding, thin, snap };
68
68
 
69
69
  return (
70
70
  <ScrollAreaProvider {...options}>
71
- <Comp {...props} className={tx('scrollArea.root', options, [className, classNames])} ref={forwardedRef}>
71
+ <Comp {...rest} className={tx('scrollArea.root', options, className)} ref={forwardedRef}>
72
72
  {children}
73
73
  </Comp>
74
74
  </ScrollAreaProvider>
@@ -7,6 +7,7 @@ import { Primitive } from '@radix-ui/react-primitive';
7
7
  import { Slot } from '@radix-ui/react-slot';
8
8
  import React, { forwardRef } from 'react';
9
9
 
10
+ import { composableProps } from '@dxos/ui-theme';
10
11
  import { type SlottableProps } from '@dxos/ui-types';
11
12
 
12
13
  import { useThemeContext } from '../../hooks';
@@ -35,33 +36,16 @@ const [SplitterProvider, useSplitterContext] = createSplitterContext<SplitterCon
35
36
 
36
37
  const ROOT_NAME = 'Splitter.Root';
37
38
 
38
- type RootProps = SlottableProps<HTMLDivElement> & Partial<SplitterContextValue>;
39
+ type RootProps = SlottableProps<HTMLDivElement, Partial<SplitterContextValue>>;
39
40
 
40
41
  const Root = forwardRef<HTMLDivElement, ScopedProps<RootProps>>(
41
- (
42
- {
43
- __scopeSplitter,
44
- classNames,
45
- className,
46
- asChild,
47
- mode = 'upper',
48
- ratio = 0.5,
49
- transition = 250,
50
- children,
51
- ...rootProps
52
- },
53
- forwardedRef,
54
- ) => {
55
- const { tx } = useThemeContext();
42
+ ({ __scopeSplitter, asChild, mode = 'upper', ratio = 0.5, transition = 250, children, ...props }, forwardedRef) => {
43
+ const { className, ...rest } = composableProps(props);
56
44
  const Comp = asChild ? Slot : Primitive.div;
45
+ const { tx } = useThemeContext();
57
46
  return (
58
47
  <SplitterProvider scope={__scopeSplitter} mode={mode} ratio={ratio} transition={transition}>
59
- <Comp
60
- role='none'
61
- {...rootProps}
62
- ref={forwardedRef}
63
- className={tx('splitter.root', {}, [className, classNames])}
64
- >
48
+ <Comp role='none' {...rest} ref={forwardedRef} className={tx('splitter.root', {}, className)}>
65
49
  {children}
66
50
  </Comp>
67
51
  </SplitterProvider>
@@ -77,15 +61,19 @@ Root.displayName = ROOT_NAME;
77
61
 
78
62
  const PANEL_NAME = 'Splitter.Panel';
79
63
 
80
- type PanelProps = SlottableProps<HTMLDivElement> & {
81
- position: 'upper' | 'lower';
82
- };
64
+ type PanelProps = SlottableProps<
65
+ HTMLDivElement,
66
+ {
67
+ position: 'upper' | 'lower';
68
+ }
69
+ >;
83
70
 
84
71
  const Panel = forwardRef<HTMLDivElement, ScopedProps<PanelProps>>(
85
- ({ __scopeSplitter, classNames, className, asChild, children, position, style, ...props }, forwardedRef) => {
72
+ ({ __scopeSplitter, asChild, children, position, style, ...props }, forwardedRef) => {
73
+ const { className, ...rest } = composableProps(props);
74
+ const Comp = asChild ? Slot : Primitive.div;
86
75
  const { mode, ratio, transition } = useSplitterContext(PANEL_NAME, __scopeSplitter);
87
76
  const { tx } = useThemeContext();
88
- const Comp = asChild ? Slot : Primitive.div;
89
77
 
90
78
  // Calculate position and height based on mode and ratio.
91
79
  const isUpper = position === 'upper';
@@ -106,9 +94,9 @@ const Panel = forwardRef<HTMLDivElement, ScopedProps<PanelProps>>(
106
94
  return (
107
95
  <Comp
108
96
  role='none'
109
- {...props}
97
+ {...rest}
110
98
  ref={forwardedRef}
111
- className={tx('splitter.panel', {}, [className, classNames])}
99
+ className={tx('splitter.panel', {}, className)}
112
100
  style={{
113
101
  top,
114
102
  height,
@@ -9,7 +9,7 @@ import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
9
9
  import React, { Fragment, forwardRef } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
 
12
- import { type ToolbarStyleProps } from '@dxos/ui-theme';
12
+ import { composableProps, type ToolbarStyleProps } from '@dxos/ui-theme';
13
13
  import { type SlottableProps } from '@dxos/ui-types';
14
14
 
15
15
  import { useThemeContext } from '../../hooks';
@@ -44,23 +44,25 @@ type ToolbarRootProps = ThemedClassName<
44
44
  // TODO(burdon): Implement asChild property.
45
45
  const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
46
46
  (
47
- { classNames, children, density, disabled, layoutManaged, textBlockWidth: textBlockWidthProp, ...props },
47
+ { children, density, disabled, layoutManaged, textBlockWidth: textBlockWidthProp, orientation, ...props },
48
48
  forwardedRef,
49
49
  ) => {
50
+ const { className, dir: _, ...rest } = composableProps(props);
50
51
  const { tx } = useThemeContext();
51
52
  const InnerRoot = textBlockWidthProp ? 'div' : Fragment;
52
53
  const innerRootProps = textBlockWidthProp
53
54
  ? {
54
55
  role: 'none',
55
- className: tx('toolbar.inner', { layoutManaged }, classNames),
56
+ className: tx('toolbar.inner', { layoutManaged }, className),
56
57
  }
57
58
  : {};
58
59
 
59
60
  return (
60
61
  <ToolbarPrimitive.Root
61
- {...props}
62
- data-arrow-keys={props.orientation === 'vertical' ? 'up down' : 'left right'}
63
- className={tx('toolbar.root', { density, disabled, layoutManaged }, classNames)}
62
+ {...rest}
63
+ orientation={orientation}
64
+ data-arrow-keys={orientation === 'vertical' ? 'up down' : 'left right'}
65
+ className={tx('toolbar.root', { density, disabled, layoutManaged }, className)}
64
66
  ref={forwardedRef}
65
67
  >
66
68
  <InnerRoot {...innerRootProps}>{children}</InnerRoot>
@@ -75,17 +77,16 @@ const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
75
77
 
76
78
  type ToolbarTextProps = SlottableProps<HTMLDivElement>;
77
79
 
78
- const ToolbarText = forwardRef<HTMLDivElement, ToolbarTextProps>(
79
- ({ classNames, className, asChild, children, ...props }, forwardedRef) => {
80
- const { tx } = useThemeContext();
81
- const Comp = asChild ? Slot : Primitive.div;
82
- return (
83
- <Comp {...props} className={tx('toolbar.text', {}, [className, classNames])} ref={forwardedRef}>
84
- {children}
85
- </Comp>
86
- );
87
- },
88
- );
80
+ const ToolbarText = forwardRef<HTMLDivElement, ToolbarTextProps>(({ children, asChild, ...props }, forwardedRef) => {
81
+ const { className, ...rest } = composableProps(props);
82
+ const Comp = asChild ? Slot : Primitive.div;
83
+ const { tx } = useThemeContext();
84
+ return (
85
+ <Comp {...rest} className={tx('toolbar.text', {}, className)} ref={forwardedRef}>
86
+ {children}
87
+ </Comp>
88
+ );
89
+ });
89
90
 
90
91
  //
91
92
  // Button
@@ -3,14 +3,14 @@
3
3
  //
4
4
 
5
5
  import { Primitive } from '@radix-ui/react-primitive';
6
- import { Slot } from '@radix-ui/react-slot';
7
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React, { type PropsWithChildren, forwardRef } from 'react';
7
+ import React, { HTMLAttributes, type PropsWithChildren, forwardRef } from 'react';
9
8
 
10
- import { useComposableProps } from '@dxos/ui-theme';
9
+ import { composableProps } from '@dxos/ui-theme';
11
10
  import { type ComposableProps, type SlottableProps, type ThemedClassName } from '@dxos/ui-types';
12
11
 
13
12
  import { withTheme } from '../testing';
13
+ import { Slot } from '@radix-ui/react-slot';
14
14
 
15
15
  /**
16
16
  * Composition
@@ -20,59 +20,49 @@ import { withTheme } from '../testing';
20
20
  * https://www.radix-ui.com/primitives/docs/guides/composition
21
21
  */
22
22
 
23
- // Outer primitive (like Tooltip.Trigger or Focus.Group).
24
23
  const Outer = forwardRef<HTMLDivElement, SlottableProps<HTMLDivElement>>(
25
24
  ({ children, asChild, ...props }, forwardedRef) => {
26
- const { className, ...rest } = useComposableProps(props);
27
25
  const Comp = asChild ? Slot : Primitive.div;
28
26
  return (
29
- <Comp {...rest} className={className} data-outer='true' ref={forwardedRef}>
27
+ <Comp {...composableProps<HTMLDivElement>(props, { role: 'none' })} ref={forwardedRef}>
30
28
  {children}
31
29
  </Comp>
32
30
  );
33
31
  },
34
32
  );
35
33
 
36
- // Middle primitive (like Dialog.Trigger or Mosaic.Cell).
37
34
  const Middle = forwardRef<HTMLDivElement, SlottableProps<HTMLDivElement>>(
38
35
  ({ children, asChild, ...props }, forwardedRef) => {
39
- const { className, ...rest } = useComposableProps(props);
40
36
  const Comp = asChild ? Slot : Primitive.div;
41
37
  return (
42
- <Comp {...rest} className={className} data-middle='true' ref={forwardedRef}>
38
+ <Comp {...composableProps<HTMLDivElement>(props, { role: 'none' })} ref={forwardedRef}>
43
39
  {children}
44
40
  </Comp>
45
41
  );
46
42
  },
47
43
  );
48
44
 
49
- // Leaf component (like Card.Root).
50
- const Leaf = forwardRef<HTMLButtonElement, ComposableProps<PropsWithChildren>>(
45
+ const Leaf = forwardRef<HTMLButtonElement, ComposableProps<HTMLButtonElement>>(
51
46
  ({ children, ...props }, forwardedRef) => {
52
- const { className, ...rest } = useComposableProps(props);
53
47
  return (
54
- <button {...rest} className={className} ref={forwardedRef}>
48
+ <button {...composableProps<HTMLButtonElement>(props, { role: 'none' })} ref={forwardedRef}>
55
49
  {children}
56
50
  </button>
57
51
  );
58
52
  },
59
53
  );
60
54
 
61
- // Test 1: Single asChild.
62
55
  const TestSingle = (props: ThemedClassName<{ role?: string }>) => {
63
- const { className, ...rest } = useComposableProps(props);
64
56
  return (
65
- <Outer asChild {...rest} className={className}>
66
- <Leaf>Single asChild</Leaf>
57
+ <Outer asChild {...composableProps<HTMLDivElement>(props, { role: 'none' })}>
58
+ <Leaf>Single asChild (non-compliant — see console)</Leaf>
67
59
  </Outer>
68
60
  );
69
61
  };
70
62
 
71
- // Test 2: Nested asChild.
72
63
  const TestNested = (props: ThemedClassName<{ role?: string }>) => {
73
- const { className, ...rest } = useComposableProps(props);
74
64
  return (
75
- <Outer asChild {...rest} className={className}>
65
+ <Outer asChild {...composableProps<HTMLDivElement>(props, { role: 'none' })}>
76
66
  <Middle asChild>
77
67
  <Leaf>Nested asChild</Leaf>
78
68
  </Middle>
@@ -80,11 +70,9 @@ const TestNested = (props: ThemedClassName<{ role?: string }>) => {
80
70
  );
81
71
  };
82
72
 
83
- // Test 3: Complex.
84
73
  const TestInner = (props: ThemedClassName<{ role?: string }>) => {
85
- const { className, ...rest } = useComposableProps(props);
86
74
  return (
87
- <Outer asChild {...rest} className={className}>
75
+ <Outer asChild {...composableProps<HTMLDivElement>(props, { role: 'none' })}>
88
76
  <Middle asChild>
89
77
  <Leaf>
90
78
  <div role='none'>Leaf</div>
@@ -32,6 +32,12 @@ const DefaultStory = () => {
32
32
  <h1 className='p-1 bg-blue-500 text-black'>Header</h1>
33
33
  </Column.Segment>
34
34
 
35
+ <Column.Row>
36
+ <div className='p-1 bg-blue-500'>A</div>
37
+ <div className='p-1 bg-red-500'>B</div>
38
+ <div className='p-1 bg-blue-500'>C</div>
39
+ </Column.Row>
40
+
35
41
  <Column.Segment asChild>
36
42
  <div className='py-2'>
37
43
  <Input.Root>