@dxos/react-ui-mosaic 0.8.4-main.bc674ce → 0.8.4-main.c85a9c8dae

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 (186) hide show
  1. package/dist/lib/browser/chunk-7S4PIFKU.mjs +953 -0
  2. package/dist/lib/browser/chunk-7S4PIFKU.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +596 -70
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/playwright/index.mjs +138 -0
  7. package/dist/lib/browser/playwright/index.mjs.map +7 -0
  8. package/dist/lib/browser/testing/index.mjs +61 -232
  9. package/dist/lib/browser/testing/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/chunk-MK6KU66S.mjs +955 -0
  11. package/dist/lib/node-esm/chunk-MK6KU66S.mjs.map +7 -0
  12. package/dist/lib/node-esm/index.mjs +596 -70
  13. package/dist/lib/node-esm/index.mjs.map +4 -4
  14. package/dist/lib/node-esm/meta.json +1 -1
  15. package/dist/lib/node-esm/playwright/index.mjs +140 -0
  16. package/dist/lib/node-esm/playwright/index.mjs.map +7 -0
  17. package/dist/lib/node-esm/testing/index.mjs +61 -232
  18. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  19. package/dist/types/src/components/Board/Board.d.ts +81 -0
  20. package/dist/types/src/components/Board/Board.d.ts.map +1 -0
  21. package/dist/types/src/components/{Mosaic/Mosaic.stories.d.ts → Board/Board.stories.d.ts} +15 -1
  22. package/dist/types/src/components/Board/Board.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/Board/Column.d.ts +40 -0
  24. package/dist/types/src/components/Board/Column.d.ts.map +1 -0
  25. package/dist/types/src/components/Board/Item.d.ts +9 -0
  26. package/dist/types/src/components/Board/Item.d.ts.map +1 -0
  27. package/dist/types/src/components/Board/index.d.ts +2 -0
  28. package/dist/types/src/components/Board/index.d.ts.map +1 -0
  29. package/dist/types/src/components/Focus/Focus.d.ts +5 -4
  30. package/dist/types/src/components/Focus/Focus.d.ts.map +1 -1
  31. package/dist/types/src/components/Focus/index.d.ts +0 -1
  32. package/dist/types/src/components/Focus/index.d.ts.map +1 -1
  33. package/dist/types/src/components/Mosaic/Container.d.ts +42 -0
  34. package/dist/types/src/components/Mosaic/Container.d.ts.map +1 -0
  35. package/dist/types/src/components/Mosaic/Mosaic.d.ts +35 -118
  36. package/dist/types/src/components/Mosaic/Mosaic.d.ts.map +1 -1
  37. package/dist/types/src/components/Mosaic/Placeholder.d.ts +21 -0
  38. package/dist/types/src/components/Mosaic/Placeholder.d.ts.map +1 -0
  39. package/dist/types/src/components/Mosaic/Root.d.ts +36 -0
  40. package/dist/types/src/components/Mosaic/Root.d.ts.map +1 -0
  41. package/dist/types/src/components/Mosaic/Stack.d.ts +23 -0
  42. package/dist/types/src/components/Mosaic/Stack.d.ts.map +1 -0
  43. package/dist/types/src/components/{Stack → Mosaic}/Stack.stories.d.ts +2 -2
  44. package/dist/types/src/components/Mosaic/Stack.stories.d.ts.map +1 -0
  45. package/dist/types/src/components/Mosaic/Tile.d.ts +48 -0
  46. package/dist/types/src/components/Mosaic/Tile.d.ts.map +1 -0
  47. package/dist/types/src/components/Mosaic/styles.d.ts.map +1 -1
  48. package/dist/types/src/components/Mosaic/types.d.ts +9 -2
  49. package/dist/types/src/components/Mosaic/types.d.ts.map +1 -1
  50. package/dist/types/src/components/index.d.ts +1 -4
  51. package/dist/types/src/components/index.d.ts.map +1 -1
  52. package/dist/types/src/hooks/index.d.ts +1 -0
  53. package/dist/types/src/hooks/index.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts +15 -0
  55. package/dist/types/src/hooks/useDefaultColumnEventHandler.d.ts.map +1 -0
  56. package/dist/types/src/hooks/useEventHandlerAdapter.d.ts +1 -1
  57. package/dist/types/src/hooks/useEventHandlerAdapter.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useVisibleItems.d.ts +2 -2
  59. package/dist/types/src/hooks/useVisibleItems.d.ts.map +1 -1
  60. package/dist/types/src/playwright/board-manager.d.ts +45 -0
  61. package/dist/types/src/playwright/board-manager.d.ts.map +1 -0
  62. package/dist/types/src/playwright/index.d.ts +9 -0
  63. package/dist/types/src/playwright/index.d.ts.map +1 -0
  64. package/dist/types/src/playwright/playwright.config.d.ts +3 -0
  65. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
  66. package/dist/types/src/playwright/smoke.spec.d.ts +2 -0
  67. package/dist/types/src/playwright/smoke.spec.d.ts.map +1 -0
  68. package/dist/types/src/testing/CardContainer.d.ts +5 -4
  69. package/dist/types/src/testing/CardContainer.d.ts.map +1 -1
  70. package/dist/types/src/testing/DefaultStackTile.d.ts +2 -2
  71. package/dist/types/src/testing/DefaultStackTile.d.ts.map +1 -1
  72. package/dist/types/src/testing/index.d.ts +1 -1
  73. package/dist/types/src/testing/index.d.ts.map +1 -1
  74. package/dist/types/src/testing/types.d.ts +21 -0
  75. package/dist/types/src/testing/types.d.ts.map +1 -0
  76. package/dist/types/src/translations.d.ts +5 -1
  77. package/dist/types/src/translations.d.ts.map +1 -1
  78. package/dist/types/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +43 -28
  80. package/src/components/Board/Board.stories.tsx +273 -0
  81. package/src/components/Board/Board.tsx +191 -0
  82. package/src/components/Board/Column.tsx +300 -0
  83. package/src/components/Board/Item.tsx +116 -0
  84. package/src/components/{Layout → Board}/index.ts +1 -1
  85. package/src/components/Focus/Focus.tsx +26 -17
  86. package/src/components/Focus/index.ts +0 -2
  87. package/src/components/Mosaic/Container.tsx +289 -0
  88. package/src/components/Mosaic/Mosaic.tsx +35 -913
  89. package/src/components/Mosaic/Placeholder.tsx +133 -0
  90. package/src/components/Mosaic/Root.tsx +292 -0
  91. package/src/components/{Stack → Mosaic}/Stack.stories.tsx +30 -26
  92. package/src/components/Mosaic/Stack.tsx +246 -0
  93. package/src/components/Mosaic/Tile.tsx +242 -0
  94. package/src/components/Mosaic/extension.ts +1 -1
  95. package/src/components/Mosaic/styles.ts +9 -8
  96. package/src/components/Mosaic/types.ts +15 -3
  97. package/src/components/index.ts +1 -4
  98. package/src/hooks/index.ts +1 -0
  99. package/src/hooks/useContainerDebug.tsx +4 -2
  100. package/src/hooks/useDefaultColumnEventHandler.ts +35 -0
  101. package/src/hooks/useEventHandlerAdapter.ts +15 -6
  102. package/src/hooks/useVisibleItems.ts +7 -2
  103. package/src/playwright/board-manager.ts +154 -0
  104. package/src/playwright/index.ts +13 -0
  105. package/src/playwright/playwright.config.ts +17 -0
  106. package/src/playwright/smoke.spec.ts +138 -0
  107. package/src/testing/CardContainer.tsx +30 -18
  108. package/src/testing/DefaultStackTile.tsx +27 -16
  109. package/src/testing/index.ts +1 -1
  110. package/src/testing/types.ts +38 -0
  111. package/src/translations.ts +5 -1
  112. package/dist/lib/browser/chunk-UMBPEG4F.mjs +0 -1637
  113. package/dist/lib/browser/chunk-UMBPEG4F.mjs.map +0 -7
  114. package/dist/lib/browser/index.css +0 -13
  115. package/dist/lib/browser/index.css.map +0 -7
  116. package/dist/lib/browser/testing/index.css +0 -13
  117. package/dist/lib/browser/testing/index.css.map +0 -7
  118. package/dist/lib/node-esm/chunk-X6GV7SMK.mjs +0 -1639
  119. package/dist/lib/node-esm/chunk-X6GV7SMK.mjs.map +0 -7
  120. package/dist/lib/node-esm/index.css +0 -13
  121. package/dist/lib/node-esm/index.css.map +0 -7
  122. package/dist/lib/node-esm/testing/index.css +0 -13
  123. package/dist/lib/node-esm/testing/index.css.map +0 -7
  124. package/dist/types/src/components/Card/Card.d.ts +0 -121
  125. package/dist/types/src/components/Card/Card.d.ts.map +0 -1
  126. package/dist/types/src/components/Card/Card.stories.d.ts +0 -21
  127. package/dist/types/src/components/Card/Card.stories.d.ts.map +0 -1
  128. package/dist/types/src/components/Card/CardDragPreview.d.ts +0 -6
  129. package/dist/types/src/components/Card/CardDragPreview.d.ts.map +0 -1
  130. package/dist/types/src/components/Card/index.d.ts +0 -4
  131. package/dist/types/src/components/Card/index.d.ts.map +0 -1
  132. package/dist/types/src/components/Card/styles.d.ts +0 -13
  133. package/dist/types/src/components/Card/styles.d.ts.map +0 -1
  134. package/dist/types/src/components/Focus/styles.d.ts +0 -6
  135. package/dist/types/src/components/Focus/styles.d.ts.map +0 -1
  136. package/dist/types/src/components/Image/Image.d.ts +0 -14
  137. package/dist/types/src/components/Image/Image.d.ts.map +0 -1
  138. package/dist/types/src/components/Image/Image.stories.d.ts +0 -33
  139. package/dist/types/src/components/Image/Image.stories.d.ts.map +0 -1
  140. package/dist/types/src/components/Image/index.d.ts +0 -2
  141. package/dist/types/src/components/Image/index.d.ts.map +0 -1
  142. package/dist/types/src/components/Layout/Layout.d.ts +0 -33
  143. package/dist/types/src/components/Layout/Layout.d.ts.map +0 -1
  144. package/dist/types/src/components/Layout/Layout.stories.d.ts +0 -10
  145. package/dist/types/src/components/Layout/Layout.stories.d.ts.map +0 -1
  146. package/dist/types/src/components/Layout/index.d.ts +0 -2
  147. package/dist/types/src/components/Layout/index.d.ts.map +0 -1
  148. package/dist/types/src/components/Mosaic/Mosaic.stories.d.ts.map +0 -1
  149. package/dist/types/src/components/Scrollable/Scrollable.d.ts +0 -24
  150. package/dist/types/src/components/Scrollable/Scrollable.d.ts.map +0 -1
  151. package/dist/types/src/components/Scrollable/Scrollable.stories.d.ts +0 -10
  152. package/dist/types/src/components/Scrollable/Scrollable.stories.d.ts.map +0 -1
  153. package/dist/types/src/components/Scrollable/index.d.ts +0 -2
  154. package/dist/types/src/components/Scrollable/index.d.ts.map +0 -1
  155. package/dist/types/src/components/Stack/Stack.d.ts +0 -22
  156. package/dist/types/src/components/Stack/Stack.d.ts.map +0 -1
  157. package/dist/types/src/components/Stack/Stack.stories.d.ts.map +0 -1
  158. package/dist/types/src/components/Stack/index.d.ts +0 -2
  159. package/dist/types/src/components/Stack/index.d.ts.map +0 -1
  160. package/dist/types/src/testing/Board.d.ts +0 -45
  161. package/dist/types/src/testing/Board.d.ts.map +0 -1
  162. package/dist/types/src/testing/Slot.stories.d.ts +0 -14
  163. package/dist/types/src/testing/Slot.stories.d.ts.map +0 -1
  164. package/dist/types/src/testing/Virtualizer.stories.d.ts +0 -12
  165. package/dist/types/src/testing/Virtualizer.stories.d.ts.map +0 -1
  166. package/src/components/Card/Card.stories.tsx +0 -148
  167. package/src/components/Card/Card.tsx +0 -458
  168. package/src/components/Card/CardDragPreview.tsx +0 -23
  169. package/src/components/Card/index.ts +0 -8
  170. package/src/components/Card/styles.ts +0 -23
  171. package/src/components/Focus/styles.ts +0 -24
  172. package/src/components/Image/Image.stories.tsx +0 -86
  173. package/src/components/Image/Image.tsx +0 -223
  174. package/src/components/Image/index.ts +0 -5
  175. package/src/components/Layout/Layout.stories.tsx +0 -59
  176. package/src/components/Layout/Layout.tsx +0 -108
  177. package/src/components/Mosaic/Mosaic.stories.tsx +0 -113
  178. package/src/components/Scrollable/Scrollable.stories.tsx +0 -59
  179. package/src/components/Scrollable/Scrollable.tsx +0 -88
  180. package/src/components/Scrollable/index.ts +0 -5
  181. package/src/components/Scrollable/scrollable.css +0 -20
  182. package/src/components/Stack/Stack.tsx +0 -210
  183. package/src/components/Stack/index.ts +0 -5
  184. package/src/testing/Board.tsx +0 -278
  185. package/src/testing/Slot.stories.tsx +0 -101
  186. package/src/testing/Virtualizer.stories.tsx +0 -133
