@jskit-ai/shell-web 0.1.83 → 0.1.85
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/package.descriptor.mjs +2 -2
- package/package.json +2 -2
- package/src/client/components/ShellRouteTransition.vue +20 -1
- package/src/client/support/routeTransitionKey.js +22 -0
- package/templates/src/pages/home/settings.vue +30 -1
- package/test/menuIcons.test.js +5 -0
- package/test/settingsPlacementContract.test.js +45 -0
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/shell-web",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.85",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Web shell layout runtime with outlet-based placement contributions.",
|
|
7
7
|
dependsOn: [],
|
|
@@ -291,7 +291,7 @@ export default Object.freeze({
|
|
|
291
291
|
dependencies: {
|
|
292
292
|
runtime: {
|
|
293
293
|
"@mdi/js": "^7.4.47",
|
|
294
|
-
"@jskit-ai/kernel": "0.1.
|
|
294
|
+
"@jskit-ai/kernel": "0.1.86"
|
|
295
295
|
},
|
|
296
296
|
dev: {}
|
|
297
297
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/shell-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@mdi/js": "^7.4.47",
|
|
29
|
-
"@jskit-ai/kernel": "0.1.
|
|
29
|
+
"@jskit-ai/kernel": "0.1.86"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"pinia": "^3.0.4",
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
normalizeMenuLinkPathname,
|
|
25
25
|
resolveMenuLinkTarget
|
|
26
26
|
} from "../support/menuLinkTarget.js";
|
|
27
|
+
import { resolveShellRouteTransitionKey } from "../support/routeTransitionKey.js";
|
|
27
28
|
|
|
28
29
|
const props = defineProps({
|
|
29
30
|
enabled: {
|
|
@@ -232,7 +233,16 @@ const routeTransitionName = computed(() => {
|
|
|
232
233
|
return "";
|
|
233
234
|
});
|
|
234
235
|
|
|
235
|
-
const routeTransitionKey = computed(() =>
|
|
236
|
+
const routeTransitionKey = computed(() => {
|
|
237
|
+
const routePathKey = routeTransitionName.value
|
|
238
|
+
? normalizeComparablePathname(route?.path || route?.fullPath || "/")
|
|
239
|
+
: "";
|
|
240
|
+
return resolveShellRouteTransitionKey({
|
|
241
|
+
routePathKey,
|
|
242
|
+
routeTransitionName: routeTransitionName.value,
|
|
243
|
+
surfaceId: currentSurfaceId.value
|
|
244
|
+
});
|
|
245
|
+
});
|
|
236
246
|
|
|
237
247
|
const swipeNavigationEnabled = computed(() =>
|
|
238
248
|
Boolean(
|
|
@@ -409,7 +419,12 @@ function isSwipeIgnoredTarget(target) {
|
|
|
409
419
|
<style scoped>
|
|
410
420
|
.shell-route-transition {
|
|
411
421
|
--shell-route-transition-distance: 100%;
|
|
422
|
+
align-items: stretch;
|
|
412
423
|
--shell-route-transition-opacity: 1;
|
|
424
|
+
display: flex;
|
|
425
|
+
flex: 1 1 auto;
|
|
426
|
+
flex-direction: column;
|
|
427
|
+
min-height: 0;
|
|
413
428
|
overflow-x: clip;
|
|
414
429
|
position: relative;
|
|
415
430
|
}
|
|
@@ -420,6 +435,10 @@ function isSwipeIgnoredTarget(target) {
|
|
|
420
435
|
|
|
421
436
|
.shell-route-transition__pane {
|
|
422
437
|
background: rgb(var(--v-theme-background));
|
|
438
|
+
display: flex;
|
|
439
|
+
flex: 1 1 auto;
|
|
440
|
+
flex-direction: column;
|
|
441
|
+
min-height: 0;
|
|
423
442
|
width: 100%;
|
|
424
443
|
}
|
|
425
444
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function normalizeText(value = "") {
|
|
2
|
+
return String(value || "").trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function resolveShellRouteTransitionKey({
|
|
6
|
+
routePathKey = "",
|
|
7
|
+
routeTransitionName = "",
|
|
8
|
+
surfaceId = ""
|
|
9
|
+
} = {}) {
|
|
10
|
+
if (normalizeText(routeTransitionName)) {
|
|
11
|
+
return normalizeText(routePathKey) || "/";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const surfaceKey = normalizeText(surfaceId);
|
|
15
|
+
if (surfaceKey && surfaceKey !== "*") {
|
|
16
|
+
return `surface:${surfaceKey}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return "stable";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { resolveShellRouteTransitionKey };
|
|
@@ -14,7 +14,7 @@ import { RouterView } from "vue-router";
|
|
|
14
14
|
<v-sheet rounded="lg" border class="settings-shell__panel">
|
|
15
15
|
<div class="settings-shell__body">
|
|
16
16
|
<nav class="settings-shell__nav" aria-label="Home settings sections">
|
|
17
|
-
<v-list nav density="
|
|
17
|
+
<v-list nav density="compact" rounded="lg">
|
|
18
18
|
<ShellOutlet target="home-settings:primary-menu" />
|
|
19
19
|
</v-list>
|
|
20
20
|
</nav>
|
|
@@ -53,7 +53,31 @@ import { RouterView } from "vue-router";
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.settings-shell__content {
|
|
56
|
+
border-left: 1px solid rgba(var(--v-theme-on-surface), 0.18);
|
|
56
57
|
min-width: 0;
|
|
58
|
+
padding-left: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.settings-shell__nav :deep(.v-list) {
|
|
62
|
+
padding: 0.35rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.settings-shell__nav :deep(.v-list-item) {
|
|
66
|
+
min-height: 48px;
|
|
67
|
+
padding-inline: 0.6rem 0.7rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.settings-shell__nav :deep(.v-list-item__prepend) {
|
|
71
|
+
margin-inline-end: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.settings-shell__nav :deep(.v-list-item__spacer) {
|
|
75
|
+
min-width: 0.5rem;
|
|
76
|
+
width: 0.5rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.settings-shell__nav :deep(.v-list-item-title) {
|
|
80
|
+
line-height: 1.2;
|
|
57
81
|
}
|
|
58
82
|
|
|
59
83
|
@media (max-width: 960px) {
|
|
@@ -72,5 +96,10 @@ import { RouterView } from "vue-router";
|
|
|
72
96
|
flex: 0 0 auto;
|
|
73
97
|
min-height: 48px;
|
|
74
98
|
}
|
|
99
|
+
|
|
100
|
+
.settings-shell__content {
|
|
101
|
+
border-left: 0;
|
|
102
|
+
padding-left: 0;
|
|
103
|
+
}
|
|
75
104
|
}
|
|
76
105
|
</style>
|
package/test/menuIcons.test.js
CHANGED
|
@@ -4,6 +4,7 @@ import test from "node:test";
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import {
|
|
7
|
+
mdiAccountKeyOutline,
|
|
7
8
|
mdiCogOutline,
|
|
8
9
|
mdiConsoleNetworkOutline,
|
|
9
10
|
mdiViewListOutline
|
|
@@ -29,6 +30,10 @@ test("shell-web leaves unknown explicit mdi metadata icons unchanged", () => {
|
|
|
29
30
|
assert.equal(resolveMenuLinkIcon({ icon: "mdi-not-a-real-supported-icon" }), "mdi-not-a-real-supported-icon");
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
test("shell-web accepts imported @mdi/js path constants as explicit menu icons", () => {
|
|
34
|
+
assert.equal(resolveMenuLinkIcon({ icon: mdiAccountKeyOutline }), mdiAccountKeyOutline);
|
|
35
|
+
});
|
|
36
|
+
|
|
32
37
|
test("shell-web menu icon resolution does not import the full mdi namespace", async () => {
|
|
33
38
|
const source = await readFile(path.join(PACKAGE_DIR, "src", "client", "lib", "menuIcons.js"), "utf8");
|
|
34
39
|
|
|
@@ -5,6 +5,7 @@ import { readFile } from "node:fs/promises";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { assertGeneratedUiSourceContract } from "@jskit-ai/kernel/shared/support/generatedUiContract";
|
|
7
7
|
import descriptor from "../package.descriptor.mjs";
|
|
8
|
+
import { resolveShellRouteTransitionKey } from "../src/client/support/routeTransitionKey.js";
|
|
8
9
|
|
|
9
10
|
const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
|
|
@@ -159,10 +160,54 @@ test("shell-web route transition keeps mobile route motion placement-driven", as
|
|
|
159
160
|
assert.match(source, /router\.push\(nextEntry\.href\)/);
|
|
160
161
|
assert.match(source, /isSwipeIgnoredTarget/);
|
|
161
162
|
assert.match(source, /touch-action:\s*pan-y/);
|
|
163
|
+
assert.match(source, /\.shell-route-transition\s*\{[\s\S]*display:\s*flex/);
|
|
164
|
+
assert.match(source, /\.shell-route-transition\s*\{[\s\S]*min-height:\s*0/);
|
|
165
|
+
assert.match(source, /\.shell-route-transition__pane\s*\{[\s\S]*flex:\s*1 1 auto/);
|
|
166
|
+
assert.match(source, /\.shell-route-transition__pane\s*\{[\s\S]*min-height:\s*0/);
|
|
162
167
|
assert.match(source, /transitionDirection\.value = nextIndex > previousIndex \? "forward" : "reverse"/);
|
|
168
|
+
assert.match(
|
|
169
|
+
source,
|
|
170
|
+
/const routeTransitionKey = computed\(\(\) => \{[\s\S]*const routePathKey = routeTransitionName\.value[\s\S]*normalizeComparablePathname\(route\?\.path \|\| route\?\.fullPath \|\| "\/"\)[\s\S]*resolveShellRouteTransitionKey\(\{[\s\S]*routeTransitionName: routeTransitionName\.value,[\s\S]*surfaceId: currentSurfaceId\.value[\s\S]*\}\);[\s\S]*\}\);/
|
|
171
|
+
);
|
|
163
172
|
assert.match(source, /prefers-reduced-motion:\s*reduce/);
|
|
164
173
|
});
|
|
165
174
|
|
|
175
|
+
test("shell-web route transition key preserves no-motion surfaces and animated path transitions", () => {
|
|
176
|
+
const previewSurfaceKey = "surface:vibe64-preview";
|
|
177
|
+
assert.equal(
|
|
178
|
+
resolveShellRouteTransitionKey({
|
|
179
|
+
routeTransitionName: "",
|
|
180
|
+
routePathKey: "/preview",
|
|
181
|
+
surfaceId: "vibe64-preview"
|
|
182
|
+
}),
|
|
183
|
+
previewSurfaceKey
|
|
184
|
+
);
|
|
185
|
+
assert.equal(
|
|
186
|
+
resolveShellRouteTransitionKey({
|
|
187
|
+
routeTransitionName: "",
|
|
188
|
+
routePathKey: "/preview/dashboard",
|
|
189
|
+
surfaceId: "vibe64-preview"
|
|
190
|
+
}),
|
|
191
|
+
previewSurfaceKey
|
|
192
|
+
);
|
|
193
|
+
assert.equal(
|
|
194
|
+
resolveShellRouteTransitionKey({
|
|
195
|
+
routeTransitionName: "shell-route-slide-forward",
|
|
196
|
+
routePathKey: "/dashboard",
|
|
197
|
+
surfaceId: "home"
|
|
198
|
+
}),
|
|
199
|
+
"/dashboard"
|
|
200
|
+
);
|
|
201
|
+
assert.equal(
|
|
202
|
+
resolveShellRouteTransitionKey({
|
|
203
|
+
routeTransitionName: "",
|
|
204
|
+
routePathKey: "/dashboard",
|
|
205
|
+
surfaceId: "*"
|
|
206
|
+
}),
|
|
207
|
+
"stable"
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
166
211
|
test("shell-web settings landing page redirects to the starter child page", async () => {
|
|
167
212
|
const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "settings", "index.vue"), "utf8");
|
|
168
213
|
|