@dxos/plugin-navtree 0.8.3 → 0.8.4-main.1f223c7

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 (92) hide show
  1. package/dist/lib/browser/{app-graph-builder-P7AY3K72.mjs → app-graph-builder-6JDBQ25U.mjs} +5 -5
  2. package/dist/lib/browser/{app-graph-builder-P7AY3K72.mjs.map → app-graph-builder-6JDBQ25U.mjs.map} +3 -3
  3. package/dist/lib/browser/{chunk-IEW3JD5C.mjs → chunk-ERDGQTH5.mjs} +2 -2
  4. package/dist/lib/browser/{chunk-33VLL4IJ.mjs → chunk-WCARAZ7J.mjs} +3 -3
  5. package/dist/lib/browser/{chunk-ZJPL4XJD.mjs → chunk-ZKVY35IG.mjs} +233 -193
  6. package/dist/lib/browser/chunk-ZKVY35IG.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +13 -14
  8. package/dist/lib/browser/index.mjs.map +3 -3
  9. package/dist/lib/browser/{intent-resolver-NAYKKAOP.mjs → intent-resolver-N35ZIHFD.mjs} +6 -6
  10. package/dist/lib/browser/{intent-resolver-NAYKKAOP.mjs.map → intent-resolver-N35ZIHFD.mjs.map} +3 -3
  11. package/dist/lib/browser/{keyboard-DDFATRUG.mjs → keyboard-HMTMA33N.mjs} +3 -3
  12. package/dist/lib/browser/{keyboard-DDFATRUG.mjs.map → keyboard-HMTMA33N.mjs.map} +2 -2
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/{react-surface-H3JRTVNU.mjs → react-surface-5PZ4XJT7.mjs} +5 -5
  15. package/dist/lib/browser/{react-surface-H3JRTVNU.mjs.map → react-surface-5PZ4XJT7.mjs.map} +1 -1
  16. package/dist/lib/browser/{state-J6AKZRB4.mjs → state-P5DGL7KS.mjs} +4 -4
  17. package/dist/lib/browser/state-P5DGL7KS.mjs.map +7 -0
  18. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  19. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  20. package/dist/types/src/capabilities/index.d.ts +6 -6
  21. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  23. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/keyboard.d.ts +1 -1
  25. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  26. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  27. package/dist/types/src/capabilities/state.d.ts +2 -2
  28. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  29. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  30. package/dist/types/src/components/NavTree/NavTree.d.ts +2 -2
  31. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  32. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +25 -2
  33. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  35. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +32 -3
  36. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +1 -1
  38. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  39. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  40. package/dist/types/src/components/Sidebar/L1Panel.d.ts +5 -6
  41. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  42. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +12 -0
  43. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -0
  44. package/dist/types/src/components/Sidebar/index.d.ts +1 -0
  45. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -1
  46. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -4
  47. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  48. package/dist/types/src/components/index.d.ts +1 -1
  49. package/dist/types/src/components/index.d.ts.map +1 -1
  50. package/dist/types/src/components/types.d.ts +2 -2
  51. package/dist/types/src/components/types.d.ts.map +1 -1
  52. package/dist/types/src/experimental/Tree.d.ts +5 -5
  53. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  54. package/dist/types/src/experimental/Tree.stories.d.ts +6 -2
  55. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  56. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  57. package/dist/types/src/translations.d.ts +14 -13
  58. package/dist/types/src/translations.d.ts.map +1 -1
  59. package/dist/types/src/util.d.ts +1 -2
  60. package/dist/types/src/util.d.ts.map +1 -1
  61. package/dist/types/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +42 -43
  63. package/src/NavTreePlugin.tsx +6 -6
  64. package/src/capabilities/app-graph-builder.ts +2 -2
  65. package/src/capabilities/intent-resolver.ts +1 -1
  66. package/src/capabilities/keyboard.ts +2 -2
  67. package/src/capabilities/react-surface.tsx +1 -1
  68. package/src/capabilities/state.ts +3 -2
  69. package/src/components/CommandsDialog/CommandsDialog.tsx +2 -2
  70. package/src/components/CommandsDialog/CommandsTrigger.tsx +3 -5
  71. package/src/components/NavTree/NavTree.stories.tsx +6 -6
  72. package/src/components/NavTree/NavTree.tsx +3 -3
  73. package/src/components/NavTreeContainer.tsx +42 -17
  74. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +3 -3
  75. package/src/components/NavTreeItem/NavTreeItemAction.tsx +6 -6
  76. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +7 -7
  77. package/src/components/Sidebar/L0Menu.tsx +2 -2
  78. package/src/components/Sidebar/L1Panel.tsx +70 -93
  79. package/src/components/Sidebar/L1Tabs.tsx +58 -0
  80. package/src/components/Sidebar/index.ts +1 -0
  81. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +6 -4
  82. package/src/components/index.ts +1 -1
  83. package/src/components/types.ts +3 -2
  84. package/src/experimental/Tree.stories.tsx +13 -11
  85. package/src/experimental/Tree.tsx +15 -16
  86. package/src/testing/app-graph-builder.ts +1 -1
  87. package/src/translations.ts +7 -4
  88. package/src/util.ts +2 -20
  89. package/dist/lib/browser/chunk-ZJPL4XJD.mjs.map +0 -7
  90. package/dist/lib/browser/state-J6AKZRB4.mjs.map +0 -7
  91. /package/dist/lib/browser/{chunk-IEW3JD5C.mjs.map → chunk-ERDGQTH5.mjs.map} +0 -0
  92. /package/dist/lib/browser/{chunk-33VLL4IJ.mjs.map → chunk-WCARAZ7J.mjs.map} +0 -0
