@dxos/plugin-navtree 0.8.4-main.3c1ae3b → 0.8.4-main.3eb6e50203
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-XGA4GPGL.mjs +44 -0
- package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +7 -0
- package/dist/lib/browser/chunk-63YLDLLE.mjs +1373 -0
- package/dist/lib/browser/chunk-63YLDLLE.mjs.map +7 -0
- package/dist/lib/browser/chunk-X2HDG62A.mjs +23 -0
- package/dist/lib/browser/chunk-X2HDG62A.mjs.map +7 -0
- package/dist/lib/browser/{chunk-E6MZBCU5.mjs → chunk-YKTYPINZ.mjs} +1 -1
- package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +83 -91
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/{keyboard-YJEK2HTH.mjs → keyboard-46XQHY2O.mjs} +18 -14
- package/dist/lib/browser/keyboard-46XQHY2O.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/operation-resolver-NNNDII45.mjs +53 -0
- package/dist/lib/browser/operation-resolver-NNNDII45.mjs.map +7 -0
- package/dist/lib/browser/{react-surface-RPXGIV45.mjs → react-surface-APSEG23H.mjs} +16 -14
- package/dist/lib/browser/react-surface-APSEG23H.mjs.map +7 -0
- package/dist/lib/browser/state-M5YUCIQU.mjs +121 -0
- package/dist/lib/browser/state-M5YUCIQU.mjs.map +7 -0
- package/dist/types/src/NavTreePlugin.d.ts +2 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +5 -108
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
- package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
- package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/state/index.d.ts +3 -0
- package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/state/state.d.ts +6 -0
- package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
- 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 +2 -2
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- 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/NavTreeDocumentTitle.d.ts +1 -1
- package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +2 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -4
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +2 -2
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts +2 -2
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +7 -7
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/hooks.d.ts +32 -1
- package/dist/types/src/hooks.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +5 -6
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/testing/update-state.d.ts +2 -2
- package/dist/types/src/testing/update-state.d.ts.map +1 -1
- package/dist/types/src/types/capabilities.d.ts +16 -0
- package/dist/types/src/types/capabilities.d.ts.map +1 -0
- package/dist/types/src/types/events.d.ts +5 -0
- package/dist/types/src/types/events.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +4 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/{types.d.ts → types/schema.d.ts} +6 -6
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +7 -7
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -52
- package/src/NavTreePlugin.tsx +53 -72
- package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
- package/src/capabilities/app-graph-builder/index.ts +7 -0
- package/src/capabilities/index.ts +5 -9
- package/src/capabilities/keyboard/index.ts +7 -0
- package/src/capabilities/keyboard/keyboard.ts +66 -0
- package/src/capabilities/operation-resolver/index.ts +7 -0
- package/src/capabilities/operation-resolver/operation-resolver.ts +42 -0
- package/src/capabilities/react-surface/index.ts +7 -0
- package/src/capabilities/react-surface/react-surface.tsx +55 -0
- package/src/capabilities/state/index.ts +7 -0
- package/src/capabilities/state/state.ts +122 -0
- package/src/components/CommandsDialog/CommandsDialog.tsx +62 -64
- package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -14
- package/src/components/NavTree/NavTree.stories.tsx +35 -43
- package/src/components/NavTree/NavTree.tsx +1 -11
- package/src/components/NavTreeContainer.tsx +70 -114
- package/src/components/NavTreeDocumentTitle.tsx +3 -2
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +17 -7
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +18 -12
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +5 -5
- package/src/components/Sidebar/L0Menu.tsx +67 -96
- package/src/components/Sidebar/L1Panel.tsx +151 -70
- package/src/components/Sidebar/L1Tabs.tsx +6 -31
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +1 -1
- package/src/components/types.ts +7 -7
- package/src/experimental/Tree.stories.tsx +44 -50
- package/src/experimental/Tree.tsx +1 -1
- package/src/hooks.ts +54 -2
- package/src/index.ts +1 -2
- package/src/meta.ts +2 -2
- package/src/testing/app-graph-builder.ts +158 -234
- package/src/testing/generator.ts +12 -12
- package/src/testing/update-state.ts +6 -6
- package/src/types/capabilities.ts +21 -0
- package/src/types/events.ts +12 -0
- package/src/types/index.ts +7 -0
- package/src/{types.ts → types/schema.ts} +5 -5
- package/src/util.ts +13 -16
- package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs +0 -46
- package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +0 -7
- package/dist/lib/browser/chunk-5DWDYPGQ.mjs +0 -1492
- package/dist/lib/browser/chunk-5DWDYPGQ.mjs.map +0 -7
- package/dist/lib/browser/chunk-5VLWX4PK.mjs +0 -15
- package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +0 -7
- package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs +0 -47
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +0 -7
- package/dist/lib/browser/keyboard-YJEK2HTH.mjs.map +0 -7
- package/dist/lib/browser/react-surface-RPXGIV45.mjs.map +0 -7
- package/dist/lib/browser/state-BFD5KJCV.mjs +0 -117
- package/dist/lib/browser/state-BFD5KJCV.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
- package/dist/types/src/capabilities/capabilities.d.ts +0 -21
- package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/keyboard.d.ts +0 -4
- package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface.d.ts +0 -4
- package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
- package/dist/types/src/capabilities/state.d.ts +0 -20
- package/dist/types/src/capabilities/state.d.ts.map +0 -1
- package/dist/types/src/events.d.ts +0 -4
- package/dist/types/src/events.d.ts.map +0 -1
- package/dist/types/src/types.d.ts.map +0 -1
- package/src/capabilities/app-graph-builder.ts +0 -53
- package/src/capabilities/capabilities.ts +0 -20
- package/src/capabilities/intent-resolver.ts +0 -37
- package/src/capabilities/keyboard.ts +0 -57
- package/src/capabilities/react-surface.tsx +0 -48
- package/src/capabilities/state.ts +0 -104
- package/src/events.ts +0 -11
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
10
|
+
import { Path } from '@dxos/react-ui-list';
|
|
11
|
+
|
|
12
|
+
import { meta } from '../../meta';
|
|
13
|
+
import { type NavTreeCapabilities as NC, NavTreeCapabilities } from '../../types';
|
|
14
|
+
|
|
15
|
+
const KEY = `${meta.id}/state/v1`;
|
|
16
|
+
|
|
17
|
+
/** Default item state for new entries. */
|
|
18
|
+
const defaultItemState: NC.NavTreeItemState = { open: false, current: false, alternateTree: false };
|
|
19
|
+
|
|
20
|
+
/** Default state entries for initial tree structure. */
|
|
21
|
+
// TODO(thure): Initialize these dynamically.
|
|
22
|
+
const defaultStateEntries: [string, NC.NavTreeItemState][] = [
|
|
23
|
+
['root', { open: true, current: false }],
|
|
24
|
+
['root~dxos.org/plugin/space-spaces', { open: true, current: false }],
|
|
25
|
+
['root~dxos.org/plugin/files', { open: true, current: false }],
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const getInitialState = (): Map<string, NC.NavTreeItemState> => {
|
|
29
|
+
const stringified = localStorage.getItem(KEY);
|
|
30
|
+
if (!stringified) {
|
|
31
|
+
return new Map(defaultStateEntries);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const cached: [string, { open: boolean; current: boolean }][] = JSON.parse(stringified);
|
|
36
|
+
return new Map(cached.map(([key, value]) => [key, { open: value.open, current: false }]));
|
|
37
|
+
} catch {
|
|
38
|
+
return new Map(defaultStateEntries);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default Capability.makeModule(
|
|
43
|
+
Effect.fnUntraced(function* () {
|
|
44
|
+
const registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
45
|
+
const layoutAtom = yield* Capability.get(AppCapabilities.Layout);
|
|
46
|
+
|
|
47
|
+
// Backing state for persistence (not reactive).
|
|
48
|
+
const backingState = getInitialState();
|
|
49
|
+
|
|
50
|
+
// Per-path atom family for fine-grained reactivity.
|
|
51
|
+
// keepAlive prevents atoms from being garbage collected when components unmount,
|
|
52
|
+
// ensuring state is preserved across deletion/restoration cycles.
|
|
53
|
+
const itemAtomFamily = Atom.family((pathString: string) =>
|
|
54
|
+
Atom.make<NC.NavTreeItemState>(backingState.get(pathString) ?? { ...defaultItemState }).pipe(Atom.keepAlive),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const getItemAtom = (path: string[]): Atom.Atom<NC.NavTreeItemState> => {
|
|
58
|
+
const pathString = Path.create(...path);
|
|
59
|
+
return itemAtomFamily(pathString);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getItem = (path: string[]): NC.NavTreeItemState => {
|
|
63
|
+
return registry.get(getItemAtom(path));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const setItem = (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => {
|
|
67
|
+
const pathString = Path.create(...path);
|
|
68
|
+
const atom = itemAtomFamily(pathString);
|
|
69
|
+
const currentValue = registry.get(atom);
|
|
70
|
+
const newValue = { ...currentValue, [key]: next };
|
|
71
|
+
|
|
72
|
+
// Update the atom.
|
|
73
|
+
registry.set(atom, newValue);
|
|
74
|
+
|
|
75
|
+
// Update backing state and persist to localStorage.
|
|
76
|
+
backingState.set(pathString, newValue);
|
|
77
|
+
localStorage.setItem(KEY, JSON.stringify(Array.from(backingState.entries())));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Subscribe to layout changes to update current state.
|
|
81
|
+
let previous: string[] = [];
|
|
82
|
+
const unsubscribe = registry.subscribe(layoutAtom, (layout) => {
|
|
83
|
+
const removed = previous.filter((id) => !layout.active.includes(id));
|
|
84
|
+
previous = layout.active;
|
|
85
|
+
|
|
86
|
+
const handleUpdate = () => {
|
|
87
|
+
// Mark removed items as not current.
|
|
88
|
+
removed.forEach((id) => {
|
|
89
|
+
const keys = Array.from(backingState.keys()).filter((key) => Path.last(key) === id);
|
|
90
|
+
keys.forEach((key) => {
|
|
91
|
+
setItem(Path.parts(key), 'current', false);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Mark active items as current.
|
|
96
|
+
layout.active.forEach((id: string) => {
|
|
97
|
+
const keys = Array.from(new Set([...backingState.keys(), id])).filter((key) => Path.last(key) === id);
|
|
98
|
+
keys.forEach((key) => {
|
|
99
|
+
setItem(Path.parts(key), 'current', true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// TODO(wittjosiah): This is setTimeout because there's a race between the keys being initialized.
|
|
105
|
+
// Keys are initialized on the first render of an item in the navtree.
|
|
106
|
+
// This could be avoided if the location was a path as well and not just an id.
|
|
107
|
+
const timeout = setTimeout(handleUpdate, 500);
|
|
108
|
+
handleUpdate();
|
|
109
|
+
return () => clearTimeout(timeout);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return Capability.contributes(
|
|
113
|
+
NavTreeCapabilities.State,
|
|
114
|
+
{
|
|
115
|
+
getItem,
|
|
116
|
+
getItemAtom,
|
|
117
|
+
setItem,
|
|
118
|
+
},
|
|
119
|
+
() => Effect.sync(() => unsubscribe()),
|
|
120
|
+
);
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
@@ -4,20 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { forwardRef, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
10
10
|
import { Keyboard, keySymbols } from '@dxos/keyboard';
|
|
11
|
+
import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
|
|
11
12
|
import { useActions } from '@dxos/plugin-graph';
|
|
12
|
-
import { Button, Dialog,
|
|
13
|
-
import { SearchList } from '@dxos/react-ui-searchlist';
|
|
14
|
-
import {
|
|
15
|
-
cardDialogContent,
|
|
16
|
-
cardDialogHeader,
|
|
17
|
-
cardDialogPaddedOverflow,
|
|
18
|
-
cardDialogSearchListRoot,
|
|
19
|
-
} from '@dxos/react-ui-stack';
|
|
20
|
-
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
13
|
+
import { Button, Dialog, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
14
|
+
import { SearchList, useSearchListResults } from '@dxos/react-ui-searchlist';
|
|
15
|
+
import { osTranslations } from '@dxos/ui-theme';
|
|
21
16
|
import { getHostPlatform } from '@dxos/util';
|
|
22
17
|
|
|
23
18
|
import { KEY_BINDING, meta } from '../../meta';
|
|
@@ -30,7 +25,8 @@ export type CommandsDialogContentProps = {
|
|
|
30
25
|
export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
|
|
31
26
|
({ selected: initial }, forwardedRef) => {
|
|
32
27
|
const { t } = useTranslation(meta.id);
|
|
33
|
-
const {
|
|
28
|
+
const { invokeSync } = useOperationInvoker();
|
|
29
|
+
const runAction = useActionRunner();
|
|
34
30
|
const { graph } = useAppGraph();
|
|
35
31
|
const [selected, setSelected] = useState<string | undefined>(initial);
|
|
36
32
|
|
|
@@ -40,11 +36,11 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
|
|
|
40
36
|
// TODO(burdon): Get from navtree (not keyboard).
|
|
41
37
|
const current = Keyboard.singleton.getCurrentContext();
|
|
42
38
|
const actionMap = new Set<string>();
|
|
43
|
-
const actions: ActionLike[] = [];
|
|
44
|
-
|
|
39
|
+
const actions: Node.ActionLike[] = [];
|
|
40
|
+
Graph.traverse(graph, {
|
|
45
41
|
visitor: (node, path) => {
|
|
46
42
|
if (
|
|
47
|
-
(isAction(node) || isActionGroup(node)) &&
|
|
43
|
+
(Node.isAction(node) || Node.isActionGroup(node)) &&
|
|
48
44
|
!actionMap.has(node.id) &&
|
|
49
45
|
current.startsWith(path.slice(0, -1).join('/'))
|
|
50
46
|
) {
|
|
@@ -65,63 +61,65 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
|
|
|
65
61
|
|
|
66
62
|
const group = allActions.find(({ id }) => id === selected);
|
|
67
63
|
const groupActions = useActions(graph, group?.id);
|
|
68
|
-
const actions = isActionGroup(group) ? groupActions : allActions;
|
|
64
|
+
const actions = Node.isActionGroup(group) ? groupActions : allActions;
|
|
65
|
+
|
|
66
|
+
const { results, handleSearch } = useSearchListResults({
|
|
67
|
+
items: actions,
|
|
68
|
+
extract: (action) => toLocalizedString(action.properties.label, t),
|
|
69
|
+
});
|
|
69
70
|
|
|
70
71
|
return (
|
|
71
|
-
<Dialog.Content
|
|
72
|
-
<Dialog.Title
|
|
72
|
+
<Dialog.Content ref={forwardedRef}>
|
|
73
|
+
<Dialog.Title>{t('commands dialog title', { ns: meta.id })}</Dialog.Title>
|
|
74
|
+
<SearchList.Root onSearch={handleSearch}>
|
|
75
|
+
<SearchList.Content>
|
|
76
|
+
<SearchList.Input placeholder={t('command list input placeholder')} />
|
|
77
|
+
<SearchList.Viewport>
|
|
78
|
+
{results.map((action) => {
|
|
79
|
+
const shortcut =
|
|
80
|
+
typeof action.properties.keyBinding === 'string'
|
|
81
|
+
? action.properties.keyBinding
|
|
82
|
+
: action.properties.keyBinding?.[getHostPlatform()];
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
value={label}
|
|
86
|
-
key={action.id}
|
|
87
|
-
onSelect={() => {
|
|
88
|
-
if (action.properties.disabled) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
84
|
+
return (
|
|
85
|
+
<SearchList.Item
|
|
86
|
+
value={action.id}
|
|
87
|
+
key={action.id}
|
|
88
|
+
label={toLocalizedString(action.properties.label, t)}
|
|
89
|
+
icon={action.properties.icon}
|
|
90
|
+
suffix={shortcut ? keySymbols(shortcut).join('') : undefined}
|
|
91
|
+
onSelect={() => {
|
|
92
|
+
if (action.properties.disabled) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
if (Node.isActionGroup(action)) {
|
|
97
|
+
setSelected(action.id);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
<Icon icon={action.properties.icon} size={4} />
|
|
115
|
-
<span className='grow truncate'>{label}</span>
|
|
116
|
-
{shortcut && <span className={mx('shrink-0', descriptionText)}>{keySymbols(shortcut).join('')}</span>}
|
|
117
|
-
</SearchList.Item>
|
|
118
|
-
);
|
|
119
|
-
})}
|
|
101
|
+
invokeSync(LayoutOperation.UpdateDialog, { state: false });
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
const node = Graph.getConnections(graph, group?.id ?? action.id, 'inbound')[0];
|
|
104
|
+
if (node && Node.isAction(action)) {
|
|
105
|
+
void runAction(action, { parent: node, caller: KEY_BINDING });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}}
|
|
109
|
+
classNames='flex items-center gap-2'
|
|
110
|
+
disabled={action.properties.disabled}
|
|
111
|
+
{...(action.properties?.testId && {
|
|
112
|
+
'data-testid': action.properties.testId,
|
|
113
|
+
})}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
})}
|
|
117
|
+
</SearchList.Viewport>
|
|
120
118
|
</SearchList.Content>
|
|
121
119
|
</SearchList.Root>
|
|
122
120
|
<div role='none' className='pli-cardSpacingInline pbe-cardSpacingBlock'>
|
|
123
121
|
<Dialog.Close asChild>
|
|
124
|
-
<Button classNames='is-full'>{t('close label', { ns:
|
|
122
|
+
<Button classNames='is-full'>{t('close label', { ns: osTranslations })}</Button>
|
|
125
123
|
</Dialog.Close>
|
|
126
124
|
</div>
|
|
127
125
|
</Dialog.Content>
|
|
@@ -4,30 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
9
|
import { Button, Icon, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
|
|
11
11
|
import { COMMANDS_DIALOG, meta } from '../../meta';
|
|
12
12
|
|
|
13
13
|
// TODO(thure): Refactor to be handled by a more appropriate plugin.
|
|
14
14
|
export const CommandsTrigger = () => {
|
|
15
|
-
const {
|
|
15
|
+
const { invokeSync } = useOperationInvoker();
|
|
16
16
|
const { t } = useTranslation(meta.id);
|
|
17
17
|
return (
|
|
18
18
|
<Button
|
|
19
19
|
classNames='m-1 pli-1 lg:pli-2'
|
|
20
|
-
onClick={() =>
|
|
21
|
-
dispatch(
|
|
22
|
-
createIntent(LayoutAction.UpdateDialog, {
|
|
23
|
-
part: 'dialog',
|
|
24
|
-
subject: COMMANDS_DIALOG,
|
|
25
|
-
options: {
|
|
26
|
-
blockAlign: 'start',
|
|
27
|
-
},
|
|
28
|
-
}),
|
|
29
|
-
)
|
|
30
|
-
}
|
|
20
|
+
onClick={() => invokeSync(LayoutOperation.UpdateDialog, { subject: COMMANDS_DIALOG, blockAlign: 'start' })}
|
|
31
21
|
>
|
|
32
22
|
<span className='text-description font-normal grow text-start'>{t('command list input placeholder')}</span>
|
|
33
23
|
<Icon icon='ph--magnifying-glass--regular' size={5} />
|
|
@@ -2,33 +2,24 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
5
6
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import * as
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
7
8
|
import React, { type KeyboardEvent, useCallback, useRef } from 'react';
|
|
8
9
|
import { expect, userEvent, within } from 'storybook/test';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
-
Capabilities,
|
|
12
|
-
IntentPlugin,
|
|
13
|
-
LayoutAction,
|
|
14
|
-
SettingsPlugin,
|
|
15
|
-
contributes,
|
|
16
|
-
createResolver,
|
|
17
|
-
defineCapability,
|
|
18
|
-
} from '@dxos/app-framework';
|
|
19
|
-
import { useCapability } from '@dxos/app-framework/react';
|
|
11
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
20
12
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
13
|
+
import { useAtomCapability } from '@dxos/app-framework/ui';
|
|
14
|
+
import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
|
|
15
|
+
import { OperationResolver } from '@dxos/operation';
|
|
16
|
+
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
26
17
|
import { faker } from '@dxos/random';
|
|
27
18
|
import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
|
|
28
19
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
29
20
|
import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
30
21
|
import { Stack, StackItem } from '@dxos/react-ui-stack';
|
|
31
|
-
import {
|
|
22
|
+
import { mx } from '@dxos/ui-theme';
|
|
32
23
|
|
|
33
24
|
import { NavTreePlugin } from '../../NavTreePlugin';
|
|
34
25
|
import { storybookGraphBuilders } from '../../testing';
|
|
@@ -37,7 +28,7 @@ import { NavTreeContainer } from '../NavTreeContainer';
|
|
|
37
28
|
|
|
38
29
|
faker.seed(1234);
|
|
39
30
|
|
|
40
|
-
const StoryState =
|
|
31
|
+
const StoryState = Capability.make<Atom.Atom<{ tab: string }>>('story-state');
|
|
41
32
|
|
|
42
33
|
// TODO(burdon): Fix outline (e.g., button in sidebar nav is clipped when focused).
|
|
43
34
|
// TODO(burdon): Consider similar containment of: Table, Sheet, Kanban Column, Form, etc.
|
|
@@ -49,7 +40,7 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
49
40
|
const { hasAttention } = useAttention(attendableId);
|
|
50
41
|
console.log('hasAttention', hasAttention);
|
|
51
42
|
return (
|
|
52
|
-
<div className='flex p-1 items-center border-
|
|
43
|
+
<div className='flex p-1 items-center border-be border-separator'>
|
|
53
44
|
<IconButton
|
|
54
45
|
density='coarse'
|
|
55
46
|
icon='ph--atom--regular'
|
|
@@ -107,10 +98,10 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
107
98
|
};
|
|
108
99
|
|
|
109
100
|
const DefaultStory = () => {
|
|
110
|
-
const state =
|
|
101
|
+
const state = useAtomCapability(StoryState);
|
|
111
102
|
|
|
112
103
|
return (
|
|
113
|
-
<Main.Root
|
|
104
|
+
<Main.Root navigationSidebarState='expanded'>
|
|
114
105
|
<Main.NavigationSidebar label='Navigation' classNames='grid'>
|
|
115
106
|
<NavTreeContainer tab={state.tab} />
|
|
116
107
|
</Main.NavigationSidebar>
|
|
@@ -129,32 +120,33 @@ const meta = {
|
|
|
129
120
|
component: NavTreeContainer,
|
|
130
121
|
render: DefaultStory,
|
|
131
122
|
decorators: [
|
|
132
|
-
withTheme,
|
|
123
|
+
withTheme(),
|
|
133
124
|
withPluginManager({
|
|
134
125
|
plugins: [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
...corePlugins(),
|
|
127
|
+
StorybookPlugin({
|
|
128
|
+
initialState: { sidebarState: 'expanded' },
|
|
129
|
+
}),
|
|
130
|
+
|
|
140
131
|
NavTreePlugin(),
|
|
141
|
-
StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
|
|
142
|
-
],
|
|
143
|
-
capabilities: (context) => [
|
|
144
|
-
contributes(StoryState, live({ tab: 'space-0' })),
|
|
145
|
-
contributes(Capabilities.AppGraphBuilder, storybookGraphBuilders(context)),
|
|
146
|
-
contributes(Capabilities.IntentResolver, [
|
|
147
|
-
createResolver({
|
|
148
|
-
intent: LayoutAction.UpdateLayout,
|
|
149
|
-
filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
|
|
150
|
-
Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
|
|
151
|
-
resolve: ({ subject }) => {
|
|
152
|
-
const state = context.getCapability(StoryState);
|
|
153
|
-
state.tab = subject;
|
|
154
|
-
},
|
|
155
|
-
}),
|
|
156
|
-
]),
|
|
157
132
|
],
|
|
133
|
+
capabilities: () => {
|
|
134
|
+
const storyStateAtom = Atom.make({ tab: 'space-0' }).pipe(Atom.keepAlive);
|
|
135
|
+
return [
|
|
136
|
+
Capability.contributes(StoryState, storyStateAtom),
|
|
137
|
+
Capability.contributes(AppCapabilities.AppGraphBuilder, storybookGraphBuilders()),
|
|
138
|
+
Capability.contributes(Capabilities.OperationResolver, [
|
|
139
|
+
OperationResolver.make({
|
|
140
|
+
operation: LayoutOperation.SwitchWorkspace,
|
|
141
|
+
handler: ({ subject }) =>
|
|
142
|
+
Effect.gen(function* () {
|
|
143
|
+
const registry: Registry.Registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
144
|
+
registry.set(storyStateAtom, { tab: subject });
|
|
145
|
+
}),
|
|
146
|
+
}),
|
|
147
|
+
]),
|
|
148
|
+
];
|
|
149
|
+
},
|
|
158
150
|
}),
|
|
159
151
|
],
|
|
160
152
|
parameters: {
|
|
@@ -19,18 +19,8 @@ export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'>
|
|
|
19
19
|
export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, ...props }, forwardedRef) => {
|
|
20
20
|
const { tab, useItems, onBack } = useNavTreeContext();
|
|
21
21
|
const topLevelActions = useItems(root, { disposition: 'menu', sort: true });
|
|
22
|
-
const topLevelCollections = useItems(root, { disposition: 'collection' });
|
|
23
22
|
const topLevelWorkspaces = useItems(root, { disposition: 'workspace' });
|
|
24
|
-
const
|
|
25
|
-
const l0Items = useMemo(
|
|
26
|
-
() => [
|
|
27
|
-
// prettier-ignore
|
|
28
|
-
...topLevelCollections,
|
|
29
|
-
...topLevelWorkspaces,
|
|
30
|
-
...topLevelNavigation,
|
|
31
|
-
],
|
|
32
|
-
[topLevelCollections, topLevelWorkspaces, topLevelNavigation],
|
|
33
|
-
);
|
|
23
|
+
const l0Items = topLevelWorkspaces;
|
|
34
24
|
const pinnedItems = useItems(root, { disposition: 'pin-end', sort: true });
|
|
35
25
|
const userAccountItem = useItems(root, { disposition: 'user-account' })[0];
|
|
36
26
|
const topLevelItems = useMemo(
|