@dxos/plugin-simple-layout 0.8.4-main.52d7546f51 → 0.8.4-main.6fa680abb7
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/{chunk-O3BQBYMW.mjs → chunk-MDPEKLKR.mjs} +181 -183
- package/dist/lib/browser/chunk-MDPEKLKR.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7VLT3S46.mjs → chunk-MRR7PXSM.mjs} +3 -3
- package/dist/lib/browser/{chunk-7VLT3S46.mjs.map → chunk-MRR7PXSM.mjs.map} +1 -1
- package/dist/lib/browser/index.mjs +6 -6
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{operation-resolver-BYRIQOQT.mjs → operation-resolver-VTZ6HZ4B.mjs} +24 -35
- package/dist/lib/browser/operation-resolver-VTZ6HZ4B.mjs.map +7 -0
- package/dist/lib/browser/{react-root-GPTKI5H2.mjs → react-root-WVQYY2JA.mjs} +3 -3
- package/dist/lib/browser/{react-surface-LT5JJTPR.mjs → react-surface-VLBR37ED.mjs} +11 -8
- package/dist/lib/browser/{react-surface-LT5JJTPR.mjs.map → react-surface-VLBR37ED.mjs.map} +3 -3
- package/dist/lib/browser/{state-A3PGDWWZ.mjs → state-TXSMUWYI.mjs} +2 -2
- package/dist/lib/browser/{url-handler-HTIUY6WL.mjs → url-handler-RBRONH7S.mjs} +18 -19
- package/dist/lib/browser/url-handler-RBRONH7S.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-UAWM4B2S.mjs → chunk-DCKASLMP.mjs} +181 -183
- package/dist/lib/node-esm/chunk-DCKASLMP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-VIDE5UMB.mjs → chunk-WMNTJ2MK.mjs} +3 -3
- package/dist/lib/node-esm/{chunk-VIDE5UMB.mjs.map → chunk-WMNTJ2MK.mjs.map} +1 -1
- package/dist/lib/node-esm/index.mjs +6 -6
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{operation-resolver-BDTFNCS2.mjs → operation-resolver-R7CQ6ERU.mjs} +24 -35
- package/dist/lib/node-esm/operation-resolver-R7CQ6ERU.mjs.map +7 -0
- package/dist/lib/node-esm/{react-root-GRG2OAI2.mjs → react-root-XBNDM7BE.mjs} +3 -3
- package/dist/lib/node-esm/{react-surface-TCUSDIN2.mjs → react-surface-U5NHA367.mjs} +11 -8
- package/dist/lib/node-esm/{react-surface-TCUSDIN2.mjs.map → react-surface-U5NHA367.mjs.map} +3 -3
- package/dist/lib/node-esm/{state-ZCFZTTPL.mjs → state-JMX6FAG4.mjs} +2 -2
- package/dist/lib/node-esm/{url-handler-WBVVKVPC.mjs → url-handler-QSMCH3JB.mjs} +18 -19
- package/dist/lib/node-esm/url-handler-QSMCH3JB.mjs.map +7 -0
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-root/react-root.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/index.d.ts +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/spotlight-dismiss/spotlight-dismiss.d.ts +1 -1
- package/dist/types/src/capabilities/state/index.d.ts +1 -1
- package/dist/types/src/capabilities/state/state.d.ts +1 -1
- package/dist/types/src/capabilities/url-handler/url-handler.d.ts +3 -3
- package/dist/types/src/capabilities/url-handler/url-handler.d.ts.map +1 -1
- package/dist/types/src/components/ContentError.stories.d.ts +1 -3
- package/dist/types/src/components/ContentError.stories.d.ts.map +1 -1
- package/dist/types/src/components/ContentLoading/ContentLoading.d.ts.map +1 -0
- package/dist/types/src/components/ContentLoading/ContentLoading.stories.d.ts.map +1 -0
- package/dist/types/src/components/ContentLoading/index.d.ts +2 -0
- package/dist/types/src/components/ContentLoading/index.d.ts.map +1 -0
- package/dist/types/src/components/Home/Home.d.ts.map +1 -1
- package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavBranch/NavBranch.d.ts +11 -0
- package/dist/types/src/components/NavBranch/NavBranch.d.ts.map +1 -0
- package/dist/types/src/components/NavBranch/index.d.ts +2 -0
- package/dist/types/src/components/NavBranch/index.d.ts.map +1 -0
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/AppBar.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/Drawer.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/Main.d.ts.map +1 -1
- package/dist/types/src/components/SimpleLayout/NavBar.d.ts.map +1 -1
- package/dist/types/src/components/hooks.d.ts +4 -2
- package/dist/types/src/components/hooks.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/hooks/actions.d.ts +3 -4
- package/dist/types/src/hooks/actions.d.ts.map +1 -1
- package/dist/types/src/hooks/useAppBarProps.d.ts.map +1 -1
- package/dist/types/src/hooks/useDrawerActions.d.ts.map +1 -1
- package/dist/types/src/hooks/useNavbarActions.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -28
- package/src/capabilities/operation-resolver/operation-resolver.ts +19 -34
- package/src/capabilities/react-surface/react-surface.tsx +8 -6
- package/src/capabilities/url-handler/url-handler.ts +11 -35
- package/src/components/ContentError.stories.tsx +7 -6
- package/src/components/{ContentLoading.stories.tsx → ContentLoading/ContentLoading.stories.tsx} +1 -1
- package/src/components/{ContentLoading.tsx → ContentLoading/ContentLoading.tsx} +1 -1
- package/src/components/ContentLoading/index.ts +5 -0
- package/src/components/Dialog/Dialog.tsx +3 -3
- package/src/components/Home/Home.tsx +30 -24
- package/src/components/MobileLayout/MobileLayout.stories.tsx +23 -19
- package/src/components/MobileLayout/MobileLayout.tsx +2 -2
- package/src/components/{Workspace/Workspace.tsx → NavBranch/NavBranch.tsx} +43 -32
- package/src/components/{Workspace → NavBranch}/index.ts +1 -1
- package/src/components/Popover/Popover.tsx +14 -4
- package/src/components/SimpleLayout/AppBar.stories.tsx +2 -2
- package/src/components/SimpleLayout/AppBar.tsx +14 -21
- package/src/components/SimpleLayout/Drawer.tsx +15 -21
- package/src/components/SimpleLayout/Main.tsx +11 -10
- package/src/components/SimpleLayout/NavBar.stories.tsx +8 -8
- package/src/components/SimpleLayout/NavBar.tsx +4 -10
- package/src/components/SimpleLayout/SimpleLayout.stories.tsx +2 -2
- package/src/components/SimpleLayout/SimpleLayout.tsx +1 -1
- package/src/components/hooks.ts +8 -8
- package/src/components/index.ts +1 -1
- package/src/hooks/actions.ts +15 -17
- package/src/hooks/useAppBarProps.ts +8 -5
- package/src/hooks/useCompanions.ts +1 -1
- package/src/hooks/useDrawerActions.ts +9 -7
- package/src/hooks/useNavbarActions.ts +10 -9
- package/src/meta.ts +1 -1
- package/src/types/capabilities.ts +1 -1
- package/dist/lib/browser/chunk-O3BQBYMW.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-BYRIQOQT.mjs.map +0 -7
- package/dist/lib/browser/url-handler-HTIUY6WL.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-UAWM4B2S.mjs.map +0 -7
- package/dist/lib/node-esm/operation-resolver-BDTFNCS2.mjs.map +0 -7
- package/dist/lib/node-esm/url-handler-WBVVKVPC.mjs.map +0 -7
- package/dist/types/src/components/ContentError.d.ts +0 -5
- package/dist/types/src/components/ContentError.d.ts.map +0 -1
- package/dist/types/src/components/ContentLoading.d.ts.map +0 -1
- package/dist/types/src/components/ContentLoading.stories.d.ts.map +0 -1
- package/dist/types/src/components/Workspace/Workspace.d.ts +0 -11
- package/dist/types/src/components/Workspace/Workspace.d.ts.map +0 -1
- package/dist/types/src/components/Workspace/index.d.ts +0 -2
- package/dist/types/src/components/Workspace/index.d.ts.map +0 -1
- package/src/components/ContentError.tsx +0 -23
- /package/dist/lib/browser/{react-root-GPTKI5H2.mjs.map → react-root-WVQYY2JA.mjs.map} +0 -0
- /package/dist/lib/browser/{state-A3PGDWWZ.mjs.map → state-TXSMUWYI.mjs.map} +0 -0
- /package/dist/lib/node-esm/{react-root-GRG2OAI2.mjs.map → react-root-XBNDM7BE.mjs.map} +0 -0
- /package/dist/lib/node-esm/{state-ZCFZTTPL.mjs.map → state-JMX6FAG4.mjs.map} +0 -0
- /package/dist/types/src/components/{ContentLoading.d.ts → ContentLoading/ContentLoading.d.ts} +0 -0
- /package/dist/types/src/components/{ContentLoading.stories.d.ts → ContentLoading/ContentLoading.stories.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-simple-layout",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.6fa680abb7",
|
|
4
4
|
"description": "Simple layout plugin for minimal UI contexts like popover windows.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
"type": "module",
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
|
+
"source": "./src/index.ts",
|
|
17
18
|
"browser": "./dist/lib/browser/index.mjs",
|
|
18
19
|
"node": "./dist/lib/node-esm/index.mjs",
|
|
19
|
-
"source": "./src/index.ts",
|
|
20
20
|
"types": "./dist/types/src/index.d.ts"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
@@ -31,19 +31,20 @@
|
|
|
31
31
|
"@radix-ui/react-context": "1.1.1",
|
|
32
32
|
"@tauri-apps/plugin-deep-link": "^2.2.0",
|
|
33
33
|
"@tauri-apps/plugin-haptics": "^2.3.2",
|
|
34
|
-
"@dxos/app-framework": "0.8.4-main.
|
|
35
|
-
"@dxos/async": "0.8.4-main.
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/react-ui-
|
|
42
|
-
"@dxos/react-ui-
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/react-ui-stack": "0.8.4-main.
|
|
46
|
-
"@dxos/
|
|
34
|
+
"@dxos/app-framework": "0.8.4-main.6fa680abb7",
|
|
35
|
+
"@dxos/async": "0.8.4-main.6fa680abb7",
|
|
36
|
+
"@dxos/app-toolkit": "0.8.4-main.6fa680abb7",
|
|
37
|
+
"@dxos/log": "0.8.4-main.6fa680abb7",
|
|
38
|
+
"@dxos/echo": "0.8.4-main.6fa680abb7",
|
|
39
|
+
"@dxos/operation": "0.8.4-main.6fa680abb7",
|
|
40
|
+
"@dxos/plugin-graph": "0.8.4-main.6fa680abb7",
|
|
41
|
+
"@dxos/react-ui-menu": "0.8.4-main.6fa680abb7",
|
|
42
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.6fa680abb7",
|
|
43
|
+
"@dxos/react-ui-attention": "0.8.4-main.6fa680abb7",
|
|
44
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.6fa680abb7",
|
|
45
|
+
"@dxos/react-ui-stack": "0.8.4-main.6fa680abb7",
|
|
46
|
+
"@dxos/schema": "0.8.4-main.6fa680abb7",
|
|
47
|
+
"@dxos/util": "0.8.4-main.6fa680abb7"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@types/react": "~19.2.7",
|
|
@@ -51,24 +52,24 @@
|
|
|
51
52
|
"effect": "3.19.16",
|
|
52
53
|
"react": "~19.2.3",
|
|
53
54
|
"react-dom": "~19.2.3",
|
|
54
|
-
"vite": "7.1.
|
|
55
|
-
"@dxos/app-graph": "0.8.4-main.
|
|
56
|
-
"@dxos/plugin-
|
|
57
|
-
"@dxos/plugin-
|
|
58
|
-
"@dxos/plugin-search": "0.8.4-main.
|
|
59
|
-
"@dxos/plugin-
|
|
60
|
-
"@dxos/plugin-
|
|
61
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
62
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
63
|
-
"@dxos/schema": "0.8.4-main.
|
|
64
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
55
|
+
"vite": "^7.1.11",
|
|
56
|
+
"@dxos/app-graph": "0.8.4-main.6fa680abb7",
|
|
57
|
+
"@dxos/plugin-client": "0.8.4-main.6fa680abb7",
|
|
58
|
+
"@dxos/plugin-preview": "0.8.4-main.6fa680abb7",
|
|
59
|
+
"@dxos/plugin-search": "0.8.4-main.6fa680abb7",
|
|
60
|
+
"@dxos/plugin-testing": "0.8.4-main.6fa680abb7",
|
|
61
|
+
"@dxos/plugin-space": "0.8.4-main.6fa680abb7",
|
|
62
|
+
"@dxos/react-ui": "0.8.4-main.6fa680abb7",
|
|
63
|
+
"@dxos/storybook-utils": "0.8.4-main.6fa680abb7",
|
|
64
|
+
"@dxos/schema": "0.8.4-main.6fa680abb7",
|
|
65
|
+
"@dxos/ui-theme": "0.8.4-main.6fa680abb7"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
67
68
|
"effect": "3.19.16",
|
|
68
69
|
"react": "~19.2.3",
|
|
69
70
|
"react-dom": "~19.2.3",
|
|
70
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
71
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
71
|
+
"@dxos/react-ui": "0.8.4-main.6fa680abb7",
|
|
72
|
+
"@dxos/ui-theme": "0.8.4-main.6fa680abb7"
|
|
72
73
|
},
|
|
73
74
|
"publishConfig": {
|
|
74
75
|
"access": "public"
|
|
@@ -5,21 +5,14 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
7
|
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
-
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { getCompanionVariant, LayoutOperation, isPinnedWorkspace } from '@dxos/app-toolkit';
|
|
9
9
|
import { Operation, OperationResolver } from '@dxos/operation';
|
|
10
|
-
import { ATTENDABLE_PATH_SEPARATOR } from '@dxos/react-ui-attention';
|
|
11
10
|
|
|
12
11
|
import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapability } from '../../types';
|
|
13
12
|
|
|
14
13
|
/** Maximum number of items to keep in navigation history. */
|
|
15
14
|
const MAX_HISTORY_LENGTH = 50;
|
|
16
15
|
|
|
17
|
-
/** Parse entry ID to extract primary ID and variant. */
|
|
18
|
-
const parseEntryId = (entryId: string) => {
|
|
19
|
-
const [id, variant] = entryId.split(ATTENDABLE_PATH_SEPARATOR);
|
|
20
|
-
return { id, variant };
|
|
21
|
-
};
|
|
22
|
-
|
|
23
16
|
export default Capability.makeModule(
|
|
24
17
|
Effect.fnUntraced(function* () {
|
|
25
18
|
const registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
@@ -51,6 +44,7 @@ export default Capability.makeModule(
|
|
|
51
44
|
//
|
|
52
45
|
// UpdateComplementary - Controls companion drawer.
|
|
53
46
|
//
|
|
47
|
+
// TODO(wittjosiah): Not sure if we should be using this for the drawer.
|
|
54
48
|
OperationResolver.make({
|
|
55
49
|
operation: LayoutOperation.UpdateComplementary,
|
|
56
50
|
handler: Effect.fnUntraced(function* (input) {
|
|
@@ -59,6 +53,13 @@ export default Capability.makeModule(
|
|
|
59
53
|
...state,
|
|
60
54
|
drawerState: 'closed',
|
|
61
55
|
}));
|
|
56
|
+
} else if (input.subject) {
|
|
57
|
+
const variant = getCompanionVariant(input.subject);
|
|
58
|
+
updateState((state) => ({
|
|
59
|
+
...state,
|
|
60
|
+
companionVariant: variant,
|
|
61
|
+
drawerState: input.state === 'expanded' ? 'expanded' : 'open',
|
|
62
|
+
}));
|
|
62
63
|
}
|
|
63
64
|
}),
|
|
64
65
|
}),
|
|
@@ -116,7 +117,7 @@ export default Capability.makeModule(
|
|
|
116
117
|
...state,
|
|
117
118
|
// TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
|
|
118
119
|
// Ideally this should be worked into the data model in a generic way.
|
|
119
|
-
previousWorkspace: !state.workspace
|
|
120
|
+
previousWorkspace: !isPinnedWorkspace(state.workspace) ? state.workspace : state.previousWorkspace,
|
|
120
121
|
workspace: input.subject,
|
|
121
122
|
active: undefined,
|
|
122
123
|
// Clear history when switching workspaces.
|
|
@@ -145,32 +146,16 @@ export default Capability.makeModule(
|
|
|
145
146
|
operation: LayoutOperation.Open,
|
|
146
147
|
handler: Effect.fnUntraced(function* (input) {
|
|
147
148
|
const id = input.subject[0];
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// TODO(wittjosiah): Factor out the change-companion operation from deck to a common layout operation.
|
|
154
|
-
const isCompanionOfCurrent = variant && (primaryId === state.workspace || primaryId === state.active);
|
|
155
|
-
if (isCompanionOfCurrent) {
|
|
156
|
-
updateState((state) => ({
|
|
149
|
+
updateState((state) => {
|
|
150
|
+
const newHistory = state.active ? [...state.history, state.active] : state.history;
|
|
151
|
+
const trimmedHistory =
|
|
152
|
+
newHistory.length > MAX_HISTORY_LENGTH ? newHistory.slice(-MAX_HISTORY_LENGTH) : newHistory;
|
|
153
|
+
return {
|
|
157
154
|
...state,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Regular navigation - update active and history (use full id for alternate-tree nodes).
|
|
163
|
-
updateState((state) => {
|
|
164
|
-
const newHistory = state.active ? [...state.history, state.active] : state.history;
|
|
165
|
-
const trimmedHistory =
|
|
166
|
-
newHistory.length > MAX_HISTORY_LENGTH ? newHistory.slice(-MAX_HISTORY_LENGTH) : newHistory;
|
|
167
|
-
return {
|
|
168
|
-
...state,
|
|
169
|
-
active: id,
|
|
170
|
-
history: trimmedHistory,
|
|
171
|
-
};
|
|
172
|
-
});
|
|
173
|
-
}
|
|
155
|
+
active: id,
|
|
156
|
+
history: trimmedHistory,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
174
159
|
}),
|
|
175
160
|
}),
|
|
176
161
|
|
|
@@ -9,7 +9,7 @@ import { Capabilities, Capability } from '@dxos/app-framework';
|
|
|
9
9
|
import { Surface } from '@dxos/app-framework/ui';
|
|
10
10
|
import { Node } from '@dxos/plugin-graph';
|
|
11
11
|
|
|
12
|
-
import { Home,
|
|
12
|
+
import { Home, NavBranch } from '../../components';
|
|
13
13
|
import { meta } from '../../meta';
|
|
14
14
|
|
|
15
15
|
type SurfaceData = {
|
|
@@ -23,18 +23,20 @@ export default Capability.makeModule(() =>
|
|
|
23
23
|
Effect.succeed(
|
|
24
24
|
Capability.contributes(Capabilities.ReactSurface, [
|
|
25
25
|
Surface.create({
|
|
26
|
-
id: `${meta.id}
|
|
26
|
+
id: `${meta.id}.home`,
|
|
27
27
|
role: 'article',
|
|
28
28
|
filter: (data): data is SurfaceData => data.attendableId === Node.RootId,
|
|
29
29
|
component: () => <Home />,
|
|
30
30
|
}),
|
|
31
31
|
Surface.create({
|
|
32
|
-
id: `${meta.id}
|
|
32
|
+
id: `${meta.id}.nav-branch`,
|
|
33
33
|
role: 'article',
|
|
34
34
|
position: 'fallback',
|
|
35
|
-
filter: (data): data is SurfaceData =>
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
filter: (data): data is SurfaceData => {
|
|
36
|
+
const props = data.properties as Record<string, any>;
|
|
37
|
+
return ALLOWED_DISPOSITIONS.includes(props?.disposition) || props?.role === 'branch';
|
|
38
|
+
},
|
|
39
|
+
component: ({ data }) => <NavBranch id={data.attendableId} />,
|
|
38
40
|
}),
|
|
39
41
|
]),
|
|
40
42
|
),
|
|
@@ -5,17 +5,16 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
7
|
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
-
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
8
|
+
import { LayoutOperation, fromUrlPath, getWorkspaceFromPath, toUrlPath } from '@dxos/app-toolkit';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import { Node } from '@dxos/plugin-graph';
|
|
11
10
|
import { isTauri } from '@dxos/util';
|
|
12
11
|
|
|
13
12
|
import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapability } from '../../types';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* URL handler for simple layout that syncs browser URL with layout state.
|
|
17
|
-
* URL
|
|
18
|
-
* Root is represented as
|
|
16
|
+
* URL paths map directly to qualified graph IDs with the leading `root` segment stripped.
|
|
17
|
+
* Root is represented as `/`.
|
|
19
18
|
*
|
|
20
19
|
* On mobile Tauri, also listens for deep links via the deep-link plugin.
|
|
21
20
|
*/
|
|
@@ -25,38 +24,33 @@ export default Capability.makeModule(
|
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Handle navigation from a pathname.
|
|
28
|
-
*
|
|
27
|
+
* Restores the qualified graph ID and dispatches layout operations.
|
|
29
28
|
*/
|
|
30
29
|
const handlePathNavigation = (pathname: string) => {
|
|
31
30
|
log.info('[UrlHandler] Navigating to path', { pathname });
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
const
|
|
32
|
+
const qualifiedId = fromUrlPath(pathname);
|
|
33
|
+
const workspace = getWorkspaceFromPath(qualifiedId);
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
const targetWorkspace = !nextWorkspace || nextWorkspace === 'root' ? Node.RootId : nextWorkspace;
|
|
35
|
+
invokeSync(LayoutOperation.SwitchWorkspace, { subject: workspace });
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
invokeSync(LayoutOperation.Open, { subject: [nextActive] });
|
|
37
|
+
const activeId = qualifiedId !== workspace ? qualifiedId : undefined;
|
|
38
|
+
if (activeId) {
|
|
39
|
+
invokeSync(LayoutOperation.Open, { subject: [activeId] });
|
|
43
40
|
}
|
|
44
41
|
};
|
|
45
42
|
|
|
46
43
|
const onNavigation = handleNavigation(handlePathNavigation);
|
|
47
44
|
|
|
48
|
-
// Handle initial URL and listen for browser navigation.
|
|
49
45
|
yield* Effect.sync(() => onNavigation());
|
|
50
46
|
window.addEventListener('popstate', onNavigation);
|
|
51
47
|
|
|
52
|
-
// Set up deep link listener for mobile Tauri.
|
|
53
48
|
let unlistenDeepLink: (() => void) | undefined;
|
|
54
49
|
if (isTauri()) {
|
|
55
50
|
yield* Effect.tryPromise({
|
|
56
51
|
try: async () => {
|
|
57
52
|
const { getCurrent, onOpenUrl } = await import('@tauri-apps/plugin-deep-link');
|
|
58
53
|
|
|
59
|
-
// Check if app was launched via deep link (cold start).
|
|
60
54
|
const launchUrls = await getCurrent();
|
|
61
55
|
if (launchUrls && launchUrls.length > 0) {
|
|
62
56
|
log.info('[UrlHandler] App launched with deep links', { urls: launchUrls });
|
|
@@ -65,7 +59,6 @@ export default Capability.makeModule(
|
|
|
65
59
|
}
|
|
66
60
|
}
|
|
67
61
|
|
|
68
|
-
// Listen for deep links while app is running.
|
|
69
62
|
unlistenDeepLink = await onOpenUrl((urls) => {
|
|
70
63
|
log.info('[UrlHandler] Deep links received', { urls });
|
|
71
64
|
for (const url of urls) {
|
|
@@ -82,18 +75,16 @@ export default Capability.makeModule(
|
|
|
82
75
|
}).pipe(Effect.catchAll(() => Effect.void));
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
// Subscribe to state changes to update the URL.
|
|
86
78
|
let lastWorkspace: string | undefined;
|
|
87
79
|
let lastActive: string | undefined;
|
|
88
80
|
const unsubscribe = yield* Capabilities.subscribeAtom(SimpleLayoutStateCapability, (state: SimpleLayoutState) => {
|
|
89
81
|
const { workspace, active } = state;
|
|
90
82
|
|
|
91
|
-
// Only update URL if relevant state changed.
|
|
92
83
|
if (workspace !== lastWorkspace || active !== lastActive) {
|
|
93
84
|
lastWorkspace = workspace;
|
|
94
85
|
lastActive = active;
|
|
95
86
|
|
|
96
|
-
const path =
|
|
87
|
+
const path = active ? toUrlPath(active) : toUrlPath(workspace);
|
|
97
88
|
if (window.location.pathname !== path) {
|
|
98
89
|
history.pushState(null, '', `${path}${window.location.search}`);
|
|
99
90
|
}
|
|
@@ -110,27 +101,12 @@ export default Capability.makeModule(
|
|
|
110
101
|
}),
|
|
111
102
|
);
|
|
112
103
|
|
|
113
|
-
// TODO(wittjosiah): Instead of hardcoding redirect paths, we should either:
|
|
114
|
-
// 1. Validate that the workspace exists in the graph before navigating.
|
|
115
|
-
// 2. Implement more structured routing with explicit route definitions.
|
|
116
104
|
/**
|
|
117
105
|
* Check if a path is a special redirect path that shouldn't be navigated to.
|
|
118
106
|
* These paths are handled by other systems (e.g., OAuth).
|
|
119
107
|
*/
|
|
120
108
|
const isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');
|
|
121
109
|
|
|
122
|
-
/**
|
|
123
|
-
* Build pathname from layout state. Root workspace is / or /root/{active}.
|
|
124
|
-
*/
|
|
125
|
-
const pathFromState = (workspace: string, active: string | undefined): string =>
|
|
126
|
-
workspace === Node.RootId
|
|
127
|
-
? active
|
|
128
|
-
? `/${Node.RootId}/${active}`
|
|
129
|
-
: '/'
|
|
130
|
-
: active
|
|
131
|
-
? `/${workspace}/${active}`
|
|
132
|
-
: `/${workspace}`;
|
|
133
|
-
|
|
134
110
|
/**
|
|
135
111
|
* Returns a handler for navigation events (initial load and popstate) that navigates to current pathname.
|
|
136
112
|
*/
|
|
@@ -4,28 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
|
|
7
|
+
import { ErrorFallback } from '@dxos/react-ui';
|
|
7
8
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
8
9
|
|
|
9
10
|
import { translations } from '../translations';
|
|
10
11
|
|
|
11
|
-
import { ContentError } from './ContentError';
|
|
12
|
-
|
|
13
12
|
const meta = {
|
|
14
|
-
title: 'plugins/plugin-simple-layout/
|
|
15
|
-
component:
|
|
13
|
+
title: 'plugins/plugin-simple-layout/components/ErrorFallback',
|
|
14
|
+
component: ErrorFallback,
|
|
16
15
|
decorators: [withTheme()],
|
|
17
16
|
parameters: {
|
|
18
17
|
layout: 'centered',
|
|
19
18
|
translations,
|
|
20
19
|
},
|
|
21
|
-
} satisfies Meta<typeof
|
|
20
|
+
} satisfies Meta<typeof ErrorFallback>;
|
|
22
21
|
|
|
23
22
|
export default meta;
|
|
24
23
|
|
|
25
24
|
type Story = StoryObj<typeof meta>;
|
|
26
25
|
|
|
27
26
|
export const Default: Story = {
|
|
28
|
-
args: {
|
|
27
|
+
args: {
|
|
28
|
+
error: new Error('An unexpected error occurred'),
|
|
29
|
+
},
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export const WithError: Story = {
|
package/src/components/{ContentLoading.stories.tsx → ContentLoading/ContentLoading.stories.tsx}
RENAMED
|
@@ -9,7 +9,7 @@ import { withTheme } from '@dxos/react-ui/testing';
|
|
|
9
9
|
import { ContentLoading } from './ContentLoading';
|
|
10
10
|
|
|
11
11
|
const meta = {
|
|
12
|
-
title: 'plugins/plugin-simple-layout/ContentLoading',
|
|
12
|
+
title: 'plugins/plugin-simple-layout/components/ContentLoading',
|
|
13
13
|
component: ContentLoading,
|
|
14
14
|
decorators: [withTheme()],
|
|
15
15
|
parameters: {
|
|
@@ -6,5 +6,5 @@ import React from 'react';
|
|
|
6
6
|
|
|
7
7
|
// TODO(burdon): Show skeleton: https://github.com/dxos/dxos/issues/8259
|
|
8
8
|
export const ContentLoading = () => {
|
|
9
|
-
return <div role='none' className='grid place-items-center attention-surface' />;
|
|
9
|
+
return <div role='none' className='grid place-items-center dx-attention-surface' />;
|
|
10
10
|
};
|
|
@@ -6,9 +6,9 @@ import React from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { Surface } from '@dxos/app-framework/ui';
|
|
8
8
|
import { AlertDialog, Dialog as NaturalDialog } from '@dxos/react-ui';
|
|
9
|
+
import { ErrorFallback } from '@dxos/react-ui';
|
|
9
10
|
|
|
10
11
|
import { useSimpleLayoutState } from '../../hooks';
|
|
11
|
-
import { ContentError } from '../ContentError';
|
|
12
12
|
|
|
13
13
|
export const Dialog = () => {
|
|
14
14
|
const { state, updateState } = useSimpleLayoutState();
|
|
@@ -23,14 +23,14 @@ export const Dialog = () => {
|
|
|
23
23
|
onOpenChange={(nextOpen) => updateState((state) => ({ ...state, dialogOpen: nextOpen }))}
|
|
24
24
|
>
|
|
25
25
|
{state.dialogBlockAlign === 'end' ? (
|
|
26
|
-
<Surface.Surface role='dialog' data={state.dialogContent} limit={1} fallback={
|
|
26
|
+
<Surface.Surface role='dialog' data={state.dialogContent} limit={1} fallback={ErrorFallback} />
|
|
27
27
|
) : (
|
|
28
28
|
<DialogOverlay
|
|
29
29
|
blockAlign={state.dialogBlockAlign}
|
|
30
30
|
classNames={state.dialogOverlayClasses}
|
|
31
31
|
style={state.dialogOverlayStyle}
|
|
32
32
|
>
|
|
33
|
-
<Surface.Surface role='dialog' data={state.dialogContent} limit={1} fallback={
|
|
33
|
+
<Surface.Surface role='dialog' data={state.dialogContent} limit={1} fallback={ErrorFallback} />
|
|
34
34
|
</DialogOverlay>
|
|
35
35
|
)}
|
|
36
36
|
</DialogRoot>
|
|
@@ -8,14 +8,15 @@ import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
|
8
8
|
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
9
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
10
10
|
import { Node, useConnections } from '@dxos/plugin-graph';
|
|
11
|
-
import { Avatar, Icon,
|
|
12
|
-
import { Card
|
|
11
|
+
import { Avatar, Icon, Panel, ScrollArea, Toolbar, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { Card } from '@dxos/react-ui';
|
|
13
|
+
import { Mosaic, type MosaicStackTileComponent } from '@dxos/react-ui-mosaic';
|
|
13
14
|
import { SearchList, useSearchListItem, useSearchListResults } from '@dxos/react-ui-searchlist';
|
|
14
15
|
import { mx } from '@dxos/ui-theme';
|
|
15
16
|
import { byPosition } from '@dxos/util';
|
|
16
17
|
|
|
17
18
|
import { meta } from '../../meta';
|
|
18
|
-
import {
|
|
19
|
+
import { useExpandPath } from '../hooks';
|
|
19
20
|
|
|
20
21
|
export type HomeProps = {};
|
|
21
22
|
|
|
@@ -27,7 +28,7 @@ export const Home = (_: HomeProps) => {
|
|
|
27
28
|
const userAccountItem = useItemsByDisposition('user-account')[0];
|
|
28
29
|
const pinnedItems = useItemsByDisposition('pin-end', true);
|
|
29
30
|
const workspaceItems = useItemsByDisposition('workspace');
|
|
30
|
-
|
|
31
|
+
useExpandPath(Node.RootId);
|
|
31
32
|
|
|
32
33
|
const items = useMemo(
|
|
33
34
|
() => [...(userAccountItem ? [userAccountItem] : []), ...pinnedItems, ...workspaceItems],
|
|
@@ -40,26 +41,31 @@ export const Home = (_: HomeProps) => {
|
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
<Toolbar
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
<SearchList.Root onSearch={handleSearch}>
|
|
45
|
+
<Panel.Root>
|
|
46
|
+
<Panel.Toolbar asChild>
|
|
47
|
+
<Toolbar.Root>
|
|
48
|
+
<SearchList.Input placeholder={t('search placeholder')} autoFocus />
|
|
49
|
+
</Toolbar.Root>
|
|
50
|
+
</Panel.Toolbar>
|
|
51
|
+
<Panel.Content asChild>
|
|
52
|
+
<SearchList.Content>
|
|
53
|
+
<Mosaic.Container asChild>
|
|
54
|
+
<ScrollArea.Root orientation='vertical'>
|
|
55
|
+
<ScrollArea.Viewport classNames='p-2'>
|
|
56
|
+
<Mosaic.Stack items={results} getId={(node) => node.id} Tile={WorkspaceTile} />
|
|
57
|
+
</ScrollArea.Viewport>
|
|
58
|
+
</ScrollArea.Root>
|
|
59
|
+
</Mosaic.Container>
|
|
60
|
+
</SearchList.Content>
|
|
61
|
+
</Panel.Content>
|
|
62
|
+
</Panel.Root>
|
|
63
|
+
</SearchList.Root>
|
|
59
64
|
);
|
|
60
65
|
};
|
|
61
66
|
|
|
62
|
-
const WorkspaceTile:
|
|
67
|
+
const WorkspaceTile: MosaicStackTileComponent<Node.Node> = (props) => {
|
|
68
|
+
const data = props.data;
|
|
63
69
|
const { t } = useTranslation(meta.id);
|
|
64
70
|
const { invokePromise } = useOperationInvoker();
|
|
65
71
|
const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
|
|
@@ -67,7 +73,7 @@ const WorkspaceTile: StackTileComponent<Node.Node> = ({ data }) => {
|
|
|
67
73
|
const isSelected = selectedValue === data.id;
|
|
68
74
|
const cardRef = useRef<HTMLDivElement>(null);
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
useExpandPath(data.id);
|
|
71
77
|
|
|
72
78
|
const handleSelect = useCallback(
|
|
73
79
|
() => invokePromise(LayoutOperation.SwitchWorkspace, { subject: data.id }),
|
|
@@ -96,7 +102,7 @@ const WorkspaceTile: StackTileComponent<Node.Node> = ({ data }) => {
|
|
|
96
102
|
fullWidth
|
|
97
103
|
tabIndex={-1} // TODO(burdon): Use Mosaic.Focus.
|
|
98
104
|
data-selected={isSelected}
|
|
99
|
-
classNames={mx('dx-focus-ring', isSelected && 'bg-
|
|
105
|
+
classNames={mx('dx-focus-ring', isSelected && 'bg-hover-overlay')}
|
|
100
106
|
onClick={handleSelect}
|
|
101
107
|
ref={cardRef}
|
|
102
108
|
>
|
|
@@ -126,7 +132,7 @@ const filterItems = (node: Node.Node, disposition: string) => {
|
|
|
126
132
|
/** Returns root-level items filtered by disposition. */
|
|
127
133
|
const useItemsByDisposition = (disposition: string, sort = false) => {
|
|
128
134
|
const { graph } = useAppGraph();
|
|
129
|
-
const connections = useConnections(graph, Node.RootId);
|
|
135
|
+
const connections = useConnections(graph, Node.RootId, 'child');
|
|
130
136
|
const filtered = connections.filter((node) => filterItems(node, disposition));
|
|
131
137
|
return sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
|
|
132
138
|
};
|
|
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import React, { type PropsWithChildren, useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { addEventListener, combine } from '@dxos/async';
|
|
9
|
-
import { Flex, Input,
|
|
9
|
+
import { Flex, Input, Panel, Splitter, type SplitterMode, Toolbar } from '@dxos/react-ui';
|
|
10
10
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
|
|
12
12
|
import { MobileLayout, type MobileLayoutRootProps } from './MobileLayout';
|
|
@@ -54,20 +54,24 @@ const WithKeyboard = ({ children }: PropsWithChildren) => {
|
|
|
54
54
|
return <div className='h-screen relative'>{children}</div>;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
const
|
|
57
|
+
const StoryPanel = ({ children, label }: PropsWithChildren<{ label: string }>) => {
|
|
58
58
|
return (
|
|
59
|
-
<
|
|
60
|
-
<Toolbar
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
<Panel.Root>
|
|
60
|
+
<Panel.Toolbar asChild>
|
|
61
|
+
<Toolbar.Root>
|
|
62
|
+
{label}
|
|
63
|
+
<Toolbar.Separator />
|
|
64
|
+
{children}
|
|
65
|
+
</Toolbar.Root>
|
|
66
|
+
</Panel.Toolbar>
|
|
67
|
+
<Panel.Content asChild>
|
|
68
|
+
<Flex column classNames='p-1'>
|
|
69
|
+
<Input.Root>
|
|
70
|
+
<Input.TextInput />
|
|
71
|
+
</Input.Root>
|
|
72
|
+
</Flex>
|
|
73
|
+
</Panel.Content>
|
|
74
|
+
</Panel.Root>
|
|
71
75
|
);
|
|
72
76
|
};
|
|
73
77
|
|
|
@@ -85,21 +89,21 @@ const DefaultStory = () => {
|
|
|
85
89
|
<MobileLayout.Panel safe={{ top: true, bottom: splitterMode === 'upper' }}>
|
|
86
90
|
<Splitter.Root mode={splitterMode} ratio={0.5}>
|
|
87
91
|
<Splitter.Panel position='upper'>
|
|
88
|
-
<
|
|
92
|
+
<StoryPanel label='Main'>
|
|
89
93
|
{splitterMode === 'upper' && (
|
|
90
94
|
<Toolbar.IconButton icon='ph--plus--regular' label='Open' onClick={() => setSplitterMode('both')} />
|
|
91
95
|
)}
|
|
92
|
-
</
|
|
96
|
+
</StoryPanel>
|
|
93
97
|
</Splitter.Panel>
|
|
94
98
|
<Splitter.Panel position='lower'>
|
|
95
|
-
<
|
|
99
|
+
<StoryPanel label='Drawer'>
|
|
96
100
|
<Toolbar.IconButton
|
|
97
101
|
icon={splitterMode === 'lower' ? 'ph--arrow-down--regular' : 'ph--arrow-up--regular'}
|
|
98
102
|
label={splitterMode === 'lower' ? 'Collapse' : 'Expand'}
|
|
99
103
|
onClick={() => setSplitterMode((splitterMode) => (splitterMode === 'both' ? 'lower' : 'both'))}
|
|
100
104
|
/>
|
|
101
105
|
<Toolbar.IconButton icon='ph--x--regular' label='Close' onClick={() => setSplitterMode('upper')} />
|
|
102
|
-
</
|
|
106
|
+
</StoryPanel>
|
|
103
107
|
</Splitter.Panel>
|
|
104
108
|
</Splitter.Root>
|
|
105
109
|
</MobileLayout.Panel>
|
|
@@ -109,7 +113,7 @@ const DefaultStory = () => {
|
|
|
109
113
|
};
|
|
110
114
|
|
|
111
115
|
const meta: Meta<MobileLayoutRootProps> = {
|
|
112
|
-
title: 'plugins/plugin-simple-layout/MobileLayout',
|
|
116
|
+
title: 'plugins/plugin-simple-layout/components/MobileLayout',
|
|
113
117
|
component: MobileLayout.Root,
|
|
114
118
|
render: DefaultStory,
|
|
115
119
|
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'relative' })],
|
|
@@ -54,7 +54,7 @@ const MobileLayoutRoot = forwardRef<HTMLDivElement, MobileLayoutRootProps>(
|
|
|
54
54
|
{...props}
|
|
55
55
|
role='none'
|
|
56
56
|
style={{
|
|
57
|
-
transition: `
|
|
57
|
+
transition: `h-size ${transition}ms ease-out`,
|
|
58
58
|
blockSize: 'calc(100vh - var(--kb-height, 0px))',
|
|
59
59
|
}}
|
|
60
60
|
className={mx('absolute top-0 left-0 right-0 flex flex-col', classNames)}
|
|
@@ -95,7 +95,7 @@ const MobileLayoutPanel = forwardRef<HTMLDivElement, MobileLayoutPanelProps>(
|
|
|
95
95
|
paddingTop: safe?.top ? 'env(safe-area-inset-top)' : undefined,
|
|
96
96
|
paddingBottom: safe?.bottom ? `calc((1 - var(--kb-open, 0)) * env(safe-area-inset-bottom))` : undefined,
|
|
97
97
|
}}
|
|
98
|
-
className={mx('relative
|
|
98
|
+
className={mx('relative h-full flex flex-col overflow-hidden', classNames)}
|
|
99
99
|
ref={forwardedRef}
|
|
100
100
|
>
|
|
101
101
|
{children}
|