@@ -5,68 +5,45 @@
5
5
  import React, { useCallback, useMemo } from 'react';
6
6
 
7
7
  import { type Node } from '@dxos/app-graph';
8
- import { Button, type ButtonProps, Icon, IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { 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
- import { hoverableControlItem, hoverableOpenControlItem, mx } from '@dxos/react-ui-theme';
11
+ import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
12
12
 
13
- import { useLoadDescendents } from '../../hooks';
14
- import { NAVTREE_PLUGIN } from '../../meta';
15
- import { l0ItemType } from '../../util';
13
+ import { meta } from '../../meta';
16
14
  import { useNavTreeContext } from '../NavTreeContext';
17
15
  import { NavTreeItemColumns } from '../NavTreeItem';
18
16
 
19
- const headingBackButtonLabel = 'inset-0 min-is-0 truncate flex items-center pis-2';
20
-
21
- const TitleButton = ({ title, onClick }: { title: string } & Pick<ButtonProps, 'onClick'>) => {
22
- return (
23
- <Button
24
- variant='ghost'
25
- classNames='pli-1 flex-1 group text-start justify-start bs-[--rail-action] font-normal'
26
- onClick={onClick}
27
- >
28
- <Icon icon='ph--caret-left--bold' size={3} />
29
- <span className={mx(headingBackButtonLabel)}>{title}</span>
30
- </Button>
31
- );
32
- };
33
-
34
17
  export type L1PanelProps = {
35
18
  open?: boolean;
36
- item: Node<any>;
37
19
  path: string[];
20
+ item: Node<any>;
38
21
  currentItemId: string;
39
22
  onBack?: () => void;
40
23
  };
41
24
 
42
- const L1Panel = ({ open, item, path, currentItemId, onBack }: L1PanelProps) => {
43
- const { isAlternateTree, setAlternateTree, ...navTreeContext } = useNavTreeContext();
44
- const { t } = useTranslation(NAVTREE_PLUGIN);
25
+ /**
26
+ * Space or settings panel.
27
+ */
28
+ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProps) => {
29
+ const { t } = useTranslation(meta.id);
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
+ const title = toLocalizedString(item.properties.label, t);
45
34
 
46
35
  // TODO(wittjosiah): Support multiple alternate trees.
47
- const alternateTree = navTreeContext.useItems(item, { disposition: 'alternate-tree' })[0];
36
+ const alternateTree = useItems(item, { disposition: 'alternate-tree' })[0];
48
37
  const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
49
- const handleOpen = useCallback(() => setAlternateTree?.(alternatePath, true), [alternatePath, setAlternateTree]);
50
38
  const isAlternate = isAlternateTree?.(alternatePath, item) ?? false;
51
39
  const useAlternateItems = useCallback(
52
40
  (node?: Node, { disposition }: { disposition?: string } = {}) => {
53
41
  // TODO(wittjosiah): Sorting is expensive, limit to necessary items for now.
54
- return navTreeContext.useItems(node, { disposition, sort: node?.id === alternateTree.id });
42
+ return useItems(node, { disposition, sort: node?.id === alternateTree.id });
55
43
  },
56
- [navTreeContext, alternateTree],
44
+ [alternateTree, useItems],
57
45
  );
58
46
 
59
- const handleBack = useCallback(() => {
60
- if (isAlternate) {
61
- setAlternateTree?.(alternatePath, false);
62
- } else {
63
- onBack?.();
64
- }
65
- }, [isAlternate, onBack, alternatePath, setAlternateTree]);
66
-
67
- const title = toLocalizedString(item.properties.label, t);
68
- const backCapable = item.id.startsWith('!') || isAlternate;
69
-
70
47
  return (
71
48
  <Tabs.Tabpanel
72
49
  key={item.id}
@@ -83,27 +60,7 @@ const L1Panel = ({ open, item, path, currentItemId, onBack }: L1PanelProps) => {
83
60
  >
84
61
  {item.id === currentItemId && (
85
62
  <>
86
- <h2 className='flex items-center border-be border-subduedSeparator app-drag pli-1'>
87
- {backCapable ? (
88
- <TitleButton title={title} onClick={handleBack} />
89
- ) : (
90
- <span className='flex-1 truncate min-is-0 pis-6'>{title}</span>
91
- )}
92
- {alternateTree && !isAlternate && (
93
- <IconButton
94
- variant='ghost'
95
- classNames={mx('shrink-0', hoverableControlItem, hoverableOpenControlItem, 'pli-2 pointer-fine:pli-1')}
96
- iconOnly
97
- size={5}
98
- density='coarse'
99
- icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
100
- label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
101
- data-testid='treeView.alternateTreeButton'
102
- onClick={handleOpen}
103
- />
104
- )}
105
- <NavTreeItemColumns path={path} item={item} open density='coarse' />
106
- </h2>
63
+ <L1PanelHeader path={path} item={item} currentItemId={currentItemId} onBack={onBack} />
107
64
  <div role='none' className='overflow-y-auto'>
108
65
  {isAlternate ? (
109
66
  <Tree
@@ -135,41 +92,61 @@ const L1Panel = ({ open, item, path, currentItemId, onBack }: L1PanelProps) => {
135
92
  );
136
93
  };
137
94
 
138
- const L1PanelCollection = ({ item, path, ...props }: L1PanelProps) => {
139
- const { useItems } = useNavTreeContext();
140
- useLoadDescendents(item);
141
- const collectionItems = useItems(item);
142
- const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
143
- return (
144
- <>
145
- {collectionItems
146
- .filter((item) => l0ItemType(item) === 'tab')
147
- .map((item) => (
148
- <L1Panel key={item.id} item={item} path={groupPath} {...props} />
149
- ))}
150
- </>
151
- );
152
- };
95
+ /**
96
+ * Header row.
97
+ */
98
+ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
99
+ const { t } = useTranslation(meta.id);
100
+ const { isAlternateTree, setAlternateTree, ...navTreeContext } = useNavTreeContext();
101
+ const title = toLocalizedString(item.properties.label, t);
153
102
 
154
- export type L1PanelsProps = Pick<L1PanelProps, 'open' | 'currentItemId' | 'onBack'> & {
155
- topLevelItems: Node<any>[];
156
- path: string[];
157
- };
103
+ // TODO(wittjosiah): Support multiple alternate trees.
104
+ const alternateTree = navTreeContext.useItems(item, { disposition: 'alternate-tree' })[0];
105
+ const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
106
+ const isAlternate = isAlternateTree?.(alternatePath, item) ?? false;
107
+ const backCapable = item.id.startsWith('!') || isAlternate;
108
+
109
+ const handleOpen = useCallback(() => {
110
+ setAlternateTree?.(alternatePath, true);
111
+ }, [alternatePath, setAlternateTree]);
158
112
 
159
- export const L1Panels = ({ topLevelItems, onBack, ...props }: L1PanelsProps) => {
113
+ const handleBack = useCallback(() => {
114
+ if (isAlternate) {
115
+ setAlternateTree?.(alternatePath, false);
116
+ } else {
117
+ onBack?.();
118
+ }
119
+ }, [isAlternate, alternatePath, setAlternateTree, onBack]);
120
+
121
+ // TODO(burdon): Reuse same grid as tree?
160
122
  return (
161
- <>
162
- {topLevelItems.map((item) => {
163
- const type = l0ItemType(item);
164
- switch (type) {
165
- case 'collection':
166
- return <L1PanelCollection key={item.id} item={item} {...props} />;
167
- case 'tab':
168
- return <L1Panel key={item.id} item={item} {...props} onBack={onBack} />;
169
- default:
170
- return null;
171
- }
172
- })}
173
- </>
123
+ <div className='flex is-full items-center border-be border-subduedSeparator app-drag density-coarse'>
124
+ <div className='is-6' />
125
+ <h2 className='flex-1 truncate min-is-0'>{title}</h2>
126
+ {(backCapable && (
127
+ <IconButton
128
+ variant='ghost'
129
+ icon='ph--arrow-u-down-left--regular'
130
+ iconOnly
131
+ size={5}
132
+ label={t('button back')}
133
+ classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
134
+ onClick={handleBack}
135
+ />
136
+ )) ||
137
+ (alternateTree && !isAlternate && (
138
+ <IconButton
139
+ data-testid='treeView.alternateTreeButton'
140
+ variant='ghost'
141
+ icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
142
+ iconOnly
143
+ size={5}
144
+ label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
145
+ classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
146
+ onClick={handleOpen}
147
+ />
148
+ ))}
149
+ <NavTreeItemColumns open path={path} item={item} density='coarse' />
150
+ </div>
174
151
  );
175
152
  };
@@ -0,0 +1,58 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useMemo } from 'react';
6
+
7
+ import { type Node } from '@dxos/app-graph';
8
+
9
+ import { useLoadDescendents } from '../../hooks';
10
+ import { l0ItemType } from '../../util';
11
+ import { useNavTreeContext } from '../NavTreeContext';
12
+
13
+ import { L1Panel, type L1PanelProps } from './L1Panel';
14
+
15
+ export type L1TabsProps = Pick<L1PanelProps, 'open' | 'currentItemId' | 'onBack'> & {
16
+ path: string[];
17
+ topLevelItems: Node<any>[];
18
+ };
19
+
20
+ /**
21
+ * Each space is an L1 tab.
22
+ */
23
+ export const L1Tabs = ({ topLevelItems, onBack, ...props }: L1TabsProps) => {
24
+ return (
25
+ <>
26
+ {topLevelItems.map((item) => {
27
+ const type = l0ItemType(item);
28
+ switch (type) {
29
+ case 'tab':
30
+ return <L1Panel key={item.id} item={item} {...props} onBack={onBack} />;
31
+ case 'collection':
32
+ return <L1PanelCollection key={item.id} item={item} {...props} />;
33
+ default:
34
+ return null;
35
+ }
36
+ })}
37
+ </>
38
+ );
39
+ };
40
+
41
+ /**
42
+ *
43
+ */
44
+ const L1PanelCollection = ({ item, path, ...props }: L1PanelProps) => {
45
+ const { useItems } = useNavTreeContext();
46
+ useLoadDescendents(item);
47
+ const collectionItems = useItems(item);
48
+ const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
49
+ return (
50
+ <>
51
+ {collectionItems
52
+ .filter((item) => l0ItemType(item) === 'tab')
53
+ .map((item) => (
54
+ <L1Panel key={item.id} item={item} path={groupPath} {...props} />
55
+ ))}
56
+ </>
57
+ );
58
+ };
@@ -4,3 +4,4 @@
4
4
 
5
5
  export * from './L0Menu';
6
6
  export * from './L1Panel';
7
+ export * from './L1Tabs';
@@ -4,18 +4,20 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
 
9
9
  import { withTheme } from '@dxos/storybook-utils';
10
10
 
11
11
  import { UserAccountAvatar } from './UserAccountAvatar';
12
12
 
13
- const meta: Meta<typeof UserAccountAvatar> = {
13
+ const meta = {
14
14
  title: 'plugins/plugin-navtree/UserAccountAvatar',
15
15
  component: UserAccountAvatar,
16
16
  decorators: [withTheme],
17
- };
17
+ } satisfies Meta<typeof UserAccountAvatar>;
18
18
 
19
19
  export default meta;
20
20
 
21
- export const Default = {};
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ export const Default: Story = {};
@@ -7,4 +7,4 @@ export * from './NavTree';
7
7
  export * from './NavTreeContainer';
8
8
  export * from './NavTreeContext';
9
9
  export * from './NavTreeDocumentTitle';
10
- export * from './types';
10
+ export type * from './types';
@@ -9,9 +9,10 @@ import type { Density } from '@dxos/react-ui';
9
9
  import type { TreeProps } from '@dxos/react-ui-list';
10
10
  import type { MaybePromise } from '@dxos/util';
11
11
 
12
- import type { L1PanelProps } from './Sidebar';
13
12
  import type { FlattenedActions, NavTreeItemGraphNode } from '../types';
14
13
 
14
+ import type { L1PanelProps } from './Sidebar';
15
+
15
16
  export type NavTreeContextValue = Pick<
16
17
  TreeProps<NavTreeItemGraphNode, { disposition?: string; sort?: boolean }>,
17
18
  'useItems' | 'getProps' | 'isCurrent' | 'isOpen' | 'canDrop' | 'onOpenChange' | 'onSelect'
@@ -21,7 +22,7 @@ export type NavTreeContextValue = Pick<
21
22
  topbar?: boolean;
22
23
  popoverAnchorId?: string;
23
24
  renderItemEnd?: FC<{ node: Node; open: boolean }>;
24
- getActions: (node: Node) => FlattenedActions;
25
+ useActions: (node: Node) => FlattenedActions;
25
26
  loadDescendents?: (node: Node) => MaybePromise<void>;
26
27
  isAlternateTree?: (path: string[], item: NavTreeItemGraphNode) => boolean;
27
28
  setAlternateTree?: (path: string[], open: boolean) => void;
@@ -4,11 +4,11 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { House, List, Planet, PlusCircle, Sailboat } from '@phosphor-icons/react';
8
- import { type Meta } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
9
8
  import React, { type JSX, type PropsWithChildren, useEffect, useState } from 'react';
10
9
 
11
10
  import { faker } from '@dxos/random';
11
+ import { Icon, IconButton } from '@dxos/react-ui';
12
12
  import { modalSurface, mx } from '@dxos/react-ui-theme';
13
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
14
14
 
@@ -48,7 +48,7 @@ const data: TreeNodeData[] = [
48
48
  id: faker.string.uuid(),
49
49
  title: 'plugins/Personal Space',
50
50
  color: 'text-green-400',
51
- Icon: House,
51
+ iconName: 'ph--house--regular',
52
52
  children: [
53
53
  {
54
54
  id: faker.string.uuid(),
@@ -83,7 +83,7 @@ const data: TreeNodeData[] = [
83
83
  {
84
84
  id: faker.string.uuid(),
85
85
  title: faker.commerce.productName(),
86
- Icon: Planet,
86
+ iconName: 'ph--planet--regular',
87
87
  children: [
88
88
  {
89
89
  id: faker.string.uuid(),
@@ -94,12 +94,12 @@ const data: TreeNodeData[] = [
94
94
  {
95
95
  id: faker.string.uuid(),
96
96
  title: faker.commerce.productName(),
97
- Icon: Sailboat,
97
+ iconName: 'ph--sailboat--regular',
98
98
  },
99
99
  {
100
100
  id: faker.string.uuid(),
101
101
  title: faker.commerce.productName(),
102
- Icon: Planet,
102
+ iconName: 'ph--planet--regular',
103
103
  },
104
104
  ],
105
105
  },
@@ -181,7 +181,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
181
181
  setItems((items) => {
182
182
  items[0].children?.push({
183
183
  id: faker.string.uuid(),
184
- Icon: Planet,
184
+ iconName: 'ph--planet--regular',
185
185
  title: faker.commerce.productName(),
186
186
  });
187
187
 
@@ -213,20 +213,22 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
213
213
  </div>
214
214
 
215
215
  <div className='flex items-center my-2 px-2 gap-2'>
216
- <PlusCircle onClick={handleCreateSpace} />
216
+ <IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
217
217
  <span className='grow text-sm' onClick={handleCreateSpace}>
218
218
  New space
219
219
  </span>
220
- <List />
220
+ <Icon icon='ph--list--regular' />
221
221
  </div>
222
222
  </div>
223
223
  );
224
224
  };
225
225
 
226
- const meta: Meta = {
226
+ const meta = {
227
227
  title: 'plugins/plugin-navtree/experimental/Tree',
228
228
  component: Tree,
229
229
  decorators: [withTheme, withLayout({ fullscreen: true, classNames: modalSurface })],
230
- };
230
+ } satisfies Meta<typeof Tree>;
231
231
 
232
232
  export default meta;
233
+
234
+ type Story = StoryObj<typeof meta>;
@@ -2,11 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { CaretRight, Circle, File, Folder, type Icon, type IconProps, Plus, UserCircle } from '@phosphor-icons/react';
6
5
  import React, { type HTMLAttributes, type PropsWithChildren } from 'react';
7
6
 
8
- import { type ClassNameValue, type Size } from '@dxos/react-ui';
9
- import { getSize, mx } from '@dxos/react-ui-theme';
7
+ import { type ClassNameValue, Icon, type Size } from '@dxos/react-ui';
8
+ import { mx } from '@dxos/react-ui-theme';
10
9
 
11
10
  // TODO(burdon): Rounded border if first/last.
12
11
  const styles = {
@@ -15,21 +14,21 @@ const styles = {
15
14
  };
16
15
 
17
16
  export const IconButton = ({
18
- Icon,
17
+ iconName,
19
18
  classNames,
20
19
  size = 4,
21
20
  onClick,
22
- ...props
21
+ weight = 'regular',
23
22
  }: {
24
- Icon: Icon;
23
+ iconName: string;
25
24
  classNames?: ClassNameValue;
26
25
  size?: Size;
27
- } & Pick<IconProps, 'weight'> &
28
- Pick<HTMLAttributes<HTMLDivElement>, 'onClick'>) => {
26
+ weight?: 'regular' | 'duotone' | 'fill';
27
+ } & Pick<HTMLAttributes<HTMLDivElement>, 'onClick'>) => {
29
28
  // TODO(burdon): Density aware.
30
29
  return (
31
30
  <div className={mx('flex w-6 h-6 items-center justify-center select-none', classNames)} onClick={onClick}>
32
- <Icon className={mx('cursor-pointer', getSize(size))} {...props} />
31
+ <Icon icon={iconName} classNames='cursor-pointer' size={size} />
33
32
  </div>
34
33
  );
35
34
  };
@@ -37,7 +36,7 @@ export const IconButton = ({
37
36
  export type TreeNodeData = {
38
37
  id: string;
39
38
  title: string;
40
- Icon?: Icon;
39
+ iconName?: string;
41
40
  color?: string;
42
41
  children?: TreeNodeData[];
43
42
  };
@@ -123,7 +122,7 @@ const StateIcon = ({ node, open, selected, active }: TreeNodeProps) => {
123
122
  // TODO(burdon): No animation if opening/closing folder.
124
123
  return (
125
124
  <IconButton
126
- Icon={isActive ? UserCircle : Circle}
125
+ iconName={isActive ? 'ph--user-circle--regular' : 'ph--circle--regular'}
127
126
  size={4}
128
127
  weight={selected?.[id] ? 'fill' : 'duotone'}
129
128
  classNames={mx(
@@ -143,7 +142,7 @@ const OpenIcon = ({
143
142
  return (
144
143
  (children?.length && open && (
145
144
  <IconButton
146
- Icon={CaretRight}
145
+ iconName='ph--caret-right--regular'
147
146
  size={3}
148
147
  classNames={mx('transition duration-200', open?.[id] ? 'rotate-90' : 'transform-none')}
149
148
  onClick={(ev) => {
@@ -157,12 +156,12 @@ const OpenIcon = ({
157
156
 
158
157
  // TODO(burdon): Drag handle,
159
158
  const ItemIcon = ({
160
- node: { children, Icon = children?.length ? Folder : File, color },
159
+ node: { children, iconName = children?.length ? 'ph--folder--regular' : 'ph--file--regular', color },
161
160
  }: Pick<TreeNodeProps, 'node'>) => {
162
161
  return (
163
- (Icon && (
162
+ (iconName && (
164
163
  <IconButton
165
- Icon={Icon}
164
+ iconName={iconName}
166
165
  classNames={color ?? 'text-neutral-700 dark:text-neutral-300'}
167
166
  weight={color ? 'duotone' : 'regular'}
168
167
  />
@@ -173,7 +172,7 @@ const ItemIcon = ({
173
172
  const MenuItem = ({ node: { id }, onMenuAction }: Pick<TreeNodeProps, 'node' | 'onMenuAction'>) => {
174
173
  return (
175
174
  <IconButton
176
- Icon={Plus}
175
+ iconName='ph--plus--regular'
177
176
  classNames='invisible group-hover:visible'
178
177
  onClick={(ev) => {
179
178
  ev.stopPropagation();
@@ -7,7 +7,7 @@ import { Option, pipe } from 'effect';
7
7
 
8
8
  import { type PluginContext } from '@dxos/app-framework';
9
9
  import { log } from '@dxos/log';
10
- import { createExtension, ROOT_ID, type BuilderExtensions } from '@dxos/plugin-graph';
10
+ import { type BuilderExtensions, ROOT_ID, createExtension } from '@dxos/plugin-graph';
11
11
  import { faker } from '@dxos/random';
12
12
 
13
13
  export const storybookGraphBuilders = (context: PluginContext): BuilderExtensions => {
@@ -2,12 +2,14 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { NAVTREE_PLUGIN } from './meta';
5
+ import { type Resource } from '@dxos/react-ui';
6
6
 
7
- export default [
7
+ import { meta } from './meta';
8
+
9
+ export const translations = [
8
10
  {
9
11
  'en-US': {
10
- [NAVTREE_PLUGIN]: {
12
+ [meta.id]: {
11
13
  'app menu label': 'App menu',
12
14
  'open settings label': 'Settings',
13
15
  'open commands label': 'Search commands',
@@ -16,7 +18,8 @@ export default [
16
18
  'node actions menu invoker label': 'More options',
17
19
  'tree item actions label': 'More actions',
18
20
  'empty branch message': ' ',
21
+ 'button back': 'Back to Space',
19
22
  },
20
23
  },
21
24
  },
22
- ];
25
+ ] as const satisfies Resource[];
package/src/util.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Action, type Node, type ReadableGraph, isAction, isActionLike } from '@dxos/app-graph';
5
+ import { type Action, type Node, type ReadableGraph, isActionLike } from '@dxos/app-graph';
6
6
  import { isNonNullable } from '@dxos/util';
7
7
 
8
- import { type FlattenedActions, type NavTreeItemGraphNode } from './types';
8
+ import { type NavTreeItemGraphNode } from './types';
9
9
 
10
10
  export const getParent = (
11
11
  graph: ReadableGraph,
@@ -88,24 +88,6 @@ export const getChildren = (
88
88
  .filter(isNonNullable) as NavTreeItemGraphNode[];
89
89
  };
90
90
 
91
- export const getActions = (graph: ReadableGraph, node: Node): FlattenedActions => {
92
- return graph.getActions(node.id).reduce(
93
- (acc: FlattenedActions, arg) => {
94
- if (arg.properties.disposition === 'item') {
95
- return acc;
96
- }
97
-
98
- acc.actions.push(arg);
99
- if (!isAction(arg)) {
100
- const actionGroup = graph.getActions(arg.id);
101
- acc.groupedActions[arg.id] = actionGroup;
102
- }
103
- return acc;
104
- },
105
- { actions: [], groupedActions: {} },
106
- );
107
- };
108
-
109
91
  export const l0ItemType = (item: Node<any>) => {
110
92
  if (item.properties.disposition === 'collection') {
111
93
  return 'collection';