@@ -1,210 +0,0 @@
1
- //
2
- // Copyright 2026 DXOS.org
3
- //
4
-
5
- import { type ReactVirtualizerOptions, useVirtualizer } from '@tanstack/react-virtual';
6
- import React, { type FC, Fragment, type ReactElement, type Ref, forwardRef } from 'react';
7
-
8
- import { invariant } from '@dxos/invariant';
9
- import { type SlottableClassName } from '@dxos/react-ui';
10
- import { mx } from '@dxos/ui-theme';
11
-
12
- import { useVisibleItems } from '../../hooks';
13
- import {
14
- type Axis,
15
- type GetId,
16
- Mosaic,
17
- type MosaicTileProps,
18
- type MosiacPlaceholderProps,
19
- useMosaicContainer,
20
- } from '../Mosaic';
21
- import { mosaicStyles } from '../Mosaic';
22
-
23
- type StackTileComponent<TData = any> = FC<MosaicTileProps<TData>>;
24
-
25
- type StackProps<TData = any> = SlottableClassName<
26
- {
27
- Tile: StackTileComponent<TData>;
28
- getId: GetId<TData>;
29
- role?: string;
30
- axis?: Axis;
31
- items?: TData[];
32
- } & Pick<MosaicTileProps<TData>, 'draggable' | 'debug'>
33
- >;
34
-
35
- /**
36
- * Linear layout of Mosaic tiles.
37
- * NOTE: This is a low-level component and should be wrapped by a scrollable container.
38
- */
39
- const StackInner = forwardRef<HTMLDivElement, StackProps>(
40
- (
41
- { className, classNames, role = 'list', axis = 'vertical', draggable = true, items, getId, Tile, debug, ...props },
42
- forwardedRef,
43
- ) => {
44
- invariant(Tile);
45
- const { id, dragging } = useMosaicContainer(StackInner.displayName!);
46
- const visibleItems = useVisibleItems({ id, items, dragging: dragging?.source.data, getId });
47
-
48
- return (
49
- <div
50
- {...props}
51
- role={role}
52
- className={mx(
53
- 'flex',
54
- axis === 'horizontal' && 'bs-full [&>*]:shrink-0',
55
- axis === 'vertical' && 'flex-col',
56
- classNames,
57
- className,
58
- )}
59
- ref={forwardedRef}
60
- >
61
- <Placeholder axis={axis} location={0.5} />
62
- {visibleItems?.map((item, index) => (
63
- <Fragment key={getId(item)}>
64
- <Tile id={getId(item)} data={item} location={index + 1} draggable={draggable} debug={debug} />
65
- <Placeholder axis={axis} location={index + 1.5} />
66
- </Fragment>
67
- ))}
68
- </div>
69
- );
70
- },
71
- );
72
-
73
- StackInner.displayName = 'Stack';
74
-
75
- const Stack = StackInner as <TData = any>(props: StackProps<TData> & { ref?: Ref<HTMLDivElement> }) => ReactElement;
76
-
77
- //
78
- // VirtualStack
79
- //
80
-
81
- type VirtualStackProps<TData = any> = StackProps<TData> &
82
- Pick<
83
- ReactVirtualizerOptions<HTMLElement, HTMLDivElement>,
84
- 'estimateSize' | 'getScrollElement' | 'overscan' | 'onChange'
85
- >;
86
-
87
- const VirtualStackInner = forwardRef<HTMLDivElement, VirtualStackProps>(
88
- (
89
- {
90
- className,
91
- classNames,
92
- role = 'list',
93
- axis = 'vertical',
94
- items,
95
- getId,
96
- Tile,
97
- estimateSize,
98
- getScrollElement,
99
- overscan = 8,
100
- onChange,
101
- debug,
102
- ...props
103
- },
104
- forwardedRef,
105
- ) => {
106
- invariant(Tile);
107
- const { id, dragging } = useMosaicContainer(VirtualStackInner.displayName!);
108
- const visibleItems = useVisibleItems({ id, items, dragging: dragging?.source.data, getId });
109
- const virtualizer = useVirtualizer({
110
- count: visibleItems.length * 2 + 1,
111
- estimateSize,
112
- getScrollElement,
113
- overscan,
114
- onChange,
115
- });
116
-
117
- const virtualItems = virtualizer.getVirtualItems();
118
-
119
- return (
120
- <div
121
- {...props}
122
- role={role}
123
- className={mx(
124
- 'flex',
125
- axis === 'horizontal' && 'bs-full [&>*]:shrink-0',
126
- axis === 'vertical' && 'flex-col',
127
- classNames,
128
- className,
129
- )}
130
- style={{
131
- position: 'relative',
132
- ...(axis === 'vertical'
133
- ? {
134
- width: '100%',
135
- height: virtualizer.getTotalSize(),
136
- }
137
- : {
138
- width: virtualizer.getTotalSize(),
139
- height: '100%',
140
- }),
141
- }}
142
- ref={forwardedRef}
143
- >
144
- {virtualItems.map((virtualItem) => {
145
- const data = visibleItems![Math.floor(virtualItem.index / 2)];
146
- return (
147
- <div
148
- key={virtualItem.key}
149
- data-index={virtualItem.index}
150
- style={{
151
- position: 'absolute',
152
- top: 0,
153
- left: 0,
154
- ...(axis === 'vertical'
155
- ? {
156
- width: '100%',
157
- transform: `translateY(${virtualItem.start}px)`,
158
- }
159
- : {
160
- height: '100%',
161
- transform: `translateX(${virtualItem.start}px)`,
162
- }),
163
- }}
164
- ref={virtualizer.measureElement}
165
- >
166
- {virtualItem.index % 2 === 0 ? (
167
- <Placeholder axis={axis} location={Math.floor(virtualItem.index / 2) + 0.5} />
168
- ) : (
169
- <Tile id={getId(data)} data={data} location={Math.floor(virtualItem.index / 2) + 1} debug={debug} />
170
- )}
171
- </div>
172
- );
173
- })}
174
- </div>
175
- );
176
- },
177
- );
178
-
179
- VirtualStackInner.displayName = 'VirtualStackInner';
180
-
181
- const VirtualStack = VirtualStackInner as <TData = any>(
182
- props: VirtualStackProps<TData> & { ref?: Ref<HTMLDivElement> },
183
- ) => ReactElement;
184
-
185
- //
186
- // Placeholder
187
- //
188
-
189
- const Placeholder = (props: MosiacPlaceholderProps<number>) => {
190
- return (
191
- <Mosaic.Placeholder {...props} classNames={mosaicStyles.placeholder.root}>
192
- <div
193
- className={mx(
194
- 'flex bs-full bg-baseSurface border border-dashed border-separator rounded-sm',
195
- mosaicStyles.placeholder.content,
196
- )}
197
- />
198
- </Mosaic.Placeholder>
199
- );
200
- };
201
-
202
- Placeholder.displayName = 'Placeholder';
203
-
204
- //
205
- // Stack
206
- //
207
-
208
- export { Stack, VirtualStack };
209
-
210
- export type { StackTileComponent, StackProps, VirtualStackProps };
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- export * from './Stack';
@@ -1,278 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { useComposedRefs } from '@radix-ui/react-compose-refs';
6
- import * as Schema from 'effect/Schema';
7
- import React, { forwardRef, useMemo, useRef, useState } from 'react';
8
-
9
- import { Obj, Ref, Type } from '@dxos/echo';
10
- import { ObjectId } from '@dxos/keys';
11
- import { useObject } from '@dxos/react-client/echo';
12
- import { Tag, type ThemedClassName } from '@dxos/react-ui';
13
- import { Json } from '@dxos/react-ui-syntax-highlighter';
14
- import { getHashStyles, mx } from '@dxos/ui-theme';
15
-
16
- import {
17
- Card,
18
- type CardMenuProps,
19
- Focus,
20
- Mosaic,
21
- type MosaicTileProps,
22
- type MosiacPlaceholderProps,
23
- mosaicStyles,
24
- useMosaic,
25
- } from '../components';
26
- import { useContainerDebug, useEventHandlerAdapter } from '../hooks';
27
-
28
- //
29
- // Test Data
30
- //
31
-
32
- export const TestItem = Schema.Struct({
33
- name: Schema.String,
34
- description: Schema.optional(Schema.String),
35
- label: Schema.optional(Schema.String),
36
- }).pipe(
37
- Type.object({
38
- typename: 'example.com/type/Item',
39
- version: '0.1.0',
40
- }),
41
- );
42
-
43
- export interface TestItem extends Schema.Schema.Type<typeof TestItem> {}
44
-
45
- export const TestColumn = Schema.Struct({
46
- id: ObjectId,
47
- items: Schema.mutable(Schema.Array(Type.Ref(TestItem))),
48
- }).pipe(
49
- Type.object({
50
- typename: 'example.com/type/Column',
51
- version: '0.1.0',
52
- }),
53
- );
54
-
55
- export interface TestColumn extends Schema.Schema.Type<typeof TestColumn> {}
56
-
57
- //
58
- // Root
59
- //
60
-
61
- type RootProps = {
62
- id: string;
63
- columns: TestColumn[];
64
- debug?: boolean;
65
- };
66
-
67
- const Root = forwardRef<HTMLDivElement, RootProps>(({ id, columns, debug }, forwardedRef) => {
68
- const [DebugInfo, debugHandler] = useContainerDebug(debug);
69
- const [viewport, setViewport] = useState<HTMLElement | null>(null);
70
-
71
- const eventHandler = useEventHandlerAdapter({
72
- id,
73
- items: columns,
74
- getId: (data) => data.id,
75
- get: (data) => data,
76
- make: (object) => object,
77
- canDrop: ({ source }) => Obj.instanceOf(TestColumn, source.data),
78
- });
79
-
80
- return (
81
- <div
82
- role='none'
83
- className={mx('plb-2 grid bs-full is-full overflow-hidden', debug && 'grid-cols-[1fr_20rem] gap-2')}
84
- ref={forwardedRef}
85
- >
86
- <Focus.Group asChild axis='horizontal'>
87
- <Mosaic.Container
88
- asChild
89
- withFocus
90
- axis='horizontal'
91
- autoScroll={viewport}
92
- eventHandler={eventHandler}
93
- debug={debugHandler}
94
- >
95
- <Mosaic.Viewport axis='horizontal' padding viewportRef={setViewport}>
96
- <Mosaic.Stack axis='horizontal' items={columns} getId={(item) => item.id} Tile={Column} debug={debug} />
97
- </Mosaic.Viewport>
98
- </Mosaic.Container>
99
- </Focus.Group>
100
- <DebugInfo />
101
- </div>
102
- );
103
- });
104
-
105
- //
106
- // Column
107
- //
108
-
109
- type ColumnProps = Pick<MosaicTileProps<TestColumn>, 'classNames' | 'location' | 'data' | 'debug'>;
110
-
111
- const Column = forwardRef<HTMLDivElement, ColumnProps>(({ classNames, location, data, debug }, forwardedRef) => {
112
- const [column, updateColumn] = useObject(data);
113
- const [DebugInfo, debugHandler] = useContainerDebug(debug);
114
- const dragHandleRef = useRef<HTMLButtonElement>(null);
115
- const [viewport, setViewport] = useState<HTMLElement | null>(null);
116
- const eventHandler = useEventHandlerAdapter<Ref.Unknown>({
117
- id: data.id,
118
- items: column.items,
119
- getId: (data) => data.dxn.toString(),
120
- get: (data) => data.target!,
121
- make: (object) => Ref.make(object),
122
- canDrop: ({ source }) => Obj.instanceOf(TestItem, source.data.target),
123
- onChange: (mutator) => updateColumn((column) => mutator(column.items)),
124
- });
125
-
126
- // Context menu.
127
- const menuItems = useMemo<NonNullable<CardMenuProps<TestItem>['items']>>(
128
- () => [
129
- {
130
- label: 'Delete',
131
- onClick: (obj) => {
132
- updateColumn((column) => {
133
- const idx = column.items.findIndex((item) => item.target?.id === obj?.id);
134
- if (idx !== -1) {
135
- column.items.splice(idx, 1);
136
- }
137
- });
138
- },
139
- },
140
- ],
141
- [updateColumn],
142
- );
143
-
144
- return (
145
- <Mosaic.Tile asChild dragHandle={dragHandleRef.current} location={location} id={data.id} data={data} debug={debug}>
146
- <Focus.Group asChild>
147
- <div
148
- className={mx(
149
- 'grid bs-full card-default-width overflow-hidden bg-deckSurface',
150
- debug ? 'grid-rows-[min-content_1fr_20rem]' : 'grid-rows-[min-content_1fr_min-content]',
151
- classNames,
152
- )}
153
- ref={forwardedRef}
154
- >
155
- <Card.Toolbar>
156
- <Card.DragHandle ref={dragHandleRef} />
157
- <Card.Title>{column.id}</Card.Title>
158
- <Card.Menu items={[]} />
159
- </Card.Toolbar>
160
- <Card.Context value={{ menuItems }}>
161
- <Mosaic.Container
162
- asChild
163
- withFocus
164
- axis='vertical'
165
- autoScroll={viewport}
166
- eventHandler={eventHandler}
167
- debug={debugHandler}
168
- >
169
- <Mosaic.Viewport axis='vertical' padding viewportRef={setViewport}>
170
- <Mosaic.Stack axis='vertical' items={column.items} getId={(data) => data.dxn.toString()} Tile={Item} />
171
- </Mosaic.Viewport>
172
- </Mosaic.Container>
173
- </Card.Context>
174
- <div role='none'>
175
- <div className='grow flex p-1 justify-center text-xs'>{column.items.length}</div>
176
- <DebugInfo />
177
- </div>
178
- </div>
179
- </Focus.Group>
180
- </Mosaic.Tile>
181
- );
182
- });
183
-
184
- Column.displayName = 'Column';
185
-
186
- //
187
- // Item
188
- //
189
-
190
- type ItemProps = Pick<MosaicTileProps<Ref.Ref<TestItem>>, 'classNames' | 'location' | 'data' | 'debug'>;
191
-
192
- const Item = forwardRef<HTMLDivElement, ItemProps>(({ classNames, data: ref, location, debug }, forwardedRef) => {
193
- const rootRef = useRef<HTMLDivElement>(null);
194
- const composedRef = useComposedRefs<HTMLDivElement>(rootRef, forwardedRef);
195
- const dragHandleRef = useRef<HTMLButtonElement>(null);
196
- const object = ref.target;
197
- if (!object) {
198
- return null;
199
- }
200
-
201
- return (
202
- <Mosaic.Tile
203
- asChild
204
- dragHandle={dragHandleRef.current}
205
- id={ref.dxn.toString()}
206
- data={ref}
207
- location={location}
208
- debug={debug}
209
- >
210
- <Focus.Group asChild>
211
- <Card.Root classNames={classNames} onClick={() => rootRef.current?.focus()} ref={composedRef}>
212
- <Card.Toolbar>
213
- <Card.DragHandle ref={dragHandleRef} />
214
- <Card.Title>{object.name}</Card.Title>
215
- <Card.Menu context={object} />
216
- </Card.Toolbar>
217
- <Card.Row icon='ph--note--regular' classNames='text-description'>
218
- {object.description}
219
- </Card.Row>
220
- <Card.Row icon='ph--tag--regular'>
221
- {object.label && (
222
- <div role='none' className='flex shrink-0 gap-1 text-xs items-center'>
223
- <Tag palette={getHashStyles(object.label).hue}>{object.label}</Tag>
224
- </div>
225
- )}
226
- </Card.Row>
227
- </Card.Root>
228
- </Focus.Group>
229
- </Mosaic.Tile>
230
- );
231
- });
232
-
233
- Item.displayName = 'Item';
234
-
235
- //
236
- // Placeholder
237
- //
238
-
239
- const Placeholder = (props: MosiacPlaceholderProps<number>) => {
240
- return (
241
- <Mosaic.Placeholder {...props} classNames={mosaicStyles.placeholder.root}>
242
- <div
243
- className={mx(
244
- 'flex bs-full border border-dashed border-separator rounded-sm',
245
- mosaicStyles.placeholder.content,
246
- )}
247
- />
248
- </Mosaic.Placeholder>
249
- );
250
- };
251
-
252
- Placeholder.displayName = 'Placeholder';
253
-
254
- //
255
- // Debug
256
- //
257
-
258
- export const Debug = forwardRef<HTMLDivElement, ThemedClassName>(({ classNames }, forwardedRef) => {
259
- const { containers, dragging } = useMosaic(Debug.displayName!);
260
- const counter = useRef(0);
261
- return (
262
- <Json
263
- data={{ containers, dragging, count: counter.current++ }}
264
- classNames={mx('text-xs', classNames)}
265
- ref={forwardedRef}
266
- />
267
- );
268
- });
269
-
270
- Debug.displayName = 'Debug';
271
-
272
- //
273
- // Board
274
- //
275
-
276
- export const Board = { Root, Column, Item, Placeholder, Debug };
277
-
278
- export type { RootProps, ColumnProps, ItemProps };
@@ -1,101 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { Slot } from '@radix-ui/react-slot';
6
- import { type Meta, type StoryObj } from '@storybook/react-vite';
7
- import React, { type ReactNode, forwardRef } from 'react';
8
-
9
- import { withTheme } from '@dxos/react-ui/testing';
10
- import { mx } from '@dxos/ui-theme';
11
-
12
- // Outer primitive (like Tooltip.Trigger or Focus.Group).
13
- const Outer = forwardRef<HTMLDivElement, { children: ReactNode; className?: string; asChild?: boolean; role?: string }>(
14
- (props, ref) => {
15
- const { children, className, asChild, ...rest } = props;
16
- const Comp = asChild ? Slot : 'div';
17
- return (
18
- <Comp {...rest} className={mx('p-2', className)} data-outer='true' ref={ref}>
19
- {children}
20
- </Comp>
21
- );
22
- },
23
- );
24
-
25
- // Middle primitive (like Dialog.Trigger or Mosaic.Cell).
26
- const Middle = forwardRef<
27
- HTMLDivElement,
28
- { children: ReactNode; className?: string; asChild?: boolean; role?: string }
29
- >((props, ref) => {
30
- const { children, className, asChild, ...rest } = props;
31
- const Comp = asChild ? Slot : 'div';
32
- return (
33
- <Comp {...rest} className={mx('p-2', className)} data-middle='true' ref={ref}>
34
- {children}
35
- </Comp>
36
- );
37
- });
38
-
39
- // Leaf component (like MyButton or Card.Root).
40
- const Leaf = forwardRef<HTMLButtonElement, { children: ReactNode; className?: string; role?: string }>(
41
- ({ className, ...props }, ref) => {
42
- return (
43
- <button className={mx('p-2 outline-none border rounded', className)} {...props} ref={ref}>
44
- {props.children}
45
- </button>
46
- );
47
- },
48
- );
49
-
50
- // Test 1: Single asChild (should work).
51
- const TestSingle = (props: { className?: string; role?: string }) => (
52
- <Outer asChild {...props}>
53
- <Leaf>Single asChild</Leaf>
54
- </Outer>
55
- );
56
-
57
- // Test 2: Nested asChild (like Radix docs example).
58
- const TestNested = (props: { className?: string; role?: string }) => {
59
- return (
60
- <Outer asChild {...props}>
61
- <Middle asChild>
62
- <Leaf>Nested asChild</Leaf>
63
- </Middle>
64
- </Outer>
65
- );
66
- };
67
-
68
- // Test 3: Our actual pattern.
69
- const TestInner = (props: { className?: string; role?: string }) => (
70
- <Outer asChild {...props}>
71
- <Middle asChild>
72
- <Leaf>
73
- <div>Leaf</div>
74
- </Leaf>
75
- </Middle>
76
- </Outer>
77
- );
78
-
79
- const meta = {
80
- title: 'ui/react-ui-mosaic/Slot',
81
- decorators: [withTheme],
82
- parameters: {
83
- layout: 'centered',
84
- },
85
- } satisfies Meta;
86
-
87
- export default meta;
88
-
89
- type Story = StoryObj<typeof meta>;
90
-
91
- export const Single: Story = {
92
- render: () => <TestSingle role='listitem' className='border-red-500' />,
93
- };
94
-
95
- export const Nested: Story = {
96
- render: () => <TestNested role='listitem' className='border-green-500' />,
97
- };
98
-
99
- export const Inner: Story = {
100
- render: () => <TestInner role='listitem' className='border-blue-500' />,
101
- };