@dxos/react-ui-list 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/lib/browser/index.mjs +657 -712
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +657 -712
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Accordion/Accordion.stories.d.ts +0 -3
  8. package/dist/types/src/components/Accordion/Accordion.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/List/List.d.ts +2 -2
  10. package/dist/types/src/components/List/List.d.ts.map +1 -1
  11. package/dist/types/src/components/List/List.stories.d.ts +2 -2
  12. package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
  13. package/dist/types/src/components/List/ListItem.d.ts +2 -2
  14. package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
  15. package/dist/types/src/components/List/ListRoot.d.ts +2 -2
  16. package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
  17. package/dist/types/src/components/Tree/Tree.d.ts +10 -6
  18. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  19. package/dist/types/src/components/Tree/Tree.stories.d.ts +9 -28
  20. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Tree/TreeContext.d.ts +18 -9
  22. package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -1
  23. package/dist/types/src/components/Tree/TreeItem.d.ts +20 -3
  24. package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
  25. package/dist/types/src/components/Tree/TreeItemHeading.d.ts +1 -1
  26. package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
  27. package/dist/types/src/components/Tree/index.d.ts +2 -0
  28. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  29. package/dist/types/src/components/Tree/testing.d.ts +2 -2
  30. package/dist/types/src/components/Tree/testing.d.ts.map +1 -1
  31. package/dist/types/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +30 -28
  33. package/src/components/Accordion/Accordion.stories.tsx +2 -5
  34. package/src/components/Accordion/AccordionItem.tsx +3 -3
  35. package/src/components/Accordion/AccordionRoot.tsx +1 -1
  36. package/src/components/List/List.stories.tsx +29 -17
  37. package/src/components/List/ListItem.tsx +13 -13
  38. package/src/components/List/ListRoot.tsx +2 -2
  39. package/src/components/List/testing.ts +2 -2
  40. package/src/components/Tree/Tree.stories.tsx +150 -60
  41. package/src/components/Tree/Tree.tsx +39 -41
  42. package/src/components/Tree/TreeContext.tsx +15 -8
  43. package/src/components/Tree/TreeItem.tsx +99 -51
  44. package/src/components/Tree/TreeItemHeading.tsx +9 -5
  45. package/src/components/Tree/TreeItemToggle.tsx +1 -1
  46. package/src/components/Tree/index.ts +2 -0
  47. package/src/components/Tree/testing.ts +4 -3
@@ -1,73 +1,71 @@
1
1
  //
2
2
  // Copyright 2024 DXOS.org
3
- //
4
3
 
4
+ import { useAtomValue } from '@effect-atom/atom-react';
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { type HasId } from '@dxos/echo/internal';
8
7
  import { Treegrid, type TreegridRootProps } from '@dxos/react-ui';
9
8
 
10
- import { type TreeContextType, TreeProvider } from './TreeContext';
11
- import { TreeItem, type TreeItemProps } from './TreeItem';
9
+ import { type TreeModel, TreeProvider } from './TreeContext';
10
+ import { TreeItemById, type TreeItemByIdProps, type TreeItemProps } from './TreeItem';
12
11
 
13
- export type TreeProps<T extends HasId = any, O = any> = {
14
- root?: T;
12
+ export type TreeProps<T extends { id: string } = any> = {
13
+ model: TreeModel<T>;
14
+ rootId?: string;
15
15
  path?: string[];
16
16
  id: string;
17
- } & TreeContextType<T, O> &
18
- Partial<Pick<TreegridRootProps, 'gridTemplateColumns' | 'classNames'>> &
17
+ } & Partial<Pick<TreegridRootProps, 'gridTemplateColumns' | 'classNames'>> &
19
18
  Pick<
20
19
  TreeItemProps<T>,
