@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.
Files changed (39) hide show
  1. package/dist/lib/browser/index.mjs +83 -77
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +83 -77
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Accordion/Accordion.stories.d.ts +7 -3
  8. package/dist/types/src/components/Accordion/Accordion.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/Accordion/AccordionItem.d.ts +1 -1
  10. package/dist/types/src/components/Accordion/AccordionItem.d.ts.map +1 -1
  11. package/dist/types/src/components/List/List.d.ts +6 -6
  12. package/dist/types/src/components/List/List.d.ts.map +1 -1
  13. package/dist/types/src/components/List/List.stories.d.ts +11 -4
  14. package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/List/ListItem.d.ts +5 -8
  16. package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
  17. package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
  18. package/dist/types/src/components/Tree/Tree.stories.d.ts +36 -5
  19. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Tree/TreeItem.d.ts +8 -7
  21. package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
  22. package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
  23. package/dist/types/src/components/Tree/TreeItemToggle.d.ts.map +1 -1
  24. package/dist/types/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +19 -20
  26. package/src/components/Accordion/Accordion.stories.tsx +3 -3
  27. package/src/components/Accordion/Accordion.tsx +1 -1
  28. package/src/components/Accordion/AccordionItem.tsx +5 -2
  29. package/src/components/List/List.stories.tsx +12 -11
  30. package/src/components/List/List.tsx +2 -5
  31. package/src/components/List/ListItem.tsx +48 -34
  32. package/src/components/List/ListRoot.tsx +1 -1
  33. package/src/components/Tree/Tree.stories.tsx +49 -45
  34. package/src/components/Tree/TreeItem.tsx +16 -16
  35. package/src/components/Tree/TreeItemHeading.tsx +3 -4
  36. package/src/components/Tree/TreeItemToggle.tsx +2 -3
  37. package/dist/lib/node/index.cjs +0 -886
  38. package/dist/lib/node/index.cjs.map +0 -7
  39. 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",
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.3",
33
- "@dxos/invariant": "0.8.3",
34
- "@dxos/echo-schema": "0.8.3",
35
- "@dxos/live-object": "0.8.3",
36
- "@dxos/log": "0.8.3",
37
- "@dxos/react-ui-text-tooltip": "0.8.3",
38
- "@dxos/react-ui-types": "0.8.3",
39
- "@dxos/util": "0.8.3"
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.14.21",
47
+ "effect": "3.17.7",
46
48
  "react": "~18.2.0",
47
49
  "react-dom": "~18.2.0",
48
- "vite": "5.4.7",
49
- "@dxos/random": "0.8.3",
50
- "@dxos/react-ui-theme": "0.8.3",
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
- "@phosphor-icons/react": "^2.1.5",
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-theme": "0.8.3",
60
- "@dxos/react-ui": "0.8.3"
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 StoryObj, type Meta } from '@storybook/react';
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: Meta<typeof Accordion> = {
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, AccordionItemHeader, AccordionItemBody } from './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
- export const [AccordionItemProvider, useAccordionItemContext] =
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 { createList, TestItemSchema, type TestItemType } from './testing';
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
- export const Default: StoryObj<ListRootProps<TestItemType>> = {
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<ListRootProps<TestItemType>> = {
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, IconButtonProps, ListItem, ListItemRecord };
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 { Icon, type ThemedClassName, ListItem as NaturalListItem } from '@dxos/react-ui';
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
- item: T;
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='none' className='relative'>
167
- <div
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'>> & Omit<IconButtonProps, 'icon'> & { autoHide?: boolean }) => {
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 <IconButton disabled={isDisabled} classNames={[classNames, autoHide && disabled && 'hidden']} {...props} />;
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
- return <IconButton ref={dragHandleRef as any} icon='ph--dots-six-vertical--regular' disabled={disabled} />;
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 { idle, type ItemDragState, type ListItemRecord } from './ListItem';
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 { extractInstruction, type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
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 { live, type Live } from '@dxos/live-object';
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: Meta<typeof Tree<TestItem>> = {
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: (testItem?: TestItem) => {
61
- return testItem?.items ?? tree.items;
62
+ useItems: (parent?: TestItem) => {
63
+ return parent?.items ?? tree.items;
62
64
  },
63
- getProps: (testItem: TestItem) => ({
64
- id: testItem.id,
65
- label: testItem.name,
66
- icon: testItem.icon,
67
- ...((testItem.items?.length ?? 0) > 0 && {
68
- parentOf: testItem.items!.map(({ id }) => id),
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
- export const Default = {};
113
+ type Story = StoryObj<typeof meta>;
114
+
115
+ export const Default: Story = {};
112
116
 
113
- export const Draggable: StoryObj<typeof Tree> = {
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, type FC, type KeyboardEvent } from 'react';
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 { Treegrid, TreeItem as NaturalTreeItem } from '@dxos/react-ui';
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?: FC<{
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={mx(
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, type Label } from '@dxos/react-ui';
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={mx(
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={mx('is-6 pli-0 dx-focus-ring-inset', hidden ? 'hidden' : !isBranch && 'invisible')}
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={mx('transition duration-200', open && 'rotate-90')} />
28
+ <Icon icon='ph--caret-right--bold' size={3} classNames={['transition duration-200', open && 'rotate-90']} />
30
29
  </Button>
31
30
  );
32
31
  }),