@jskit-ai/users-web 0.1.48 → 0.1.50
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 +89 -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,17 @@
|
|
|
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.50",
|
|
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",
|
|
14
|
+
"@jskit-ai/console-web",
|
|
8
15
|
"@jskit-ai/http-runtime",
|
|
9
16
|
"@jskit-ai/shell-web",
|
|
10
17
|
"@jskit-ai/uploads-image-web",
|
|
@@ -47,14 +54,6 @@ export default Object.freeze({
|
|
|
47
54
|
subpath: "./client/components/ProfileClientElement",
|
|
48
55
|
summary: "Exports profile settings client element scaffold component."
|
|
49
56
|
},
|
|
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
57
|
{
|
|
59
58
|
subpath: "./client/composables/useAddEdit",
|
|
60
59
|
summary: "Exports add/edit operation composable."
|
|
@@ -95,9 +94,8 @@ export default Object.freeze({
|
|
|
95
94
|
containerTokens: {
|
|
96
95
|
server: [],
|
|
97
96
|
client: [
|
|
98
|
-
"users.web.shell.menu-link-item",
|
|
99
|
-
"users.web.shell.surface-aware-menu-link-item",
|
|
100
97
|
"users.web.profile.menu.surface-switch-item",
|
|
98
|
+
"users.web.home.tools.widget",
|
|
101
99
|
"users.web.profile.element",
|
|
102
100
|
"users.web.bootstrap-placement.runtime"
|
|
103
101
|
]
|
|
@@ -107,8 +105,20 @@ export default Object.freeze({
|
|
|
107
105
|
placements: {
|
|
108
106
|
outlets: [
|
|
109
107
|
{
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
target: HOME_TOOLS_OUTLET.target,
|
|
109
|
+
defaultLinkComponentToken: HOME_TOOLS_OUTLET.defaultLinkComponentToken,
|
|
110
|
+
surfaces: ["home"],
|
|
111
|
+
source: "src/client/components/UsersHomeToolsWidget.vue"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
target: WORKSPACE_TOOLS_OUTLET.target,
|
|
115
|
+
defaultLinkComponentToken: WORKSPACE_TOOLS_OUTLET.defaultLinkComponentToken,
|
|
116
|
+
surfaces: ["admin"],
|
|
117
|
+
source: "src/client/components/UsersWorkspaceToolsWidget.vue"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
target: "console-settings:primary-menu",
|
|
121
|
+
defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
112
122
|
surfaces: ["console"],
|
|
113
123
|
source: "templates/src/pages/console/settings.vue"
|
|
114
124
|
}
|
|
@@ -116,8 +126,7 @@ export default Object.freeze({
|
|
|
116
126
|
contributions: [
|
|
117
127
|
{
|
|
118
128
|
id: "users.profile.menu.surface-switch",
|
|
119
|
-
|
|
120
|
-
position: "primary-menu",
|
|
129
|
+
target: "auth-profile-menu:primary-menu",
|
|
121
130
|
surfaces: ["*"],
|
|
122
131
|
order: 100,
|
|
123
132
|
componentToken: "users.web.profile.menu.surface-switch-item",
|
|
@@ -126,23 +135,48 @@ export default Object.freeze({
|
|
|
126
135
|
},
|
|
127
136
|
{
|
|
128
137
|
id: "users.profile.menu.settings",
|
|
129
|
-
|
|
130
|
-
position: "primary-menu",
|
|
138
|
+
target: "auth-profile-menu:primary-menu",
|
|
131
139
|
surfaces: ["*"],
|
|
132
140
|
order: 500,
|
|
133
|
-
componentToken: "
|
|
141
|
+
componentToken: "auth.web.profile.menu.link-item",
|
|
134
142
|
when: "auth.authenticated === true",
|
|
135
143
|
source: "mutations.text#users-web-profile-settings-placement"
|
|
136
144
|
},
|
|
145
|
+
{
|
|
146
|
+
id: "users.home.menu.home",
|
|
147
|
+
target: "shell-layout:primary-menu",
|
|
148
|
+
surfaces: ["*"],
|
|
149
|
+
order: 50,
|
|
150
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
151
|
+
when: "auth.authenticated === true",
|
|
152
|
+
source: "mutations.text#users-web-home-shell-menu-placement"
|
|
153
|
+
},
|
|
137
154
|
{
|
|
138
155
|
id: "users.console.menu.settings",
|
|
139
|
-
|
|
140
|
-
position: "primary-menu",
|
|
156
|
+
target: "shell-layout:primary-menu",
|
|
141
157
|
surfaces: ["console"],
|
|
142
158
|
order: 100,
|
|
143
|
-
componentToken: "
|
|
159
|
+
componentToken: "local.main.ui.menu-link-item",
|
|
144
160
|
when: "auth.authenticated === true",
|
|
145
161
|
source: "mutations.text#users-web-console-settings-placement"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: "users.home.tools.widget",
|
|
165
|
+
target: "shell-layout:top-right",
|
|
166
|
+
surfaces: ["home"],
|
|
167
|
+
order: 900,
|
|
168
|
+
componentToken: "users.web.home.tools.widget",
|
|
169
|
+
when: "auth.authenticated === true",
|
|
170
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "users.home.menu.settings",
|
|
174
|
+
target: "home-tools:primary-menu",
|
|
175
|
+
surfaces: ["home"],
|
|
176
|
+
order: 100,
|
|
177
|
+
componentToken: "local.main.ui.surface-aware-menu-link-item",
|
|
178
|
+
when: "auth.authenticated === true",
|
|
179
|
+
source: "mutations.text#users-web-home-tools-placement"
|
|
146
180
|
}
|
|
147
181
|
]
|
|
148
182
|
}
|
|
@@ -153,12 +187,13 @@ export default Object.freeze({
|
|
|
153
187
|
runtime: {
|
|
154
188
|
"@tanstack/vue-query": "5.92.12",
|
|
155
189
|
"@mdi/js": "^7.4.47",
|
|
156
|
-
"@jskit-ai/
|
|
157
|
-
"@jskit-ai/
|
|
158
|
-
"@jskit-ai/
|
|
159
|
-
"@jskit-ai/
|
|
160
|
-
"@jskit-ai/
|
|
161
|
-
"@jskit-ai/
|
|
190
|
+
"@jskit-ai/console-web": "0.1.2",
|
|
191
|
+
"@jskit-ai/http-runtime": "0.1.34",
|
|
192
|
+
"@jskit-ai/realtime": "0.1.34",
|
|
193
|
+
"@jskit-ai/kernel": "0.1.35",
|
|
194
|
+
"@jskit-ai/shell-web": "0.1.34",
|
|
195
|
+
"@jskit-ai/uploads-image-web": "0.1.13",
|
|
196
|
+
"@jskit-ai/users-core": "0.1.45",
|
|
162
197
|
vuetify: "^4.0.0"
|
|
163
198
|
},
|
|
164
199
|
dev: {}
|
|
@@ -226,7 +261,7 @@ export default Object.freeze({
|
|
|
226
261
|
from: "templates/src/pages/console/settings/index.vue",
|
|
227
262
|
toSurface: "console",
|
|
228
263
|
toSurfacePath: "settings/index.vue",
|
|
229
|
-
reason: "Install console settings
|
|
264
|
+
reason: "Install console settings index stub scaffold for app-owned landing or redirect behavior.",
|
|
230
265
|
category: "users-web",
|
|
231
266
|
id: "users-web-page-console-settings"
|
|
232
267
|
}
|
|
@@ -249,7 +284,7 @@ export default Object.freeze({
|
|
|
249
284
|
position: "bottom",
|
|
250
285
|
skipIfContains: "id: \"users.profile.menu.surface-switch\"",
|
|
251
286
|
value:
|
|
252
|
-
"\naddPlacement({\n id: \"users.profile.menu.surface-switch\",\n
|
|
287
|
+
"\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
288
|
reason: "Append users-web profile surface switch placement into app-owned placement registry.",
|
|
254
289
|
category: "users-web",
|
|
255
290
|
id: "users-web-profile-surface-switch-placement"
|
|
@@ -260,21 +295,43 @@ export default Object.freeze({
|
|
|
260
295
|
position: "bottom",
|
|
261
296
|
skipIfContains: "id: \"users.profile.menu.settings\"",
|
|
262
297
|
value:
|
|
263
|
-
"\naddPlacement({\n id: \"users.profile.menu.settings\",\n
|
|
298
|
+
"\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
299
|
reason: "Append users-web profile settings menu placement into app-owned placement registry.",
|
|
265
300
|
category: "users-web",
|
|
266
301
|
id: "users-web-profile-settings-placement"
|
|
267
302
|
},
|
|
303
|
+
{
|
|
304
|
+
op: "append-text",
|
|
305
|
+
file: "src/placement.js",
|
|
306
|
+
position: "bottom",
|
|
307
|
+
skipIfContains: "id: \"users.home.menu.home\"",
|
|
308
|
+
value:
|
|
309
|
+
"\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",
|
|
310
|
+
reason: "Append users-web home shell menu placement into app-owned placement registry.",
|
|
311
|
+
category: "users-web",
|
|
312
|
+
id: "users-web-home-shell-menu-placement"
|
|
313
|
+
},
|
|
268
314
|
{
|
|
269
315
|
op: "append-text",
|
|
270
316
|
file: "src/placement.js",
|
|
271
317
|
position: "bottom",
|
|
272
318
|
skipIfContains: "id: \"users.console.menu.settings\"",
|
|
273
319
|
value:
|
|
274
|
-
"\naddPlacement({\n id: \"users.console.menu.settings\",\n
|
|
320
|
+
"\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
321
|
reason: "Append users-web console settings menu placement into app-owned placement registry.",
|
|
276
322
|
category: "users-web",
|
|
277
323
|
id: "users-web-console-settings-placement"
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
op: "append-text",
|
|
327
|
+
file: "src/placement.js",
|
|
328
|
+
position: "bottom",
|
|
329
|
+
skipIfContains: "id: \"users.home.tools.widget\"",
|
|
330
|
+
value:
|
|
331
|
+
"\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",
|
|
332
|
+
reason: "Append users-web home tools widget and settings menu placements into app-owned placement registry.",
|
|
333
|
+
category: "users-web",
|
|
334
|
+
id: "users-web-home-tools-placement"
|
|
278
335
|
}
|
|
279
336
|
]
|
|
280
337
|
}
|
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.50",
|
|
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.34",
|
|
30
|
+
"@jskit-ai/kernel": "0.1.35",
|
|
31
|
+
"@jskit-ai/realtime": "0.1.34",
|
|
32
|
+
"@jskit-ai/shell-web": "0.1.34",
|
|
33
|
+
"@jskit-ai/uploads-image-web": "0.1.13",
|
|
34
|
+
"@jskit-ai/users-core": "0.1.45",
|
|
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>
|