@dxos/react-ui-list 0.8.4-main.70d3990 → 0.8.4-main.74a063c4e0
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 +680 -728
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +680 -728
- 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 +8 -6
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/List.stories.d.ts +2 -2
- package/dist/types/src/components/List/List.stories.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 +10 -6
- package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts +9 -28
- package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeContext.d.ts +21 -8
- package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItem.d.ts +14 -3
- 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/src/components/Tree/testing.d.ts +2 -2
- package/dist/types/src/components/Tree/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +31 -28
- package/src/components/Accordion/Accordion.stories.tsx +6 -6
- package/src/components/Accordion/AccordionItem.tsx +3 -4
- package/src/components/Accordion/AccordionRoot.tsx +1 -1
- package/src/components/List/List.stories.tsx +34 -22
- package/src/components/List/List.tsx +4 -9
- package/src/components/List/ListItem.tsx +60 -40
- package/src/components/List/ListRoot.tsx +3 -3
- package/src/components/List/testing.ts +4 -4
- package/src/components/Tree/Tree.stories.tsx +153 -64
- package/src/components/Tree/Tree.tsx +30 -41
- package/src/components/Tree/TreeContext.tsx +18 -7
- package/src/components/Tree/TreeItem.tsx +182 -108
- package/src/components/Tree/TreeItemHeading.tsx +4 -5
- package/src/components/Tree/TreeItemToggle.tsx +4 -4
- package/src/components/Tree/index.ts +2 -0
- package/src/components/Tree/testing.ts +7 -7
|
@@ -2,27 +2,38 @@
|
|
|
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
|
-
import { type HasId } from '@dxos/echo/internal';
|
|
17
27
|
import { invariant } from '@dxos/invariant';
|
|
18
|
-
import { TreeItem as NaturalTreeItem, Treegrid } from '@dxos/react-ui';
|
|
28
|
+
import { TreeItem as NaturalTreeItem, Treegrid, TREEGRID_PARENT_OF_SEPARATOR } from '@dxos/react-ui';
|
|
19
29
|
import {
|
|
20
30
|
ghostFocusWithin,
|
|
21
31
|
ghostHover,
|
|
22
32
|
hoverableControls,
|
|
23
33
|
hoverableFocusedKeyboardControls,
|
|
24
34
|
hoverableFocusedWithinControls,
|
|
25
|
-
|
|
35
|
+
mx,
|
|
36
|
+
} from '@dxos/ui-theme';
|
|
26
37
|
|
|
27
38
|
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
28
39
|
import { useTree } from './TreeContext';
|
|
@@ -32,7 +43,7 @@ import { TreeItemToggle } from './TreeItemToggle';
|
|
|
32
43
|
const hoverableDescriptionIcons =
|
|
33
44
|
'[--icons-color:inherit] hover-hover:[--icons-color:var(--description-text)] hover-hover:hover:[--icons-color:inherit] focus-within:[--icons-color:inherit]';
|
|
34
45
|
|
|
35
|
-
type
|
|
46
|
+
type TreeItemDragState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
|
|
36
47
|
|
|
37
48
|
export const TreeDataSchema = Schema.Struct({
|
|
38
49
|
id: Schema.String,
|
|
@@ -43,7 +54,7 @@ export const TreeDataSchema = Schema.Struct({
|
|
|
43
54
|
export type TreeData = Schema.Schema.Type<typeof TreeDataSchema>;
|
|
44
55
|
export const isTreeData = (data: unknown): data is TreeData => Schema.is(TreeDataSchema)(data);
|
|
45
56
|
|
|
46
|
-
export type ColumnRenderer<T extends
|
|
57
|
+
export type ColumnRenderer<T extends { id: string } = any> = FC<{
|
|
47
58
|
item: T;
|
|
48
59
|
path: string[];
|
|
49
60
|
open: boolean;
|
|
@@ -51,7 +62,7 @@ export type ColumnRenderer<T extends HasId = any> = FC<{
|
|
|
51
62
|
setMenuOpen: (open: boolean) => void;
|
|
52
63
|
}>;
|
|
53
64
|
|
|
54
|
-
export type TreeItemProps<T extends
|
|
65
|
+
export type TreeItemProps<T extends { id: string } = any> = {
|
|
55
66
|
item: T;
|
|
56
67
|
path: string[];
|
|
57
68
|
levelOffset?: number;
|
|
@@ -63,39 +74,62 @@ export type TreeItemProps<T extends HasId = any> = {
|
|
|
63
74
|
canSelect?: (params: { item: T; path: string[] }) => boolean;
|
|
64
75
|
onOpenChange?: (params: { item: T; path: string[]; open: boolean }) => void;
|
|
65
76
|
onSelect?: (params: { item: T; path: string[]; current: boolean; option: boolean }) => void;
|
|
77
|
+
onItemHover?: (params: { item: T }) => void;
|
|
66
78
|
};
|
|
67
79
|
|
|
68
|
-
const RawTreeItem = <T extends
|
|
80
|
+
const RawTreeItem = <T extends { id: string } = any>({
|
|
69
81
|
item,
|
|
70
|
-
path:
|
|
82
|
+
path: pathProp,
|
|
71
83
|
levelOffset = 2,
|
|
72
84
|
last,
|
|
73
|
-
draggable:
|
|
85
|
+
draggable: draggableProp,
|
|
74
86
|
renderColumns: Columns,
|
|
75
87
|
blockInstruction,
|
|
76
88
|
canDrop,
|
|
77
89
|
canSelect,
|
|
78
90
|
onOpenChange,
|
|
79
91
|
onSelect,
|
|
92
|
+
onItemHover,
|
|
80
93
|
}: TreeItemProps<T>) => {
|
|
81
94
|
const rowRef = useRef<HTMLDivElement | null>(null);
|
|
82
95
|
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
83
96
|
const openRef = useRef(false);
|
|
84
97
|
const cancelExpandRef = useRef<NodeJS.Timeout | null>(null);
|
|
85
|
-
const [_state, setState] = useState<
|
|
98
|
+
const [_state, setState] = useState<TreeItemDragState>('idle');
|
|
86
99
|
const [instruction, setInstruction] = useState<Instruction | null>(null);
|
|
87
100
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
88
101
|
|
|
89
|
-
const {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
+
|
|
95
127
|
const level = path.length - levelOffset;
|
|
96
128
|
const isBranch = !!parentOf;
|
|
97
129
|
const mode: ItemMode = last ? 'last-in-group' : open ? 'expanded' : 'standard';
|
|
98
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');
|
|
99
133
|
|
|
100
134
|
const cancelExpand = useCallback(() => {
|
|
101
135
|
if (cancelExpandRef.current) {
|
|
@@ -104,20 +138,27 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
104
138
|
}
|
|
105
139
|
}, []);
|
|
106
140
|
|
|
141
|
+
const isItemDraggable = draggableProp && itemDraggable !== false;
|
|
142
|
+
const isItemDroppable = itemDroppable !== false;
|
|
143
|
+
const nativeDragText = id;
|
|
144
|
+
|
|
107
145
|
useEffect(() => {
|
|
108
|
-
if (!
|
|
146
|
+
if (!draggableProp) {
|
|
109
147
|
return;
|
|
110
148
|
}
|
|
111
149
|
|
|
112
150
|
invariant(buttonRef.current);
|
|
113
151
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
// https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
|
|
117
|
-
return combine(
|
|
152
|
+
const makeDraggable = () =>
|
|
118
153
|
draggable({
|
|
119
|
-
element: buttonRef.current
|
|
154
|
+
element: buttonRef.current!,
|
|
120
155
|
getInitialData: () => data,
|
|
156
|
+
getInitialDataForExternal: () => {
|
|
157
|
+
if (!shouldSeedNativeDragData) {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
return { 'text/plain': nativeDragText };
|
|
161
|
+
},
|
|
121
162
|
onDragStart: () => {
|
|
122
163
|
setState('dragging');
|
|
123
164
|
if (open) {
|
|
@@ -131,62 +172,72 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
131
172
|
onOpenChange?.({ item, path, open: true });
|
|
132
173
|
}
|
|
133
174
|
},
|
|
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
|
-
|
|
171
|
-
setInstruction(instruction);
|
|
172
|
-
} else if (instruction?.type === 'reparent') {
|
|
173
|
-
// TODO(wittjosiah): This is not occurring in the current implementation.
|
|
174
|
-
setInstruction(instruction);
|
|
175
|
-
} else {
|
|
176
|
-
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);
|
|
177
210
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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 {
|
|
185
221
|
setInstruction(null);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
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]);
|
|
190
241
|
|
|
191
242
|
// Cancel expand on unmount.
|
|
192
243
|
useEffect(() => () => cancelExpand(), [cancelExpand]);
|
|
@@ -224,6 +275,29 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
224
275
|
[isBranch, open, handleOpenToggle, handleSelect],
|
|
225
276
|
);
|
|
226
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
|
+
|
|
227
301
|
return (
|
|
228
302
|
<>
|
|
229
303
|
<Treegrid.Row
|
|
@@ -231,28 +305,26 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
231
305
|
key={id}
|
|
232
306
|
id={id}
|
|
233
307
|
aria-labelledby={`${id}__label`}
|
|
234
|
-
parentOf={parentOf?.join(
|
|
235
|
-
classNames={[
|
|
236
|
-
'grid grid-cols-subgrid col-[tree-row] mbs-0.5 aria-[current]:bg-activeSurface',
|
|
237
|
-
hoverableControls,
|
|
238
|
-
hoverableFocusedKeyboardControls,
|
|
239
|
-
hoverableFocusedWithinControls,
|
|
240
|
-
hoverableDescriptionIcons,
|
|
241
|
-
ghostHover,
|
|
242
|
-
ghostFocusWithin,
|
|
243
|
-
className,
|
|
244
|
-
]}
|
|
308
|
+
parentOf={parentOf?.join(TREEGRID_PARENT_OF_SEPARATOR)}
|
|
245
309
|
data-object-id={id}
|
|
246
310
|
data-testid={testId}
|
|
247
311
|
// NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
|
|
248
312
|
// without alerting the user (except for in the correct link element). See also:
|
|
249
313
|
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
|
|
250
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
|
+
)}
|
|
251
325
|
onKeyDown={handleKeyDown}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
setMenuOpen(true);
|
|
255
|
-
}}
|
|
326
|
+
onMouseEnter={handleItemHover}
|
|
327
|
+
onContextMenu={handleContextMenu}
|
|
256
328
|
>
|
|
257
329
|
<div
|
|
258
330
|
role='none'
|
|
@@ -277,23 +349,25 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
277
349
|
</div>
|
|
278
350
|
</Treegrid.Row>
|
|
279
351
|
{open &&
|
|
280
|
-
|
|
281
|
-
<
|
|
282
|
-
key={item.id}
|
|
283
|
-
item={item}
|
|
284
|
-
path={path}
|
|
285
|
-
last={index === items.length - 1}
|
|
286
|
-
draggable={_draggable}
|
|
287
|
-
renderColumns={Columns}
|
|
288
|
-
blockInstruction={blockInstruction}
|
|
289
|
-
canDrop={canDrop}
|
|
290
|
-
canSelect={canSelect}
|
|
291
|
-
onOpenChange={onOpenChange}
|
|
292
|
-
onSelect={onSelect}
|
|
293
|
-
/>
|
|
352
|
+
childIds.map((childId, index) => (
|
|
353
|
+
<TreeItemById key={childId} id={childId} path={path} last={index === childIds.length - 1} {...childProps} />
|
|
294
354
|
))}
|
|
295
355
|
</>
|
|
296
356
|
);
|
|
297
357
|
};
|
|
298
358
|
|
|
299
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>;
|
|
@@ -6,7 +6,7 @@ import React, { type KeyboardEvent, type MouseEvent, forwardRef, memo, useCallba
|
|
|
6
6
|
|
|
7
7
|
import { Button, Icon, type Label, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
9
|
-
import { getStyles } from '@dxos/
|
|
9
|
+
import { getStyles } from '@dxos/ui-theme';
|
|
10
10
|
|
|
11
11
|
// TODO(wittjosiah): Consider whether there should be a separate disabled prop which was visually distinct
|
|
12
12
|
// rather than just making the item unselectable.
|
|
@@ -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
|
]}
|
|
@@ -6,13 +6,13 @@ import { type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import { type HasId } from '@dxos/echo/internal';
|
|
10
9
|
import { log } from '@dxos/log';
|
|
11
|
-
import {
|
|
10
|
+
import { random } from '@dxos/random';
|
|
12
11
|
|
|
13
12
|
import { type TreeData } from './TreeItem';
|
|
14
13
|
|
|
15
|
-
export type TestItem =
|
|
14
|
+
export type TestItem = {
|
|
15
|
+
id: string;
|
|
16
16
|
name: string;
|
|
17
17
|
icon?: string;
|
|
18
18
|
items: TestItem[];
|
|
@@ -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) => {
|