@dxos/plugin-navtree 0.8.2-main.f081794 → 0.8.2-main.fbd8ed0
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-26D3MUCA.mjs → app-graph-builder-P7AY3K72.mjs} +7 -6
- package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs.map +7 -0
- package/dist/lib/browser/{chunk-FHOAKMRS.mjs → chunk-UM2C5XK3.mjs} +352 -360
- package/dist/lib/browser/chunk-UM2C5XK3.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +4 -10
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-OBGGMBVJ.mjs → intent-resolver-PW7B76HO.mjs} +6 -6
- package/dist/lib/browser/intent-resolver-PW7B76HO.mjs.map +7 -0
- package/dist/lib/browser/keyboard-DDFATRUG.mjs +51 -0
- package/dist/lib/browser/keyboard-DDFATRUG.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-DWMOF2TM.mjs → react-surface-M3CAS7ZP.mjs} +2 -2
- package/dist/lib/browser/{state-EW62QYEN.mjs → state-3UBYOTQT.mjs} +2 -2
- package/dist/lib/browser/state-3UBYOTQT.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +4 -180
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +2 -2
- package/dist/types/src/capabilities/keyboard.d.ts.map +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 -0
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -0
- package/dist/types/src/components/CommandsDialog/index.d.ts +3 -0
- package/dist/types/src/components/CommandsDialog/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts +8 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +8 -0
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/index.d.ts +2 -0
- package/dist/types/src/components/NavTree/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeContext.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
- package/dist/types/src/components/{NavTreeItemAction.d.ts → NavTreeItem/NavTreeItemAction.d.ts} +2 -3
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +8 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -0
- package/dist/types/src/components/{NavTreeItemColumns.d.ts → NavTreeItem/NavTreeItemColumns.d.ts} +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/index.d.ts +3 -0
- package/dist/types/src/components/NavTreeItem/index.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +13 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +15 -0
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/index.d.ts +3 -0
- package/dist/types/src/components/Sidebar/index.d.ts.map +1 -0
- package/dist/types/src/components/{UserAccountAvatar.d.ts → UserAccountAvatar/UserAccountAvatar.d.ts} +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -0
- package/dist/types/src/components/{UserAccountAvatar.stories.d.ts → UserAccountAvatar/UserAccountAvatar.stories.d.ts} +1 -2
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -0
- package/dist/types/src/components/UserAccountAvatar/index.d.ts +2 -0
- package/dist/types/src/components/UserAccountAvatar/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +17 -15
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/hooks.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts +3 -0
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/testing/generator.d.ts +5 -4
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/update-state.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/util.d.ts +7 -10
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -31
- package/src/NavTreePlugin.tsx +2 -2
- package/src/capabilities/app-graph-builder.ts +38 -28
- package/src/capabilities/intent-resolver.ts +8 -7
- package/src/capabilities/keyboard.ts +37 -32
- package/src/capabilities/state.ts +3 -3
- package/src/components/{CommandsDialog.tsx → CommandsDialog/CommandsDialog.tsx} +8 -7
- package/src/components/{CommandsTrigger.tsx → CommandsDialog/CommandsTrigger.tsx} +1 -1
- package/src/components/CommandsDialog/index.ts +6 -0
- package/src/components/NavTree/NavTree.stories.tsx +159 -0
- package/src/components/NavTree/NavTree.tsx +62 -0
- package/src/components/NavTree/index.ts +5 -0
- package/src/components/NavTreeContainer.tsx +85 -79
- package/src/components/NavTreeContext.tsx +1 -1
- package/src/components/{NavTreeItemAction.stories.tsx → NavTreeItem/NavTreeItemAction.stories.tsx} +12 -6
- package/src/components/{NavTreeItemAction.tsx → NavTreeItem/NavTreeItemAction.tsx} +13 -14
- package/src/components/{NavTreeItemColumns.tsx → NavTreeItem/NavTreeItemColumns.tsx} +5 -5
- package/src/components/NavTreeItem/index.ts +6 -0
- package/src/components/{L0Menu.tsx → Sidebar/L0Menu.tsx} +188 -131
- package/src/components/{L1Panels.tsx → Sidebar/L1Panel.tsx} +40 -65
- package/src/components/Sidebar/index.ts +6 -0
- package/src/components/{UserAccountAvatar.stories.tsx → UserAccountAvatar/UserAccountAvatar.stories.tsx} +5 -5
- package/src/components/{UserAccountAvatar.tsx → UserAccountAvatar/UserAccountAvatar.tsx} +6 -6
- package/src/components/UserAccountAvatar/index.ts +5 -0
- package/src/components/index.ts +0 -1
- package/src/components/types.ts +17 -16
- package/src/hooks.ts +12 -11
- package/src/testing/app-graph-builder.ts +224 -0
- package/src/testing/generator.ts +12 -9
- package/src/testing/index.ts +1 -0
- package/src/translations.ts +1 -1
- package/src/util.ts +10 -32
- package/dist/lib/browser/app-graph-builder-26D3MUCA.mjs.map +0 -7
- package/dist/lib/browser/chunk-FHOAKMRS.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-OBGGMBVJ.mjs.map +0 -7
- package/dist/lib/browser/keyboard-IQXHKUWM.mjs +0 -46
- package/dist/lib/browser/keyboard-IQXHKUWM.mjs.map +0 -7
- package/dist/lib/browser/state-EW62QYEN.mjs.map +0 -7
- package/dist/types/src/components/CommandsDialog.d.ts.map +0 -1
- package/dist/types/src/components/CommandsTrigger.d.ts.map +0 -1
- package/dist/types/src/components/L0Menu.d.ts +0 -10
- package/dist/types/src/components/L0Menu.d.ts.map +0 -1
- package/dist/types/src/components/L1Panels.d.ts +0 -14
- package/dist/types/src/components/L1Panels.d.ts.map +0 -1
- package/dist/types/src/components/NavTree.d.ts +0 -5
- package/dist/types/src/components/NavTree.d.ts.map +0 -1
- package/dist/types/src/components/NavTree.stories.d.ts +0 -7
- package/dist/types/src/components/NavTree.stories.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemAction.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts +0 -41
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemColumns.d.ts.map +0 -1
- package/dist/types/src/components/UserAccountAvatar.d.ts.map +0 -1
- package/dist/types/src/components/UserAccountAvatar.stories.d.ts.map +0 -1
- package/dist/types/src/stories/Graph.stories.d.ts +0 -7
- package/dist/types/src/stories/Graph.stories.d.ts.map +0 -1
- package/dist/types/src/stories/StorybookNavTree.d.ts +0 -4
- package/dist/types/src/stories/StorybookNavTree.d.ts.map +0 -1
- package/src/components/NavTree.stories.tsx +0 -107
- package/src/components/NavTree.tsx +0 -51
- package/src/stories/Graph.stories.tsx +0 -130
- package/src/stories/StorybookNavTree.tsx +0 -63
- /package/dist/lib/browser/{react-surface-DWMOF2TM.mjs.map → react-surface-M3CAS7ZP.mjs.map} +0 -0
- /package/dist/types/src/components/{CommandsDialog.d.ts → CommandsDialog/CommandsDialog.d.ts} +0 -0
- /package/dist/types/src/components/{CommandsTrigger.d.ts → CommandsDialog/CommandsTrigger.d.ts} +0 -0
|
@@ -7,88 +7,92 @@ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-d
|
|
|
7
7
|
import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
|
|
8
8
|
import { scrollJustEnoughIntoView } from '@atlaskit/pragmatic-drag-and-drop/element/scroll-just-enough-into-view';
|
|
9
9
|
import {
|
|
10
|
-
attachClosestEdge,
|
|
11
10
|
type Edge,
|
|
11
|
+
attachClosestEdge,
|
|
12
12
|
extractClosestEdge,
|
|
13
13
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
14
|
-
import React, {
|
|
14
|
+
import React, {
|
|
15
|
+
forwardRef,
|
|
16
|
+
type MouseEvent,
|
|
17
|
+
type PropsWithChildren,
|
|
18
|
+
useCallback,
|
|
19
|
+
useLayoutEffect,
|
|
20
|
+
useMemo,
|
|
21
|
+
useRef,
|
|
22
|
+
useState,
|
|
23
|
+
} from 'react';
|
|
15
24
|
|
|
16
|
-
import { createIntent, LayoutAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
17
25
|
import { type Node } from '@dxos/app-graph';
|
|
18
26
|
import { invariant } from '@dxos/invariant';
|
|
19
27
|
import {
|
|
20
|
-
|
|
28
|
+
Icon,
|
|
29
|
+
IconButton,
|
|
30
|
+
ListItem,
|
|
21
31
|
ScrollArea,
|
|
22
|
-
toLocalizedString,
|
|
23
32
|
Tooltip,
|
|
33
|
+
toLocalizedString,
|
|
24
34
|
useMediaQuery,
|
|
25
|
-
useSidebars,
|
|
26
35
|
useTranslation,
|
|
27
|
-
ListItem,
|
|
28
|
-
Icon,
|
|
29
36
|
} from '@dxos/react-ui';
|
|
37
|
+
import { MenuProvider, DropdownMenu, type MenuItem } from '@dxos/react-ui-menu';
|
|
30
38
|
import type { StackItemRearrangeHandler } from '@dxos/react-ui-stack';
|
|
31
39
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
32
40
|
import { mx } from '@dxos/react-ui-theme';
|
|
33
41
|
import { arrayMove, getFirstTwoRenderableChars } from '@dxos/util';
|
|
34
42
|
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
43
|
+
import { useLoadDescendents } from '../../hooks';
|
|
44
|
+
import { NAVTREE_PLUGIN } from '../../meta';
|
|
45
|
+
import { l0ItemType } from '../../util';
|
|
46
|
+
import { useNavTreeContext } from '../NavTreeContext';
|
|
47
|
+
import { UserAccountAvatar } from '../UserAccountAvatar';
|
|
48
|
+
|
|
49
|
+
//
|
|
50
|
+
// L0Item
|
|
51
|
+
//
|
|
52
|
+
|
|
53
|
+
type L0ItemData = {
|
|
54
|
+
id: L0ItemProps['item']['id'];
|
|
55
|
+
type: 'l0Item';
|
|
56
|
+
};
|
|
40
57
|
|
|
41
|
-
type
|
|
58
|
+
type L0ItemRootProps = {
|
|
59
|
+
item: Node<any>;
|
|
60
|
+
parent?: Node<any>;
|
|
61
|
+
path: string[];
|
|
62
|
+
};
|
|
42
63
|
|
|
43
|
-
type L0ItemProps = {
|
|
64
|
+
type L0ItemProps = L0ItemRootProps & {
|
|
44
65
|
item: Node<any>;
|
|
45
66
|
parent?: Node<any>;
|
|
46
67
|
path: string[];
|
|
47
68
|
pinned?: boolean;
|
|
48
|
-
userAccount?: boolean;
|
|
49
69
|
onRearrange?: StackItemRearrangeHandler<L0ItemData>;
|
|
50
70
|
};
|
|
51
71
|
|
|
52
72
|
const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
|
|
53
|
-
const { tab,
|
|
54
|
-
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
55
|
-
const { navigationSidebarState } = useSidebars(NAVTREE_PLUGIN);
|
|
73
|
+
const { tab, isCurrent, onSelect, onTabChange } = useNavTreeContext();
|
|
56
74
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
57
75
|
|
|
58
76
|
return useCallback(
|
|
59
77
|
(event: MouseEvent) => {
|
|
60
78
|
switch (type) {
|
|
61
|
-
case 'tab':
|
|
62
|
-
// TODO(thure): This dispatch should rightly be in `onTabChange`, but that callback wasn’t reacting to changes
|
|
63
|
-
// to its dependencies.
|
|
64
|
-
void dispatch(
|
|
65
|
-
createIntent(LayoutAction.UpdateSidebar, {
|
|
66
|
-
part: 'sidebar',
|
|
67
|
-
options: {
|
|
68
|
-
state:
|
|
69
|
-
item.id === tab
|
|
70
|
-
? navigationSidebarState === 'expanded'
|
|
71
|
-
? isLg
|
|
72
|
-
? 'collapsed'
|
|
73
|
-
: 'closed'
|
|
74
|
-
: 'expanded'
|
|
75
|
-
: 'expanded',
|
|
76
|
-
},
|
|
77
|
-
}),
|
|
78
|
-
);
|
|
79
|
-
return onTabChange?.(item);
|
|
80
|
-
case 'link':
|
|
81
|
-
return onSelect?.({ item, path, current: !isCurrent(path, item), option: event.altKey });
|
|
82
79
|
case 'action': {
|
|
83
80
|
const {
|
|
84
81
|
data: invoke,
|
|
85
82
|
properties: { caller },
|
|
86
83
|
} = item;
|
|
84
|
+
|
|
87
85
|
return invoke?.(caller ? { node: parent, caller } : { node: parent });
|
|
88
86
|
}
|
|
87
|
+
|
|
88
|
+
case 'tab':
|
|
89
|
+
return onTabChange?.(item);
|
|
90
|
+
|
|
91
|
+
case 'link':
|
|
92
|
+
return onSelect?.({ item, path, current: !isCurrent(path, item), option: event.altKey });
|
|
89
93
|
}
|
|
90
94
|
},
|
|
91
|
-
[item, type,
|
|
95
|
+
[item, parent, type, tab, isCurrent, onSelect, onTabChange, isLg],
|
|
92
96
|
);
|
|
93
97
|
};
|
|
94
98
|
|
|
@@ -96,28 +100,70 @@ const l0Breakpoints: Record<string, string> = {
|
|
|
96
100
|
lg: 'hidden lg:grid',
|
|
97
101
|
};
|
|
98
102
|
|
|
99
|
-
const
|
|
103
|
+
const L0ItemRoot = forwardRef<HTMLElement, PropsWithChildren<L0ItemRootProps>>(
|
|
104
|
+
({ item, parent, path, children }, forwardedRef) => {
|
|
105
|
+
const { getProps } = useNavTreeContext();
|
|
106
|
+
const { id, testId } = getProps?.(item, path) ?? {};
|
|
107
|
+
const type = l0ItemType(item);
|
|
108
|
+
const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
109
|
+
|
|
110
|
+
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
111
|
+
const localizedString = toLocalizedString(item.properties.label, t);
|
|
112
|
+
|
|
113
|
+
const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
|
|
114
|
+
const rootProps =
|
|
115
|
+
type === 'tab'
|
|
116
|
+
? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
|
|
117
|
+
: type !== 'collection'
|
|
118
|
+
? { onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
|
|
119
|
+
: { onClick: handleClick };
|
|
120
|
+
|
|
121
|
+
const Root = type === 'collection' ? 'h2' : type === 'tab' ? Tabs.TabPrimitive : 'button';
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
|
|
125
|
+
<Root
|
|
126
|
+
{...(rootProps as any)}
|
|
127
|
+
data-type={type}
|
|
128
|
+
className={mx(
|
|
129
|
+
'group/l0item flex w-full justify-center items-center relative data[type!="collection"]:cursor-pointer app-no-drag dx-focus-ring-group',
|
|
130
|
+
l0Breakpoints[item.properties.l0Breakpoint],
|
|
131
|
+
)}
|
|
132
|
+
ref={forwardedRef}
|
|
133
|
+
>
|
|
134
|
+
{children}
|
|
135
|
+
</Root>
|
|
136
|
+
</Tooltip.Trigger>
|
|
137
|
+
);
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const L0Avator = ({ item }: Pick<L0ItemProps, 'item'>) => {
|
|
100
142
|
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
101
|
-
const itemElement = useRef<HTMLElement | null>(null);
|
|
102
|
-
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
103
143
|
const type = l0ItemType(item);
|
|
104
|
-
const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
105
|
-
const { getProps, popoverAnchorId } = useNavTreeContext();
|
|
106
|
-
const { id, testId } = getProps?.(item, path) ?? {};
|
|
107
|
-
const Root = type === 'collection' ? 'h2' : type === 'tab' ? Tabs.TabPrimitive : 'button';
|
|
108
|
-
const handleClick = useL0ItemClick({ item, path: itemPath, parent }, type);
|
|
109
|
-
const rootProps =
|
|
110
|
-
type === 'tab'
|
|
111
|
-
? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
|
|
112
|
-
: type !== 'collection'
|
|
113
|
-
? { onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
|
|
114
|
-
: { onClick: handleClick };
|
|
115
|
-
const localizedString = toLocalizedString(item.properties.label, t);
|
|
116
144
|
const hue = item.properties.hue ?? null;
|
|
145
|
+
const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
|
|
146
|
+
const localizedString = toLocalizedString(item.properties.label, t);
|
|
117
147
|
const avatarValue = useMemo(
|
|
118
148
|
() => (type === 'tab' ? getFirstTwoRenderableChars(localizedString).join('').toUpperCase() : []),
|
|
119
149
|
[type, item.properties.label, t],
|
|
120
150
|
);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<span role='img' className='place-self-center text-xl font-light' {...hueFgStyle}>
|
|
154
|
+
{avatarValue}
|
|
155
|
+
</span>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// TODO(burdon): Factor out pinned (non-draggable) items.
|
|
160
|
+
const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
|
|
161
|
+
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
162
|
+
const itemElement = useRef<HTMLElement | null>(null);
|
|
163
|
+
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
164
|
+
const type = l0ItemType(item);
|
|
165
|
+
const localizedString = toLocalizedString(item.properties.label, t);
|
|
166
|
+
const hue = item.properties.hue ?? null;
|
|
121
167
|
const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
|
|
122
168
|
|
|
123
169
|
useLayoutEffect(() => {
|
|
@@ -125,6 +171,7 @@ const L0Item = ({ item, parent, path, pinned, userAccount, onRearrange }: L0Item
|
|
|
125
171
|
if (!itemElement.current || !onRearrange) {
|
|
126
172
|
return;
|
|
127
173
|
}
|
|
174
|
+
|
|
128
175
|
return combine(
|
|
129
176
|
draggable({
|
|
130
177
|
element: itemElement.current,
|
|
@@ -175,80 +222,43 @@ const L0Item = ({ item, parent, path, pinned, userAccount, onRearrange }: L0Item
|
|
|
175
222
|
);
|
|
176
223
|
}, [item, onRearrange]);
|
|
177
224
|
|
|
178
|
-
|
|
179
|
-
<
|
|
180
|
-
{...(rootProps as any)}
|
|
181
|
-
data-type={type}
|
|
182
|
-
className={mx(
|
|
183
|
-
'group/l0i dx-focus-ring-group grid relative data[type!="collection"]:cursor-pointer app-no-drag',
|
|
184
|
-
l0Breakpoints[item.properties.l0Breakpoint],
|
|
185
|
-
)}
|
|
186
|
-
ref={itemElement}
|
|
187
|
-
>
|
|
188
|
-
{userAccount ? (
|
|
189
|
-
<UserAccountAvatar
|
|
190
|
-
userId={item.properties.userId}
|
|
191
|
-
hue={item.properties.hue}
|
|
192
|
-
emoji={item.properties.emoji}
|
|
193
|
-
status={item.properties.status}
|
|
194
|
-
/>
|
|
195
|
-
) : (
|
|
196
|
-
<div
|
|
197
|
-
role='none'
|
|
198
|
-
data-frame={true}
|
|
199
|
-
className={mx(
|
|
200
|
-
'absolute grid dx-focus-ring-group-indicator transition-colors',
|
|
201
|
-
type === 'tab' || pinned ? 'rounded' : 'rounded-full',
|
|
202
|
-
pinned
|
|
203
|
-
? 'bg-transparent group-hover/l0i:bg-groupSurface inset-inline-3 inset-block-0.5'
|
|
204
|
-
: 'bg-groupSurface inset-inline-3 inset-block-2',
|
|
205
|
-
)}
|
|
206
|
-
{...(hue && { style: { background: `var(--dx-${hue}Surface)` } })}
|
|
207
|
-
>
|
|
208
|
-
{(item.properties.icon && (
|
|
209
|
-
<Icon icon={item.properties.icon} size={pinned ? 5 : 7} classNames='place-self-center' {...hueFgStyle} />
|
|
210
|
-
)) ||
|
|
211
|
-
(type === 'tab' && item.properties.disposition !== 'pin-end' ? (
|
|
212
|
-
<span role='img' className='place-self-center text-3xl font-light' {...hueFgStyle}>
|
|
213
|
-
{avatarValue}
|
|
214
|
-
</span>
|
|
215
|
-
) : (
|
|
216
|
-
item.properties.icon && (
|
|
217
|
-
<Icon icon='ph--planet--regular' size={pinned ? 5 : 7} classNames='place-self-center' {...hueFgStyle} />
|
|
218
|
-
)
|
|
219
|
-
))}
|
|
220
|
-
</div>
|
|
221
|
-
)}
|
|
225
|
+
return (
|
|
226
|
+
<L0ItemRoot ref={itemElement} item={item} parent={parent} path={path}>
|
|
222
227
|
<div
|
|
223
228
|
role='none'
|
|
224
|
-
|
|
229
|
+
data-frame={true}
|
|
230
|
+
className={mx(
|
|
231
|
+
'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded',
|
|
232
|
+
// TODO(burdon): Create reusable button/component and/or create var for size.
|
|
233
|
+
pinned
|
|
234
|
+
? 'bg-transparent w-[50px] p-2 group-hover/l0item:bg-groupSurface'
|
|
235
|
+
: 'bg-groupSurface w-[50px] h-[50px]',
|
|
236
|
+
)}
|
|
237
|
+
{...(hue && { style: { background: `var(--dx-${hue}Surface)` } })}
|
|
238
|
+
>
|
|
239
|
+
{(item.properties.icon && <Icon icon={item.properties.icon} size={pinned ? 5 : 6} {...hueFgStyle} />) ||
|
|
240
|
+
(type === 'tab' && item.properties.disposition !== 'pin-end' && <L0Avator item={item} />)}
|
|
241
|
+
</div>
|
|
242
|
+
<div
|
|
243
|
+
role='none'
|
|
244
|
+
className='hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-4 is-1 bg-accentSurface rounded-ie'
|
|
225
245
|
/>
|
|
226
246
|
<span id={`${item.id}__label`} className='sr-only'>
|
|
227
247
|
{localizedString}
|
|
228
248
|
</span>
|
|
229
249
|
{onRearrange && closestEdge && <ListItem.DropIndicator edge={closestEdge} />}
|
|
230
|
-
</
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
return popoverAnchorId === id ? (
|
|
234
|
-
<Popover.Anchor asChild>{l0ItemTrigger}</Popover.Anchor>
|
|
235
|
-
) : (
|
|
236
|
-
<Tooltip.Root delayDuration={0}>
|
|
237
|
-
<Tooltip.Trigger asChild>{l0ItemTrigger}</Tooltip.Trigger>
|
|
238
|
-
<Tooltip.Portal>
|
|
239
|
-
<Tooltip.Content side='right'>
|
|
240
|
-
{localizedString}
|
|
241
|
-
<Tooltip.Arrow />
|
|
242
|
-
</Tooltip.Content>
|
|
243
|
-
</Tooltip.Portal>
|
|
244
|
-
</Tooltip.Root>
|
|
250
|
+
</L0ItemRoot>
|
|
245
251
|
);
|
|
246
252
|
};
|
|
247
253
|
|
|
248
|
-
|
|
254
|
+
//
|
|
255
|
+
// L0Collection
|
|
256
|
+
//
|
|
257
|
+
|
|
258
|
+
const L0Collection = ({ item, path }: L0ItemProps) => {
|
|
249
259
|
const navTreeContext = useNavTreeContext();
|
|
250
260
|
useLoadDescendents(item);
|
|
251
|
-
const collectionItems = navTreeContext.
|
|
261
|
+
const collectionItems = navTreeContext.useItems(item);
|
|
252
262
|
const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
253
263
|
const { id, testId } = navTreeContext.getProps?.(item, path) ?? {};
|
|
254
264
|
const handleRearrange = useCallback<StackItemRearrangeHandler<L0ItemData>>(
|
|
@@ -266,6 +276,7 @@ const L0Collection = ({ item, path, parent }: L0ItemProps) => {
|
|
|
266
276
|
},
|
|
267
277
|
[collectionItems, item.properties.onRearrangeChildren],
|
|
268
278
|
);
|
|
279
|
+
|
|
269
280
|
return (
|
|
270
281
|
<div
|
|
271
282
|
role='group'
|
|
@@ -287,26 +298,61 @@ const L0Collection = ({ item, path, parent }: L0ItemProps) => {
|
|
|
287
298
|
);
|
|
288
299
|
};
|
|
289
300
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}: {
|
|
301
|
+
//
|
|
302
|
+
// L0Menu
|
|
303
|
+
//
|
|
304
|
+
|
|
305
|
+
export type L0MenuProps = {
|
|
306
|
+
menuActions: MenuItem[];
|
|
297
307
|
topLevelItems: Node<any>[];
|
|
298
308
|
pinnedItems: Node<any>[];
|
|
299
309
|
userAccountItem: Node<any>;
|
|
300
310
|
parent?: Node<any>;
|
|
301
311
|
path: string[];
|
|
302
|
-
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountItem, parent, path }: L0MenuProps) => {
|
|
315
|
+
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
316
|
+
const suppressNextTooltip = useRef(true);
|
|
317
|
+
|
|
303
318
|
return (
|
|
304
|
-
<Tabs.Tablist
|
|
319
|
+
<Tabs.Tablist
|
|
320
|
+
classNames={[
|
|
321
|
+
'group/l0 absolute z-[1] inset-block-0 inline-start-0 rounded-is-lg',
|
|
322
|
+
'grid grid-cols-[var(--l0-size)] grid-rows-[var(--rail-size)_1fr_min-content_var(--l0-size)] gap-1 contain-layout',
|
|
323
|
+
'!is-[--l0-size] bg-baseSurface border-ie border-separator app-drag pbe-[env(safe-area-inset-bottom)]',
|
|
324
|
+
]}
|
|
325
|
+
>
|
|
326
|
+
<div role='none' className='flex justify-center p-1'>
|
|
327
|
+
<MenuProvider>
|
|
328
|
+
<DropdownMenu.Root group={parent} items={menuActions}>
|
|
329
|
+
<DropdownMenu.Trigger asChild>
|
|
330
|
+
{/* TODO(wittjosiah): Use L0Item trigger. */}
|
|
331
|
+
{/* TODO(burdon): Replace with Sigil. */}
|
|
332
|
+
<IconButton
|
|
333
|
+
iconOnly
|
|
334
|
+
icon='ph--dots-three--regular'
|
|
335
|
+
size={5}
|
|
336
|
+
label={t('app menu label')}
|
|
337
|
+
tooltipSide='right'
|
|
338
|
+
classNames='w-[50px] _bg-primary-500'
|
|
339
|
+
suppressNextTooltip={suppressNextTooltip}
|
|
340
|
+
data-testid='spacePlugin.addSpace'
|
|
341
|
+
/>
|
|
342
|
+
</DropdownMenu.Trigger>
|
|
343
|
+
</DropdownMenu.Root>
|
|
344
|
+
</MenuProvider>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
305
347
|
<ScrollArea.Root>
|
|
306
348
|
<ScrollArea.Viewport>
|
|
307
349
|
<div
|
|
308
350
|
role='none'
|
|
309
|
-
className=
|
|
351
|
+
className={mx([
|
|
352
|
+
'flex flex-col gap-2 pbs-1',
|
|
353
|
+
'[body[data-platform="darwin"]_&]:pbs-[calc(30px+0.25rem)]',
|
|
354
|
+
'[body[data-platform="ios"]_&]:pbs-[max(env(safe-area-inset-top),0.25rem)]',
|
|
355
|
+
])}
|
|
310
356
|
>
|
|
311
357
|
{topLevelItems.map((item) => {
|
|
312
358
|
if (l0ItemType(item) === 'collection') {
|
|
@@ -321,6 +367,7 @@ export const L0Menu = ({
|
|
|
321
367
|
</ScrollArea.Scrollbar>
|
|
322
368
|
</ScrollArea.Viewport>
|
|
323
369
|
</ScrollArea.Root>
|
|
370
|
+
|
|
324
371
|
<div role='none' className='grid grid-cols-1 auto-rows-[--rail-action] pbs-2'>
|
|
325
372
|
{pinnedItems
|
|
326
373
|
.filter((item) => l0ItemType(item) !== 'collection')
|
|
@@ -328,17 +375,27 @@ export const L0Menu = ({
|
|
|
328
375
|
<L0Item key={item.id} item={item} parent={parent} path={path} pinned />
|
|
329
376
|
))}
|
|
330
377
|
</div>
|
|
378
|
+
|
|
331
379
|
{userAccountItem && (
|
|
332
|
-
<div role='none' className='grid
|
|
333
|
-
<
|
|
380
|
+
<div role='none' className='grid app-no-drag'>
|
|
381
|
+
<L0ItemRoot key={userAccountItem.id} item={userAccountItem} parent={parent} path={path}>
|
|
382
|
+
<UserAccountAvatar
|
|
383
|
+
userId={userAccountItem.properties.userId}
|
|
384
|
+
hue={userAccountItem.properties.hue}
|
|
385
|
+
emoji={userAccountItem.properties.emoji}
|
|
386
|
+
status={userAccountItem.properties.status}
|
|
387
|
+
size={11}
|
|
388
|
+
/>
|
|
389
|
+
</L0ItemRoot>
|
|
334
390
|
</div>
|
|
335
391
|
)}
|
|
392
|
+
|
|
336
393
|
<div
|
|
337
394
|
role='none'
|
|
338
395
|
className='hidden [body[data-platform="darwin"]_&]:block absolute block-start-0 is-[calc(var(--l0-size)-1px)] bs-[calc(40px+0.25rem)]'
|
|
339
396
|
style={{
|
|
340
397
|
background:
|
|
341
|
-
'linear-gradient(to bottom, var(--dx-
|
|
398
|
+
'linear-gradient(to bottom, var(--dx-sidebarSurface) 0%, var(--dx-sidebarSurface) 70%, transparent 100%)',
|
|
342
399
|
}}
|
|
343
400
|
/>
|
|
344
401
|
</Tabs.Tablist>
|
|
@@ -4,77 +4,54 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { useLayout } from '@dxos/app-framework';
|
|
8
7
|
import { type Node } from '@dxos/app-graph';
|
|
9
8
|
import { Button, type ButtonProps, Icon, IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
10
9
|
import { Tree } from '@dxos/react-ui-list';
|
|
11
10
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
12
11
|
import { hoverableControlItem, hoverableOpenControlItem, mx } from '@dxos/react-ui-theme';
|
|
13
|
-
import { byPosition } from '@dxos/util';
|
|
14
12
|
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
13
|
+
import { useLoadDescendents } from '../../hooks';
|
|
14
|
+
import { NAVTREE_PLUGIN } from '../../meta';
|
|
15
|
+
import { l0ItemType } from '../../util';
|
|
16
|
+
import { useNavTreeContext } from '../NavTreeContext';
|
|
17
|
+
import { NavTreeItemColumns } from '../NavTreeItem';
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
item: Node<any>;
|
|
23
|
-
path: string[];
|
|
24
|
-
currentItemId: string;
|
|
25
|
-
onBack?: () => void;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const headingBackButtonLabel =
|
|
29
|
-
'absolute inset-0 min-is-0 truncate flex items-center pis-6 transition-[transform,opacity] ease-in-out duration-200';
|
|
19
|
+
const headingBackButtonLabel = 'inset-0 min-is-0 truncate flex items-center pis-2';
|
|
30
20
|
|
|
31
|
-
const
|
|
32
|
-
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
21
|
+
const TitleButton = ({ title, onClick }: { title: string } & Pick<ButtonProps, 'onClick'>) => {
|
|
33
22
|
return (
|
|
34
23
|
<Button
|
|
35
24
|
variant='ghost'
|
|
36
|
-
classNames='pli-1 flex-1
|
|
25
|
+
classNames='pli-1 flex-1 group text-start justify-start bs-[--rail-action] font-normal'
|
|
37
26
|
onClick={onClick}
|
|
38
27
|
>
|
|
39
28
|
<Icon icon='ph--caret-left--bold' size={3} />
|
|
40
|
-
<span
|
|
41
|
-
className={mx(
|
|
42
|
-
headingBackButtonLabel,
|
|
43
|
-
'translate-y-2 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 group-focus:translate-y-0 group-focus:opacity-100',
|
|
44
|
-
)}
|
|
45
|
-
>
|
|
46
|
-
{t('back label')}
|
|
47
|
-
</span>
|
|
48
|
-
<span
|
|
49
|
-
className={mx(
|
|
50
|
-
headingBackButtonLabel,
|
|
51
|
-
'translate-y-0 opacity-100 group-hover:-translate-y-2 group-hover:opacity-0 group-focus:-translate-y-2 group-focus:opacity-0',
|
|
52
|
-
)}
|
|
53
|
-
>
|
|
54
|
-
{title}
|
|
55
|
-
</span>
|
|
29
|
+
<span className={mx(headingBackButtonLabel)}>{title}</span>
|
|
56
30
|
</Button>
|
|
57
31
|
);
|
|
58
32
|
};
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
34
|
+
export type L1PanelProps = {
|
|
35
|
+
open?: boolean;
|
|
36
|
+
item: Node<any>;
|
|
37
|
+
path: string[];
|
|
38
|
+
currentItemId: string;
|
|
39
|
+
onBack?: () => void;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const L1Panel = ({ open, item, path, currentItemId, onBack }: L1PanelProps) => {
|
|
62
43
|
const { isAlternateTree, setAlternateTree, ...navTreeContext } = useNavTreeContext();
|
|
63
44
|
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
64
45
|
|
|
65
46
|
// TODO(wittjosiah): Support multiple alternate trees.
|
|
66
|
-
const alternateTree = navTreeContext.
|
|
47
|
+
const alternateTree = navTreeContext.useItems(item, { disposition: 'alternate-tree' })[0];
|
|
67
48
|
const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
68
49
|
const handleOpen = useCallback(() => setAlternateTree?.(alternatePath, true), [alternatePath, setAlternateTree]);
|
|
69
50
|
const isAlternate = isAlternateTree?.(alternatePath, item) ?? false;
|
|
70
|
-
const
|
|
71
|
-
(node?: Node, disposition?: string) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// TODO(wittjosiah): Sorting is expensive, limit to necessary items for now.
|
|
75
|
-
return items.toSorted((a, b) => byPosition(a.properties, b.properties));
|
|
76
|
-
}
|
|
77
|
-
return items;
|
|
51
|
+
const useAlternateItems = useCallback(
|
|
52
|
+
(node?: Node, { disposition }: { disposition?: string } = {}) => {
|
|
53
|
+
// TODO(wittjosiah): Sorting is expensive, limit to necessary items for now.
|
|
54
|
+
return navTreeContext.useItems(node, { disposition, sort: node?.id === alternateTree.id });
|
|
78
55
|
},
|
|
79
56
|
[navTreeContext, alternateTree],
|
|
80
57
|
);
|
|
@@ -95,18 +72,20 @@ const L1Panel = ({ item, path, currentItemId, onBack }: L1PanelProps) => {
|
|
|
95
72
|
key={item.id}
|
|
96
73
|
value={item.id}
|
|
97
74
|
classNames={[
|
|
98
|
-
'absolute inset-block-0 inline-end-0
|
|
75
|
+
'absolute inset-block-0 inline-end-0',
|
|
76
|
+
'is-[calc(100%-var(--l0-size))] lg:is-[--l1-size] grid-cols-1 grid-rows-[var(--rail-size)_1fr]',
|
|
77
|
+
'pbs-[env(safe-area-inset-top)]',
|
|
99
78
|
item.id === currentItemId && 'grid',
|
|
100
79
|
]}
|
|
101
80
|
tabIndex={-1}
|
|
102
81
|
aria-label={title}
|
|
103
|
-
{...(!
|
|
82
|
+
{...(!open && { inert: 'true' })}
|
|
104
83
|
>
|
|
105
84
|
{item.id === currentItemId && (
|
|
106
85
|
<>
|
|
107
|
-
<h2 className='flex items-center border-be border-separator app-drag
|
|
86
|
+
<h2 className='flex items-center border-be border-separator app-drag pli-1'>
|
|
108
87
|
{backCapable ? (
|
|
109
|
-
<
|
|
88
|
+
<TitleButton title={title} onClick={handleBack} />
|
|
110
89
|
) : (
|
|
111
90
|
<span className='flex-1 truncate min-is-0 pis-6'>{title}</span>
|
|
112
91
|
)}
|
|
@@ -129,7 +108,7 @@ const L1Panel = ({ item, path, currentItemId, onBack }: L1PanelProps) => {
|
|
|
129
108
|
{isAlternate ? (
|
|
130
109
|
<Tree
|
|
131
110
|
{...navTreeContext}
|
|
132
|
-
|
|
111
|
+
useItems={useAlternateItems}
|
|
133
112
|
id={alternateTree.id}
|
|
134
113
|
root={alternateTree}
|
|
135
114
|
path={alternatePath}
|
|
@@ -156,41 +135,37 @@ const L1Panel = ({ item, path, currentItemId, onBack }: L1PanelProps) => {
|
|
|
156
135
|
);
|
|
157
136
|
};
|
|
158
137
|
|
|
159
|
-
const L1PanelCollection = ({ item, path,
|
|
160
|
-
const {
|
|
138
|
+
const L1PanelCollection = ({ item, path, ...props }: L1PanelProps) => {
|
|
139
|
+
const { useItems } = useNavTreeContext();
|
|
161
140
|
useLoadDescendents(item);
|
|
162
|
-
const collectionItems =
|
|
141
|
+
const collectionItems = useItems(item);
|
|
163
142
|
const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
164
143
|
return (
|
|
165
144
|
<>
|
|
166
145
|
{collectionItems
|
|
167
146
|
.filter((item) => l0ItemType(item) === 'tab')
|
|
168
147
|
.map((item) => (
|
|
169
|
-
<L1Panel key={item.id} item={item} path={groupPath}
|
|
148
|
+
<L1Panel key={item.id} item={item} path={groupPath} {...props} />
|
|
170
149
|
))}
|
|
171
150
|
</>
|
|
172
151
|
);
|
|
173
152
|
};
|
|
174
153
|
|
|
175
|
-
export
|
|
176
|
-
topLevelItems,
|
|
177
|
-
path,
|
|
178
|
-
currentItemId,
|
|
179
|
-
onBack,
|
|
180
|
-
}: Pick<L1PanelProps, 'onBack'> & {
|
|
154
|
+
export type L1PanelsProps = Pick<L1PanelProps, 'open' | 'currentItemId' | 'onBack'> & {
|
|
181
155
|
topLevelItems: Node<any>[];
|
|
182
156
|
path: string[];
|
|
183
|
-
|
|
184
|
-
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const L1Panels = ({ topLevelItems, onBack, ...props }: L1PanelsProps) => {
|
|
185
160
|
return (
|
|
186
161
|
<>
|
|
187
162
|
{topLevelItems.map((item) => {
|
|
188
163
|
const type = l0ItemType(item);
|
|
189
164
|
switch (type) {
|
|
190
165
|
case 'collection':
|
|
191
|
-
return <L1PanelCollection key={item.id} item={item}
|
|
166
|
+
return <L1PanelCollection key={item.id} item={item} {...props} />;
|
|
192
167
|
case 'tab':
|
|
193
|
-
return <L1Panel key={item.id} item={item}
|
|
168
|
+
return <L1Panel key={item.id} item={item} {...props} onBack={onBack} />;
|
|
194
169
|
default:
|
|
195
170
|
return null;
|
|
196
171
|
}
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta } from '@storybook/react';
|
|
8
|
-
import React from 'react';
|
|
9
8
|
|
|
10
|
-
import {
|
|
9
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
return <UserAccountAvatar {...props} />;
|
|
14
|
-
};
|
|
11
|
+
import { UserAccountAvatar } from './UserAccountAvatar';
|
|
15
12
|
|
|
16
13
|
const meta: Meta<typeof UserAccountAvatar> = {
|
|
17
14
|
title: 'plugins/plugin-navtree/UserAccountAvatar',
|
|
18
15
|
component: UserAccountAvatar,
|
|
16
|
+
decorators: [withTheme],
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
export default meta;
|
|
20
|
+
|
|
21
|
+
export const Default = {};
|