21
- 'draggable' | 'renderColumns' | 'canDrop' | 'canSelect' | 'onOpenChange' | 'onSelect' | 'levelOffset'
20
+ | 'draggable'
21
+ | 'renderColumns'
22
+ | 'blockInstruction'
23
+ | 'canDrop'
24
+ | 'canSelect'
25
+ | 'onOpenChange'
26
+ | 'onSelect'
27
+ | 'onItemHover'
28
+ | 'levelOffset'
22
29
  >;
23
30
 
24
- export const Tree = <T extends HasId = any, O = any>({
25
- root,
31
+ export const Tree = <T extends { id: string } = any>({
32
+ model,
33
+ rootId,
26
34
  path,
27
35
  id,
28
- useItems,
29
- getProps,
30
- isOpen,
31
- isCurrent,
32
36
  draggable = false,
33
37
  gridTemplateColumns = '[tree-row-start] 1fr min-content [tree-row-end]',
34
38
  classNames,
35
39
  levelOffset,
36
40
  renderColumns,
41
+ blockInstruction,
37
42
  canDrop,
38
43
  canSelect,
39
44
  onOpenChange,
40
45
  onSelect,
41
- }: TreeProps<T, O>) => {
42
- const context = useMemo(
43
- () => ({
44
- useItems,
45
- getProps,
46
- isOpen,
47
- isCurrent,
48
- }),
49
- [useItems, getProps, isOpen, isCurrent],
50
- );
51
- const items = useItems(root);
46
+ onItemHover,
47
+ }: TreeProps<T>) => {
48
+ const childIds = useAtomValue(model.childIds(rootId));
52
49
  const treePath = useMemo(() => (path ? [...path, id] : [id]), [id, path]);
53
50
 
51
+ const childProps: Omit<TreeItemByIdProps, 'id' | 'last'> = {
52
+ path: treePath,
53
+ levelOffset,
54
+ draggable,
55
+ renderColumns,
56
+ blockInstruction,
57
+ canDrop,
58
+ canSelect,
59
+ onOpenChange,
60
+ onSelect,
61
+ onItemHover,
62
+ };
63
+
54
64
  return (
55
65
  <Treegrid.Root gridTemplateColumns={gridTemplateColumns} classNames={classNames}>
56
- <TreeProvider value={context}>
57
- {items.map((item, index) => (
58
- <TreeItem
59
- key={item.id}
60
- item={item}
61
- last={index === items.length - 1}
62
- path={treePath}
63
- levelOffset={levelOffset}
64
- draggable={draggable}
65
- renderColumns={renderColumns}
66
- canDrop={canDrop}
67
- canSelect={canSelect}
68
- onOpenChange={onOpenChange}
69
- onSelect={onSelect}
70
- />
66
+ <TreeProvider value={model}>
67
+ {childIds.map((childId, index) => (
68
+ <TreeItemById key={childId} id={childId} last={index === childIds.length - 1} {...childProps} />
71
69
  ))}
72
70
  </TreeProvider>
73
71
  </Treegrid.Root>
@@ -2,6 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type Atom } from '@effect-atom/atom-react';
5
6
  import { createContext, useContext } from 'react';
6
7
 
7
8
  import { raise } from '@dxos/debug';
@@ -14,19 +15,25 @@ export type TreeItemDataProps = {
14
15
  className?: string;
15
16
  headingClassName?: string;
16
17
  icon?: string;
17
- iconClassName?: string;
18
+ iconHue?: string;
18
19
  disabled?: boolean;
19
20
  testId?: string;
20
21
  };
21
22
 
22
- export type TreeContextType<T = any, O = any> = {
23
- useItems: (parent?: T, options?: O) => T[];
24
- getProps: (item: T, parent: string[]) => TreeItemDataProps;
25
- isOpen: (path: string[], item: T) => boolean;
26
- isCurrent: (path: string[], item: T) => boolean;
27
- };
23
+ export interface TreeModel<T extends { id: string } = any> {
24
+ /** Atom family: resolve item by ID (content). */
25
+ item: (id: string) => Atom.Atom<T | undefined>;
26
+ /** Atom family: open state keyed by path. */
27
+ itemOpen: (path: string[]) => Atom.Atom<boolean>;
28
+ /** Atom family: current (selected) state keyed by path. */
29
+ itemCurrent: (path: string[]) => Atom.Atom<boolean>;
30
+ /** Atom family: display props for an item at a given path (path includes item's own ID at end). */
31
+ itemProps: (path: string[]) => Atom.Atom<TreeItemDataProps>;
32
+ /** Atom family: outbound child IDs for a parent ID (topology). Undefined = root. */
33
+ childIds: (parentId?: string) => Atom.Atom<string[]>;
34
+ }
28
35
 
29
- const TreeContext = createContext<null | TreeContextType>(null);
36
+ const TreeContext = createContext<TreeModel | null>(null);
30
37
 
31
38
  export const TreeProvider = TreeContext.Provider;
32
39
 
@@ -10,10 +10,20 @@ import {
10
10
  attachInstruction,
11
11
  extractInstruction,
12
12
  } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
13
+ import { useAtomValue } from '@effect-atom/atom-react';
13
14
  import * as Schema from 'effect/Schema';
14
- import React, { type FC, type KeyboardEvent, memo, useCallback, useEffect, useMemo, useRef, useState } from '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
28
  import { TreeItem as NaturalTreeItem, Treegrid } from '@dxos/react-ui';
19
29
  import {
@@ -22,7 +32,8 @@ import {
22
32
  hoverableControls,
23
33
  hoverableFocusedKeyboardControls,
24
34
  hoverableFocusedWithinControls,
25
- } from '@dxos/react-ui-theme';
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 TreeItemState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
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 HasId = any> = FC<{
57
+ export type ColumnRenderer<T extends { id: string } = any> = FC<{
47
58
  item: T;
48
59
  path: string[];
49
60
  open: boolean;
@@ -51,52 +62,63 @@ export type ColumnRenderer<T extends HasId = any> = FC<{
51
62
  setMenuOpen: (open: boolean) => void;
52
63
  }>;
53
64
 
54
- export type TreeItemProps<T extends HasId = any> = {
65
+ export type TreeItemProps<T extends { id: string } = any> = {
55
66
  item: T;
56
67
  path: string[];
57
68
  levelOffset?: number;
58
69
  last: boolean;
59
70
  draggable?: boolean;
60
71
  renderColumns?: ColumnRenderer<T>;
72
+ blockInstruction?: (params: { instruction: Instruction; source: TreeData; target: TreeData }) => boolean;
61
73
  canDrop?: (params: { source: TreeData; target: TreeData }) => boolean;
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
- const RawTreeItem = <T extends HasId = any>({
80
+ const RawTreeItem = <T extends { id: string } = any>({
68
81
  item,
69
- path: _path,
82
+ path: pathProp,
70
83
  levelOffset = 2,
71
84
  last,
72
- draggable: _draggable,
85
+ draggable: draggableProp,
73
86
  renderColumns: Columns,
87
+ blockInstruction,
74
88
  canDrop,
75
89
  canSelect,
76
90
  onOpenChange,
77
91
  onSelect,
92
+ onItemHover,
78
93
  }: TreeItemProps<T>) => {
79
94
  const rowRef = useRef<HTMLDivElement | null>(null);
80
95
  const buttonRef = useRef<HTMLButtonElement | null>(null);
81
96
  const openRef = useRef(false);
82
97
  const cancelExpandRef = useRef<NodeJS.Timeout | null>(null);
83
- const [_state, setState] = useState<TreeItemState>('idle');
98
+ const [_state, setState] = useState<TreeItemDragState>('idle');
84
99
  const [instruction, setInstruction] = useState<Instruction | null>(null);
85
100
  const [menuOpen, setMenuOpen] = useState(false);
86
101
 
87
- const { useItems, getProps, isOpen, isCurrent } = useTree();
88
- const items = useItems(item);
89
- const { id, parentOf, label, className, headingClassName, icon, iconClassName, disabled, testId } = getProps(
90
- item,
91
- _path,
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 { id, parentOf, label, className, headingClassName, icon, iconHue, disabled, testId } = useAtomValue(
111
+ itemPropsAtom(path),
92
112
  );
93
- const path = useMemo(() => [..._path, id], [_path, id]);
94
- const open = isOpen(path, item);
95
- const current = isCurrent(path, item);
113
+ const childIds = useAtomValue(childIdsAtom(item.id));
114
+ const open = useAtomValue(itemOpenAtom(path));
115
+ const current = useAtomValue(itemCurrentAtom(path));
116
+
96
117
  const level = path.length - levelOffset;
97
118
  const isBranch = !!parentOf;
98
119
  const mode: ItemMode = last ? 'last-in-group' : open ? 'expanded' : 'standard';
99
120
  const canSelectItem = canSelect?.({ item, path }) ?? true;
121
+ const data = { id, path, item } satisfies TreeData;
100
122
 
101
123
  const cancelExpand = useCallback(() => {
102
124
  if (cancelExpandRef.current) {
@@ -106,14 +128,12 @@ const RawTreeItem = <T extends HasId = any>({
106
128
  }, []);
107
129
 
108
130
  useEffect(() => {
109
- if (!_draggable) {
131
+ if (!draggableProp) {
110
132
  return;
111
133
  }
112
134
 
113
135
  invariant(buttonRef.current);
114
136
 
115
- const data = { id, path, item } satisfies TreeData;
116
-
117
137
  // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
118
138
  return combine(
119
139
  draggable({
@@ -152,7 +172,11 @@ const RawTreeItem = <T extends HasId = any>({
152
172
  },
153
173
  getIsSticky: () => true,
154
174
  onDrag: ({ self, source }) => {
155
- const instruction = extractInstruction(self.data);
175
+ const desired = extractInstruction(self.data);
176
+ const block =
177
+ desired && blockInstruction?.({ instruction: desired, source: source.data as TreeData, target: data });
178
+ const instruction: Instruction | null =
179
+ block && desired.type !== 'instruction-blocked' ? { type: 'instruction-blocked', desired } : desired;
156
180
 
157
181
  if (source.data.id !== id) {
158
182
  if (instruction?.type === 'make-child' && isBranch && !open && !cancelExpandRef.current) {
@@ -183,7 +207,7 @@ const RawTreeItem = <T extends HasId = any>({
183
207
  },
184
208
  }),
185
209
  );
186
- }, [_draggable, item, id, mode, path, open, canDrop]);
210
+ }, [draggableProp, item, id, mode, path, open, blockInstruction, canDrop]);
187
211
 
188
212
  // Cancel expand on unmount.
189
213
  useEffect(() => () => cancelExpand(), [cancelExpand]);
@@ -221,6 +245,29 @@ const RawTreeItem = <T extends HasId = any>({
221
245
  [isBranch, open, handleOpenToggle, handleSelect],
222
246
  );
223
247
 
248
+ const handleItemHover = useCallback(() => {
249
+ onItemHover?.({ item });
250
+ }, [onItemHover, item]);
251
+
252
+ const handleContextMenu = useCallback(
253
+ (event: MouseEvent) => {
254
+ event.preventDefault();
255
+ setMenuOpen(true);
256
+ },
257
+ [setMenuOpen],
258
+ );
259
+
260
+ const childProps = {
261
+ draggable: draggableProp,
262
+ renderColumns: Columns,
263
+ blockInstruction,
264
+ canDrop,
265
+ canSelect,
266
+ onItemHover,
267
+ onOpenChange,
268
+ onSelect,
269
+ };
270
+
224
271
  return (
225
272
  <>
226
273
  <Treegrid.Row
@@ -229,27 +276,25 @@ const RawTreeItem = <T extends HasId = any>({
229
276
  id={id}
230
277
  aria-labelledby={`${id}__label`}
231
278
  parentOf={parentOf?.join(Treegrid.PARENT_OF_SEPARATOR)}
232
- classNames={[
233
- 'grid grid-cols-subgrid col-[tree-row] mbs-0.5 aria-[current]:bg-activeSurface',
279
+ data-object-id={id}
280
+ data-testid={testId}
281
+ // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
282
+ // without alerting the user (except for in the correct link element). See also:
283
+ // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
284
+ aria-current={current ? ('' as 'page') : undefined}
285
+ classNames={mx(
286
+ 'grid grid-cols-subgrid col-[tree-row] mt-0.5 is-current:bg-active-surface',
234
287
  hoverableControls,
235
288
  hoverableFocusedKeyboardControls,
236
289
  hoverableFocusedWithinControls,
237
290
  hoverableDescriptionIcons,
238
- ghostHover,
239
291
  ghostFocusWithin,
292
+ ghostHover,
240
293
  className,
241
- ]}
242
- data-itemid={id}
243
- data-testid={testId}
244
- // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
245
- // without alerting the user (except for in the correct link element). See also:
246
- // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
247
- aria-current={current ? ('' as 'page') : undefined}
294
+ )}
248
295
  onKeyDown={handleKeyDown}
249
- onContextMenu={(event) => {
250
- event.preventDefault();
251
- setMenuOpen(true);
252
- }}
296
+ onMouseEnter={handleItemHover}
297
+ onContextMenu={handleContextMenu}
253
298
  >
254
299
  <div
255
300
  role='none'
@@ -264,7 +309,7 @@ const RawTreeItem = <T extends HasId = any>({
264
309
  label={label}
265
310
  className={headingClassName}
266
311
  icon={icon}
267
- iconClassName={iconClassName}
312
+ iconHue={iconHue}
268
313
  onSelect={handleSelect}
269
314
  ref={buttonRef}
270
315
  />
@@ -274,22 +319,25 @@ const RawTreeItem = <T extends HasId = any>({
274
319
  </div>
275
320
  </Treegrid.Row>
276
321
  {open &&
277
- items.map((item, index) => (
278
- <TreeItem
279
- key={item.id}
280
- item={item}
281
- path={path}
282
- last={index === items.length - 1}
283
- draggable={_draggable}
284
- renderColumns={Columns}
285
- canDrop={canDrop}
286
- canSelect={canSelect}
287
- onOpenChange={onOpenChange}
288
- onSelect={onSelect}
289
- />
322
+ childIds.map((childId, index) => (
323
+ <TreeItemById key={childId} id={childId} path={path} last={index === childIds.length - 1} {...childProps} />
290
324
  ))}
291
325
  </>
292
326
  );
293
327
  };
294
328
 
295
329
  export const TreeItem = memo(RawTreeItem) as FC<TreeItemProps>;
330
+
331
+ /** Resolves a child ID to an item via the `item` atom and renders a TreeItem. */
332
+ export type TreeItemByIdProps = Omit<TreeItemProps, 'item'> & { id: string };
333
+
334
+ const RawTreeItemById = <T extends { id: string } = any>({ id, ...props }: TreeItemByIdProps) => {
335
+ const { item: itemAtom } = useTree();
336
+ const item = useAtomValue(itemAtom(id)) as T | undefined;
337
+ if (!item) {
338
+ return null;
339
+ }
340
+ return <TreeItem item={item} {...props} />;
341
+ };
342
+
343
+ export const TreeItemById = memo(RawTreeItemById) as FC<TreeItemByIdProps>;
@@ -6,6 +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/ui-theme';
9
10
 
10
11
  // TODO(wittjosiah): Consider whether there should be a separate disabled prop which was visually distinct
11
12
  // rather than just making the item unselectable.
@@ -13,7 +14,7 @@ export type TreeItemHeadingProps = {
13
14
  label: Label;
14
15
  className?: string;
15
16
  icon?: string;
16
- iconClassName?: string;
17
+ iconHue?: string;
17
18
  disabled?: boolean;
18
19
  current?: boolean;
19
20
  onSelect?: (option: boolean) => void;
@@ -21,8 +22,9 @@ export type TreeItemHeadingProps = {
21
22
 
22
23
  export const TreeItemHeading = memo(
23
24
  forwardRef<HTMLButtonElement, TreeItemHeadingProps>(
24
- ({ label, className, icon, iconClassName, disabled, current, onSelect }, forwardedRef) => {
25
+ ({ label, className, icon, iconHue, disabled, current, onSelect }, forwardedRef) => {
25
26
  const { t } = useTranslation();
27
+ const styles = iconHue ? getStyles(iconHue) : undefined;
26
28
 
27
29
  const handleSelect = useCallback(
28
30
  (event: MouseEvent) => {
@@ -56,7 +58,7 @@ export const TreeItemHeading = memo(
56
58
  variant='ghost'
57
59
  density='fine'
58
60
  classNames={[
59
- 'grow gap-2 pis-0.5 hover:bg-transparent dark:hover:bg-transparent',
61
+ 'grow gap-2 ps-0.5 hover:bg-transparent dark:hover:bg-transparent',
60
62
  'disabled:cursor-default disabled:opacity-100',
61
63
  className,
62
64
  ]}
@@ -65,8 +67,10 @@ export const TreeItemHeading = memo(
65
67
  onKeyDown={handleButtonKeydown}
66
68
  {...(current && { 'aria-current': 'location' })}
67
69
  >
68
- {icon && <Icon icon={icon ?? 'ph--placeholder--regular'} size={5} classNames={['mlb-1', iconClassName]} />}
69
- <span className='flex-1 is-0 truncate text-start text-sm font-normal' data-tooltip>
70
+ {icon && (
71
+ <Icon icon={icon ?? 'ph--placeholder--regular'} size={5} classNames={['my-1', styles?.surfaceText]} />
72
+ )}
73
+ <span className='flex-1 w-0 truncate text-start font-normal' data-tooltip>
70
74
  {toLocalizedString(label, t)}
71
75
  </span>
72
76
  </Button>
@@ -23,7 +23,7 @@ export const TreeItemToggle = memo(
23
23
  variant='ghost'
24
24
  density='fine'
25
25
  classNames={[
26
- 'bs-full is-6 pli-0',
26
+ 'h-full w-6 px-0',
27
27
  '[&_svg]:transition-[transform] [&_svg]:duration-200',
28
28
  open && '[&_svg]:rotate-90',
29
29
  hidden ? 'hidden' : !isBranch && 'invisible',
@@ -5,3 +5,5 @@
5
5
  export * from './Tree';
6
6
  export * from './TreeContext';
7
7
  export * from './TreeItem';
8
+ export * from './TreeItemToggle';
9
+ export * from './helpers';
@@ -5,20 +5,21 @@
5
5
  import { type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
6
6
  import * as Schema from 'effect/Schema';
7
7
 
8
- import { type HasId, ObjectId } from '@dxos/echo/internal';
8
+ import { Obj } from '@dxos/echo';
9
9
  import { log } from '@dxos/log';
10
10
  import { faker } from '@dxos/random';
11
11
 
12
12
  import { type TreeData } from './TreeItem';
13
13
 
14
- export type TestItem = HasId & {
14
+ export type TestItem = {
15
+ id: string;
15
16
  name: string;
16
17
  icon?: string;
17
18
  items: TestItem[];
18
19
  };
19
20
 
20
21
  export const TestItemSchema = Schema.Struct({
21
- id: ObjectId,
22
+ id: Obj.ID,
22
23
  name: Schema.String,
23
24
  icon: Schema.optional(Schema.String),
24
25
  items: Schema.mutable(Schema.Array(Schema.suspend((): Schema.Schema<TestItem> => TestItemSchema))),