@dxos/react-ui 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d

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 (174) hide show
  1. package/dist/lib/browser/{chunk-B7HPXBP2.mjs → chunk-N5GDJTT2.mjs} +421 -228
  2. package/dist/lib/browser/chunk-N5GDJTT2.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -1
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +47 -19
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/{chunk-6JCSY5Y7.mjs → chunk-SP7VQH72.mjs} +421 -228
  9. package/dist/lib/node-esm/chunk-SP7VQH72.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +5 -1
  11. package/dist/lib/node-esm/index.mjs.map +1 -1
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +47 -19
  14. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  15. package/dist/types/src/components/Button/Button.d.ts.map +1 -0
  16. package/dist/types/src/components/Button/Button.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/{Buttons → Button}/IconButton.d.ts +0 -1
  18. package/dist/types/src/components/Button/IconButton.d.ts.map +1 -0
  19. package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -0
  20. package/dist/types/src/components/Button/Toggle.d.ts.map +1 -0
  21. package/dist/types/src/components/Button/Toggle.stories.d.ts +16 -0
  22. package/dist/types/src/components/Button/Toggle.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/Button/ToggleGroup.d.ts.map +1 -0
  24. package/dist/types/src/components/Button/ToggleGroup.stories.d.ts.map +1 -0
  25. package/dist/types/src/components/Button/index.d.ts.map +1 -0
  26. package/dist/types/src/components/Clipboard/CopyButton.d.ts +1 -1
  27. package/dist/types/src/components/Clipboard/CopyButton.d.ts.map +1 -1
  28. package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -0
  29. package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -0
  30. package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -0
  31. package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -0
  32. package/dist/types/src/components/Dialog/index.d.ts.map +1 -0
  33. package/dist/types/src/components/Icon/Icon.stories.d.ts +17 -0
  34. package/dist/types/src/components/Icon/Icon.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/List/List.d.ts.map +1 -0
  36. package/dist/types/src/components/List/List.stories.d.ts.map +1 -0
  37. package/dist/types/src/components/List/ListDropIndicator.d.ts.map +1 -0
  38. package/dist/types/src/components/List/Tree.d.ts.map +1 -0
  39. package/dist/types/src/components/List/Tree.stories.d.ts.map +1 -0
  40. package/dist/types/src/components/List/TreeDropIndicator.d.ts.map +1 -0
  41. package/dist/types/src/components/List/Treegrid.d.ts.map +1 -0
  42. package/dist/types/src/components/List/Treegrid.stories.d.ts.map +1 -0
  43. package/dist/types/src/components/List/index.d.ts.map +1 -0
  44. package/dist/types/src/components/Main/Main.d.ts +8 -8
  45. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  46. package/dist/types/src/components/Menus/DropdownMenu.d.ts +2 -3
  47. package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
  48. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  49. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +37 -0
  50. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -0
  51. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +18 -0
  52. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -0
  53. package/dist/types/src/components/ScrollContainer/index.d.ts +2 -0
  54. package/dist/types/src/components/ScrollContainer/index.d.ts.map +1 -0
  55. package/dist/types/src/components/Select/Select.d.ts +1 -1
  56. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  57. package/dist/types/src/components/Toolbar/Toolbar.d.ts +10 -6
  58. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  59. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/index.d.ts +4 -3
  61. package/dist/types/src/components/index.d.ts.map +1 -1
  62. package/dist/types/src/hooks/useVisualViewport.d.ts +2 -2
  63. package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
  64. package/dist/types/src/playground/Custom.stories.d.ts.map +1 -1
  65. package/dist/types/src/testing/decorators/index.d.ts +2 -1
  66. package/dist/types/src/testing/decorators/index.d.ts.map +1 -1
  67. package/dist/types/src/testing/decorators/withLayout.d.ts +15 -0
  68. package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -0
  69. package/dist/types/src/testing/decorators/{withSurfaceVariantsLayout.d.ts → withLayoutVariants.d.ts} +2 -2
  70. package/dist/types/src/testing/decorators/withLayoutVariants.d.ts.map +1 -0
  71. package/dist/types/src/util/domino.d.ts +1 -1
  72. package/dist/types/src/util/domino.d.ts.map +1 -1
  73. package/dist/types/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +18 -15
  75. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  76. package/src/components/{Buttons → Button}/Button.stories.tsx +2 -2
  77. package/src/components/{Buttons → Button}/IconButton.tsx +12 -11
  78. package/src/components/{Buttons → Button}/Toggle.stories.tsx +5 -4
  79. package/src/components/Clipboard/CopyButton.tsx +1 -1
  80. package/src/components/{Dialogs → Dialog}/AlertDialog.stories.tsx +1 -1
  81. package/src/components/{Dialogs → Dialog}/Dialog.stories.tsx +1 -1
  82. package/src/components/Icon/Icon.stories.tsx +113 -0
  83. package/src/components/Icon/Icon.tsx +1 -1
  84. package/src/components/{Lists → List}/List.stories.tsx +1 -1
  85. package/src/components/{Lists → List}/ListDropIndicator.tsx +1 -1
  86. package/src/components/Main/Main.stories.tsx +1 -1
  87. package/src/components/Main/Main.tsx +13 -13
  88. package/src/components/Menus/DropdownMenu.stories.tsx +1 -1
  89. package/src/components/Menus/DropdownMenu.tsx +22 -3
  90. package/src/components/Popover/Popover.stories.tsx +1 -1
  91. package/src/components/Popover/Popover.tsx +20 -3
  92. package/src/components/ScrollArea/ScrollArea.stories.tsx +1 -1
  93. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +69 -0
  94. package/src/components/ScrollContainer/ScrollContainer.tsx +231 -0
  95. package/src/components/ScrollContainer/index.ts +5 -0
  96. package/src/components/Select/Select.stories.tsx +2 -2
  97. package/src/components/Select/Select.tsx +4 -4
  98. package/src/components/Toast/Toast.stories.tsx +1 -1
  99. package/src/components/Toolbar/Toolbar.stories.tsx +2 -4
  100. package/src/components/Toolbar/Toolbar.tsx +17 -6
  101. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  102. package/src/components/index.ts +4 -3
  103. package/src/hooks/useSafeArea.ts +2 -2
  104. package/src/hooks/useVisualViewport.ts +3 -3
  105. package/src/playground/Controls.stories.tsx +2 -2
  106. package/src/playground/Custom.stories.tsx +6 -8
  107. package/src/testing/decorators/index.ts +2 -1
  108. package/src/testing/decorators/withLayout.tsx +56 -0
  109. package/src/testing/decorators/{withSurfaceVariantsLayout.tsx → withLayoutVariants.tsx} +2 -2
  110. package/src/util/domino.ts +6 -4
  111. package/dist/lib/browser/chunk-B7HPXBP2.mjs.map +0 -7
  112. package/dist/lib/node-esm/chunk-6JCSY5Y7.mjs.map +0 -7
  113. package/dist/types/src/components/Buttons/Button.d.ts.map +0 -1
  114. package/dist/types/src/components/Buttons/Button.stories.d.ts.map +0 -1
  115. package/dist/types/src/components/Buttons/IconButton.d.ts.map +0 -1
  116. package/dist/types/src/components/Buttons/IconButton.stories.d.ts.map +0 -1
  117. package/dist/types/src/components/Buttons/Toggle.d.ts.map +0 -1
  118. package/dist/types/src/components/Buttons/Toggle.stories.d.ts +0 -13
  119. package/dist/types/src/components/Buttons/Toggle.stories.d.ts.map +0 -1
  120. package/dist/types/src/components/Buttons/ToggleGroup.d.ts.map +0 -1
  121. package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts.map +0 -1
  122. package/dist/types/src/components/Buttons/index.d.ts.map +0 -1
  123. package/dist/types/src/components/Dialogs/AlertDialog.d.ts.map +0 -1
  124. package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts.map +0 -1
  125. package/dist/types/src/components/Dialogs/Dialog.d.ts.map +0 -1
  126. package/dist/types/src/components/Dialogs/Dialog.stories.d.ts.map +0 -1
  127. package/dist/types/src/components/Dialogs/index.d.ts.map +0 -1
  128. package/dist/types/src/components/Lists/List.d.ts.map +0 -1
  129. package/dist/types/src/components/Lists/List.stories.d.ts.map +0 -1
  130. package/dist/types/src/components/Lists/ListDropIndicator.d.ts.map +0 -1
  131. package/dist/types/src/components/Lists/Tree.d.ts.map +0 -1
  132. package/dist/types/src/components/Lists/Tree.stories.d.ts.map +0 -1
  133. package/dist/types/src/components/Lists/TreeDropIndicator.d.ts.map +0 -1
  134. package/dist/types/src/components/Lists/Treegrid.d.ts.map +0 -1
  135. package/dist/types/src/components/Lists/Treegrid.stories.d.ts.map +0 -1
  136. package/dist/types/src/components/Lists/index.d.ts.map +0 -1
  137. package/dist/types/src/testing/decorators/withSurfaceVariantsLayout.d.ts.map +0 -1
  138. /package/dist/types/src/components/{Buttons → Button}/Button.d.ts +0 -0
  139. /package/dist/types/src/components/{Buttons → Button}/Button.stories.d.ts +0 -0
  140. /package/dist/types/src/components/{Buttons → Button}/IconButton.stories.d.ts +0 -0
  141. /package/dist/types/src/components/{Buttons → Button}/Toggle.d.ts +0 -0
  142. /package/dist/types/src/components/{Buttons → Button}/ToggleGroup.d.ts +0 -0
  143. /package/dist/types/src/components/{Buttons → Button}/ToggleGroup.stories.d.ts +0 -0
  144. /package/dist/types/src/components/{Buttons → Button}/index.d.ts +0 -0
  145. /package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.d.ts +0 -0
  146. /package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.stories.d.ts +0 -0
  147. /package/dist/types/src/components/{Dialogs → Dialog}/Dialog.d.ts +0 -0
  148. /package/dist/types/src/components/{Dialogs → Dialog}/Dialog.stories.d.ts +0 -0
  149. /package/dist/types/src/components/{Dialogs → Dialog}/index.d.ts +0 -0
  150. /package/dist/types/src/components/{Lists → List}/List.d.ts +0 -0
  151. /package/dist/types/src/components/{Lists → List}/List.stories.d.ts +0 -0
  152. /package/dist/types/src/components/{Lists → List}/ListDropIndicator.d.ts +0 -0
  153. /package/dist/types/src/components/{Lists → List}/Tree.d.ts +0 -0
  154. /package/dist/types/src/components/{Lists → List}/Tree.stories.d.ts +0 -0
  155. /package/dist/types/src/components/{Lists → List}/TreeDropIndicator.d.ts +0 -0
  156. /package/dist/types/src/components/{Lists → List}/Treegrid.d.ts +0 -0
  157. /package/dist/types/src/components/{Lists → List}/Treegrid.stories.d.ts +0 -0
  158. /package/dist/types/src/components/{Lists → List}/index.d.ts +0 -0
  159. /package/src/components/{Buttons → Button}/Button.tsx +0 -0
  160. /package/src/components/{Buttons → Button}/IconButton.stories.tsx +0 -0
  161. /package/src/components/{Buttons → Button}/Toggle.tsx +0 -0
  162. /package/src/components/{Buttons → Button}/ToggleGroup.stories.tsx +0 -0
  163. /package/src/components/{Buttons → Button}/ToggleGroup.tsx +0 -0
  164. /package/src/components/{Buttons → Button}/index.ts +0 -0
  165. /package/src/components/{Dialogs → Dialog}/AlertDialog.tsx +0 -0
  166. /package/src/components/{Dialogs → Dialog}/Dialog.tsx +0 -0
  167. /package/src/components/{Dialogs → Dialog}/index.ts +0 -0
  168. /package/src/components/{Lists → List}/List.tsx +0 -0
  169. /package/src/components/{Lists → List}/Tree.stories.tsx +0 -0
  170. /package/src/components/{Lists → List}/Tree.tsx +0 -0
  171. /package/src/components/{Lists → List}/TreeDropIndicator.tsx +0 -0
  172. /package/src/components/{Lists → List}/Treegrid.stories.tsx +0 -0
  173. /package/src/components/{Lists → List}/Treegrid.tsx +0 -0
  174. /package/src/components/{Lists → List}/index.ts +0 -0
