@dxos/plugin-navtree 0.8.4-main.fd6878d → 0.8.4-main.fffef41

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 (96) hide show
  1. package/dist/lib/browser/{app-graph-builder-6JDBQ25U.mjs → app-graph-builder-OHSCN3GX.mjs} +11 -10
  2. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-WCARAZ7J.mjs → chunk-5VLWX4PK.mjs} +4 -4
  4. package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-SNGLGBFJ.mjs → chunk-7D2MPHXV.mjs} +128 -114
  6. package/dist/lib/browser/chunk-7D2MPHXV.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-E6MZBCU5.mjs +19 -0
  8. package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +5 -7
  10. package/dist/lib/browser/index.mjs.map +3 -3
  11. package/dist/lib/browser/{intent-resolver-N35ZIHFD.mjs → intent-resolver-YUVXVPHG.mjs} +6 -6
  12. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
  13. package/dist/lib/browser/{keyboard-HMTMA33N.mjs → keyboard-YYTYJW4M.mjs} +4 -4
  14. package/dist/lib/browser/{keyboard-HMTMA33N.mjs.map → keyboard-YYTYJW4M.mjs.map} +1 -1
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/{react-surface-WKWXMM2N.mjs → react-surface-J7MWWAGD.mjs} +10 -10
  17. package/dist/lib/browser/react-surface-J7MWWAGD.mjs.map +7 -0
  18. package/dist/lib/browser/{state-P5DGL7KS.mjs → state-EUSVT6BI.mjs} +7 -7
  19. package/dist/lib/browser/state-EUSVT6BI.mjs.map +7 -0
  20. package/dist/types/src/NavTreePlugin.d.ts +1 -1
  21. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  23. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  25. package/dist/types/src/capabilities/index.d.ts +18 -15
  26. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  27. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  28. package/dist/types/src/capabilities/keyboard.d.ts +1 -1
  29. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  30. package/dist/types/src/capabilities/state.d.ts +2 -2
  31. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  32. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
  33. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  34. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  35. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +26 -4
  36. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  38. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  39. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +32 -4
  40. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  41. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +1 -1
  42. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  43. package/dist/types/src/components/Sidebar/L0Menu.d.ts +2 -0
  44. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  45. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  46. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  47. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
  48. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  49. package/dist/types/src/events.d.ts.map +1 -1
  50. package/dist/types/src/experimental/Tree.d.ts +1 -2
  51. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  52. package/dist/types/src/experimental/Tree.stories.d.ts +7 -4
  53. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  54. package/dist/types/src/meta.d.ts +2 -3
  55. package/dist/types/src/meta.d.ts.map +1 -1
  56. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  57. package/dist/types/src/testing/generator.d.ts +1 -1
  58. package/dist/types/src/testing/generator.d.ts.map +1 -1
  59. package/dist/types/src/types.d.ts +4 -3
  60. package/dist/types/src/types.d.ts.map +1 -1
  61. package/dist/types/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +48 -48
  63. package/src/NavTreePlugin.tsx +70 -71
  64. package/src/capabilities/app-graph-builder.ts +8 -7
  65. package/src/capabilities/capabilities.ts +2 -2
  66. package/src/capabilities/intent-resolver.ts +1 -1
  67. package/src/capabilities/react-surface.tsx +4 -4
  68. package/src/capabilities/state.ts +2 -2
  69. package/src/components/CommandsDialog/CommandsDialog.tsx +14 -6
  70. package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
  71. package/src/components/NavTree/NavTree.stories.tsx +65 -51
  72. package/src/components/NavTree/NavTree.tsx +4 -0
  73. package/src/components/NavTreeContainer.tsx +54 -22
  74. package/src/components/NavTreeDocumentTitle.tsx +2 -2
  75. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +4 -6
  76. package/src/components/NavTreeItem/NavTreeItemAction.tsx +5 -4
  77. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +27 -21
  78. package/src/components/Sidebar/L0Menu.tsx +54 -53
  79. package/src/components/Sidebar/L1Panel.tsx +23 -28
  80. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -8
  81. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
  82. package/src/events.ts +2 -2
  83. package/src/experimental/Tree.stories.tsx +28 -30
  84. package/src/experimental/Tree.tsx +6 -23
  85. package/src/meta.ts +10 -7
  86. package/src/testing/app-graph-builder.ts +19 -18
  87. package/src/testing/generator.ts +2 -2
  88. package/src/types.ts +4 -3
  89. package/dist/lib/browser/app-graph-builder-6JDBQ25U.mjs.map +0 -7
  90. package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
  91. package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
  92. package/dist/lib/browser/chunk-SNGLGBFJ.mjs.map +0 -7
  93. package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
  94. package/dist/lib/browser/intent-resolver-N35ZIHFD.mjs.map +0 -7
  95. package/dist/lib/browser/react-surface-WKWXMM2N.mjs.map +0 -7
  96. package/dist/lib/browser/state-P5DGL7KS.mjs.map +0 -7
