@dxos/react-ui-list 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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.
- package/dist/lib/browser/index.mjs +233 -194
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +233 -194
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Accordion/AccordionItem.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts +6 -4
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/ListItem.d.ts +8 -6
- package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
- package/dist/types/src/components/List/ListRoot.d.ts +2 -2
- package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.d.ts +6 -5
- package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeContext.d.ts +21 -10
- package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItem.d.ts +8 -0
- package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
- package/dist/types/src/components/Tree/index.d.ts +2 -0
- package/dist/types/src/components/Tree/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -18
- package/src/components/Accordion/Accordion.stories.tsx +6 -6
- package/src/components/Accordion/AccordionItem.tsx +1 -2
- package/src/components/List/List.stories.tsx +10 -10
- package/src/components/List/List.tsx +4 -9
- package/src/components/List/ListItem.tsx +58 -38
- package/src/components/List/ListRoot.tsx +3 -3
- package/src/components/List/testing.ts +4 -4
- package/src/components/Tree/Tree.stories.tsx +106 -31
- package/src/components/Tree/Tree.tsx +30 -40
- package/src/components/Tree/TreeContext.tsx +18 -9
- package/src/components/Tree/TreeItem.tsx +178 -103
- package/src/components/Tree/TreeItemHeading.tsx +3 -4
- package/src/components/Tree/TreeItemToggle.tsx +4 -4
- package/src/components/Tree/index.ts +2 -0
- package/src/components/Tree/testing.ts +5 -5
|
@@ -2,25 +2,37 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
6
|
-
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
7
5
|
import {
|
|
8
6
|
type Instruction,
|
|
9
7
|
type ItemMode,
|
|
10
8
|
attachInstruction,
|
|
11
9
|
extractInstruction,
|
|
12
10
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
11
|
+
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
12
|
+
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
13
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
13
14
|
import * as Schema from 'effect/Schema';
|
|
14
|
-
import React, {
|
|
15
|
+
import React, {
|
|
16
|
+
type FC,
|
|
17
|
+
type KeyboardEvent,
|
|
18
|
+
type MouseEvent,
|
|
19
|
+
memo,
|
|
20
|
+
useCallback,
|
|
21
|
+
useEffect,
|
|
22
|
+
useMemo,
|
|
23
|
+
useRef,
|
|
24
|
+
useState,
|
|
25
|
+
} from 'react';
|
|
15
26
|
|
|
16
27
|
import { invariant } from '@dxos/invariant';
|
|
17
|
-
import { TreeItem as NaturalTreeItem, Treegrid } from '@dxos/react-ui';
|
|
28
|
+
import { TreeItem as NaturalTreeItem, Treegrid, TREEGRID_PARENT_OF_SEPARATOR } from '@dxos/react-ui';
|
|
18
29
|
import {
|
|
19
30
|
ghostFocusWithin,
|
|
20
31
|
ghostHover,
|
|
21
32
|
hoverableControls,
|
|
22
33
|
hoverableFocusedKeyboardControls,
|
|
23
34
|
hoverableFocusedWithinControls,
|
|
35
|
+
mx,
|
|
24
36
|
} from '@dxos/ui-theme';
|
|
25
37
|
|
|
26
38
|
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
@@ -31,7 +43,7 @@ import { TreeItemToggle } from './TreeItemToggle';
|
|
|
31
43
|
const hoverableDescriptionIcons =
|
|
32
44
|
'[--icons-color:inherit] hover-hover:[--icons-color:var(--description-text)] hover-hover:hover:[--icons-color:inherit] focus-within:[--icons-color:inherit]';
|
|
33
45
|
|
|
34
|
-
type
|
|
46
|
+
type TreeItemDragState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
|
|
35
47
|
|
|
36
48
|
export const TreeDataSchema = Schema.Struct({
|
|
37
49
|
id: Schema.String,
|
|
@@ -62,39 +74,62 @@ export type TreeItemProps<T extends { id: string } = any> = {
|
|
|
62
74
|
canSelect?: (params: { item: T; path: string[] }) => boolean;
|
|
63
75
|
onOpenChange?: (params: { item: T; path: string[]; open: boolean }) => void;
|
|
64
76
|
onSelect?: (params: { item: T; path: string[]; current: boolean; option: boolean }) => void;
|
|
77
|
+
onItemHover?: (params: { item: T }) => void;
|
|
65
78
|
};
|
|
66
79
|
|
|
67
80
|
const RawTreeItem = <T extends { id: string } = any>({
|
|
68
81
|
item,
|
|
69
|
-
path:
|
|
82
|
+
path: pathProp,
|
|
70
83
|
levelOffset = 2,
|
|
71
84
|
last,
|
|
72
|
-
draggable:
|
|
85
|
+
draggable: draggableProp,
|
|
73
86
|
renderColumns: Columns,
|
|
74
87
|
blockInstruction,
|
|
75
88
|
canDrop,
|
|
76
89
|
canSelect,
|
|
77
90
|
onOpenChange,
|
|
78
91
|
onSelect,
|
|
92
|
+
onItemHover,
|
|
79
93
|
}: TreeItemProps<T>) => {
|
|
80
94
|
const rowRef = useRef<HTMLDivElement | null>(null);
|
|
81
95
|
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
82
96
|
const openRef = useRef(false);
|
|
83
97
|
const cancelExpandRef = useRef<NodeJS.Timeout | null>(null);
|
|
84
|
-
const [_state, setState] = useState<
|
|
98
|
+
const [_state, setState] = useState<TreeItemDragState>('idle');
|
|
85
99
|
const [instruction, setInstruction] = useState<Instruction | null>(null);
|
|
86
100
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
87
101
|
|
|
88
|
-
const {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
const {
|
|
103
|
+
itemProps: itemPropsAtom,
|
|
104
|
+
childIds: childIdsAtom,
|
|
105
|
+
itemOpen: itemOpenAtom,
|
|
106
|
+
itemCurrent: itemCurrentAtom,
|
|
107
|
+
} = useTree();
|
|
108
|
+
const path = useMemo(() => [...pathProp, item.id], [pathProp, item.id]);
|
|
109
|
+
|
|
110
|
+
const {
|
|
111
|
+
id,
|
|
112
|
+
parentOf,
|
|
113
|
+
draggable: itemDraggable,
|
|
114
|
+
droppable: itemDroppable,
|
|
115
|
+
label,
|
|
116
|
+
className,
|
|
117
|
+
headingClassName,
|
|
118
|
+
icon,
|
|
119
|
+
iconHue,
|
|
120
|
+
disabled,
|
|
121
|
+
testId,
|
|
122
|
+
} = useAtomValue(itemPropsAtom(path));
|
|
123
|
+
const childIds = useAtomValue(childIdsAtom(item.id));
|
|
124
|
+
const open = useAtomValue(itemOpenAtom(path));
|
|
125
|
+
const current = useAtomValue(itemCurrentAtom(path));
|
|
126
|
+
|
|
94
127
|
const level = path.length - levelOffset;
|
|
95
128
|
const isBranch = !!parentOf;
|
|
96
129
|
const mode: ItemMode = last ? 'last-in-group' : open ? 'expanded' : 'standard';
|
|
97
130
|
const canSelectItem = canSelect?.({ item, path }) ?? true;
|
|
131
|
+
const data = { id, path, item } satisfies TreeData;
|
|
132
|
+
const shouldSeedNativeDragData = typeof document !== 'undefined' && document.body.hasAttribute('data-platform');
|
|
98
133
|
|
|
99
134
|
const cancelExpand = useCallback(() => {
|
|
100
135
|
if (cancelExpandRef.current) {
|
|
@@ -103,20 +138,27 @@ const RawTreeItem = <T extends { id: string } = any>({
|
|
|
103
138
|
}
|
|
104
139
|
}, []);
|
|
105
140
|
|
|
141
|
+
const isItemDraggable = draggableProp && itemDraggable !== false;
|
|
142
|
+
const isItemDroppable = itemDroppable !== false;
|
|
143
|
+
const nativeDragText = id;
|
|
144
|
+
|
|
106
145
|
useEffect(() => {
|
|
107
|
-
if (!
|
|
146
|
+
if (!draggableProp) {
|
|
108
147
|
return;
|
|
109
148
|
}
|
|
110
149
|
|
|
111
150
|
invariant(buttonRef.current);
|
|
112
151
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
// https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
|
|
116
|
-
return combine(
|
|
152
|
+
const makeDraggable = () =>
|
|
117
153
|
draggable({
|
|
118
|
-
element: buttonRef.current
|
|
154
|
+
element: buttonRef.current!,
|
|
119
155
|
getInitialData: () => data,
|
|
156
|
+
getInitialDataForExternal: () => {
|
|
157
|
+
if (!shouldSeedNativeDragData) {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
return { 'text/plain': nativeDragText };
|
|
161
|
+
},
|
|
120
162
|
onDragStart: () => {
|
|
121
163
|
setState('dragging');
|
|
122
164
|
if (open) {
|
|
@@ -130,62 +172,72 @@ const RawTreeItem = <T extends { id: string } = any>({
|
|
|
130
172
|
onOpenChange?.({ item, path, open: true });
|
|
131
173
|
}
|
|
132
174
|
},
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
setInstruction(instruction);
|
|
171
|
-
} else if (instruction?.type === 'reparent') {
|
|
172
|
-
// TODO(wittjosiah): This is not occurring in the current implementation.
|
|
173
|
-
setInstruction(instruction);
|
|
174
|
-
} else {
|
|
175
|
-
setInstruction(null);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (!isItemDroppable) {
|
|
178
|
+
return isItemDraggable ? makeDraggable() : undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const dropTarget = dropTargetForElements({
|
|
182
|
+
element: buttonRef.current,
|
|
183
|
+
getData: ({ input, element }) => {
|
|
184
|
+
return attachInstruction(data, {
|
|
185
|
+
input,
|
|
186
|
+
element,
|
|
187
|
+
indentPerLevel: DEFAULT_INDENTATION,
|
|
188
|
+
currentLevel: level,
|
|
189
|
+
mode,
|
|
190
|
+
block: isBranch ? [] : ['make-child'],
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
canDrop: ({ source }) => {
|
|
194
|
+
const _canDrop = canDrop ?? (() => true);
|
|
195
|
+
return source.element !== buttonRef.current && _canDrop({ source: source.data as TreeData, target: data });
|
|
196
|
+
},
|
|
197
|
+
getIsSticky: () => true,
|
|
198
|
+
onDrag: ({ self, source }) => {
|
|
199
|
+
const desired = extractInstruction(self.data);
|
|
200
|
+
const block =
|
|
201
|
+
desired && blockInstruction?.({ instruction: desired, source: source.data as TreeData, target: data });
|
|
202
|
+
const instruction: Instruction | null =
|
|
203
|
+
block && desired.type !== 'instruction-blocked' ? { type: 'instruction-blocked', desired } : desired;
|
|
204
|
+
|
|
205
|
+
if (source.data.id !== id) {
|
|
206
|
+
if (instruction?.type === 'make-child' && isBranch && !open && !cancelExpandRef.current) {
|
|
207
|
+
cancelExpandRef.current = setTimeout(() => {
|
|
208
|
+
onOpenChange?.({ item, path, open: true });
|
|
209
|
+
}, 500);
|
|
176
210
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
211
|
+
|
|
212
|
+
if (instruction?.type !== 'make-child') {
|
|
213
|
+
cancelExpand();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setInstruction(instruction);
|
|
217
|
+
} else if (instruction?.type === 'reparent') {
|
|
218
|
+
// TODO(wittjosiah): This is not occurring in the current implementation.
|
|
219
|
+
setInstruction(instruction);
|
|
220
|
+
} else {
|
|
184
221
|
setInstruction(null);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
onDragLeave: () => {
|
|
225
|
+
cancelExpand();
|
|
226
|
+
setInstruction(null);
|
|
227
|
+
},
|
|
228
|
+
onDrop: () => {
|
|
229
|
+
cancelExpand();
|
|
230
|
+
setInstruction(null);
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
if (!isItemDraggable) {
|
|
235
|
+
return dropTarget;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
|
|
239
|
+
return combine(makeDraggable(), dropTarget);
|
|
240
|
+
}, [draggableProp, isItemDraggable, isItemDroppable, item, id, mode, path, open, blockInstruction, canDrop]);
|
|
189
241
|
|
|
190
242
|
// Cancel expand on unmount.
|
|
191
243
|
useEffect(() => () => cancelExpand(), [cancelExpand]);
|
|
@@ -223,6 +275,29 @@ const RawTreeItem = <T extends { id: string } = any>({
|
|
|
223
275
|
[isBranch, open, handleOpenToggle, handleSelect],
|
|
224
276
|
);
|
|
225
277
|
|
|
278
|
+
const handleItemHover = useCallback(() => {
|
|
279
|
+
onItemHover?.({ item });
|
|
280
|
+
}, [onItemHover, item]);
|
|
281
|
+
|
|
282
|
+
const handleContextMenu = useCallback(
|
|
283
|
+
(event: MouseEvent) => {
|
|
284
|
+
event.preventDefault();
|
|
285
|
+
setMenuOpen(true);
|
|
286
|
+
},
|
|
287
|
+
[setMenuOpen],
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const childProps = {
|
|
291
|
+
draggable: draggableProp,
|
|
292
|
+
renderColumns: Columns,
|
|
293
|
+
blockInstruction,
|
|
294
|
+
canDrop,
|
|
295
|
+
canSelect,
|
|
296
|
+
onItemHover,
|
|
297
|
+
onOpenChange,
|
|
298
|
+
onSelect,
|
|
299
|
+
};
|
|
300
|
+
|
|
226
301
|
return (
|
|
227
302
|
<>
|
|
228
303
|
<Treegrid.Row
|
|
@@ -230,28 +305,26 @@ const RawTreeItem = <T extends { id: string } = any>({
|
|
|
230
305
|
key={id}
|
|
231
306
|
id={id}
|
|
232
307
|
aria-labelledby={`${id}__label`}
|
|
233
|
-
parentOf={parentOf?.join(
|
|
234
|
-
classNames={[
|
|
235
|
-
'grid grid-cols-subgrid col-[tree-row] mbs-0.5 aria-[current]:bg-activeSurface',
|
|
236
|
-
hoverableControls,
|
|
237
|
-
hoverableFocusedKeyboardControls,
|
|
238
|
-
hoverableFocusedWithinControls,
|
|
239
|
-
hoverableDescriptionIcons,
|
|
240
|
-
ghostHover,
|
|
241
|
-
ghostFocusWithin,
|
|
242
|
-
className,
|
|
243
|
-
]}
|
|
308
|
+
parentOf={parentOf?.join(TREEGRID_PARENT_OF_SEPARATOR)}
|
|
244
309
|
data-object-id={id}
|
|
245
310
|
data-testid={testId}
|
|
246
311
|
// NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
|
|
247
312
|
// without alerting the user (except for in the correct link element). See also:
|
|
248
313
|
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
|
|
249
314
|
aria-current={current ? ('' as 'page') : undefined}
|
|
315
|
+
classNames={mx(
|
|
316
|
+
'grid grid-cols-subgrid col-[tree-row] mt-0.5 is-current:bg-active-surface',
|
|
317
|
+
hoverableControls,
|
|
318
|
+
hoverableFocusedKeyboardControls,
|
|
319
|
+
hoverableFocusedWithinControls,
|
|
320
|
+
hoverableDescriptionIcons,
|
|
321
|
+
ghostFocusWithin,
|
|
322
|
+
ghostHover,
|
|
323
|
+
className,
|
|
324
|
+
)}
|
|
250
325
|
onKeyDown={handleKeyDown}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
setMenuOpen(true);
|
|
254
|
-
}}
|
|
326
|
+
onMouseEnter={handleItemHover}
|
|
327
|
+
onContextMenu={handleContextMenu}
|
|
255
328
|
>
|
|
256
329
|
<div
|
|
257
330
|
role='none'
|
|
@@ -276,23 +349,25 @@ const RawTreeItem = <T extends { id: string } = any>({
|
|
|
276
349
|
</div>
|
|
277
350
|
</Treegrid.Row>
|
|
278
351
|
{open &&
|
|
279
|
-
|
|
280
|
-
<
|
|
281
|
-
key={item.id}
|
|
282
|
-
item={item}
|
|
283
|
-
path={path}
|
|
284
|
-
last={index === items.length - 1}
|
|
285
|
-
draggable={_draggable}
|
|
286
|
-
renderColumns={Columns}
|
|
287
|
-
blockInstruction={blockInstruction}
|
|
288
|
-
canDrop={canDrop}
|
|
289
|
-
canSelect={canSelect}
|
|
290
|
-
onOpenChange={onOpenChange}
|
|
291
|
-
onSelect={onSelect}
|
|
292
|
-
/>
|
|
352
|
+
childIds.map((childId, index) => (
|
|
353
|
+
<TreeItemById key={childId} id={childId} path={path} last={index === childIds.length - 1} {...childProps} />
|
|
293
354
|
))}
|
|
294
355
|
</>
|
|
295
356
|
);
|
|
296
357
|
};
|
|
297
358
|
|
|
298
359
|
export const TreeItem = memo(RawTreeItem) as FC<TreeItemProps>;
|
|
360
|
+
|
|
361
|
+
/** Resolves a child ID to an item via the `item` atom and renders a TreeItem. */
|
|
362
|
+
export type TreeItemByIdProps = Omit<TreeItemProps, 'item'> & { id: string };
|
|
363
|
+
|
|
364
|
+
const RawTreeItemById = <T extends { id: string } = any>({ id, ...props }: TreeItemByIdProps) => {
|
|
365
|
+
const { item: itemAtom } = useTree();
|
|
366
|
+
const item = useAtomValue(itemAtom(id)) as T | undefined;
|
|
367
|
+
if (!item) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
return <TreeItem item={item} {...props} />;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
export const TreeItemById = memo(RawTreeItemById) as FC<TreeItemByIdProps>;
|
|
@@ -56,9 +56,8 @@ export const TreeItemHeading = memo(
|
|
|
56
56
|
<Button
|
|
57
57
|
data-testid='treeItem.heading'
|
|
58
58
|
variant='ghost'
|
|
59
|
-
density='fine'
|
|
60
59
|
classNames={[
|
|
61
|
-
'grow gap-2
|
|
60
|
+
'grow gap-2 ps-0.5 hover:bg-transparent dark:hover:bg-transparent',
|
|
62
61
|
'disabled:cursor-default disabled:opacity-100',
|
|
63
62
|
className,
|
|
64
63
|
]}
|
|
@@ -67,8 +66,8 @@ export const TreeItemHeading = memo(
|
|
|
67
66
|
onKeyDown={handleButtonKeydown}
|
|
68
67
|
{...(current && { 'aria-current': 'location' })}
|
|
69
68
|
>
|
|
70
|
-
{icon && <Icon icon={icon ?? 'ph--placeholder--regular'}
|
|
71
|
-
<span className='flex-1
|
|
69
|
+
{icon && <Icon icon={icon ?? 'ph--placeholder--regular'} classNames={['my-1', styles?.surfaceText]} />}
|
|
70
|
+
<span className='flex-1 w-0 truncate text-start font-normal' data-tooltip>
|
|
72
71
|
{toLocalizedString(label, t)}
|
|
73
72
|
</span>
|
|
74
73
|
</Button>
|
|
@@ -14,7 +14,7 @@ export type TreeItemToggleProps = Omit<IconButtonProps, 'icon' | 'size' | 'label
|
|
|
14
14
|
|
|
15
15
|
export const TreeItemToggle = memo(
|
|
16
16
|
forwardRef<HTMLButtonElement, TreeItemToggleProps>(
|
|
17
|
-
({ open, isBranch, hidden,
|
|
17
|
+
({ classNames, open, isBranch, hidden, ...props }, forwardedRef) => {
|
|
18
18
|
return (
|
|
19
19
|
<IconButton
|
|
20
20
|
ref={forwardedRef}
|
|
@@ -23,9 +23,9 @@ export const TreeItemToggle = memo(
|
|
|
23
23
|
variant='ghost'
|
|
24
24
|
density='fine'
|
|
25
25
|
classNames={[
|
|
26
|
-
'
|
|
27
|
-
'[&_svg]:transition-
|
|
28
|
-
open
|
|
26
|
+
'h-full w-6 px-0',
|
|
27
|
+
'[&_svg]:transition-transform [&_svg]:duration-200',
|
|
28
|
+
open ? '[&_svg]:rotate-90' : '[&_svg]:rotate-0',
|
|
29
29
|
hidden ? 'hidden' : !isBranch && 'invisible',
|
|
30
30
|
classNames,
|
|
31
31
|
]}
|
|
@@ -7,7 +7,7 @@ import * as Schema from 'effect/Schema';
|
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import {
|
|
10
|
+
import { random } from '@dxos/random';
|
|
11
11
|
|
|
12
12
|
import { type TreeData } from './TreeItem';
|
|
13
13
|
|
|
@@ -26,18 +26,18 @@ export const TestItemSchema = Schema.Struct({
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
export const createTree = (n = 4, d = 4): TestItem => ({
|
|
29
|
-
id:
|
|
30
|
-
name:
|
|
29
|
+
id: random.string.uuid(),
|
|
30
|
+
name: random.commerce.productName(),
|
|
31
31
|
icon:
|
|
32
32
|
d === 3
|
|
33
33
|
? undefined
|
|
34
|
-
:
|
|
34
|
+
: random.helpers.arrayElement([
|
|
35
35
|
'ph--planet--regular',
|
|
36
36
|
'ph--sailboat--regular',
|
|
37
37
|
'ph--house--regular',
|
|
38
38
|
'ph--gear--regular',
|
|
39
39
|
]),
|
|
40
|
-
items: d > 0 ?
|
|
40
|
+
items: d > 0 ? random.helpers.multiple(() => createTree(n, d - 1), { count: n }) : [],
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
const removeItem = (tree: TestItem, source: TreeData) => {
|