@@ -0,0 +1,231 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useState } from '@preact-signals/safe-react/react';
6
+ import { createContext } from '@radix-ui/react-context';
7
+ import React, {
8
+ type HTMLAttributes,
9
+ type PropsWithChildren,
10
+ forwardRef,
11
+ useCallback,
12
+ useEffect,
13
+ useImperativeHandle,
14
+ useMemo,
15
+ useRef,
16
+ } from 'react';
17
+
18
+ // TODO(burdon): Move these deps to @dxos/dom-util.
19
+ import { addEventListener, combine } from '@dxos/async';
20
+ import { invariant } from '@dxos/invariant';
21
+ import { useForwardedRef } from '@dxos/react-hooks';
22
+ import { mx } from '@dxos/react-ui-theme';
23
+
24
+ import { type ThemedClassName } from '../../util';
25
+ import { IconButton } from '../Button';
26
+
27
+ const isBottom = (el: HTMLElement | null) => {
28
+ return !!(el && el.scrollHeight - el.scrollTop === el.clientHeight);
29
+ };
30
+
31
+ export interface ScrollController {
32
+ viewport: HTMLDivElement | null;
33
+ scrollToTop: (behavior?: ScrollBehavior) => void;
34
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
35
+ }
36
+
37
+ type ScrollContainerContextValue = {
38
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
39
+ controller?: ScrollController;
40
+ pinned?: boolean;
41
+ };
42
+
43
+ const [ScrollContainerProvider, useScrollContainerContext] =
44
+ createContext<ScrollContainerContextValue>('ScrollContainer');
45
+
46
+ //
47
+ // Root
48
+ //
49
+
50
+ type RootProps = ThemedClassName<
51
+ PropsWithChildren<{
52
+ pin?: boolean;
53
+ fade?: boolean;
54
+ }>
55
+ >;
56
+
57
+ /**
58
+ * Scroll container that automatically scrolls to the bottom when new content is added.
59
+ */
60
+ const Root = forwardRef<ScrollController, RootProps>(({ children, classNames, pin, fade }, forwardedRef) => {
61
+ const scrollerRef = useRef<HTMLDivElement>(null);
62
+ const autoScrollRef = useRef(false);
63
+ const [overflow, setOverflow] = useState(false);
64
+ const [pinned, setPinned] = useState(pin);
65
+
66
+ const timeoutRef = useRef<NodeJS.Timeout>(undefined);
67
+ const scrollToBottom = useCallback((behavior: ScrollBehavior = 'instant') => {
68
+ if (scrollerRef.current) {
69
+ // Temporarily hide scrollbar to prevent flicker.
70
+ autoScrollRef.current = true;
71
+ scrollerRef.current.classList.add('scrollbar-none');
72
+ scrollerRef.current.scrollTo({
73
+ top: scrollerRef.current.scrollHeight,
74
+ behavior,
75
+ });
76
+
77
+ clearTimeout(timeoutRef.current);
78
+ if (behavior !== 'instant') {
79
+ timeoutRef.current = setTimeout(() => {
80
+ scrollerRef.current?.classList.remove('scrollbar-none');
81
+ autoScrollRef.current = false;
82
+ }, 500);
83
+ }
84
+ setPinned(true);
85
+ }
86
+ }, []);
87
+
88
+ const controller = useMemo(
89
+ () => ({
90
+ viewport: scrollerRef.current,
91
+ scrollToTop: () => {
92
+ invariant(scrollerRef.current);
93
+ scrollerRef.current.scrollTo({ top: 0, behavior: 'smooth' });
94
+ setPinned(false);
95
+ },
96
+ scrollToBottom: () => {
97
+ scrollToBottom('smooth');
98
+ },
99
+ }),
100
+ [scrollToBottom, scrollerRef.current],
101
+ );
102
+
103
+ // Scroll controller imperative ref.
104
+ useImperativeHandle(forwardedRef, () => controller, [controller]);
105
+
106
+ // Listen for scroll events.
107
+ useEffect(() => {
108
+ if (!scrollerRef.current) {
109
+ return;
110
+ }
111
+
112
+ return combine(
113
+ // Check if user scrolls.
114
+ addEventListener(scrollerRef.current, 'wheel', () => {
115
+ setPinned(isBottom(scrollerRef.current));
116
+ }),
117
+ // Check if scrolls.
118
+ addEventListener(scrollerRef.current, 'scroll', () => {
119
+ setOverflow((scrollerRef.current?.scrollTop ?? 0) > 0);
120
+ }),
121
+ );
122
+ }, []);
123
+
124
+ return (
125
+ <ScrollContainerProvider pinned={pinned} controller={controller} scrollToBottom={scrollToBottom}>
126
+ <div className='relative grid flex-1 min-bs-0 overflow-hidden'>
127
+ {fade && (
128
+ <div
129
+ role='none'
130
+ data-visible={overflow}
131
+ className={mx(
132
+ // NOTE: Gradients may not be visible with dark reader extensions.
133
+ 'z-10 absolute block-start-0 inset-inline-0 bs-24 is-full',
134
+ 'opacity-0 duration-200 transition-opacity data-[visible="true"]:opacity-100',
135
+ 'bg-gradient-to-b from-[--surface-bg] to-transparent pointer-events-none',
136
+ )}
137
+ />
138
+ )}
139
+ <div className={mx('flex flex-col min-bs-0 overflow-y-auto scrollbar-thin', classNames)} ref={scrollerRef}>
140
+ {children}
141
+ </div>
142
+ </div>
143
+ </ScrollContainerProvider>
144
+ );
145
+ });
146
+
147
+ Root.displayName = 'ScrollContainer.Root';
148
+
149
+ //
150
+ // Viewport
151
+ //
152
+
153
+ type ViewportProps = ThemedClassName<PropsWithChildren<Omit<HTMLAttributes<HTMLDivElement>, 'className'>>>;
154
+
155
+ const Viewport = forwardRef<HTMLDivElement, ViewportProps>(({ classNames, children, ...props }, forwardedRef) => {
156
+ const contentRef = useForwardedRef(forwardedRef);
157
+ const { pinned, scrollToBottom } = useScrollContainerContext(Viewport.displayName!);
158
+
159
+ useEffect(() => {
160
+ if (!pinned || !contentRef.current) {
161
+ return;
162
+ }
163
+
164
+ // Setup resize observer to detect content changes.
165
+ const resizeObserver = new ResizeObserver(() => scrollToBottom());
166
+ scrollToBottom('instant');
167
+
168
+ resizeObserver.observe(contentRef.current);
169
+ return () => {
170
+ resizeObserver.disconnect();
171
+ };
172
+ }, [pinned, scrollToBottom]);
173
+
174
+ return (
175
+ <div className={mx('is-full', classNames)} {...props} ref={contentRef}>
176
+ {children}
177
+ </div>
178
+ );
179
+ });
180
+
181
+ Viewport.displayName = 'ScrollContainer.Viewport';
182
+
183
+ //
184
+ // ScrollDownButton
185
+ //
186
+
187
+ type ScrollDownButtonProps = ThemedClassName;
188
+
189
+ const ScrollDownButton = ({ classNames }: ScrollDownButtonProps) => {
190
+ const { pinned, scrollToBottom } = useScrollContainerContext(ScrollDownButton.displayName!);
191
+
192
+ return (
193
+ <div
194
+ role='none'
195
+ className={mx(
196
+ 'absolute bottom-2 right-4 opacity-100 transition-opacity duration-300',
197
+ pinned && 'opacity-0',
198
+ classNames,
199
+ )}
200
+ >
201
+ <IconButton
202
+ variant='primary'
203
+ icon='ph--arrow-down--regular'
204
+ iconOnly
205
+ size={4}
206
+ label='Scroll down'
207
+ onClick={() => scrollToBottom()}
208
+ />
209
+ </div>
210
+ );
211
+ };
212
+
213
+ ScrollDownButton.displayName = 'ScrollContainer.ScrollDownButton';
214
+
215
+ //
216
+ // ScrollContainer
217
+ //
218
+
219
+ export { useScrollContainerContext };
220
+
221
+ export const ScrollContainer = {
222
+ Root,
223
+ Viewport,
224
+ ScrollDownButton,
225
+ };
226
+
227
+ export type {
228
+ RootProps as ScrollContainerRootProps,
229
+ ViewportProps as ScrollContainerViewportProps,
230
+ ScrollDownButtonProps as ScrollContainerScrollDownButtonProps,
231
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './ScrollContainer';
@@ -8,7 +8,7 @@ import React, { useState } from 'react';
8
8
  import { faker } from '@dxos/random';
9
9
 
10
10
  import { withTheme } from '../../testing';
11
- import { withSurfaceVariantsLayout } from '../../testing';
11
+ import { withLayoutVariants } from '../../testing';
12
12
 
13
13
  import { Select } from './Select';
14
14
 
@@ -44,7 +44,7 @@ const DefaultStory = ({ items = [] }: StoryProps) => {
44
44
  const meta = {
45
45
  title: 'ui/react-ui-core/Select',
46
46
  render: DefaultStory,
47
- decorators: [withTheme, withSurfaceVariantsLayout()],
47
+ decorators: [withTheme, withLayoutVariants()],
48
48
  } satisfies Meta<typeof DefaultStory>;
49
49
 
50
50
  export default meta;
@@ -8,7 +8,7 @@ import React, { forwardRef } from 'react';
8
8
  import { useElevationContext, useThemeContext } from '../../hooks';
9
9
  import { useSafeCollisionPadding } from '../../hooks/useSafeCollisionPadding';
10
10
  import { type ThemedClassName } from '../../util';
11
- import { Button, type ButtonProps } from '../Buttons';
11
+ import { Button, type ButtonProps } from '../Button';
12
12
  import { Icon } from '../Icon';
13
13
 
14
14
  type SelectRootProps = SelectPrimitive.SelectProps;
@@ -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='w-1 flex-1' />
42
+ <span className='is-1 flex-1' />
43
43
  <SelectPrimitive.Icon asChild>
44
44
  <Icon size={3} icon='ph--caret-down--bold' />
45
45
  </SelectPrimitive.Icon>
@@ -108,7 +108,7 @@ const SelectScrollDownButton = forwardRef<HTMLDivElement, SelectScrollDownButton
108
108
  type SelectViewportProps = ThemedClassName<SelectPrimitive.SelectViewportProps>;
109
109
 
110
110
  const SelectViewport = forwardRef<HTMLDivElement, SelectViewportProps>(
111
- ({ classNames, asChild, children, ...props }, forwardedRef) => {
111
+ ({ classNames, children, ...props }, forwardedRef) => {
112
112
  const { tx } = useThemeContext();
113
113
  return (
114
114
  <SelectPrimitive.SelectViewport
@@ -158,7 +158,7 @@ const SelectOption = forwardRef<HTMLDivElement, SelectItemProps>(({ children, cl
158
158
  return (
159
159
  <SelectPrimitive.Item {...props} className={tx('select.item', 'option', {}, classNames)} ref={forwardedRef}>
160
160
  <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
161
- <span className='grow w-1' />
161
+ <span className='grow is-1' />
162
162
  {/* <SelectPrimitive.ItemIndicator className={tx('select.itemIndicator', 'option__indicator', {})}> */}
163
163
  <Icon icon='ph--check--regular' />
164
164
  {/* </SelectPrimitive.ItemIndicator> */}
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { type ReactNode, useState } from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
 
11
11
  import { Toast } from './Toast';
12
12
 
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Toggle } from '../Buttons';
9
+ import { Toggle } from '../Button';
10
10
  import { Icon } from '../Icon';
11
11
  import { Select } from '../Select';
12
12
 
@@ -63,9 +63,7 @@ const DefaultStory = (props: StorybookToolbarProps) => {
63
63
  </Toolbar.Button>
64
64
  <Toolbar.Separator />
65
65
  <Toolbar.Button>Test</Toolbar.Button>
66
- <Toolbar.Button>
67
- <Icon icon='ph--arrow-clockwise--regular' />
68
- </Toolbar.Button>
66
+ <Toolbar.IconButton icon='ph--arrow-clockwise--regular' label='Refresh' iconOnly />
69
67
  </Toolbar.Root>
70
68
  );
71
69
  };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type { ToggleGroupItemProps as ToggleGroupItemPrimitiveProps } from '@radix-ui/react-toggle-group';
6
6
  import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
7
- import React, { forwardRef } from 'react';
7
+ import React, { Fragment, forwardRef } from 'react';
8
8
 
9
9
  import { useThemeContext } from '../../hooks';
10
10
  import { type ThemedClassName } from '../../util';
@@ -18,23 +18,34 @@ import {
18
18
  Toggle,
19
19
  type ToggleGroupItemProps,
20
20
  type ToggleProps,
21
- } from '../Buttons';
21
+ } from '../Button';
22
22
  import { Link, type LinkProps } from '../Link';
23
23
  import { Separator, type SeparatorProps } from '../Separator';
24
24
 
25
- type ToolbarRootProps = ThemedClassName<ToolbarPrimitive.ToolbarProps> & { layoutManaged?: boolean };
25
+ type ToolbarRootProps = ThemedClassName<
26
+ ToolbarPrimitive.ToolbarProps & {
27
+ textBlockWidth?: boolean;
28
+ layoutManaged?: boolean;
29
+ disabled?: boolean;
30
+ }
31
+ >;
26
32
 
27
33
  const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
28
- ({ classNames, children, layoutManaged, ...props }, forwardedRef) => {
34
+ ({ classNames, children, layoutManaged, textBlockWidth: textBlockWidthParam, disabled, ...props }, forwardedRef) => {
29
35
  const { tx } = useThemeContext();
36
+ const InnerRoot = textBlockWidthParam ? 'div' : Fragment;
37
+ const innerRootProps = textBlockWidthParam
38
+ ? { role: 'none', className: tx('toolbar.inner', 'toolbar', { layoutManaged }, classNames) }
39
+ : {};
40
+
30
41
  return (
31
42
  <ToolbarPrimitive.Root
32
43
  {...props}
33
44
  data-arrow-keys={props.orientation === 'vertical' ? 'up down' : 'left right'}
34
- className={tx('toolbar.root', 'toolbar', { layoutManaged }, classNames)}
45
+ className={tx('toolbar.root', 'toolbar', { layoutManaged, disabled }, classNames)}
35
46
  ref={forwardedRef}
36
47
  >
37
- {children}
48
+ <InnerRoot {...innerRootProps}>{children}</InnerRoot>
38
49
  </ToolbarPrimitive.Root>
39
50
  );
40
51
  },
@@ -8,7 +8,7 @@ import React from 'react';
8
8
  import { faker } from '@dxos/random';
9
9
 
10
10
  import { withTheme } from '../../testing';
11
- import { Button } from '../Buttons';
11
+ import { Button } from '../Button';
12
12
 
13
13
  import { Tooltip } from './Tooltip';
14
14
 
@@ -5,19 +5,20 @@
5
5
  export * from './AnchoredOverflow';
6
6
  export * from './Avatars';
7
7
  export * from './Breadcrumb';
8
- export * from './Buttons';
8
+ export * from './Button';
9
9
  export * from './Clipboard';
10
- export * from './Dialogs';
10
+ export * from './Dialog';
11
11
  export * from './Icon';
12
12
  export * from './Input';
13
13
  export * from './Link';
14
- export * from './Lists';
14
+ export * from './List';
15
15
  export * from './Main';
16
16
  export * from './Menus';
17
17
  export * from './Message';
18
18
  export * from './Popover';
19
19
  export * from './Status';
20
20
  export * from './ScrollArea';
21
+ export * from './ScrollContainer';
21
22
  export * from './Select';
22
23
  export * from './Separator';
23
24
  export * from './Tag';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useCallback, useState } from 'react';
6
6
 
7
- import { useResize } from '@dxos/react-hooks';
7
+ import { useViewportResize } from '@dxos/react-hooks';
8
8
 
9
9
  export type SafeAreaPadding = Record<'top' | 'right' | 'bottom' | 'left', number>;
10
10
 
@@ -21,6 +21,6 @@ export const useSafeArea = (): SafeAreaPadding => {
21
21
  });
22
22
  }, []);
23
23
 
24
- useResize(handleResize);
24
+ useViewportResize(handleResize);
25
25
  return padding;
26
26
  };
@@ -4,9 +4,9 @@
4
4
 
5
5
  import { useCallback, useState } from 'react';
6
6
 
7
- import { useResize } from '@dxos/react-hooks';
7
+ import { useViewportResize } from '@dxos/react-hooks';
8
8
 
9
- export const useVisualViewport = (deps?: Parameters<typeof useResize>[1]) => {
9
+ export const useVisualViewport = (deps?: Parameters<typeof useViewportResize>[1]) => {
10
10
  const [width, setWidth] = useState<number | null>(null);
11
11
  const [height, setHeight] = useState<number | null>(null);
12
12
 
@@ -17,7 +17,7 @@ export const useVisualViewport = (deps?: Parameters<typeof useResize>[1]) => {
17
17
  }
18
18
  }, []);
19
19
 
20
- useResize(handleResize, deps);
20
+ useViewportResize(handleResize, deps);
21
21
 
22
22
  return { width, height };
23
23
  };
@@ -7,7 +7,7 @@ import React, { useState } from 'react';
7
7
 
8
8
  import { Icon, Input, Select, Toggle, Toolbar } from '../components';
9
9
  import { withTheme } from '../testing';
10
- import { withSurfaceVariantsLayout } from '../testing';
10
+ import { withLayoutVariants } from '../testing';
11
11
 
12
12
  const DefaultStory = () => {
13
13
  const [checked, setChecked] = useState<boolean>(false);
@@ -84,7 +84,7 @@ const DefaultStory = () => {
84
84
  const meta = {
85
85
  title: 'ui/react-ui-core/Playground/Controls',
86
86
  render: DefaultStory,
87
- decorators: [withTheme, withSurfaceVariantsLayout()],
87
+ decorators: [withTheme, withLayoutVariants()],
88
88
  } satisfies Meta<typeof Icon>;
89
89
 
90
90
  export default meta;
@@ -30,7 +30,7 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
30
30
  iconOnly
31
31
  size={7}
32
32
  density='coarse'
33
- classNames='px-1.5'
33
+ classNames='pli-1.5'
34
34
  />
35
35
  </div>
36
36
  </div>
@@ -43,7 +43,7 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
43
43
  </Button>
44
44
  </div>
45
45
  <div className='flex justify-center'>
46
- <IconButton {...args} label='Test' icon='ph--atom--regular' size={5} density='fine' classNames='px-2' />
46
+ <IconButton {...args} label='Test' icon='ph--atom--regular' density='fine' classNames='pli-2' />
47
47
  </div>
48
48
  <div className='flex justify-center'>
49
49
  <IconButton
@@ -51,9 +51,8 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
51
51
  label='Test'
52
52
  icon='ph--atom--regular'
53
53
  iconOnly
54
- size={5}
55
54
  density='fine'
56
- classNames='py-1 px-1.5'
55
+ classNames='plb-1 pli-1.5'
57
56
  />
58
57
  </div>
59
58
  </div>
@@ -61,7 +60,7 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
61
60
  {/* Small */}
62
61
  <div className='grid grid-cols-3 gap-4'>
63
62
  <div className='flex justify-center'>
64
- <Button {...args} density='fine' classNames={'!h-[24px] !text-[14px] p-0 px-1.5 min-bs-0'}>
63
+ <Button {...args} density='fine' classNames={'!h-[24px] !text-[14px] p-0 pli-1.5 min-bs-0'}>
65
64
  {children}
66
65
  </Button>
67
66
  </div>
@@ -90,18 +89,17 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
90
89
 
91
90
  {/* TODO(burdon): Full variant with max width. */}
92
91
  <div className='flex justify-center'>
93
- <Button classNames='w-full max-w-[15rem] rounded' variant='default'>
92
+ <Button classNames='is-full max-is-[15rem] rounded' variant='default'>
94
93
  Test
95
94
  </Button>
96
95
  </div>
97
96
  <div className='flex justify-center'>
98
97
  {/* TODO(burdon): Option to have button on RHS. Default size for icon should be 5 for this (medium) density. */}
99
98
  <IconButton
100
- classNames='w-full max-w-[15rem] rounded'
99
+ classNames='is-full max-is-[15rem] rounded'
101
100
  variant='primary'
102
101
  icon='ph--arrows-clockwise--regular'
103
102
  label='Test'
104
- size={5}
105
103
  />
106
104
  </div>
107
105
  </div>
@@ -2,5 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './withLayout';
6
+ export * from './withLayoutVariants';
5
7
  export * from './withTheme';
6
- export * from './withSurfaceVariantsLayout';
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Decorator } from '@storybook/react';
6
+ import React, { type FC, type PropsWithChildren } from 'react';
7
+
8
+ import { type ClassNameValue, type ThemedClassName } from '@dxos/react-ui';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+
11
+ export type ContainerProps = ThemedClassName<PropsWithChildren>;
12
+
13
+ export type ContainerType = 'default' | 'column';
14
+
15
+ export type WithLayoutProps =
16
+ | FC<ContainerProps>
17
+ | { classNames?: ClassNameValue; container?: ContainerType; scroll?: boolean };
18
+
19
+ /**
20
+ * Adds layout container.
21
+ */
22
+ export const withLayout =
23
+ (props: WithLayoutProps): Decorator =>
24
+ (Story) => {
25
+ if (typeof props === 'function') {
26
+ const Container = props;
27
+ return (
28
+ <Container>
29
+ <Story />
30
+ </Container>
31
+ );
32
+ }
33
+
34
+ const Container = layouts[(props as any).container as ContainerType] ?? layouts.default;
35
+ return (
36
+ <Container classNames={mx(props.classNames, props.scroll ? 'overflow-y-auto' : 'overflow-hidden')}>
37
+ <Story />
38
+ </Container>
39
+ );
40
+ };
41
+
42
+ const layouts: Record<ContainerType, FC<ContainerProps>> = {
43
+ default: ({ children, classNames }: ContainerProps) => (
44
+ <div role='none' className={mx(classNames)}>
45
+ {children}
46
+ </div>
47
+ ),
48
+
49
+ column: ({ children, classNames }: ContainerProps) => (
50
+ <div role='none' className='fixed inset-0 flex justify-center overflow-hidden bg-deckSurface'>
51
+ <div role='none' className={mx('flex flex-col is-[40rem] bg-baseSurface', classNames)}>
52
+ {children}
53
+ </div>
54
+ </div>
55
+ ),
56
+ };
@@ -23,7 +23,7 @@ const Panel = ({
23
23
  densities,
24
24
  className,
25
25
  }: { Story: ComponentType } & Config & { className?: string }) => (
26
- <div className={mx('flex flex-col h-full p-4 gap-4', className)}>
26
+ <div className={mx('flex flex-col bs-full p-4 gap-4', className)}>
27
27
  {elevations?.map(({ elevation, surface }) =>
28
28
  densities?.map((density) => (
29
29
  <Container key={`${elevation}--${density}`} surface={surface} elevation={elevation}>
@@ -34,7 +34,7 @@ const Panel = ({
34
34
  </div>
35
35
  );
36
36
 
37
- export const withSurfaceVariantsLayout = ({
37
+ export const withLayoutVariants = ({
38
38
  elevations = [
39
39
  { elevation: 'base', surface: 'bg-baseSurface' },
40
40
  { elevation: 'positioned', surface: 'bg-cardSurface' },
@@ -29,12 +29,14 @@ export class Domino<T extends HTMLElement> {
29
29
  this._el.dataset[key] = value;
30
30
  return this;
31
31
  }
32
- style(styles: Partial<CSSStyleDeclaration>): this {
33
- Object.assign(this._el.style, styles);
32
+ attributes(attr: Record<string, string | undefined>): this {
33
+ Object.entries(attr)
34
+ .filter(([_, value]) => value !== undefined)
35
+ .map(([key, value]) => this._el.setAttribute(key, value!));
34
36
  return this;
35
37
  }
36
- attr<K extends keyof T>(key: K, value: T[K]): this {
37
- (this._el as any)[key] = value;
38
+ style(styles: Partial<CSSStyleDeclaration>): this {
39
+ Object.assign(this._el.style, styles);
38
40
  return this;
39
41
  }
40
42
  children<C extends HTMLElement>(...children: Domino<C>[]): this {