@@ -24,15 +24,25 @@ import React, {
24
24
 
25
25
  import { type Node } from '@dxos/app-graph';
26
26
  import { invariant } from '@dxos/invariant';
27
- import { Icon, ListItem, ScrollArea, Tooltip, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
27
+ import { DxAvatar } from '@dxos/lit-ui/react';
28
+ import {
29
+ Icon,
30
+ ListItem,
31
+ ScrollArea,
32
+ type ThemedClassName,
33
+ Tooltip,
34
+ toLocalizedString,
35
+ useMediaQuery,
36
+ useTranslation,
37
+ } from '@dxos/react-ui';
28
38
  import { DropdownMenu, type MenuItem, MenuProvider } from '@dxos/react-ui-menu';
29
39
  import type { StackItemRearrangeHandler } from '@dxos/react-ui-stack';
30
40
  import { Tabs } from '@dxos/react-ui-tabs';
31
41
  import { mx } from '@dxos/react-ui-theme';
32
- import { arrayMove, getFirstTwoRenderableChars } from '@dxos/util';
42
+ import { arrayMove } from '@dxos/util';
33
43
 
34
44
  import { useLoadDescendents } from '../../hooks';
35
- import { NAVTREE_PLUGIN } from '../../meta';
45
+ import { meta } from '../../meta';
36
46
  import { l0ItemType } from '../../util';
37
47
  import { useNavTreeContext } from '../NavTreeContext';
38
48
  import { UserAccountAvatar } from '../UserAccountAvatar';
@@ -62,7 +72,7 @@ type L0ItemProps = L0ItemRootProps & {
62
72
 
63
73
  const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
64
74
  const { tab, isCurrent, onSelect, onTabChange } = useNavTreeContext();
65
- const [isLg] = useMediaQuery('lg', { ssr: false });
75
+ const [isLg] = useMediaQuery('lg');
66
76
 
67
77
  return useCallback(
68
78
  (event: MouseEvent) => {
@@ -92,7 +102,7 @@ const l0Breakpoints: Record<string, string> = {
92
102
  };
93
103
 
94
104
  const l0ItemRoot =
95
- 'group/l0item flex w-full justify-center items-center relative data[type!="collection"]:cursor-pointer app-no-drag dx-focus-ring-group';
105
+ 'group/l0item flex is-full justify-center items-center relative data[type!="collection"]:cursor-pointer app-no-drag dx-focus-ring-group';
96
106
 
97
107
  const l0ItemContent = 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm';
98
108
 
@@ -103,55 +113,45 @@ const L0ItemRoot = forwardRef<HTMLElement, PropsWithChildren<L0ItemRootProps>>(
103
113
  const type = l0ItemType(item);
104
114
  const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
105
115
 
106
- const { t } = useTranslation(NAVTREE_PLUGIN);
116
+ const { t } = useTranslation(meta.id);
107
117
  const localizedString = toLocalizedString(item.properties.label, t);
108
118
 
109
119
  const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
110
120
  const rootProps =
111
121
  type === 'tab'
112
- ? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
122
+ ? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-object-id': id }
113
123
  : type !== 'collection'
114
- ? { onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
115
- : { onClick: handleClick };
116
-
117
- const Root = type === 'collection' ? 'h2' : type === 'tab' ? Tabs.TabPrimitive : 'button';
124
+ ? { onClick: handleClick, 'data-testid': testId, 'data-object-id': id }
125
+ : { onClick: handleClick, role: 'button' };
118
126
 
119
127
  return (
120
128
  <Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
121
- <Root
129
+ <Tabs.TabPrimitive
122
130
  {...(rootProps as any)}
123
131
  data-type={type}
124
132
  className={mx(l0ItemRoot, l0Breakpoints[item.properties.l0Breakpoint])}
125
133
  ref={forwardedRef}
126
134
  >
127
135
  {children}
128
- </Root>
136
+ </Tabs.TabPrimitive>
129
137
  </Tooltip.Trigger>
130
138
  );
131
139
  },
132
140
  );
133
141
 
134
- const L0Avatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
135
- const { t } = useTranslation(NAVTREE_PLUGIN);
136
- const type = l0ItemType(item);
137
- const hue = item.properties.hue ?? null;
138
- const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
139
- const localizedString = toLocalizedString(item.properties.label, t);
140
- const avatarValue = useMemo(
141
- () => (type === 'tab' ? getFirstTwoRenderableChars(localizedString).join('').toUpperCase() : []),
142
- [type, item.properties.label, t],
143
- );
144
-
145
- return (
146
- <span role='img' className='place-self-center text-xl font-light' {...hueFgStyle}>
147
- {avatarValue}
148
- </span>
149
- );
150
- };
142
+ export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) => (
143
+ <div
144
+ role='none'
145
+ className={mx(
146
+ 'hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-2 is-1 bg-accentSurface rounded-ie',
147
+ classNames,
148
+ )}
149
+ />
150
+ );
151
151
 
152
152
  // TODO(burdon): Factor out pinned (non-draggable) items.
153
153
  const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
154
- const { t } = useTranslation(NAVTREE_PLUGIN);
154
+ const { t } = useTranslation(meta.id);
155
155
  const itemElement = useRef<HTMLElement | null>(null);
156
156
  const [closestEdge, setEdge] = useState<Edge | null>(null);
157
157
  const localizedString = toLocalizedString(item.properties.label, t);
@@ -228,10 +228,7 @@ const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
228
228
  >
229
229
  <ItemAvatar item={item} />
230
230
  </div>
231
- <div
232
- role='none'
233
- className='hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-2 is-1 bg-accentSurface rounded-ie'
234
- />
231
+ <L0ItemActiveTabIndicator />
235
232
  <span id={`${item.id}__label`} className='sr-only'>
236
233
  {localizedString}
237
234
  </span>
@@ -241,15 +238,18 @@ const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
241
238
  };
242
239
 
243
240
  const ItemAvatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
241
+ const { t } = useTranslation(meta.id);
244
242
  const type = l0ItemType(item);
245
243
  if (item.properties.icon) {
246
244
  const hue = item.properties.hue ?? null;
247
245
  const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
248
- return <Icon icon={item.properties.icon} size={5} {...hueFgStyle} />;
246
+ return <Icon icon={item.properties.icon} size={6} {...hueFgStyle} />;
249
247
  }
250
248
 
251
249
  if (type === 'tab' && item.properties.disposition !== 'pin-end') {
252
- return <L0Avatar item={item} />;
250
+ const hue = item.properties.hue ?? null;
251
+ const localizedString = toLocalizedString(item.properties.label, t);
252
+ return <DxAvatar hue={hue} hueVariant='surface' variant='square' size={12} fallback={localizedString} />;
253
253
  }
254
254
 
255
255
  return null;
@@ -287,7 +287,7 @@ const L0Collection = ({ item, path }: L0ItemProps) => {
287
287
  role='group'
288
288
  className='contents group/l0c'
289
289
  aria-labelledby={`${item.id}__label`}
290
- data-itemid={id}
290
+ data-object-id={id}
291
291
  data-testid={testId}
292
292
  >
293
293
  {collectionItems.map((collectionItem) => (
@@ -317,7 +317,7 @@ export type L0MenuProps = {
317
317
  };
318
318
 
319
319
  export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountItem, parent, path }: L0MenuProps) => {
320
- const { t } = useTranslation(NAVTREE_PLUGIN);
320
+ const { t } = useTranslation(meta.id);
321
321
 
322
322
  return (
323
323
  <Tabs.Tablist
@@ -331,20 +331,22 @@ export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountIte
331
331
  <MenuProvider>
332
332
  <DropdownMenu.Root group={parent} items={menuActions}>
333
333
  <Tooltip.Trigger content={t('app menu label')} side='right' asChild>
334
- <DropdownMenu.Trigger
335
- data-testid='spacePlugin.addSpace'
336
- className={mx(l0ItemRoot, 'grid place-items-center')}
337
- >
338
- <div
339
- role='none'
340
- className={mx(
341
- l0ItemContent,
342
- 'is-[--rail-action] bs-[--rail-action] group-hover/l0item:bg-hoverSurface',
343
- )}
334
+ <Tabs.TabPrimitive value='options' asChild role='button'>
335
+ <DropdownMenu.Trigger
336
+ data-testid='spacePlugin.addSpace'
337
+ className={mx(l0ItemRoot, 'grid place-items-center dx-focus-ring-group')}
344
338
  >
345
- <Icon icon='ph--list--regular' size={5} />
346
- </div>
347
- </DropdownMenu.Trigger>
339
+ <div
340
+ role='none'
341
+ className={mx(
342
+ l0ItemContent,
343
+ 'is-[--rail-action] bs-[--rail-action] group-hover/l0item:bg-hoverSurface',
344
+ )}
345
+ >
346
+ <Icon icon='ph--list--regular' size={5} />
347
+ </div>
348
+ </DropdownMenu.Trigger>
349
+ </Tabs.TabPrimitive>
348
350
  </Tooltip.Trigger>
349
351
  </DropdownMenu.Root>
350
352
  </MenuProvider>
@@ -396,7 +398,6 @@ export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountIte
396
398
  </L0ItemRoot>
397
399
  </div>
398
400
  )}
399
-
400
401
  <div
401
402
  role='none'
402
403
  className='hidden [body[data-platform="darwin"]_&]:block absolute block-start-0 is-[calc(var(--l0-size)-1px)] bs-[calc(40px+0.25rem)]'
@@ -5,7 +5,7 @@
5
5
  import React, { useCallback, useMemo } from 'react';
6
6
 
7
7
  import { type Node } from '@dxos/app-graph';
8
- import { IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { DensityProvider, IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
9
  import { Tree } from '@dxos/react-ui-list';
10
10
  import { Tabs } from '@dxos/react-ui-tabs';
11
11
  import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
@@ -28,11 +28,10 @@ export type L1PanelProps = {
28
28
  export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProps) => {
29
29
  const { t } = useTranslation(meta.id);
30
30
  const { isAlternateTree, ...navTreeContext } = useNavTreeContext();
31
- // NOTE(burdon): I'm very uneasy about this pattern of declaring hooks as context props.
32
- const { useItems } = navTreeContext;
33
31
  const title = toLocalizedString(item.properties.label, t);
34
32
 
35
33
  // TODO(wittjosiah): Support multiple alternate trees.
34
+ const { useItems } = navTreeContext;
36
35
  const alternateTree = useItems(item, { disposition: 'alternate-tree' })[0];
37
36
  const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
38
37
  const isAlternate = isAlternateTree?.(alternatePath, item) ?? false;
@@ -56,22 +55,22 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
56
55
  ]}
57
56
  tabIndex={-1}
58
57
  aria-label={title}
59
- {...(!open && { inert: 'true' })}
58
+ {...(!open && { inert: true })}
60
59
  >
61
60
  {item.id === currentItemId && (
62
- <>
61
+ <DensityProvider density='fine'>
63
62
  <L1PanelHeader path={path} item={item} currentItemId={currentItemId} onBack={onBack} />
64
63
  <div role='none' className='overflow-y-auto'>
65
64
  {isAlternate ? (
66
65
  <Tree
67
66
  {...navTreeContext}
68
- useItems={useAlternateItems}
69
67
  id={alternateTree.id}
70
68
  root={alternateTree}
71
69
  path={alternatePath}
72
70
  levelOffset={5}
73
71
  gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
74
72
  renderColumns={NavTreeItemColumns}
73
+ useItems={useAlternateItems}
75
74
  />
76
75
  ) : (
77
76
  <Tree
@@ -80,13 +79,13 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
80
79
  root={item}
81
80
  path={path}
82
81
  levelOffset={5}
83
- draggable
84
82
  gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
85
83
  renderColumns={NavTreeItemColumns}
84
+ draggable
86
85
  />
87
86
  )}
88
87
  </div>
89
- </>
88
+ </DensityProvider>
90
89
  )}
91
90
  </Tabs.Tabpanel>
92
91
  );
@@ -123,31 +122,27 @@ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
123
122
  <div className='flex is-full items-center border-be border-subduedSeparator app-drag density-coarse'>
124
123
  <div className='is-6' />
125
124
  <h2 className='flex-1 truncate min-is-0'>{title}</h2>
126
- <div>
127
- {(backCapable && (
125
+ {(backCapable && (
126
+ <IconButton
127
+ variant='ghost'
128
+ icon='ph--arrow-u-down-left--regular'
129
+ iconOnly
130
+ label={t('button back')}
131
+ classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
132
+ onClick={handleBack}
133
+ />
134
+ )) ||
135
+ (alternateTree && !isAlternate && (
128
136
  <IconButton
137
+ data-testid='treeView.alternateTreeButton'
129
138
  variant='ghost'
130
- icon='ph--arrow-u-down-left--regular'
139
+ icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
131
140
  iconOnly
132
- size={5}
133
- label={t('button back')}
141
+ label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
134
142
  classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
135
- onClick={handleBack}
143
+ onClick={handleOpen}
136
144
  />
137
- )) ||
138
- (alternateTree && !isAlternate && (
139
- <IconButton
140
- data-testid='treeView.alternateTreeButton'
141
- variant='ghost'
142
- icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
143
- iconOnly
144
- size={5}
145
- label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
146
- classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
147
- onClick={handleOpen}
148
- />
149
- )) || <div />}
150
- </div>
145
+ ))}
151
146
  <NavTreeItemColumns open path={path} item={item} density='coarse' />
152
147
  </div>
153
148
  );
@@ -2,20 +2,21 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
 
7
- import { type Meta } from '@storybook/react-vite';
8
-
9
- import { withTheme } from '@dxos/storybook-utils';
7
+ import { withTheme } from '@dxos/react-ui/testing';
10
8
 
11
9
  import { UserAccountAvatar } from './UserAccountAvatar';
12
10
 
13
- const meta: Meta<typeof UserAccountAvatar> = {
11
+ const meta = {
14
12
  title: 'plugins/plugin-navtree/UserAccountAvatar',
15
- component: UserAccountAvatar,
13
+
16
14
  decorators: [withTheme],
17
- };
15
+ component: UserAccountAvatar,
16
+ } satisfies Meta<typeof UserAccountAvatar>;
18
17
 
19
18
  export default meta;
20
19
 
21
- export const Default = {};
20
+ type Story = StoryObj<typeof meta>;
21
+
22
+ export const Default: Story = {};
@@ -7,6 +7,8 @@ import React from 'react';
7
7
  import { Avatar, type AvatarStatus, type Size } from '@dxos/react-ui';
8
8
  import { hexToFallback } from '@dxos/util';
9
9
 
10
+ import { L0ItemActiveTabIndicator } from '../Sidebar';
11
+
10
12
  export type UserAccountAvatarProps = {
11
13
  size?: Size;
12
14
  userId?: string;
@@ -19,17 +21,23 @@ export const UserAccountAvatar = ({ size, userId, hue, emoji, status }: UserAcco
19
21
  const fallbackValue = hexToFallback(userId ?? '0');
20
22
 
21
23
  return (
22
- <div className='grid place-items-center' data-joyride='welcome/account'>
23
- <Avatar.Root>
24
- <Avatar.Content
25
- variant='circle'
26
- size={size ?? 12}
27
- status={status ?? 'active'}
28
- hue={hue || fallbackValue.hue}
29
- fallback={emoji || fallbackValue.emoji}
30
- data-testid='treeView.userAccount'
31
- />
32
- </Avatar.Root>
33
- </div>
24
+ <>
25
+ <L0ItemActiveTabIndicator classNames='inset-block-6' />
26
+ <div
27
+ className='grid place-items-center dx-focus-ring-group-indicator rounded-full'
28
+ data-joyride='welcome/account'
29
+ >
30
+ <Avatar.Root>
31
+ <Avatar.Content
32
+ variant='circle'
33
+ size={size ?? 12}
34
+ status={status ?? 'active'}
35
+ hue={hue || fallbackValue.hue}
36
+ fallback={emoji || fallbackValue.emoji}
37
+ data-testid='treeView.userAccount'
38
+ />
39
+ </Avatar.Root>
40
+ </div>
41
+ </>
34
42
  );
35
43
  };
package/src/events.ts CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  import { Events } from '@dxos/app-framework';
6
6
 
7
- import { NAVTREE_PLUGIN } from './meta';
7
+ import { meta } from './meta';
8
8
 
9
9
  export namespace NavTreeEvents {
10
- export const StateReady = Events.createStateEvent(NAVTREE_PLUGIN);
10
+ export const StateReady = Events.createStateEvent(meta.id);
11
11
  }
@@ -2,15 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta } from '@storybook/react-vite';
8
6
  import React, { type JSX, type PropsWithChildren, useEffect, useState } from 'react';
9
7
 
10
8
  import { faker } from '@dxos/random';
11
9
  import { Icon, IconButton } from '@dxos/react-ui';
12
- import { modalSurface, mx } from '@dxos/react-ui-theme';
13
- import { withLayout, withTheme } from '@dxos/storybook-utils';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+ import { mx } from '@dxos/react-ui-theme';
14
12
 
15
13
  import { type ItemMap, Tree, type TreeNodeData, type TreeProps, visitNodes, visitor } from './Tree';
16
14
 
@@ -32,7 +30,7 @@ const Container = ({ children, sidebar }: PropsWithChildren<{ sidebar: JSX.Eleme
32
30
  <div className='flex'>
33
31
  {/* TODO(burdon): Custom thin scrollbar. */}
34
32
  {/* TODO(burdon): Horizontal scrolling within navtree? */}
35
- <div className='flex flex-col overflow-y-auto w-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
33
+ <div className='flex flex-col overflow-y-auto is-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
36
34
  <div className='flex flex-col grow overflow-hidden'>{children}</div>
37
35
  </div>
38
36
  );
@@ -105,27 +103,6 @@ const data: TreeNodeData[] = [
105
103
  },
106
104
  ];
107
105
 
108
- export const Default = () => {
109
- return <Container sidebar={<Sidebar mutate />} />;
110
- };
111
-
112
- export const Visitor = () => {
113
- const [items] = useState(data);
114
- const root = items[0];
115
- const [openItems] = useState<ItemMap>({ [root.children![0].id]: true });
116
- return (
117
- <div className='flex flex-col'>
118
- {Array.from(visitor(root, openItems)).map(({ node: { id, title }, depth }) => (
119
- <div key={id} className='grid grid-cols-[40px_400px_400px] font-mono'>
120
- <div>{depth}</div>
121
- <div>{id}</div>
122
- <div>{title}</div>
123
- </div>
124
- ))}
125
- </div>
126
- );
127
- };
128
-
129
106
  const Sidebar = ({ mutate }: { mutate?: boolean }) => {
130
107
  const [items, setItems] = useState(data);
131
108
  const root = items[0];
@@ -212,7 +189,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
212
189
  />
213
190
  </div>
214
191
 
215
- <div className='flex items-center my-2 px-2 gap-2'>
192
+ <div className='flex items-center my-2 pli-2 gap-2'>
216
193
  <IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
217
194
  <span className='grow text-sm' onClick={handleCreateSpace}>
218
195
  New space
@@ -223,10 +200,31 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
223
200
  );
224
201
  };
225
202
 
226
- const meta: Meta = {
203
+ const meta = {
227
204
  title: 'plugins/plugin-navtree/experimental/Tree',
228
205
  component: Tree,
229
- decorators: [withTheme, withLayout({ fullscreen: true, classNames: modalSurface })],
230
- };
206
+ decorators: [withTheme, withLayout({ container: 'column' })],
207
+ } satisfies Meta<typeof Tree>;
231
208
 
232
209
  export default meta;
210
+
211
+ export const Default = () => {
212
+ return <Container sidebar={<Sidebar mutate />} />;
213
+ };
214
+
215
+ export const Visitor = () => {
216
+ const [items] = useState(data);
217
+ const root = items[0];
218
+ const [openItems] = useState<ItemMap>({ [root.children![0].id]: true });
219
+ return (
220
+ <div className='flex flex-col'>
221
+ {Array.from(visitor(root, openItems)).map(({ node: { id, title }, depth }) => (
222
+ <div key={id} className='grid grid-cols-[40px_400px_400px] font-mono'>
223
+ <div>{depth}</div>
224
+ <div>{id}</div>
225
+ <div>{title}</div>
226
+ </div>
227
+ ))}
228
+ </div>
229
+ );
230
+ };
@@ -7,27 +7,19 @@ import React, { type HTMLAttributes, type PropsWithChildren } from 'react';
7
7
  import { type ClassNameValue, Icon, type Size } from '@dxos/react-ui';
8
8
  import { mx } from '@dxos/react-ui-theme';
9
9
 
10
- // TODO(burdon): Rounded border if first/last.
11
- const styles = {
12
- hover: 'hover:bg-slate-400 dark:hover:bg-slate-800',
13
- selected: '!bg-slate-300 dark:!bg-slate-700',
14
- };
15
-
16
10
  export const IconButton = ({
17
11
  iconName,
18
12
  classNames,
19
13
  size = 4,
20
14
  onClick,
21
- weight = 'regular',
22
15
  }: {
23
16
  iconName: string;
24
17
  classNames?: ClassNameValue;
25
18
  size?: Size;
26
- weight?: 'regular' | 'duotone' | 'fill';
27
19
  } & Pick<HTMLAttributes<HTMLDivElement>, 'onClick'>) => {
28
20
  // TODO(burdon): Density aware.
29
21
  return (
30
- <div className={mx('flex w-6 h-6 items-center justify-center select-none', classNames)} onClick={onClick}>
22
+ <div className={mx('flex is-6 bs-6 items-center justify-center select-none', classNames)} onClick={onClick}>
31
23
  <Icon icon={iconName} classNames='cursor-pointer' size={size} />
32
24
  </div>
33
25
  );
@@ -124,7 +116,6 @@ const StateIcon = ({ node, open, selected, active }: TreeNodeProps) => {
124
116
  <IconButton
125
117
  iconName={isActive ? 'ph--user-circle--regular' : 'ph--circle--regular'}
126
118
  size={4}
127
- weight={selected?.[id] ? 'fill' : 'duotone'}
128
119
  classNames={mx(
129
120
  'text-slate-500',
130
121
  !isChildActive && 'opacity-0 transition duration-500',
@@ -158,15 +149,7 @@ const OpenIcon = ({
158
149
  const ItemIcon = ({
159
150
  node: { children, iconName = children?.length ? 'ph--folder--regular' : 'ph--file--regular', color },
160
151
  }: Pick<TreeNodeProps, 'node'>) => {
161
- return (
162
- (iconName && (
163
- <IconButton
164
- iconName={iconName}
165
- classNames={color ?? 'text-neutral-700 dark:text-neutral-300'}
166
- weight={color ? 'duotone' : 'regular'}
167
- />
168
- )) || <div />
169
- );
152
+ return (iconName && <IconButton iconName={iconName} classNames={color ?? 'text-subdued'} />) || <div />;
170
153
  };
171
154
 
172
155
  const MenuItem = ({ node: { id }, onMenuAction }: Pick<TreeNodeProps, 'node' | 'onMenuAction'>) => {
@@ -210,9 +193,9 @@ export const TreeNodeRow = (props: TreeNodeProps & { className?: string }) => {
210
193
  <Grid
211
194
  style={{ paddingLeft: `${(depth - 1) * 24}px` }}
212
195
  className={mx(
213
- 'group w-full items-center cursor-pointer',
214
- styles.hover,
215
- selected?.[id] && styles.selected,
196
+ 'group is-full items-center cursor-pointer bg-hoverSurface',
197
+ // TODO(burdon): Use data-active.
198
+ selected?.[id] && 'bg-activeSurface',
216
199
  className,
217
200
  )}
218
201
  onClick={() => selected && onChangeSelected?.(id, !selected[id])}
@@ -255,7 +238,7 @@ export const TreeChildNodes = ({
255
238
  } = props;
256
239
  return (
257
240
  <div className={mx('flex flex-col mt-0.5 gap-0.5', className)}>
258
- {children?.map((child, i) => (
241
+ {children?.map((child) => (
259
242
  <TreeNode key={child.id} {...props} depth={depth + 1} node={child} ancestors={[...ancestors, props.node]} />
260
243
  ))}
261
244
  </div>
package/src/meta.ts CHANGED
@@ -3,15 +3,18 @@
3
3
  //
4
4
 
5
5
  import { type PluginMeta } from '@dxos/app-framework';
6
+ import { trim } from '@dxos/util';
6
7
 
7
- export const NAVTREE_PLUGIN = 'dxos.org/plugin/navtree';
8
+ export const meta: PluginMeta = {
9
+ id: 'dxos.org/plugin/navtree',
10
+ name: 'Navtree',
11
+ description: trim`
12
+ Hierarchical navigation tree for browsing spaces, folders, and objects.
13
+ Provides sidebar navigation with collapsible sections and quick access to workspace content.
14
+ `,
15
+ };
8
16
 
9
17
  // TODO(wittjosiah): Factor out.
10
18
  export const KEY_BINDING = 'KeyBinding';
11
19
  // TODO(wittjosiah): Factor out.
12
- export const COMMANDS_DIALOG = `${NAVTREE_PLUGIN}/CommandsDialog`;
13
-
14
- export const meta: PluginMeta = {
15
- id: NAVTREE_PLUGIN,
16
- name: 'Navtree',
17
- };
20
+ export const COMMANDS_DIALOG = `${meta.id}/CommandsDialog`;