@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.
- package/dist/lib/browser/{app-graph-builder-6JDBQ25U.mjs → app-graph-builder-OHSCN3GX.mjs} +11 -10
- package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-WCARAZ7J.mjs → chunk-5VLWX4PK.mjs} +4 -4
- package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +7 -0
- package/dist/lib/browser/{chunk-SNGLGBFJ.mjs → chunk-7D2MPHXV.mjs} +128 -114
- package/dist/lib/browser/chunk-7D2MPHXV.mjs.map +7 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs +19 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -7
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-N35ZIHFD.mjs → intent-resolver-YUVXVPHG.mjs} +6 -6
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
- package/dist/lib/browser/{keyboard-HMTMA33N.mjs → keyboard-YYTYJW4M.mjs} +4 -4
- package/dist/lib/browser/{keyboard-HMTMA33N.mjs.map → keyboard-YYTYJW4M.mjs.map} +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-WKWXMM2N.mjs → react-surface-J7MWWAGD.mjs} +10 -10
- package/dist/lib/browser/react-surface-J7MWWAGD.mjs.map +7 -0
- package/dist/lib/browser/{state-P5DGL7KS.mjs → state-EUSVT6BI.mjs} +7 -7
- package/dist/lib/browser/state-EUSVT6BI.mjs.map +7 -0
- package/dist/types/src/NavTreePlugin.d.ts +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +18 -15
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/state.d.ts +2 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +26 -4
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +32 -4
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +2 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts +1 -2
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +7 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +1 -1
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -48
- package/src/NavTreePlugin.tsx +70 -71
- package/src/capabilities/app-graph-builder.ts +8 -7
- package/src/capabilities/capabilities.ts +2 -2
- package/src/capabilities/intent-resolver.ts +1 -1
- package/src/capabilities/react-surface.tsx +4 -4
- package/src/capabilities/state.ts +2 -2
- package/src/components/CommandsDialog/CommandsDialog.tsx +14 -6
- package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
- package/src/components/NavTree/NavTree.stories.tsx +65 -51
- package/src/components/NavTree/NavTree.tsx +4 -0
- package/src/components/NavTreeContainer.tsx +54 -22
- package/src/components/NavTreeDocumentTitle.tsx +2 -2
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +4 -6
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +5 -4
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +27 -21
- package/src/components/Sidebar/L0Menu.tsx +54 -53
- package/src/components/Sidebar/L1Panel.tsx +23 -28
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -8
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
- package/src/events.ts +2 -2
- package/src/experimental/Tree.stories.tsx +28 -30
- package/src/experimental/Tree.tsx +6 -23
- package/src/meta.ts +10 -7
- package/src/testing/app-graph-builder.ts +19 -18
- package/src/testing/generator.ts +2 -2
- package/src/types.ts +4 -3
- package/dist/lib/browser/app-graph-builder-6JDBQ25U.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
- package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
- package/dist/lib/browser/chunk-SNGLGBFJ.mjs.map +0 -7
- package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-N35ZIHFD.mjs.map +0 -7
- package/dist/lib/browser/react-surface-WKWXMM2N.mjs.map +0 -7
- 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 {
|
|
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
|
|
42
|
+
import { arrayMove } from '@dxos/util';
|
|
33
43
|
|
|
34
44
|
import { useLoadDescendents } from '../../hooks';
|
|
35
|
-
import {
|
|
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'
|
|
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
|
|
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(
|
|
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-
|
|
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-
|
|
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
|
-
<
|
|
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
|
-
</
|
|
136
|
+
</Tabs.TabPrimitive>
|
|
129
137
|
</Tooltip.Trigger>
|
|
130
138
|
);
|
|
131
139
|
},
|
|
132
140
|
);
|
|
133
141
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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(
|
|
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
|
-
<
|
|
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={
|
|
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
|
-
|
|
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-
|
|
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(
|
|
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
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
<
|
|
346
|
-
|
|
347
|
-
|
|
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:
|
|
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
|
-
|
|
127
|
-
|
|
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--
|
|
139
|
+
icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
|
|
131
140
|
iconOnly
|
|
132
|
-
|
|
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={
|
|
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 '@
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
|
|
7
|
-
import {
|
|
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
|
|
11
|
+
const meta = {
|
|
14
12
|
title: 'plugins/plugin-navtree/UserAccountAvatar',
|
|
15
|
-
|
|
13
|
+
|
|
16
14
|
decorators: [withTheme],
|
|
17
|
-
|
|
15
|
+
component: UserAccountAvatar,
|
|
16
|
+
} satisfies Meta<typeof UserAccountAvatar>;
|
|
18
17
|
|
|
19
18
|
export default meta;
|
|
20
19
|
|
|
21
|
-
|
|
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
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 {
|
|
7
|
+
import { meta } from './meta';
|
|
8
8
|
|
|
9
9
|
export namespace NavTreeEvents {
|
|
10
|
-
export const StateReady = Events.createStateEvent(
|
|
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 {
|
|
13
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
203
|
+
const meta = {
|
|
227
204
|
title: 'plugins/plugin-navtree/experimental/Tree',
|
|
228
205
|
component: Tree,
|
|
229
|
-
decorators: [withTheme, withLayout({
|
|
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
|
|
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
|
|
214
|
-
|
|
215
|
-
selected?.[id] &&
|
|
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
|
|
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
|
|
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 = `${
|
|
13
|
-
|
|
14
|
-
export const meta: PluginMeta = {
|
|
15
|
-
id: NAVTREE_PLUGIN,
|
|
16
|
-
name: 'Navtree',
|
|
17
|
-
};
|
|
20
|
+
export const COMMANDS_DIALOG = `${meta.id}/CommandsDialog`;
|