@noego/wood 0.2.1 → 0.3.0
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/codegen/renderer_app_generator.cjs +17 -1
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +17 -1
- package/dist/codegen/renderer_app_generator.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/navigation/index.cjs +17 -12
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +8 -4
- package/dist/navigation/index.d.ts +8 -4
- package/dist/navigation/index.js +17 -12
- package/dist/navigation/index.js.map +1 -1
- package/dist/parser/views_parser.cjs +24 -3
- package/dist/parser/views_parser.cjs.map +1 -1
- package/dist/parser/views_parser.js +24 -3
- package/dist/parser/views_parser.js.map +1 -1
- package/dist/types/views.cjs.map +1 -1
- package/dist/types/views.d.cts +3 -0
- package/dist/types/views.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/NavigationShell.svelte +154 -27
- package/src/components/WoodRecursiveRender.svelte +49 -36
|
@@ -9,14 +9,20 @@
|
|
|
9
9
|
key: string;
|
|
10
10
|
windowName: string;
|
|
11
11
|
pageName: string;
|
|
12
|
+
layoutPaths: string[];
|
|
13
|
+
layoutControllers: Array<string | undefined>;
|
|
14
|
+
layoutPreserve?: boolean[];
|
|
12
15
|
view: any;
|
|
13
16
|
layouts: any[];
|
|
14
17
|
[k: string]: unknown;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
type CreateViewControllerFn = (entry: ViewManifestEntry) => Promise<unknown>;
|
|
18
|
-
type CreateLayoutControllersFn = (entry: ViewManifestEntry) => Promise<Array<unknown>>;
|
|
21
|
+
type CreateLayoutControllersFn = (entry: ViewManifestEntry, startIndex?: number) => Promise<Array<unknown>>;
|
|
19
22
|
type GetViewManifestEntryFn = (viewKey: string) => ViewManifestEntry | null;
|
|
23
|
+
type NavigationOptions = {
|
|
24
|
+
preserveLayouts?: boolean;
|
|
25
|
+
};
|
|
20
26
|
type WoodBridgeLike = {
|
|
21
27
|
__window?: {
|
|
22
28
|
open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;
|
|
@@ -45,12 +51,29 @@
|
|
|
45
51
|
getViewManifestEntryFn: GetViewManifestEntryFn;
|
|
46
52
|
} = $props();
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
function getInitialEntry(): ViewManifestEntry {
|
|
55
|
+
return initialEntry;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getInitialController(): unknown {
|
|
59
|
+
return initialController;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getInitialLayoutControllers(): Array<unknown> {
|
|
63
|
+
return initialLayoutControllers;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let activeEntry = $state(getInitialEntry());
|
|
67
|
+
let activeController = $state(getInitialController());
|
|
68
|
+
let activeLayoutControllers: Array<unknown> = $state(getInitialLayoutControllers());
|
|
51
69
|
let activeParams: Record<string, unknown> = $state({});
|
|
52
70
|
let activeQuery: Record<string, unknown> = $state({});
|
|
53
|
-
let
|
|
71
|
+
let activePageKey = $state(createNavigationMountKey(getInitialEntry().key, {}));
|
|
72
|
+
let activeLayoutKeys: string[] = $state(
|
|
73
|
+
getInitialEntry().layoutPaths.map((_, index) =>
|
|
74
|
+
createLayoutMountKey(getInitialEntry(), index, activePageKey),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
54
77
|
|
|
55
78
|
let activePage = $derived({
|
|
56
79
|
key: activeEntry.key,
|
|
@@ -88,7 +111,7 @@
|
|
|
88
111
|
async function navigateWithinCurrentWindow(
|
|
89
112
|
viewKey: string,
|
|
90
113
|
params?: Record<string, unknown>,
|
|
91
|
-
options?: { replace?: boolean },
|
|
114
|
+
options?: { replace?: boolean; preserveLayouts?: boolean },
|
|
92
115
|
): Promise<void> {
|
|
93
116
|
const normalizedViewKey = viewKey.replace(',', '.');
|
|
94
117
|
const entry = getViewManifestEntryFn(normalizedViewKey);
|
|
@@ -102,13 +125,83 @@
|
|
|
102
125
|
}
|
|
103
126
|
|
|
104
127
|
const p = (params ?? {}) as Record<string, string>;
|
|
128
|
+
const navigationOptions: NavigationOptions = {
|
|
129
|
+
...(options?.preserveLayouts === undefined
|
|
130
|
+
? {}
|
|
131
|
+
: { preserveLayouts: options.preserveLayouts }),
|
|
132
|
+
};
|
|
105
133
|
if (options?.replace) {
|
|
106
|
-
navigation.replace(normalizedViewKey, p);
|
|
134
|
+
navigation.replace(normalizedViewKey, p, {}, navigationOptions);
|
|
107
135
|
} else {
|
|
108
|
-
navigation.go(normalizedViewKey, p);
|
|
136
|
+
navigation.go(normalizedViewKey, p, {}, navigationOptions);
|
|
109
137
|
}
|
|
110
138
|
}
|
|
111
139
|
|
|
140
|
+
function createLayoutMountKey(
|
|
141
|
+
entry: ViewManifestEntry,
|
|
142
|
+
index: number,
|
|
143
|
+
pageKey: string,
|
|
144
|
+
): string {
|
|
145
|
+
const layoutPath = entry.layoutPaths[index] ?? '';
|
|
146
|
+
const controllerPath = entry.layoutControllers[index] ?? '';
|
|
147
|
+
return `${pageKey}:layout:${index}:${layoutPath}:${controllerPath}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function createLayoutMountKeys(
|
|
151
|
+
entry: ViewManifestEntry,
|
|
152
|
+
pageKey: string,
|
|
153
|
+
preservedKeys: string[] = [],
|
|
154
|
+
): string[] {
|
|
155
|
+
return entry.layoutPaths.map((_, index) =>
|
|
156
|
+
preservedKeys[index] ?? createLayoutMountKey(entry, index, pageKey),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function layoutIdentityMatches(
|
|
161
|
+
left: ViewManifestEntry,
|
|
162
|
+
right: ViewManifestEntry,
|
|
163
|
+
index: number,
|
|
164
|
+
): boolean {
|
|
165
|
+
return (
|
|
166
|
+
left.layoutPaths[index] === right.layoutPaths[index] &&
|
|
167
|
+
left.layoutControllers[index] === right.layoutControllers[index]
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function shouldPreserveLayout(
|
|
172
|
+
from: ViewManifestEntry,
|
|
173
|
+
to: ViewManifestEntry,
|
|
174
|
+
index: number,
|
|
175
|
+
options: NavigationOptions,
|
|
176
|
+
): boolean {
|
|
177
|
+
if (options.preserveLayouts === false) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (options.preserveLayouts === true) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return from.layoutPreserve?.[index] === true && to.layoutPreserve?.[index] === true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function computePreservedLayoutDepth(
|
|
187
|
+
from: ViewManifestEntry,
|
|
188
|
+
to: ViewManifestEntry,
|
|
189
|
+
options: NavigationOptions,
|
|
190
|
+
): number {
|
|
191
|
+
const maxDepth = Math.min(from.layoutPaths.length, to.layoutPaths.length);
|
|
192
|
+
let depth = 0;
|
|
193
|
+
for (let index = 0; index < maxDepth; index += 1) {
|
|
194
|
+
if (!layoutIdentityMatches(from, to, index)) {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
if (!shouldPreserveLayout(from, to, index, options)) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
depth += 1;
|
|
201
|
+
}
|
|
202
|
+
return depth;
|
|
203
|
+
}
|
|
204
|
+
|
|
112
205
|
async function disposeActiveControllers(): Promise<void> {
|
|
113
206
|
await disposeControllerLifecycle(activeController);
|
|
114
207
|
for (const layoutController of activeLayoutControllers) {
|
|
@@ -116,10 +209,31 @@
|
|
|
116
209
|
}
|
|
117
210
|
}
|
|
118
211
|
|
|
212
|
+
async function disposeChangedControllers(fromLayoutIndex: number): Promise<void> {
|
|
213
|
+
await disposeControllerLifecycle(activeController);
|
|
214
|
+
for (let index = fromLayoutIndex; index < activeLayoutControllers.length; index += 1) {
|
|
215
|
+
await disposeControllerLifecycle(activeLayoutControllers[index]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function mergeLayoutControllers(
|
|
220
|
+
preservedDepth: number,
|
|
221
|
+
createdControllers: Array<unknown>,
|
|
222
|
+
nextLayoutCount: number,
|
|
223
|
+
): Array<unknown> {
|
|
224
|
+
const merged: Array<unknown> = [];
|
|
225
|
+
for (let index = 0; index < nextLayoutCount; index += 1) {
|
|
226
|
+
merged[index] = index < preservedDepth
|
|
227
|
+
? activeLayoutControllers[index]
|
|
228
|
+
: createdControllers[index];
|
|
229
|
+
}
|
|
230
|
+
return merged;
|
|
231
|
+
}
|
|
232
|
+
|
|
119
233
|
setContext('wood:navigate', (
|
|
120
234
|
viewKey: string,
|
|
121
235
|
params?: Record<string, unknown>,
|
|
122
|
-
options?: { replace?: boolean },
|
|
236
|
+
options?: { replace?: boolean; preserveLayouts?: boolean },
|
|
123
237
|
) => {
|
|
124
238
|
void navigateWithinCurrentWindow(viewKey, params, options).catch((error) => {
|
|
125
239
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -148,7 +262,8 @@
|
|
|
148
262
|
if (navEntry) {
|
|
149
263
|
activeParams = { ...navEntry.params };
|
|
150
264
|
activeQuery = { ...navEntry.query };
|
|
151
|
-
|
|
265
|
+
activePageKey = createNavigationMountKey(navEntry.page, navEntry.params);
|
|
266
|
+
activeLayoutKeys = createLayoutMountKeys(activeEntry, activePageKey);
|
|
152
267
|
}
|
|
153
268
|
return;
|
|
154
269
|
}
|
|
@@ -159,26 +274,38 @@
|
|
|
159
274
|
const entry = getViewManifestEntryFn(newKey);
|
|
160
275
|
if (!entry) return;
|
|
161
276
|
|
|
162
|
-
const
|
|
277
|
+
const nextPageKey = createNavigationMountKey(newKey, navEntry.params);
|
|
163
278
|
|
|
164
|
-
if (
|
|
279
|
+
if (nextPageKey === activePageKey) {
|
|
165
280
|
activeParams = { ...navEntry.params };
|
|
166
281
|
activeQuery = { ...navEntry.query };
|
|
167
282
|
return;
|
|
168
283
|
}
|
|
169
284
|
|
|
170
|
-
|
|
285
|
+
const preservedDepth = computePreservedLayoutDepth(
|
|
286
|
+
activeEntry,
|
|
287
|
+
entry,
|
|
288
|
+
navEntry.options ?? {},
|
|
289
|
+
);
|
|
290
|
+
const preservedLayoutKeys = activeLayoutKeys.slice(0, preservedDepth);
|
|
291
|
+
|
|
292
|
+
await disposeChangedControllers(preservedDepth);
|
|
171
293
|
const [controller, layoutControllers] = await Promise.all([
|
|
172
294
|
createViewControllerFn(entry),
|
|
173
|
-
createLayoutControllersFn(entry),
|
|
295
|
+
createLayoutControllersFn(entry, preservedDepth),
|
|
174
296
|
]);
|
|
175
297
|
|
|
176
298
|
activeEntry = entry;
|
|
177
299
|
activeController = controller;
|
|
178
|
-
activeLayoutControllers =
|
|
300
|
+
activeLayoutControllers = mergeLayoutControllers(
|
|
301
|
+
preservedDepth,
|
|
302
|
+
layoutControllers,
|
|
303
|
+
entry.layoutPaths.length,
|
|
304
|
+
);
|
|
179
305
|
activeParams = { ...navEntry.params };
|
|
180
306
|
activeQuery = { ...navEntry.query };
|
|
181
|
-
|
|
307
|
+
activePageKey = nextPageKey;
|
|
308
|
+
activeLayoutKeys = createLayoutMountKeys(entry, nextPageKey, preservedLayoutKeys);
|
|
182
309
|
});
|
|
183
310
|
|
|
184
311
|
const bridge = getWoodBridge();
|
|
@@ -207,14 +334,14 @@
|
|
|
207
334
|
});
|
|
208
335
|
</script>
|
|
209
336
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
337
|
+
<WoodRecursiveRender
|
|
338
|
+
layouts={activeEntry.layouts}
|
|
339
|
+
layoutControllers={activeLayoutControllers}
|
|
340
|
+
layoutKeys={activeLayoutKeys}
|
|
341
|
+
view={activeEntry.view}
|
|
342
|
+
controller={activeController}
|
|
343
|
+
pageKey={activePageKey}
|
|
344
|
+
params={activeParams}
|
|
345
|
+
query={activeQuery}
|
|
346
|
+
page={activePage}
|
|
347
|
+
/>
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { PageController } from '../client/controller_contract';
|
|
3
|
+
import WoodRecursiveRender from './WoodRecursiveRender.svelte';
|
|
3
4
|
|
|
4
5
|
let {
|
|
5
6
|
layouts = [],
|
|
6
7
|
layoutControllers = [],
|
|
8
|
+
layoutKeys = [],
|
|
7
9
|
view = null,
|
|
8
10
|
controller = undefined,
|
|
11
|
+
pageKey = '',
|
|
9
12
|
params = {},
|
|
10
13
|
query = {},
|
|
11
14
|
page = {},
|
|
12
15
|
} = $props<{
|
|
13
16
|
layouts?: any[];
|
|
14
17
|
layoutControllers?: Array<PageController<any, any, any> | undefined>;
|
|
18
|
+
layoutKeys?: string[];
|
|
15
19
|
view?: any;
|
|
16
20
|
controller?: PageController<any, any, any>;
|
|
21
|
+
pageKey?: string;
|
|
17
22
|
params?: Record<string, unknown>;
|
|
18
23
|
query?: Record<string, unknown>;
|
|
19
24
|
page?: Record<string, unknown>;
|
|
20
25
|
}>();
|
|
21
26
|
|
|
22
|
-
let
|
|
27
|
+
let TopLayout = $derived(layouts[0]);
|
|
23
28
|
let childLayouts = $derived(layouts.slice(1));
|
|
24
29
|
let topLayoutController = $derived(layoutControllers[0]);
|
|
25
30
|
let childLayoutControllers = $derived(layoutControllers.slice(1));
|
|
31
|
+
let topLayoutKey = $derived(layoutKeys[0] ?? '');
|
|
32
|
+
let childLayoutKeys = $derived(layoutKeys.slice(1));
|
|
26
33
|
let ViewComponent = $derived(view);
|
|
27
34
|
|
|
28
35
|
let topData = $derived(topLayoutController?.data);
|
|
@@ -36,30 +43,56 @@
|
|
|
36
43
|
|
|
37
44
|
{#if childLayouts.length > 0}
|
|
38
45
|
{#snippet nestedChild()}
|
|
39
|
-
<
|
|
46
|
+
<WoodRecursiveRender
|
|
40
47
|
layouts={childLayouts}
|
|
41
48
|
layoutControllers={childLayoutControllers}
|
|
49
|
+
layoutKeys={childLayoutKeys}
|
|
42
50
|
{view}
|
|
43
51
|
{controller}
|
|
52
|
+
{pageKey}
|
|
44
53
|
{params}
|
|
45
54
|
{query}
|
|
46
55
|
{page}
|
|
47
56
|
/>
|
|
48
57
|
{/snippet}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
58
|
+
{#key topLayoutKey}
|
|
59
|
+
<TopLayout
|
|
60
|
+
data={topData}
|
|
61
|
+
input={topInput}
|
|
62
|
+
events={topEvents}
|
|
63
|
+
{params}
|
|
64
|
+
{query}
|
|
65
|
+
{page}
|
|
66
|
+
children={nestedChild}
|
|
67
|
+
/>
|
|
68
|
+
{/key}
|
|
69
|
+
{:else if TopLayout}
|
|
60
70
|
{#snippet viewChild()}
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
{#key pageKey}
|
|
72
|
+
<ViewComponent
|
|
73
|
+
data={viewData}
|
|
74
|
+
input={viewInput}
|
|
75
|
+
events={viewEvents}
|
|
76
|
+
{params}
|
|
77
|
+
{query}
|
|
78
|
+
{page}
|
|
79
|
+
/>
|
|
80
|
+
{/key}
|
|
81
|
+
{/snippet}
|
|
82
|
+
{#key topLayoutKey}
|
|
83
|
+
<TopLayout
|
|
84
|
+
data={topData}
|
|
85
|
+
input={topInput}
|
|
86
|
+
events={topEvents}
|
|
87
|
+
{params}
|
|
88
|
+
{query}
|
|
89
|
+
{page}
|
|
90
|
+
children={viewChild}
|
|
91
|
+
/>
|
|
92
|
+
{/key}
|
|
93
|
+
{:else}
|
|
94
|
+
{#key pageKey}
|
|
95
|
+
<ViewComponent
|
|
63
96
|
data={viewData}
|
|
64
97
|
input={viewInput}
|
|
65
98
|
events={viewEvents}
|
|
@@ -67,25 +100,5 @@
|
|
|
67
100
|
{query}
|
|
68
101
|
{page}
|
|
69
102
|
/>
|
|
70
|
-
{/
|
|
71
|
-
<svelte:component
|
|
72
|
-
this={topLayout}
|
|
73
|
-
data={topData}
|
|
74
|
-
input={topInput}
|
|
75
|
-
events={topEvents}
|
|
76
|
-
{params}
|
|
77
|
-
{query}
|
|
78
|
-
{page}
|
|
79
|
-
children={viewChild}
|
|
80
|
-
/>
|
|
81
|
-
{:else}
|
|
82
|
-
<svelte:component
|
|
83
|
-
this={ViewComponent}
|
|
84
|
-
data={viewData}
|
|
85
|
-
input={viewInput}
|
|
86
|
-
events={viewEvents}
|
|
87
|
-
{params}
|
|
88
|
-
{query}
|
|
89
|
-
{page}
|
|
90
|
-
/>
|
|
103
|
+
{/key}
|
|
91
104
|
{/if}
|