@jskit-ai/users-web 0.1.48 → 0.1.49
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 +87 -32
- package/package.json +7 -9
- package/src/client/components/UsersHomeToolsWidget.vue +12 -0
- package/src/client/components/UsersWorkspaceToolsWidget.vue +7 -18
- package/src/client/index.js +0 -2
- package/src/client/providers/UsersWebClientProvider.js +2 -4
- package/src/client/providers/UsersWorkspacesClientProvider.js +0 -2
- package/src/shared/toolsOutletContracts.js +19 -0
- package/templates/src/pages/console/settings/index.vue +5 -4
- package/templates/src/pages/console/settings.vue +4 -1
- package/test/settingsPlacementContract.test.js +193 -6
- package/src/client/components/UsersShellMenuLinkItem.vue +0 -140
- package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +0 -76
package/package.descriptor.mjs
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOME_TOOLS_OUTLET,
|
|
3
|
+
WORKSPACE_TOOLS_OUTLET
|
|
4
|
+
} from "./src/shared/toolsOutletContracts.js";
|
|
5
|
+
|
|
1
6
|
export default Object.freeze({
|
|
2
7
|
packageVersion: 1,
|
|
3
8
|
packageId: "@jskit-ai/users-web",
|
|
4
|
-
version: "0.1.
|
|
9
|
+
version: "0.1.49",
|
|
5
10
|
kind: "runtime",
|
|
6
|
-
description: "Users web module: account/profile UI plus shared
|
|
11
|
+
description: "Users web module: account/profile UI plus shared users web widgets.",
|
|
7
12
|
dependsOn: [
|
|
13
|
+
"@jskit-ai/auth-web",
|
|
8
14
|
"@jskit-ai/http-runtime",
|
|
9
15
|
"@jskit-ai/shell-web",
|
|
10
16
|
"@jskit-ai/uploads-image-web",
|
|
@@ -47,14 +53,6 @@ export default Object.freeze({
|
|
|
47
53
|
subpath: "./client/components/ProfileClientElement",
|
|
48
54
|
summary: "Exports profile settings client element scaffold component."
|
|
49
55
|
},
|
|
50
|
-
{
|
|
51
|
-
subpath: "./client/components/ConsoleSettingsClientElement",
|
|
52
|
-
summary: "Exports console settings landing-page client element."
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
subpath: "./client/components/WorkspaceSettingsClientElement",
|
|
56
|
-
summary: "Exports workspace settings client element."
|
|
57
|
-
},
|
|
58
56
|
{
|
|
59
57
|
subpath: "./client/composables/useAddEdit",
|
|
60
58
|
summary: "Exports add/edit operation composable."
|
|
@@ -95,9 +93,8 @@ export default Object.freeze({
|
|
|
95
93
|
containerTokens: {
|
|
96
94
|
server: [],
|
|
97
95
|
client: [
|
|
98
|
-
"users.web.shell.menu-link-item",
|
|
99
|
-
"users.web.shell.surface-aware-menu-link-item",
|
|
100
96
|
"users.web.profile.menu.surface-switch-item",
|
|
97
|
+
"users.web.home.tools.widget",
|
|
101
98
|
"users.web.profile.element",
|
|
102
99
|
"users.web.bootstrap-placement.runtime"
|
|
103
100
|
]
|
|
@@ -107,8 +104,20 @@ export default Object.freeze({
|
|
|
107
104
|
placements: {
|
|
108
105
|
outlets: [
|
|
109
106
|
{
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
target: HOME_TOOLS_OUTLET.target,
|
|
108
|
+
defaultLinkComponentToken: HOME_TOOLS_OUTLET.defaultLinkComponentToken,
|
|
109
|
+
surfaces: ["home"],
|
|
110
|
+
source: "src/client/components/UsersHomeToolsWidget.vue"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
target: WORKSPACE_TOOLS_OUTLET.target,
|
|
114
|
+
defaultLinkComponentToken: WORKSPACE_TOOLS_OUTLET.defaultLinkComponentToken,
|
|
115
|
+
surfaces: ["admin"],
|
|
116
|
+
source: "src/client/components/UsersWorkspaceToolsWidget.vue"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
target: "console-settings:primary-menu",
|
|
120
|
+
defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
112
121
|
surfaces: ["console"],
|
|
113
122
|
source: "templates/src/pages/console/settings.vue"
|
|
114
123
|
}
|
|
@@ -116,8 +125,7 @@ export default Object.freeze({
|
|
|
116
125
|
contributions: [
|
|
117
126
|
{
|
|
118
127
|
id: "users.profile.menu.surface-switch",
|
|
119
|
-
|
|
120
|
-
position: "primary-menu",
|
|
128
|
+
target: "auth-profile-menu:primary-menu",
|
|
121
129
|
surfaces: ["*"],
|
|
122
130
|
order: 100,
|
|
123
131
|
componentToken: "users.web.profile.menu.surface-switch-item",
|
|
@@ -126,23 +134,48 @@ export default Object.freeze({
|
|
|
126
134
|
},
|
|
127
135
|
{
|
|
128
136
|
id: "users.profile.menu.settings",
|
|
129
|
-
|
|
130
|
-
position: "primary-menu",
|
|
137
|
+
target: "auth-profile-menu:primary-menu",
|
|
131
138
|
surfaces: ["*"],
|
|
132
139
|
order: 500,
|
|
133
|
-
componentToken: "
|
|
140
|
+
componentToken: "auth.web.profile.menu.link-item",
|
|
134
141
|
when: "auth.authenticated === true",
|
|
135
142
|
source: "mutations.text#users-web-profile-settings-placement"
|
|
136
143
|
},
|
|
144
|
+
{
|
|
145
|
+
id: "users.home.menu.home",
|
|
146
|
+
target: "shell-layout:primary-menu",
|
|
147
|
+
surfaces: ["*"],
|
|
148
|
+
order: 50,
|
|
149
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
150
|
+
when: "auth.authenticated === true",
|
|
151
|
+
source: "mutations.text#users-web-home-shell-menu-placement"
|
|
152
|
+
},
|
|
137
153
|
{
|
|
138
154
|
id: "users.console.menu.settings",
|
|
139
|
-
|
|
140
|
-
position: "primary-menu",
|
|
155
|
+
target: "shell-layout:primary-menu",
|
|
141
156
|
surfaces: ["console"],
|
|
142
157
|
order: 100,
|
|
143
|
-
componentToken: "
|
|
158
|
+
componentToken: "local.main.ui.menu-link-item",
|
|
144
159
|
when: "auth.authenticated === true",
|
|
145
160
|
source: "mutations.text#users-web-console-settings-placement"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "users.home.tools.widget",
|
|
164
|
+
target: "shell-layout:top-right",
|
|
165
|
+
surfaces: ["home"],
|
|
166
|
+
order: 900,
|
|
167
|
+
componentToken: "users.web.home.tools.widget",
|
|
168
|
+
when: "auth.authenticated === true",
|
|
169
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "users.home.menu.settings",
|
|
173
|
+
target: "home-tools:primary-menu",
|
|
174
|
+
surfaces: ["home"],
|
|
175
|
+
order: 100,
|
|
176
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
177
|
+
when: "auth.authenticated === true",
|
|
178
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
146
179
|
}
|
|
147
180
|
]
|
|
148
181
|
}
|
|
@@ -153,12 +186,12 @@ export default Object.freeze({
|
|
|
153
186
|
runtime: {
|
|
154
187
|
"@tanstack/vue-query": "5.92.12",
|
|
155
188
|
"@mdi/js": "^7.4.47",
|
|
156
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
157
|
-
"@jskit-ai/realtime": "0.1.
|
|
158
|
-
"@jskit-ai/kernel": "0.1.
|
|
159
|
-
"@jskit-ai/shell-web": "0.1.
|
|
160
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
161
|
-
"@jskit-ai/users-core": "0.1.
|
|
189
|
+
"@jskit-ai/http-runtime": "0.1.33",
|
|
190
|
+
"@jskit-ai/realtime": "0.1.33",
|
|
191
|
+
"@jskit-ai/kernel": "0.1.34",
|
|
192
|
+
"@jskit-ai/shell-web": "0.1.33",
|
|
193
|
+
"@jskit-ai/uploads-image-web": "0.1.12",
|
|
194
|
+
"@jskit-ai/users-core": "0.1.44",
|
|
162
195
|
vuetify: "^4.0.0"
|
|
163
196
|
},
|
|
164
197
|
dev: {}
|
|
@@ -226,7 +259,7 @@ export default Object.freeze({
|
|
|
226
259
|
from: "templates/src/pages/console/settings/index.vue",
|
|
227
260
|
toSurface: "console",
|
|
228
261
|
toSurfacePath: "settings/index.vue",
|
|
229
|
-
reason: "Install console settings
|
|
262
|
+
reason: "Install console settings index stub scaffold for app-owned landing or redirect behavior.",
|
|
230
263
|
category: "users-web",
|
|
231
264
|
id: "users-web-page-console-settings"
|
|
232
265
|
}
|
|
@@ -249,7 +282,7 @@ export default Object.freeze({
|
|
|
249
282
|
position: "bottom",
|
|
250
283
|
skipIfContains: "id: \"users.profile.menu.surface-switch\"",
|
|
251
284
|
value:
|
|
252
|
-
"\naddPlacement({\n id: \"users.profile.menu.surface-switch\",\n
|
|
285
|
+
"\naddPlacement({\n id: \"users.profile.menu.surface-switch\",\n target: \"auth-profile-menu:primary-menu\",\n surfaces: [\"*\"],\n order: 100,\n componentToken: \"users.web.profile.menu.surface-switch-item\",\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
253
286
|
reason: "Append users-web profile surface switch placement into app-owned placement registry.",
|
|
254
287
|
category: "users-web",
|
|
255
288
|
id: "users-web-profile-surface-switch-placement"
|
|
@@ -260,21 +293,43 @@ export default Object.freeze({
|
|
|
260
293
|
position: "bottom",
|
|
261
294
|
skipIfContains: "id: \"users.profile.menu.settings\"",
|
|
262
295
|
value:
|
|
263
|
-
"\naddPlacement({\n id: \"users.profile.menu.settings\",\n
|
|
296
|
+
"\naddPlacement({\n id: \"users.profile.menu.settings\",\n target: \"auth-profile-menu:primary-menu\",\n surfaces: [\"*\"],\n order: 500,\n componentToken: \"auth.web.profile.menu.link-item\",\n props: {\n label: \"Settings\",\n to: \"/account\"\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
264
297
|
reason: "Append users-web profile settings menu placement into app-owned placement registry.",
|
|
265
298
|
category: "users-web",
|
|
266
299
|
id: "users-web-profile-settings-placement"
|
|
267
300
|
},
|
|
301
|
+
{
|
|
302
|
+
op: "append-text",
|
|
303
|
+
file: "src/placement.js",
|
|
304
|
+
position: "bottom",
|
|
305
|
+
skipIfContains: "id: \"users.home.menu.home\"",
|
|
306
|
+
value:
|
|
307
|
+
"\naddPlacement({\n id: \"users.home.menu.home\",\n target: \"shell-layout:primary-menu\",\n surfaces: [\"*\"],\n order: 50,\n componentToken: \"local.main.ui.surface-aware-menu-link-item\",\n props: {\n label: \"Home\",\n surface: \"home\",\n workspaceSuffix: \"/\",\n nonWorkspaceSuffix: \"/\",\n exact: true\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
308
|
+
reason: "Append users-web home shell menu placement into app-owned placement registry.",
|
|
309
|
+
category: "users-web",
|
|
310
|
+
id: "users-web-home-shell-menu-placement"
|
|
311
|
+
},
|
|
268
312
|
{
|
|
269
313
|
op: "append-text",
|
|
270
314
|
file: "src/placement.js",
|
|
271
315
|
position: "bottom",
|
|
272
316
|
skipIfContains: "id: \"users.console.menu.settings\"",
|
|
273
317
|
value:
|
|
274
|
-
"\naddPlacement({\n id: \"users.console.menu.settings\",\n
|
|
318
|
+
"\naddPlacement({\n id: \"users.console.menu.settings\",\n target: \"shell-layout:primary-menu\",\n surfaces: [\"console\"],\n order: 100,\n componentToken: \"local.main.ui.menu-link-item\",\n props: {\n label: \"Settings\",\n to: \"/console/settings\",\n icon: \"mdi-cog-outline\"\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
275
319
|
reason: "Append users-web console settings menu placement into app-owned placement registry.",
|
|
276
320
|
category: "users-web",
|
|
277
321
|
id: "users-web-console-settings-placement"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
op: "append-text",
|
|
325
|
+
file: "src/placement.js",
|
|
326
|
+
position: "bottom",
|
|
327
|
+
skipIfContains: "id: \"users.home.tools.widget\"",
|
|
328
|
+
value:
|
|
329
|
+
"\naddPlacement({\n id: \"users.home.tools.widget\",\n target: \"shell-layout:top-right\",\n surfaces: [\"home\"],\n order: 900,\n componentToken: \"users.web.home.tools.widget\",\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n\naddPlacement({\n id: \"users.home.menu.settings\",\n target: \"home-tools:primary-menu\",\n surfaces: [\"home\"],\n order: 100,\n componentToken: \"local.main.ui.surface-aware-menu-link-item\",\n props: {\n label: \"Settings\",\n surface: \"home\",\n workspaceSuffix: \"/settings\",\n nonWorkspaceSuffix: \"/settings\"\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
330
|
+
reason: "Append users-web home tools widget and settings menu placements into app-owned placement registry.",
|
|
331
|
+
category: "users-web",
|
|
332
|
+
id: "users-web-home-tools-placement"
|
|
278
333
|
}
|
|
279
334
|
]
|
|
280
335
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/users-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
"./client": "./src/client/index.js",
|
|
10
10
|
"./client/providers/UsersWorkspacesClientProvider": "./src/client/providers/UsersWorkspacesClientProvider.js",
|
|
11
|
-
"./client/components/ConsoleSettingsClientElement": "./src/client/components/ConsoleSettingsClientElement.vue",
|
|
12
|
-
"./client/components/WorkspaceSettingsClientElement": "./src/client/components/WorkspaceSettingsClientElement.vue",
|
|
13
11
|
"./client/components/WorkspaceMembersClientElement": "./src/client/components/WorkspaceMembersClientElement.vue",
|
|
14
12
|
"./client/composables/useAddEdit": "./src/client/composables/records/useAddEdit.js",
|
|
15
13
|
"./client/composables/useCrudAddEdit": "./src/client/composables/records/useCrudAddEdit.js",
|
|
@@ -28,12 +26,12 @@
|
|
|
28
26
|
"dependencies": {
|
|
29
27
|
"@tanstack/vue-query": "5.92.12",
|
|
30
28
|
"@mdi/js": "^7.4.47",
|
|
31
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
32
|
-
"@jskit-ai/kernel": "0.1.
|
|
33
|
-
"@jskit-ai/realtime": "0.1.
|
|
34
|
-
"@jskit-ai/shell-web": "0.1.
|
|
35
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
36
|
-
"@jskit-ai/users-core": "0.1.
|
|
29
|
+
"@jskit-ai/http-runtime": "0.1.33",
|
|
30
|
+
"@jskit-ai/kernel": "0.1.34",
|
|
31
|
+
"@jskit-ai/realtime": "0.1.33",
|
|
32
|
+
"@jskit-ai/shell-web": "0.1.33",
|
|
33
|
+
"@jskit-ai/uploads-image-web": "0.1.12",
|
|
34
|
+
"@jskit-ai/users-core": "0.1.44",
|
|
37
35
|
"vuetify": "^4.0.0"
|
|
38
36
|
}
|
|
39
37
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import ShellOutletMenuWidget from "@jskit-ai/shell-web/client/components/ShellOutletMenuWidget";
|
|
3
|
+
import { HOME_TOOLS_OUTLET } from "../../shared/toolsOutletContracts.js";
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<ShellOutletMenuWidget
|
|
8
|
+
:target="HOME_TOOLS_OUTLET.target"
|
|
9
|
+
:default-link-component-token="HOME_TOOLS_OUTLET.defaultLinkComponentToken"
|
|
10
|
+
:aria-label="HOME_TOOLS_OUTLET.ariaLabel"
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import ShellOutletMenuWidget from "@jskit-ai/shell-web/client/components/ShellOutletMenuWidget";
|
|
3
|
+
import { WORKSPACE_TOOLS_OUTLET } from "../../shared/toolsOutletContracts.js";
|
|
4
4
|
</script>
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
variant="text"
|
|
13
|
-
aria-label="Workspace tools"
|
|
14
|
-
>
|
|
15
|
-
<v-icon :icon="mdiCogOutline" />
|
|
16
|
-
</v-btn>
|
|
17
|
-
</template>
|
|
18
|
-
|
|
19
|
-
<v-list min-width="220" density="comfortable" class="py-1">
|
|
20
|
-
<ShellOutlet host="workspace-tools" position="primary-menu" />
|
|
21
|
-
</v-list>
|
|
22
|
-
</v-menu>
|
|
7
|
+
<ShellOutletMenuWidget
|
|
8
|
+
:target="WORKSPACE_TOOLS_OUTLET.target"
|
|
9
|
+
:default-link-component-token="WORKSPACE_TOOLS_OUTLET.defaultLinkComponentToken"
|
|
10
|
+
:aria-label="WORKSPACE_TOOLS_OUTLET.ariaLabel"
|
|
11
|
+
/>
|
|
23
12
|
</template>
|
package/src/client/index.js
CHANGED
|
@@ -2,8 +2,6 @@ import { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
|
|
|
2
2
|
|
|
3
3
|
export { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
|
|
4
4
|
export { UsersWorkspacesClientProvider } from "./providers/UsersWorkspacesClientProvider.js";
|
|
5
|
-
export { default as ConsoleSettingsClientElement } from "./components/ConsoleSettingsClientElement.vue";
|
|
6
|
-
export { default as WorkspaceSettingsClientElement } from "./components/WorkspaceSettingsClientElement.vue";
|
|
7
5
|
|
|
8
6
|
const clientProviders = Object.freeze([UsersWebClientProvider]);
|
|
9
7
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import UsersShellMenuLinkItem from "../components/UsersShellMenuLinkItem.vue";
|
|
2
|
-
import UsersSurfaceAwareMenuLinkItem from "../components/UsersSurfaceAwareMenuLinkItem.vue";
|
|
3
1
|
import UsersProfileSurfaceSwitchMenuItem from "../components/UsersProfileSurfaceSwitchMenuItem.vue";
|
|
2
|
+
import UsersHomeToolsWidget from "../components/UsersHomeToolsWidget.vue";
|
|
4
3
|
import ProfileClientElement from "../components/ProfileClientElement.vue";
|
|
5
4
|
import {
|
|
6
5
|
createBootstrapPlacementRuntime
|
|
@@ -15,9 +14,8 @@ class UsersWebClientProvider {
|
|
|
15
14
|
throw new Error("UsersWebClientProvider requires application singleton().");
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
app.singleton("users.web.shell.menu-link-item", () => UsersShellMenuLinkItem);
|
|
19
|
-
app.singleton("users.web.shell.surface-aware-menu-link-item", () => UsersSurfaceAwareMenuLinkItem);
|
|
20
17
|
app.singleton("users.web.profile.menu.surface-switch-item", () => UsersProfileSurfaceSwitchMenuItem);
|
|
18
|
+
app.singleton("users.web.home.tools.widget", () => UsersHomeToolsWidget);
|
|
21
19
|
app.singleton("users.web.profile.element", () => ProfileClientElement);
|
|
22
20
|
app.singleton("users.web.bootstrap-placement.runtime", (scope) => createBootstrapPlacementRuntime({ app: scope }));
|
|
23
21
|
}
|
|
@@ -3,7 +3,6 @@ import UsersWorkspaceToolsWidget from "../components/UsersWorkspaceToolsWidget.v
|
|
|
3
3
|
import UsersWorkspaceSettingsMenuItem from "../components/UsersWorkspaceSettingsMenuItem.vue";
|
|
4
4
|
import UsersWorkspaceMembersMenuItem from "../components/UsersWorkspaceMembersMenuItem.vue";
|
|
5
5
|
import MembersAdminClientElement from "../components/MembersAdminClientElement.vue";
|
|
6
|
-
import WorkspaceSettingsClientElement from "../components/WorkspaceSettingsClientElement.vue";
|
|
7
6
|
|
|
8
7
|
class UsersWorkspacesClientProvider {
|
|
9
8
|
static id = "workspaces.web.client";
|
|
@@ -19,7 +18,6 @@ class UsersWorkspacesClientProvider {
|
|
|
19
18
|
app.singleton("users.web.workspace-settings.menu-item", () => UsersWorkspaceSettingsMenuItem);
|
|
20
19
|
app.singleton("users.web.workspace-members.menu-item", () => UsersWorkspaceMembersMenuItem);
|
|
21
20
|
app.singleton("users.web.members-admin.element", () => MembersAdminClientElement);
|
|
22
|
-
app.singleton("users.web.workspace-settings.element", () => WorkspaceSettingsClientElement);
|
|
23
21
|
}
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const DEFAULT_TOOLS_LINK_COMPONENT_TOKEN = "local.main.ui.surface-aware-menu-link-item";
|
|
2
|
+
|
|
3
|
+
const HOME_TOOLS_OUTLET = Object.freeze({
|
|
4
|
+
target: "home-tools:primary-menu",
|
|
5
|
+
defaultLinkComponentToken: DEFAULT_TOOLS_LINK_COMPONENT_TOKEN,
|
|
6
|
+
ariaLabel: "Home tools"
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const WORKSPACE_TOOLS_OUTLET = Object.freeze({
|
|
10
|
+
target: "workspace-tools:primary-menu",
|
|
11
|
+
defaultLinkComponentToken: DEFAULT_TOOLS_LINK_COMPONENT_TOKEN,
|
|
12
|
+
ariaLabel: "Workspace tools"
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
DEFAULT_TOOLS_LINK_COMPONENT_TOKEN,
|
|
17
|
+
HOME_TOOLS_OUTLET,
|
|
18
|
+
WORKSPACE_TOOLS_OUTLET
|
|
19
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
|
|
2
|
+
// To redirect this settings shell to a child page, uncomment and edit the example below.
|
|
3
|
+
// definePage({
|
|
4
|
+
// redirect: (to) => `${to.path}/your_child_segment`
|
|
5
|
+
// });
|
|
3
6
|
</script>
|
|
4
7
|
|
|
5
|
-
<template
|
|
6
|
-
<ConsoleSettingsClientElement />
|
|
7
|
-
</template>
|
|
8
|
+
<template />
|
|
@@ -15,7 +15,10 @@ import { RouterView } from "vue-router";
|
|
|
15
15
|
<v-row no-gutters>
|
|
16
16
|
<v-col cols="12" md="3" lg="2" class="pr-md-4 mb-4 mb-md-0">
|
|
17
17
|
<v-list nav density="comfortable" rounded="lg" border>
|
|
18
|
-
<ShellOutlet
|
|
18
|
+
<ShellOutlet
|
|
19
|
+
target="console-settings:primary-menu"
|
|
20
|
+
default-link-component-token="local.main.ui.surface-aware-menu-link-item"
|
|
21
|
+
/>
|
|
19
22
|
</v-list>
|
|
20
23
|
</v-col>
|
|
21
24
|
|
|
@@ -8,30 +8,217 @@ import descriptor from "../package.descriptor.mjs";
|
|
|
8
8
|
const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
|
|
10
10
|
|
|
11
|
-
function
|
|
11
|
+
function readOutlets(host = "") {
|
|
12
12
|
const outlets = descriptor?.metadata?.ui?.placements?.outlets;
|
|
13
|
+
const normalizedTarget = String(host || "").trim();
|
|
13
14
|
return Array.isArray(outlets)
|
|
14
|
-
? outlets.filter((entry) => String(entry?.
|
|
15
|
+
? outlets.filter((entry) => String(entry?.target || "").trim() === normalizedTarget)
|
|
15
16
|
: [];
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function findContribution(id) {
|
|
20
|
+
const contributions = descriptor?.metadata?.ui?.placements?.contributions;
|
|
21
|
+
return Array.isArray(contributions)
|
|
22
|
+
? contributions.find((entry) => String(entry?.id || "").trim() === id) || null
|
|
23
|
+
: null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findTextMutation(id) {
|
|
27
|
+
const textMutations = descriptor?.mutations?.text;
|
|
28
|
+
return Array.isArray(textMutations)
|
|
29
|
+
? textMutations.find((entry) => String(entry?.id || "").trim() === id) || null
|
|
30
|
+
: null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function findFileMutation(id) {
|
|
34
|
+
const fileMutations = descriptor?.mutations?.files;
|
|
35
|
+
return Array.isArray(fileMutations)
|
|
36
|
+
? fileMutations.find((entry) => String(entry?.id || "").trim() === id) || null
|
|
37
|
+
: null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function expectContribution(id, expected = {}) {
|
|
41
|
+
const contribution = findContribution(id);
|
|
42
|
+
assert.ok(contribution, `Expected contribution "${id}".`);
|
|
43
|
+
|
|
44
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
45
|
+
assert.deepEqual(contribution[key], value);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function expectTextMutation(id, { reason = "", category = "", skipIfContains = "", snippets = [] } = {}) {
|
|
50
|
+
const mutation = findTextMutation(id);
|
|
51
|
+
assert.ok(mutation, `Expected text mutation "${id}".`);
|
|
52
|
+
assert.equal(mutation.op, "append-text");
|
|
53
|
+
assert.equal(mutation.file, "src/placement.js");
|
|
54
|
+
assert.equal(mutation.position, "bottom");
|
|
55
|
+
assert.equal(mutation.id, id);
|
|
56
|
+
|
|
57
|
+
if (reason) {
|
|
58
|
+
assert.equal(mutation.reason, reason);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (category) {
|
|
62
|
+
assert.equal(mutation.category, category);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (skipIfContains) {
|
|
66
|
+
assert.equal(mutation.skipIfContains, skipIfContains);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const snippet of snippets) {
|
|
70
|
+
assert.ok(mutation.value.includes(snippet), `Expected mutation "${id}" to include "${snippet}".`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
18
74
|
test("users-web console settings template exposes surface-derived settings outlets", async () => {
|
|
19
75
|
const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "console", "settings.vue"), "utf8");
|
|
20
76
|
|
|
21
|
-
assert.match(source,
|
|
77
|
+
assert.match(source, /target="console-settings:primary-menu"/);
|
|
78
|
+
assert.match(source, /default-link-component-token="local\.main\.ui\.surface-aware-menu-link-item"/);
|
|
79
|
+
assert.match(source, /<RouterView \/>/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("users-web console settings index template is a simple developer-owned stub", async () => {
|
|
83
|
+
const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "console", "settings", "index.vue"), "utf8");
|
|
84
|
+
|
|
85
|
+
assert.match(source, /definePage/);
|
|
86
|
+
assert.match(source, /your_child_segment/);
|
|
22
87
|
});
|
|
23
88
|
|
|
24
89
|
test("users-web descriptor metadata advertises console settings outlets with standard positions", () => {
|
|
25
|
-
const outlets =
|
|
90
|
+
const outlets = readOutlets("console-settings:primary-menu");
|
|
26
91
|
assert.deepEqual(
|
|
27
92
|
outlets,
|
|
28
93
|
[
|
|
29
94
|
{
|
|
30
|
-
|
|
31
|
-
|
|
95
|
+
target: "console-settings:primary-menu",
|
|
96
|
+
defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
32
97
|
surfaces: ["console"],
|
|
33
98
|
source: "templates/src/pages/console/settings.vue"
|
|
34
99
|
}
|
|
35
100
|
]
|
|
36
101
|
);
|
|
102
|
+
assert.deepEqual(findFileMutation("users-web-page-console-settings"), {
|
|
103
|
+
from: "templates/src/pages/console/settings/index.vue",
|
|
104
|
+
toSurface: "console",
|
|
105
|
+
toSurfacePath: "settings/index.vue",
|
|
106
|
+
reason: "Install console settings index stub scaffold for app-owned landing or redirect behavior.",
|
|
107
|
+
category: "users-web",
|
|
108
|
+
id: "users-web-page-console-settings"
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("users-web home tools widget exposes home-tools outlet", async () => {
|
|
113
|
+
const source = await readFile(path.join(PACKAGE_DIR, "src", "client", "components", "UsersHomeToolsWidget.vue"), "utf8");
|
|
114
|
+
|
|
115
|
+
assert.match(source, /import \{ HOME_TOOLS_OUTLET \} from "\.\.\/\.\.\/shared\/toolsOutletContracts\.js";/);
|
|
116
|
+
assert.match(source, /<ShellOutletMenuWidget/);
|
|
117
|
+
assert.match(source, /:target="HOME_TOOLS_OUTLET\.target"/);
|
|
118
|
+
assert.match(source, /:default-link-component-token="HOME_TOOLS_OUTLET\.defaultLinkComponentToken"/);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("users-web workspace tools widget exposes workspace-tools outlet", async () => {
|
|
122
|
+
const source = await readFile(path.join(PACKAGE_DIR, "src", "client", "components", "UsersWorkspaceToolsWidget.vue"), "utf8");
|
|
123
|
+
|
|
124
|
+
assert.match(source, /import \{ WORKSPACE_TOOLS_OUTLET \} from "\.\.\/\.\.\/shared\/toolsOutletContracts\.js";/);
|
|
125
|
+
assert.match(source, /<ShellOutletMenuWidget/);
|
|
126
|
+
assert.match(source, /:target="WORKSPACE_TOOLS_OUTLET\.target"/);
|
|
127
|
+
assert.match(source, /:default-link-component-token="WORKSPACE_TOOLS_OUTLET\.defaultLinkComponentToken"/);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("users-web descriptor metadata advertises home tools outlet and standard home settings placements", () => {
|
|
131
|
+
assert.deepEqual(
|
|
132
|
+
readOutlets("home-tools:primary-menu"),
|
|
133
|
+
[
|
|
134
|
+
{
|
|
135
|
+
target: "home-tools:primary-menu",
|
|
136
|
+
defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
137
|
+
surfaces: ["home"],
|
|
138
|
+
source: "src/client/components/UsersHomeToolsWidget.vue"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
expectContribution("users.home.menu.home", {
|
|
144
|
+
target: "shell-layout:primary-menu",
|
|
145
|
+
surfaces: ["*"],
|
|
146
|
+
order: 50,
|
|
147
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
148
|
+
when: "auth.authenticated === true",
|
|
149
|
+
source: "mutations.text#users-web-home-shell-menu-placement"
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expectContribution("users.profile.menu.settings", {
|
|
153
|
+
target: "auth-profile-menu:primary-menu",
|
|
154
|
+
surfaces: ["*"],
|
|
155
|
+
order: 500,
|
|
156
|
+
componentToken: "auth.web.profile.menu.link-item",
|
|
157
|
+
when: "auth.authenticated === true",
|
|
158
|
+
source: "mutations.text#users-web-profile-settings-placement"
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expectContribution("users.home.tools.widget", {
|
|
162
|
+
target: "shell-layout:top-right",
|
|
163
|
+
surfaces: ["home"],
|
|
164
|
+
order: 900,
|
|
165
|
+
componentToken: "users.web.home.tools.widget",
|
|
166
|
+
when: "auth.authenticated === true",
|
|
167
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expectContribution("users.home.menu.settings", {
|
|
171
|
+
target: "home-tools:primary-menu",
|
|
172
|
+
surfaces: ["home"],
|
|
173
|
+
order: 100,
|
|
174
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
175
|
+
when: "auth.authenticated === true",
|
|
176
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
177
|
+
});
|
|
178
|
+
assert.equal(findContribution("users.home.settings.general"), null);
|
|
179
|
+
|
|
180
|
+
expectTextMutation("users-web-home-tools-placement", {
|
|
181
|
+
reason: "Append users-web home tools widget and settings menu placements into app-owned placement registry.",
|
|
182
|
+
category: "users-web",
|
|
183
|
+
skipIfContains: 'id: "users.home.tools.widget"',
|
|
184
|
+
snippets: [
|
|
185
|
+
'id: "users.home.tools.widget"',
|
|
186
|
+
'componentToken: "users.web.home.tools.widget"',
|
|
187
|
+
'id: "users.home.menu.settings"',
|
|
188
|
+
'target: "home-tools:primary-menu"',
|
|
189
|
+
'componentToken: "local.main.ui.surface-aware-menu-link-item"',
|
|
190
|
+
'workspaceSuffix: "/settings"',
|
|
191
|
+
'nonWorkspaceSuffix: "/settings"'
|
|
192
|
+
]
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expectTextMutation("users-web-profile-settings-placement", {
|
|
196
|
+
reason: "Append users-web profile settings menu placement into app-owned placement registry.",
|
|
197
|
+
category: "users-web",
|
|
198
|
+
skipIfContains: 'id: "users.profile.menu.settings"',
|
|
199
|
+
snippets: [
|
|
200
|
+
'id: "users.profile.menu.settings"',
|
|
201
|
+
'target: "auth-profile-menu:primary-menu"',
|
|
202
|
+
'componentToken: "auth.web.profile.menu.link-item"',
|
|
203
|
+
'label: "Settings"',
|
|
204
|
+
'to: "/account"'
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expectTextMutation("users-web-home-shell-menu-placement", {
|
|
209
|
+
reason: "Append users-web home shell menu placement into app-owned placement registry.",
|
|
210
|
+
category: "users-web",
|
|
211
|
+
skipIfContains: 'id: "users.home.menu.home"',
|
|
212
|
+
snippets: [
|
|
213
|
+
'id: "users.home.menu.home"',
|
|
214
|
+
'target: "shell-layout:primary-menu"',
|
|
215
|
+
'componentToken: "local.main.ui.surface-aware-menu-link-item"',
|
|
216
|
+
'label: "Home"',
|
|
217
|
+
'surface: "home"',
|
|
218
|
+
'workspaceSuffix: "/"',
|
|
219
|
+
'nonWorkspaceSuffix: "/"',
|
|
220
|
+
'exact: true'
|
|
221
|
+
]
|
|
222
|
+
});
|
|
223
|
+
|
|
37
224
|
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import { useRoute } from "vue-router";
|
|
4
|
-
import { appendQueryString } from "@jskit-ai/kernel/shared/support";
|
|
5
|
-
import { isExternalLinkTarget, splitPathQueryHash } from "@jskit-ai/kernel/shared/support/linkPath";
|
|
6
|
-
import {
|
|
7
|
-
useWebPlacementContext,
|
|
8
|
-
resolveSurfaceNavigationTargetFromPlacementContext
|
|
9
|
-
} from "@jskit-ai/shell-web/client/placement";
|
|
10
|
-
import { resolveAccountSettingsPathFromPlacementContext } from "../lib/workspaceSurfacePaths.js";
|
|
11
|
-
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
12
|
-
|
|
13
|
-
const props = defineProps({
|
|
14
|
-
label: {
|
|
15
|
-
type: String,
|
|
16
|
-
default: ""
|
|
17
|
-
},
|
|
18
|
-
to: {
|
|
19
|
-
type: String,
|
|
20
|
-
default: ""
|
|
21
|
-
},
|
|
22
|
-
icon: {
|
|
23
|
-
type: String,
|
|
24
|
-
default: ""
|
|
25
|
-
},
|
|
26
|
-
disabled: {
|
|
27
|
-
type: Boolean,
|
|
28
|
-
default: false
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const route = useRoute();
|
|
33
|
-
const { context: placementContext } = useWebPlacementContext();
|
|
34
|
-
|
|
35
|
-
function resolveFallbackReturnTo() {
|
|
36
|
-
if (typeof window !== "object" || !window || !window.location) {
|
|
37
|
-
return "/";
|
|
38
|
-
}
|
|
39
|
-
const pathname = String(window.location.pathname || "").trim() || "/";
|
|
40
|
-
const search = String(window.location.search || "").trim();
|
|
41
|
-
const hash = String(window.location.hash || "").trim();
|
|
42
|
-
return `${pathname}${search}${hash}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function resolveFallbackReturnToHref() {
|
|
46
|
-
if (typeof window !== "object" || !window || !window.location) {
|
|
47
|
-
return "/";
|
|
48
|
-
}
|
|
49
|
-
return String(window.location.href || "").trim() || resolveFallbackReturnTo();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function resolvePathnameFromLinkTarget(target = "") {
|
|
53
|
-
const normalizedTarget = String(target || "").trim();
|
|
54
|
-
if (!normalizedTarget) {
|
|
55
|
-
return "";
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (isExternalLinkTarget(normalizedTarget)) {
|
|
59
|
-
try {
|
|
60
|
-
const parsed = new URL(normalizedTarget);
|
|
61
|
-
return String(parsed.pathname || "").trim();
|
|
62
|
-
} catch {
|
|
63
|
-
return "";
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return splitPathQueryHash(normalizedTarget).pathname;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const accountSettingsPathname = computed(() => {
|
|
71
|
-
const settingsPath = resolveAccountSettingsPathFromPlacementContext(placementContext.value);
|
|
72
|
-
return resolvePathnameFromLinkTarget(settingsPath);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const resolvedTo = computed(() => {
|
|
76
|
-
const target = String(props.to || "").trim();
|
|
77
|
-
if (!target) {
|
|
78
|
-
return "";
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const targetPathname = resolvePathnameFromLinkTarget(target);
|
|
82
|
-
if (!targetPathname || targetPathname !== accountSettingsPathname.value) {
|
|
83
|
-
return target;
|
|
84
|
-
}
|
|
85
|
-
if (target.includes("returnTo=")) {
|
|
86
|
-
return target;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const accountSettingsTarget = resolveSurfaceNavigationTargetFromPlacementContext(placementContext.value, {
|
|
90
|
-
path: target,
|
|
91
|
-
surfaceId: "account"
|
|
92
|
-
});
|
|
93
|
-
const routeFullPath = String(route?.fullPath || "").trim();
|
|
94
|
-
const routePath = String(route?.path || "").trim();
|
|
95
|
-
const returnTo = accountSettingsTarget.sameOrigin
|
|
96
|
-
? routeFullPath || routePath || resolveFallbackReturnTo()
|
|
97
|
-
: resolveFallbackReturnToHref();
|
|
98
|
-
const queryParams = new URLSearchParams({
|
|
99
|
-
returnTo
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return appendQueryString(target, queryParams.toString());
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const resolvedNavigationTarget = computed(() => {
|
|
106
|
-
const target = String(resolvedTo.value || "").trim();
|
|
107
|
-
if (!target) {
|
|
108
|
-
return {
|
|
109
|
-
href: "",
|
|
110
|
-
sameOrigin: true
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const navigationTarget = resolveSurfaceNavigationTargetFromPlacementContext(placementContext.value, {
|
|
115
|
-
path: target
|
|
116
|
-
});
|
|
117
|
-
return {
|
|
118
|
-
href: navigationTarget.href,
|
|
119
|
-
sameOrigin: navigationTarget.sameOrigin
|
|
120
|
-
};
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const resolvedIcon = computed(() =>
|
|
124
|
-
resolveMenuLinkIcon({
|
|
125
|
-
icon: props.icon,
|
|
126
|
-
label: props.label,
|
|
127
|
-
to: resolvedTo.value
|
|
128
|
-
})
|
|
129
|
-
);
|
|
130
|
-
</script>
|
|
131
|
-
|
|
132
|
-
<template>
|
|
133
|
-
<v-list-item
|
|
134
|
-
:title="props.label"
|
|
135
|
-
:to="resolvedNavigationTarget.sameOrigin ? resolvedNavigationTarget.href : undefined"
|
|
136
|
-
:href="resolvedNavigationTarget.sameOrigin ? undefined : resolvedNavigationTarget.href"
|
|
137
|
-
:prepend-icon="resolvedIcon || undefined"
|
|
138
|
-
:disabled="props.disabled"
|
|
139
|
-
/>
|
|
140
|
-
</template>
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import { useRoute } from "vue-router";
|
|
4
|
-
import { useWebPlacementContext } from "@jskit-ai/shell-web/client/placement";
|
|
5
|
-
import { usePaths } from "../composables/usePaths.js";
|
|
6
|
-
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
7
|
-
import { resolveMenuLinkTarget } from "../support/menuLinkTarget.js";
|
|
8
|
-
|
|
9
|
-
const props = defineProps({
|
|
10
|
-
label: {
|
|
11
|
-
type: String,
|
|
12
|
-
default: ""
|
|
13
|
-
},
|
|
14
|
-
to: {
|
|
15
|
-
type: String,
|
|
16
|
-
default: ""
|
|
17
|
-
},
|
|
18
|
-
icon: {
|
|
19
|
-
type: String,
|
|
20
|
-
default: ""
|
|
21
|
-
},
|
|
22
|
-
surface: {
|
|
23
|
-
type: String,
|
|
24
|
-
default: ""
|
|
25
|
-
},
|
|
26
|
-
workspaceSuffix: {
|
|
27
|
-
type: String,
|
|
28
|
-
default: "/"
|
|
29
|
-
},
|
|
30
|
-
nonWorkspaceSuffix: {
|
|
31
|
-
type: String,
|
|
32
|
-
default: "/"
|
|
33
|
-
},
|
|
34
|
-
disabled: {
|
|
35
|
-
type: Boolean,
|
|
36
|
-
default: false
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const route = useRoute();
|
|
41
|
-
const paths = usePaths();
|
|
42
|
-
const { context: placementContext } = useWebPlacementContext();
|
|
43
|
-
|
|
44
|
-
const resolvedTo = computed(() => {
|
|
45
|
-
return resolveMenuLinkTarget({
|
|
46
|
-
to: props.to,
|
|
47
|
-
surface: props.surface,
|
|
48
|
-
currentSurfaceId: paths.currentSurfaceId.value,
|
|
49
|
-
placementContext: placementContext.value,
|
|
50
|
-
workspaceSuffix: props.workspaceSuffix,
|
|
51
|
-
nonWorkspaceSuffix: props.nonWorkspaceSuffix,
|
|
52
|
-
routeParams: route.params || {},
|
|
53
|
-
resolvePagePath(relativePath, options = {}) {
|
|
54
|
-
return paths.page(relativePath, options);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const resolvedIcon = computed(() =>
|
|
60
|
-
resolveMenuLinkIcon({
|
|
61
|
-
icon: props.icon,
|
|
62
|
-
label: props.label,
|
|
63
|
-
to: resolvedTo.value
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
</script>
|
|
67
|
-
|
|
68
|
-
<template>
|
|
69
|
-
<v-list-item
|
|
70
|
-
v-if="resolvedTo"
|
|
71
|
-
:title="props.label"
|
|
72
|
-
:to="resolvedTo"
|
|
73
|
-
:prepend-icon="resolvedIcon || undefined"
|
|
74
|
-
:disabled="props.disabled"
|
|
75
|
-
/>
|
|
76
|
-
</template>
|