@dxos/react-ui-list 0.8.3 → 0.8.4-main.1da679c
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 +83 -77
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +83 -77
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Accordion/Accordion.stories.d.ts +7 -3
- package/dist/types/src/components/Accordion/Accordion.stories.d.ts.map +1 -1
- package/dist/types/src/components/Accordion/AccordionItem.d.ts +1 -1
- package/dist/types/src/components/Accordion/AccordionItem.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts +6 -6
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/List.stories.d.ts +11 -4
- package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/ListItem.d.ts +5 -8
- package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
- package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts +36 -5
- package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItem.d.ts +8 -7
- 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/TreeItemToggle.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -20
- package/src/components/Accordion/Accordion.stories.tsx +3 -3
- package/src/components/Accordion/Accordion.tsx +1 -1
- package/src/components/Accordion/AccordionItem.tsx +5 -2
- package/src/components/List/List.stories.tsx +12 -11
- package/src/components/List/List.tsx +2 -5
- package/src/components/List/ListItem.tsx +48 -34
- package/src/components/List/ListRoot.tsx +1 -1
- package/src/components/Tree/Tree.stories.tsx +49 -45
- package/src/components/Tree/TreeItem.tsx +16 -16
- package/src/components/Tree/TreeItemHeading.tsx +3 -4
- package/src/components/Tree/TreeItemToggle.tsx +2 -3
- package/dist/lib/node/index.cjs +0 -886
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-list",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.1da679c",
|
|
4
4
|
"description": "A list component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
+
"source": "./src/index.ts",
|
|
12
13
|
"types": "./dist/types/src/index.d.ts",
|
|
13
14
|
"browser": "./dist/lib/browser/index.mjs",
|
|
14
15
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
@@ -29,35 +30,33 @@
|
|
|
29
30
|
"@preact/signals-core": "^1.9.0",
|
|
30
31
|
"@radix-ui/react-accordion": "1.2.3",
|
|
31
32
|
"@radix-ui/react-context": "1.1.1",
|
|
32
|
-
"@dxos/debug": "0.8.
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/live-object": "0.8.
|
|
36
|
-
"@dxos/log": "0.8.
|
|
37
|
-
"@dxos/react-ui
|
|
38
|
-
"@dxos/react-ui-
|
|
39
|
-
"@dxos/
|
|
33
|
+
"@dxos/debug": "0.8.4-main.1da679c",
|
|
34
|
+
"@dxos/echo-schema": "0.8.4-main.1da679c",
|
|
35
|
+
"@dxos/invariant": "0.8.4-main.1da679c",
|
|
36
|
+
"@dxos/live-object": "0.8.4-main.1da679c",
|
|
37
|
+
"@dxos/log": "0.8.4-main.1da679c",
|
|
38
|
+
"@dxos/react-ui": "0.8.4-main.1da679c",
|
|
39
|
+
"@dxos/react-ui-theme": "0.8.4-main.1da679c",
|
|
40
|
+
"@dxos/react-ui-text-tooltip": "0.8.4-main.1da679c",
|
|
41
|
+
"@dxos/react-ui-types": "0.8.4-main.1da679c",
|
|
42
|
+
"@dxos/util": "0.8.4-main.1da679c"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
43
45
|
"@types/react": "~18.2.0",
|
|
44
46
|
"@types/react-dom": "~18.2.0",
|
|
45
|
-
"effect": "3.
|
|
47
|
+
"effect": "3.17.7",
|
|
46
48
|
"react": "~18.2.0",
|
|
47
49
|
"react-dom": "~18.2.0",
|
|
48
|
-
"vite": "
|
|
49
|
-
"@dxos/random": "0.8.
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/react-ui": "0.8.3",
|
|
52
|
-
"@dxos/storybook-utils": "0.8.3"
|
|
50
|
+
"vite": "7.1.1",
|
|
51
|
+
"@dxos/random": "0.8.4-main.1da679c",
|
|
52
|
+
"@dxos/storybook-utils": "0.8.4-main.1da679c"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"
|
|
56
|
-
"effect": "3.13.3",
|
|
55
|
+
"effect": "^3.13.3",
|
|
57
56
|
"react": "~18.2.0",
|
|
58
57
|
"react-dom": "~18.2.0",
|
|
59
|
-
"@dxos/react-ui
|
|
60
|
-
"@dxos/react-ui": "0.8.
|
|
58
|
+
"@dxos/react-ui": "0.8.4-main.1da679c",
|
|
59
|
+
"@dxos/react-ui-theme": "0.8.4-main.1da679c"
|
|
61
60
|
},
|
|
62
61
|
"publishConfig": {
|
|
63
62
|
"access": "public"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
@@ -41,11 +41,11 @@ const DefaultStory = () => {
|
|
|
41
41
|
);
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const meta
|
|
44
|
+
const meta = {
|
|
45
45
|
title: 'ui/react-ui-list/Accordion',
|
|
46
46
|
render: DefaultStory,
|
|
47
47
|
decorators: [withTheme, withLayout({ fullscreen: true, classNames: 'flex justify-center' })],
|
|
48
|
-
}
|
|
48
|
+
} satisfies Meta<typeof Accordion>;
|
|
49
49
|
|
|
50
50
|
export default meta;
|
|
51
51
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { AccordionItem,
|
|
5
|
+
import { AccordionItem, AccordionItemBody, AccordionItemHeader } from './AccordionItem';
|
|
6
6
|
import { AccordionRoot } from './AccordionRoot';
|
|
7
7
|
|
|
8
8
|
// TODO(burdon): Next iteration should be based on Radix UI Accordion:
|
|
@@ -9,16 +9,19 @@ import React, { type PropsWithChildren } from 'react';
|
|
|
9
9
|
import { Icon, type ThemedClassName } from '@dxos/react-ui';
|
|
10
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
11
11
|
|
|
12
|
-
import { useAccordionContext } from './AccordionRoot';
|
|
13
12
|
import { type ListItemRecord } from '../List';
|
|
14
13
|
|
|
14
|
+
import { useAccordionContext } from './AccordionRoot';
|
|
15
|
+
|
|
15
16
|
const ACCORDION_ITEM_NAME = 'AccordionItem';
|
|
16
17
|
|
|
17
18
|
type AccordionItemContext<T extends ListItemRecord> = {
|
|
18
19
|
item: T;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
// TODO(wittjosiah): This seems to be conflicting with something in the bundle.
|
|
23
|
+
// Perhaps @radix-ui/react-accordion?
|
|
24
|
+
export const [AccordionItemProvider, useDxAccordionItemContext] =
|
|
22
25
|
createContext<AccordionItemContext<any>>(ACCORDION_ITEM_NAME);
|
|
23
26
|
|
|
24
27
|
export type AccordionItemProps<T extends ListItemRecord> = ThemedClassName<PropsWithChildren<{ item: T }>>;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
9
|
import React from 'react';
|
|
10
10
|
|
|
@@ -14,18 +14,11 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
14
14
|
import { arrayMove } from '@dxos/util';
|
|
15
15
|
|
|
16
16
|
import { List, type ListRootProps } from './List';
|
|
17
|
-
import {
|
|
17
|
+
import { TestItemSchema, type TestItemType, createList } from './testing';
|
|
18
18
|
|
|
19
19
|
// TODO(burdon): var-icon-size.
|
|
20
20
|
const grid = 'grid grid-cols-[32px_1fr_32px] min-bs-[2rem] rounded';
|
|
21
21
|
|
|
22
|
-
const meta: Meta = {
|
|
23
|
-
title: 'ui/react-ui-list/List',
|
|
24
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default meta;
|
|
28
|
-
|
|
29
22
|
const DefaultStory = ({ items = [], ...props }: ListRootProps<TestItemType>) => {
|
|
30
23
|
const handleSelect = (item: TestItemType) => {
|
|
31
24
|
console.log('select', item);
|
|
@@ -98,7 +91,15 @@ const SimpleStory = ({ items = [], ...props }: ListRootProps<TestItemType>) => {
|
|
|
98
91
|
|
|
99
92
|
const list = live(createList(100));
|
|
100
93
|
|
|
101
|
-
|
|
94
|
+
const meta = {
|
|
95
|
+
title: 'ui/react-ui-list/List',
|
|
96
|
+
component: List.Root,
|
|
97
|
+
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
98
|
+
} satisfies Meta<typeof List.Root>;
|
|
99
|
+
|
|
100
|
+
export default meta;
|
|
101
|
+
|
|
102
|
+
export const Default: StoryObj<typeof DefaultStory> = {
|
|
102
103
|
render: DefaultStory,
|
|
103
104
|
args: {
|
|
104
105
|
items: list.items,
|
|
@@ -106,7 +107,7 @@ export const Default: StoryObj<ListRootProps<TestItemType>> = {
|
|
|
106
107
|
},
|
|
107
108
|
};
|
|
108
109
|
|
|
109
|
-
export const Simple: StoryObj<
|
|
110
|
+
export const Simple: StoryObj<typeof SimpleStory> = {
|
|
110
111
|
render: SimpleStory,
|
|
111
112
|
args: {
|
|
112
113
|
items: list.items,
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
IconButton,
|
|
7
|
-
type IconButtonProps,
|
|
8
6
|
ListItem,
|
|
9
|
-
ListItemDeleteButton,
|
|
10
7
|
ListItemButton,
|
|
8
|
+
ListItemDeleteButton,
|
|
11
9
|
ListItemDragHandle,
|
|
12
10
|
ListItemDragPreview,
|
|
13
11
|
type ListItemProps,
|
|
@@ -38,9 +36,8 @@ export const List = {
|
|
|
38
36
|
ItemDeleteButton: ListItemDeleteButton,
|
|
39
37
|
ItemButton: ListItemButton,
|
|
40
38
|
ItemTitle: ListItemTitle,
|
|
41
|
-
IconButton,
|
|
42
39
|
};
|
|
43
40
|
|
|
44
41
|
type ListItem = ListItemRecord;
|
|
45
42
|
|
|
46
|
-
export type { ListRootProps, ListItemProps,
|
|
43
|
+
export type { ListRootProps, ListItemProps, ListItem, ListItemRecord };
|
|
@@ -17,7 +17,6 @@ import React, {
|
|
|
17
17
|
type MutableRefObject,
|
|
18
18
|
type PropsWithChildren,
|
|
19
19
|
type ReactNode,
|
|
20
|
-
forwardRef,
|
|
21
20
|
useEffect,
|
|
22
21
|
useRef,
|
|
23
22
|
useState,
|
|
@@ -25,7 +24,13 @@ import React, {
|
|
|
25
24
|
import { createPortal } from 'react-dom';
|
|
26
25
|
|
|
27
26
|
import { invariant } from '@dxos/invariant';
|
|
28
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
IconButton,
|
|
29
|
+
type IconButtonProps,
|
|
30
|
+
ListItem as NaturalListItem,
|
|
31
|
+
type ThemedClassName,
|
|
32
|
+
useTranslation,
|
|
33
|
+
} from '@dxos/react-ui';
|
|
29
34
|
import { mx } from '@dxos/react-ui-theme';
|
|
30
35
|
|
|
31
36
|
import { useListContext } from './ListRoot';
|
|
@@ -72,10 +77,11 @@ export const [ListItemProvider, useListItemContext] = createContext<ListItemCont
|
|
|
72
77
|
);
|
|
73
78
|
|
|
74
79
|
export type ListItemProps<T extends ListItemRecord> = ThemedClassName<
|
|
75
|
-
PropsWithChildren<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
HTMLAttributes<HTMLDivElement>
|
|
80
|
+
PropsWithChildren<
|
|
81
|
+
{
|
|
82
|
+
item: T;
|
|
83
|
+
} & HTMLAttributes<HTMLDivElement>
|
|
84
|
+
>
|
|
79
85
|
>;
|
|
80
86
|
|
|
81
87
|
/**
|
|
@@ -86,6 +92,7 @@ export const ListItem = <T extends ListItemRecord>({ children, classNames, item,
|
|
|
86
92
|
const ref = useRef<HTMLDivElement | null>(null);
|
|
87
93
|
const dragHandleRef = useRef<HTMLElement | null>(null);
|
|
88
94
|
const [state, setState] = useState<ItemDragState>(idle);
|
|
95
|
+
|
|
89
96
|
useEffect(() => {
|
|
90
97
|
const element = ref.current;
|
|
91
98
|
invariant(element);
|
|
@@ -142,6 +149,9 @@ export const ListItem = <T extends ListItemRecord>({ children, classNames, item,
|
|
|
142
149
|
const closestEdge = extractClosestEdge(self.data);
|
|
143
150
|
setState({ type: 'is-dragging-over', closestEdge });
|
|
144
151
|
},
|
|
152
|
+
onDragLeave: () => {
|
|
153
|
+
setState(idle);
|
|
154
|
+
},
|
|
145
155
|
onDrag: ({ self }) => {
|
|
146
156
|
const closestEdge = extractClosestEdge(self.data);
|
|
147
157
|
setState((current) => {
|
|
@@ -151,9 +161,6 @@ export const ListItem = <T extends ListItemRecord>({ children, classNames, item,
|
|
|
151
161
|
return { type: 'is-dragging-over', closestEdge };
|
|
152
162
|
});
|
|
153
163
|
},
|
|
154
|
-
onDragLeave: () => {
|
|
155
|
-
setState(idle);
|
|
156
|
-
},
|
|
157
164
|
onDrop: () => {
|
|
158
165
|
setState(idle);
|
|
159
166
|
},
|
|
@@ -163,15 +170,8 @@ export const ListItem = <T extends ListItemRecord>({ children, classNames, item,
|
|
|
163
170
|
|
|
164
171
|
return (
|
|
165
172
|
<ListItemProvider item={item} dragHandleRef={dragHandleRef}>
|
|
166
|
-
<div role='
|
|
167
|
-
|
|
168
|
-
ref={ref}
|
|
169
|
-
role='listitem'
|
|
170
|
-
className={mx('flex overflow-hidden', classNames, stateStyles[state.type])}
|
|
171
|
-
{...props}
|
|
172
|
-
>
|
|
173
|
-
{children}
|
|
174
|
-
</div>
|
|
173
|
+
<div ref={ref} role='listitem' className={mx('flex relative', classNames, stateStyles[state.type])} {...props}>
|
|
174
|
+
{children}
|
|
175
175
|
{state.type === 'is-dragging-over' && state.closestEdge && (
|
|
176
176
|
<NaturalListItem.DropIndicator edge={state.closestEdge} />
|
|
177
177
|
)}
|
|
@@ -184,51 +184,65 @@ export const ListItem = <T extends ListItemRecord>({ children, classNames, item,
|
|
|
184
184
|
// List item components
|
|
185
185
|
//
|
|
186
186
|
|
|
187
|
-
export type IconButtonProps = ThemedClassName<ComponentProps<'button'>> & { icon: string };
|
|
188
|
-
|
|
189
|
-
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
190
|
-
({ classNames, icon, ...props }, forwardedRef) => {
|
|
191
|
-
return (
|
|
192
|
-
<button ref={forwardedRef} className={mx('flex items-center justify-center', classNames)} {...props}>
|
|
193
|
-
<Icon icon={icon} classNames='cursor-pointer' size={4} />
|
|
194
|
-
</button>
|
|
195
|
-
);
|
|
196
|
-
},
|
|
197
|
-
);
|
|
198
|
-
|
|
199
187
|
export const ListItemDeleteButton = ({
|
|
200
188
|
autoHide = true,
|
|
201
189
|
classNames,
|
|
202
190
|
disabled,
|
|
203
191
|
icon = 'ph--x--regular',
|
|
192
|
+
label,
|
|
204
193
|
...props
|
|
205
|
-
}: Partial<Pick<IconButtonProps, 'icon'>> &
|
|
194
|
+
}: Partial<Pick<IconButtonProps, 'icon'>> &
|
|
195
|
+
Omit<IconButtonProps, 'icon' | 'label'> & { autoHide?: boolean; label?: string }) => {
|
|
206
196
|
const { state } = useListContext('DELETE_BUTTON');
|
|
207
197
|
const isDisabled = state.type !== 'idle' || disabled;
|
|
198
|
+
const { t } = useTranslation('os');
|
|
208
199
|
return (
|
|
209
200
|
<IconButton
|
|
201
|
+
iconOnly
|
|
202
|
+
variant='ghost'
|
|
203
|
+
{...props}
|
|
210
204
|
icon={icon}
|
|
211
205
|
disabled={isDisabled}
|
|
206
|
+
label={label ?? t('delete label')}
|
|
212
207
|
classNames={[classNames, autoHide && disabled && 'hidden']}
|
|
213
|
-
{...props}
|
|
214
208
|
/>
|
|
215
209
|
);
|
|
216
210
|
};
|
|
217
211
|
|
|
218
212
|
export const ListItemButton = ({
|
|
219
213
|
autoHide = true,
|
|
214
|
+
iconOnly = true,
|
|
215
|
+
variant = 'ghost',
|
|
220
216
|
classNames,
|
|
221
217
|
disabled,
|
|
222
218
|
...props
|
|
223
219
|
}: IconButtonProps & { autoHide?: boolean }) => {
|
|
224
220
|
const { state } = useListContext('ITEM_BUTTON');
|
|
225
221
|
const isDisabled = state.type !== 'idle' || disabled;
|
|
226
|
-
return
|
|
222
|
+
return (
|
|
223
|
+
<IconButton
|
|
224
|
+
{...props}
|
|
225
|
+
disabled={isDisabled}
|
|
226
|
+
iconOnly={iconOnly}
|
|
227
|
+
variant={variant}
|
|
228
|
+
classNames={[classNames, autoHide && disabled && 'hidden']}
|
|
229
|
+
/>
|
|
230
|
+
);
|
|
227
231
|
};
|
|
228
232
|
|
|
229
233
|
export const ListItemDragHandle = ({ disabled }: Pick<IconButtonProps, 'disabled'>) => {
|
|
230
234
|
const { dragHandleRef } = useListItemContext('DRAG_HANDLE');
|
|
231
|
-
|
|
235
|
+
const { t } = useTranslation('os');
|
|
236
|
+
return (
|
|
237
|
+
<IconButton
|
|
238
|
+
iconOnly
|
|
239
|
+
variant='ghost'
|
|
240
|
+
label={t('drag handle label')}
|
|
241
|
+
ref={dragHandleRef as any}
|
|
242
|
+
icon='ph--dots-six-vertical--regular'
|
|
243
|
+
disabled={disabled}
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
232
246
|
};
|
|
233
247
|
|
|
234
248
|
export const ListItemDragPreview = <T extends ListItemRecord>({
|
|
@@ -8,7 +8,7 @@ import { getReorderDestinationIndex } from '@atlaskit/pragmatic-drag-and-drop-hi
|
|
|
8
8
|
import { createContext } from '@radix-ui/react-context';
|
|
9
9
|
import React, { type ReactNode, useCallback, useEffect, useState } from 'react';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { type ItemDragState, type ListItemRecord, idle } from './ListItem';
|
|
12
12
|
|
|
13
13
|
type ListContext<T extends ListItemRecord> = {
|
|
14
14
|
// TODO(burdon): Rename drag state.
|
|
@@ -5,67 +5,69 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
8
|
-
import {
|
|
9
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
9
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
10
10
|
import React, { useEffect } from 'react';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { type Live, live } from '@dxos/live-object';
|
|
13
13
|
import { faker } from '@dxos/random';
|
|
14
14
|
import { Icon } from '@dxos/react-ui';
|
|
15
15
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
16
16
|
|
|
17
|
-
import { Tree } from './Tree';
|
|
18
|
-
import { type TreeData } from './TreeItem';
|
|
19
|
-
import { createTree, updateState, type TestItem } from './testing';
|
|
20
17
|
import { Path } from '../../util';
|
|
21
18
|
|
|
19
|
+
import { type TestItem, createTree, updateState } from './testing';
|
|
20
|
+
import { Tree, type TreeProps } from './Tree';
|
|
21
|
+
import { type TreeData } from './TreeItem';
|
|
22
|
+
|
|
22
23
|
faker.seed(1234);
|
|
23
24
|
|
|
25
|
+
const DefaultStory = (props: TreeProps) => {
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
return monitorForElements({
|
|
28
|
+
canMonitor: ({ source }) => typeof source.data.id === 'string' && Array.isArray(source.data.path),
|
|
29
|
+
onDrop: ({ location, source }) => {
|
|
30
|
+
// Didn't drop on anything.
|
|
31
|
+
if (!location.current.dropTargets.length) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const target = location.current.dropTargets[0];
|
|
36
|
+
const instruction: Instruction | null = extractInstruction(target.data);
|
|
37
|
+
if (instruction !== null) {
|
|
38
|
+
updateState({
|
|
39
|
+
state: tree,
|
|
40
|
+
instruction,
|
|
41
|
+
source: source.data as TreeData,
|
|
42
|
+
target: target.data as TreeData,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
return <Tree {...props} />;
|
|
50
|
+
};
|
|
51
|
+
|
|
24
52
|
const tree = live<TestItem>(createTree());
|
|
25
53
|
const state = new Map<string, Live<{ open: boolean; current: boolean }>>();
|
|
26
54
|
|
|
27
|
-
const meta
|
|
55
|
+
const meta = {
|
|
28
56
|
title: 'ui/react-ui-list/Tree',
|
|
29
57
|
component: Tree,
|
|
58
|
+
render: DefaultStory,
|
|
30
59
|
decorators: [withTheme, withLayout()],
|
|
31
|
-
render: (args) => {
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
return monitorForElements({
|
|
34
|
-
canMonitor: ({ source }) => typeof source.data.id === 'string' && Array.isArray(source.data.path),
|
|
35
|
-
onDrop: ({ location, source }) => {
|
|
36
|
-
// Didn't drop on anything.
|
|
37
|
-
if (!location.current.dropTargets.length) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const target = location.current.dropTargets[0];
|
|
42
|
-
|
|
43
|
-
const instruction: Instruction | null = extractInstruction(target.data);
|
|
44
|
-
if (instruction !== null) {
|
|
45
|
-
updateState({
|
|
46
|
-
state: tree,
|
|
47
|
-
instruction,
|
|
48
|
-
source: source.data as TreeData,
|
|
49
|
-
target: target.data as TreeData,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
}, []);
|
|
55
|
-
|
|
56
|
-
return <Tree {...args} />;
|
|
57
|
-
},
|
|
58
60
|
args: {
|
|
59
61
|
id: tree.id,
|
|
60
|
-
useItems: (
|
|
61
|
-
return
|
|
62
|
+
useItems: (parent?: TestItem) => {
|
|
63
|
+
return parent?.items ?? tree.items;
|
|
62
64
|
},
|
|
63
|
-
getProps: (
|
|
64
|
-
id:
|
|
65
|
-
label:
|
|
66
|
-
icon:
|
|
67
|
-
...((
|
|
68
|
-
parentOf:
|
|
65
|
+
getProps: (parent: TestItem) => ({
|
|
66
|
+
id: parent.id,
|
|
67
|
+
label: parent.name,
|
|
68
|
+
icon: parent.icon,
|
|
69
|
+
...((parent.items?.length ?? 0) > 0 && {
|
|
70
|
+
parentOf: parent.items!.map(({ id }) => id),
|
|
69
71
|
}),
|
|
70
72
|
}),
|
|
71
73
|
isOpen: (_path: string[]) => {
|
|
@@ -104,13 +106,15 @@ const meta: Meta<typeof Tree<TestItem>> = {
|
|
|
104
106
|
object!.current = current;
|
|
105
107
|
},
|
|
106
108
|
},
|
|
107
|
-
}
|
|
109
|
+
} satisfies Meta<typeof Tree<TestItem>>;
|
|
108
110
|
|
|
109
111
|
export default meta;
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
type Story = StoryObj<typeof meta>;
|
|
114
|
+
|
|
115
|
+
export const Default: Story = {};
|
|
112
116
|
|
|
113
|
-
export const Draggable:
|
|
117
|
+
export const Draggable: Story = {
|
|
114
118
|
args: {
|
|
115
119
|
draggable: true,
|
|
116
120
|
},
|
|
@@ -5,29 +5,28 @@
|
|
|
5
5
|
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
6
6
|
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
7
7
|
import {
|
|
8
|
-
attachInstruction,
|
|
9
|
-
extractInstruction,
|
|
10
8
|
type Instruction,
|
|
11
9
|
type ItemMode,
|
|
10
|
+
attachInstruction,
|
|
11
|
+
extractInstruction,
|
|
12
12
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
13
13
|
import { Schema } from 'effect';
|
|
14
|
-
import React, { memo, useCallback, useEffect, useMemo, useRef, useState
|
|
14
|
+
import React, { type FC, type KeyboardEvent, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
|
|
16
16
|
import { type HasId } from '@dxos/echo-schema';
|
|
17
17
|
import { invariant } from '@dxos/invariant';
|
|
18
|
-
import {
|
|
18
|
+
import { TreeItem as NaturalTreeItem, Treegrid } from '@dxos/react-ui';
|
|
19
19
|
import {
|
|
20
20
|
ghostHover,
|
|
21
21
|
hoverableControls,
|
|
22
22
|
hoverableFocusedKeyboardControls,
|
|
23
23
|
hoverableFocusedWithinControls,
|
|
24
|
-
mx,
|
|
25
24
|
} from '@dxos/react-ui-theme';
|
|
26
25
|
|
|
26
|
+
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
27
27
|
import { useTree } from './TreeContext';
|
|
28
28
|
import { TreeItemHeading } from './TreeItemHeading';
|
|
29
29
|
import { TreeItemToggle } from './TreeItemToggle';
|
|
30
|
-
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
31
30
|
|
|
32
31
|
type TreeItemState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
|
|
33
32
|
|
|
@@ -41,22 +40,23 @@ export const TreeDataSchema = Schema.Struct({
|
|
|
41
40
|
});
|
|
42
41
|
|
|
43
42
|
export type TreeData = Schema.Schema.Type<typeof TreeDataSchema>;
|
|
44
|
-
|
|
45
43
|
export const isTreeData = (data: unknown): data is TreeData => Schema.is(TreeDataSchema)(data);
|
|
46
44
|
|
|
45
|
+
export type ColumnRenderer<T extends HasId = any> = FC<{
|
|
46
|
+
item: T;
|
|
47
|
+
path: string[];
|
|
48
|
+
open: boolean;
|
|
49
|
+
menuOpen: boolean;
|
|
50
|
+
setMenuOpen: (open: boolean) => void;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
47
53
|
export type TreeItemProps<T extends HasId = any> = {
|
|
48
54
|
item: T;
|
|
49
55
|
path: string[];
|
|
50
56
|
levelOffset?: number;
|
|
51
57
|
last: boolean;
|
|
52
58
|
draggable?: boolean;
|
|
53
|
-
renderColumns?:
|
|
54
|
-
item: T;
|
|
55
|
-
path: string[];
|
|
56
|
-
open: boolean;
|
|
57
|
-
menuOpen: boolean;
|
|
58
|
-
setMenuOpen: (open: boolean) => void;
|
|
59
|
-
}>;
|
|
59
|
+
renderColumns?: ColumnRenderer<T>;
|
|
60
60
|
canDrop?: (params: { source: TreeData; target: TreeData }) => boolean;
|
|
61
61
|
onOpenChange?: (params: { item: T; path: string[]; open: boolean }) => void;
|
|
62
62
|
onSelect?: (params: { item: T; path: string[]; current: boolean; option: boolean }) => void;
|
|
@@ -223,7 +223,7 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
223
223
|
id={id}
|
|
224
224
|
aria-labelledby={`${id}__label`}
|
|
225
225
|
parentOf={parentOf?.join(Treegrid.PARENT_OF_SEPARATOR)}
|
|
226
|
-
classNames={
|
|
226
|
+
classNames={[
|
|
227
227
|
'grid grid-cols-subgrid col-[tree-row] mbs-0.5 aria-[current]:bg-activeSurface',
|
|
228
228
|
hoverableControls,
|
|
229
229
|
hoverableFocusedKeyboardControls,
|
|
@@ -231,7 +231,7 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
231
231
|
hoverableDescriptionIcons,
|
|
232
232
|
ghostHover,
|
|
233
233
|
className,
|
|
234
|
-
|
|
234
|
+
]}
|
|
235
235
|
data-itemid={id}
|
|
236
236
|
data-testid={testId}
|
|
237
237
|
// NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type KeyboardEvent, type MouseEvent, forwardRef, memo, useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Button, Icon, toLocalizedString, useTranslation
|
|
7
|
+
import { Button, Icon, type Label, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
9
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
10
9
|
|
|
11
10
|
// TODO(wittjosiah): Consider whether there should be a separate disabled prop which was visually distinct
|
|
12
11
|
// rather than just making the item unselectable.
|
|
@@ -55,11 +54,11 @@ export const TreeItemHeading = memo(
|
|
|
55
54
|
data-testid='treeItem.heading'
|
|
56
55
|
variant='ghost'
|
|
57
56
|
density='fine'
|
|
58
|
-
classNames={
|
|
57
|
+
classNames={[
|
|
59
58
|
'grow gap-2 pis-0.5 hover:bg-transparent dark:hover:bg-transparent',
|
|
60
59
|
'disabled:cursor-default disabled:opacity-100',
|
|
61
60
|
className,
|
|
62
|
-
|
|
61
|
+
]}
|
|
63
62
|
disabled={disabled}
|
|
64
63
|
onClick={handleSelect}
|
|
65
64
|
onKeyDown={handleButtonKeydown}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import React, { forwardRef, memo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Button, Icon } from '@dxos/react-ui';
|
|
8
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
9
8
|
|
|
10
9
|
export type TreeItemToggleProps = {
|
|
11
10
|
open?: boolean;
|
|
@@ -23,10 +22,10 @@ export const TreeItemToggle = memo(
|
|
|
23
22
|
aria-expanded={open}
|
|
24
23
|
variant='ghost'
|
|
25
24
|
density='fine'
|
|
26
|
-
classNames={
|
|
25
|
+
classNames={['is-6 pli-0 dx-focus-ring-inset', hidden ? 'hidden' : !isBranch && 'invisible']}
|
|
27
26
|
onClick={onToggle}
|
|
28
27
|
>
|
|
29
|
-
<Icon icon='ph--caret-right--bold' size={3} classNames={
|
|
28
|
+
<Icon icon='ph--caret-right--bold' size={3} classNames={['transition duration-200', open && 'rotate-90']} />
|
|
30
29
|
</Button>
|
|
31
30
|
);
|
|
32
31
|
}),
|