@vcmap/module-selector 2.0.0-rc.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # v1.0.0
2
+
3
+ Document features and fixes
package/LICENSE.md ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2024 pwuerstle@vc.systems
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
6
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
11
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
12
+ OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
13
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # module-selector
2
+
3
+ > Part of the [VC Map Project](https://github.com/virtualcitySYSTEMS/map-ui)
4
+ > describe your plugin
package/dist/index.js ADDED
@@ -0,0 +1,459 @@
1
+
2
+ function loadCss(href) {
3
+ return new Promise((resolve, reject) => {
4
+ const elem = document.createElement('link');
5
+ elem.rel = 'stylesheet';
6
+ elem.href = href;
7
+ elem.defer = false;
8
+ elem.async = false;
9
+ elem.onload = resolve;
10
+ elem.onerror = reject;
11
+ document.head.appendChild(elem);
12
+ });
13
+ } await loadCss('data:text/css;base64,LnYtY2FyZFtkYXRhLXYtMWRjYTRjN2Rde2Rpc3BsYXk6ZmxleDtmbGV4LWRpcmVjdGlvbjpjb2x1bW47anVzdGlmeS1jb250ZW50OmNlbnRlcjthbGlnbi1pdGVtczpjZW50ZXI7aGVpZ2h0OjE1MHB4O3dpZHRoOjE1MHB4O2FzcGVjdC1yYXRpbzoxO3RyYW5zaXRpb246YmFja2dyb3VuZC1jb2xvciAuM3N9LmZpeGVkLWNhcmRbZGF0YS12LTFkY2E0YzdkXXtoZWlnaHQ6MTUwcHg7d2lkdGg6MTUwcHg7YXNwZWN0LXJhdGlvOjF9LnNlbGVjdGVkLWNhcmRbZGF0YS12LTFkY2E0YzdkXXtiYWNrZ3JvdW5kLWNvbG9yOnJnYih2YXIoLS12LXRoZW1lLXByaW1hcnkpKTtjb2xvcjpyZ2IodmFyKC0tdi10aGVtZS1vbi1wcmltYXJ5KSl9Cg==');import { VcsFormButton as A, WindowSlot as D, createToggleAction as O, ButtonLocation as j } from "../../../assets/ui.js";
14
+ import { defineComponent as E, inject as F, computed as V, resolveComponent as c, openBlock as r, createBlock as p, withCtx as l, createVNode as a, createTextVNode as u, toDisplayString as m, createCommentVNode as S, createElementBlock as h, Fragment as B, renderList as N, normalizeClass as T, ref as b } from "../../../assets/vue.js";
15
+ import { VSheet as J, VContainer as L, VRow as P, VCol as R, VCard as G, VIcon as H, VCardTitle as K } from "../../../assets/vuetify.js";
16
+ import { VcsModule as U } from "../../../assets/core.js";
17
+ const k = "@vcmap/module-selector", W = "2.0.0-rc.2", Z = "^6.0";
18
+ async function $(e, o) {
19
+ const t = await fetch(e, {
20
+ method: "GET"
21
+ }).then((s) => {
22
+ if (s.ok)
23
+ return s.json();
24
+ });
25
+ if (t) {
26
+ t._id = "moduleSelector_module_id";
27
+ const s = new U(t);
28
+ await o.addModule(s);
29
+ }
30
+ }
31
+ async function Q(e) {
32
+ const o = "moduleSelector_module_id";
33
+ e.getModuleById(o) && await e.removeModule(o);
34
+ }
35
+ const x = "moduleSelector_window_id", X = E({
36
+ name: "ModuleSelector",
37
+ components: {
38
+ VSheet: J,
39
+ VContainer: L,
40
+ VRow: P,
41
+ VCol: R,
42
+ VCard: G,
43
+ VIcon: H,
44
+ VCardTitle: K,
45
+ VcsFormButton: A
46
+ },
47
+ props: {
48
+ modules: {
49
+ type: Array,
50
+ required: !0
51
+ },
52
+ basisModule: {
53
+ type: Object,
54
+ required: !1,
55
+ default: void 0
56
+ },
57
+ requireModuleSelection: {
58
+ type: Boolean,
59
+ required: !1,
60
+ default: !1
61
+ }
62
+ },
63
+ setup(e) {
64
+ const o = F("vcsApp"), t = o.plugins.getByKey(k), { modules: s } = e, C = V(() => {
65
+ const n = e.modules[t.selectedMainModuleIndex.value];
66
+ return (n == null ? void 0 : n.type) === "group" ? n.cards : [];
67
+ }), I = (n) => {
68
+ t.selectedMainModuleIndex.value === n ? t.selectedMainModuleIndex.value = null : t.selectedMainModuleIndex.value = n;
69
+ }, v = (n) => {
70
+ t.selectedNestedModuleIndex.value === n ? t.selectedNestedModuleIndex.value = null : t.selectedNestedModuleIndex.value = n;
71
+ }, g = () => {
72
+ t.selectedNestedModuleIndex.value = null, t.selectedMainModuleIndex.value = null;
73
+ }, M = async () => {
74
+ var i;
75
+ await Q(o);
76
+ const n = t.selectedMainModuleIndex.value, y = t.selectedNestedModuleIndex.value;
77
+ if (n === null) return;
78
+ const d = s[n], w = (d == null ? void 0 : d.type) === "group" ? (i = d.cards) == null ? void 0 : i[y] : null;
79
+ w != null && w.moduleUrl ? await $(w.moduleUrl, o) : (d == null ? void 0 : d.type) === "url" && await $(d.moduleUrl, o), o.windowManager.remove(x);
80
+ }, _ = V(() => {
81
+ if (!e.requireModuleSelection) return !1;
82
+ const n = t.selectedMainModuleIndex.value, y = t.selectedNestedModuleIndex.value, d = s[n];
83
+ return n == null || (d == null ? void 0 : d.type) === "group" && d.cards && y == null;
84
+ });
85
+ return {
86
+ nestedCards: C,
87
+ plugin: t,
88
+ selectModule: I,
89
+ selectNestedModule: v,
90
+ showMainCards: g,
91
+ startApplication: M,
92
+ isButtonDisabled: _
93
+ };
94
+ }
95
+ }), Y = (e, o) => {
96
+ const t = e.__vccOpts || e;
97
+ for (const [s, C] of o)
98
+ t[s] = C;
99
+ return t;
100
+ };
101
+ function ee(e, o, t, s, C, I) {
102
+ const v = c("v-icon"), g = c("v-card-title"), M = c("v-card"), _ = c("v-col"), n = c("v-row"), y = c("VcsFormButton"), d = c("v-container"), w = c("v-sheet");
103
+ return r(), p(w, {
104
+ class: "module-selector",
105
+ elevation: "1"
106
+ }, {
107
+ default: l(() => [
108
+ a(d, null, {
109
+ default: l(() => [
110
+ a(n, {
111
+ class: "justify-center align-center",
112
+ "no-gutters": ""
113
+ }, {
114
+ default: l(() => [
115
+ e.basisModule && !e.nestedCards.length ? (r(), p(_, {
116
+ key: 0,
117
+ cols: "auto",
118
+ class: "pa-2"
119
+ }, {
120
+ default: l(() => [
121
+ a(M, {
122
+ class: "pa-3 fixed-card selected-card",
123
+ outlined: ""
124
+ }, {
125
+ default: l(() => [
126
+ e.basisModule.icon ? (r(), p(v, {
127
+ key: 0,
128
+ size: "50"
129
+ }, {
130
+ default: l(() => [
131
+ u(
132
+ m(e.basisModule.icon),
133
+ 1
134
+ /* TEXT */
135
+ )
136
+ ]),
137
+ _: 1
138
+ /* STABLE */
139
+ })) : S("v-if", !0),
140
+ a(g, { class: "text-center" }, {
141
+ default: l(() => [
142
+ u(
143
+ m(e.$st(e.basisModule.title)),
144
+ 1
145
+ /* TEXT */
146
+ )
147
+ ]),
148
+ _: 1
149
+ /* STABLE */
150
+ })
151
+ ]),
152
+ _: 1
153
+ /* STABLE */
154
+ })
155
+ ]),
156
+ _: 1
157
+ /* STABLE */
158
+ })) : S("v-if", !0),
159
+ e.nestedCards.length ? (r(), h(
160
+ B,
161
+ { key: 2 },
162
+ [
163
+ a(_, {
164
+ cols: "auto",
165
+ class: "pa-2"
166
+ }, {
167
+ default: l(() => [
168
+ a(M, {
169
+ class: "fixed-card hover-card",
170
+ outlined: "",
171
+ onClick: o[0] || (o[0] = (i) => e.showMainCards())
172
+ }, {
173
+ default: l(() => [
174
+ a(v, { size: "50" }, {
175
+ default: l(() => [
176
+ u("mdi-arrow-left")
177
+ ]),
178
+ _: 1
179
+ /* STABLE */
180
+ }),
181
+ u(
182
+ " " + m(e.$st("moduleSelector.cardBack")),
183
+ 1
184
+ /* TEXT */
185
+ )
186
+ ]),
187
+ _: 1
188
+ /* STABLE */
189
+ })
190
+ ]),
191
+ _: 1
192
+ /* STABLE */
193
+ }),
194
+ (r(!0), h(
195
+ B,
196
+ null,
197
+ N(e.nestedCards, (i, f) => (r(), p(
198
+ _,
199
+ {
200
+ key: f,
201
+ cols: "auto",
202
+ class: "pa-2"
203
+ },
204
+ {
205
+ default: l(() => [
206
+ a(M, {
207
+ class: T(["fixed-card hover-card", {
208
+ "selected-card": e.plugin.selectedNestedModuleIndex.value === f
209
+ }]),
210
+ outlined: "",
211
+ onClick: (z) => e.selectNestedModule(f)
212
+ }, {
213
+ default: l(() => [
214
+ i.icon ? (r(), p(
215
+ v,
216
+ {
217
+ key: 0,
218
+ size: "50"
219
+ },
220
+ {
221
+ default: l(() => [
222
+ u(
223
+ m(i.icon),
224
+ 1
225
+ /* TEXT */
226
+ )
227
+ ]),
228
+ _: 2
229
+ /* DYNAMIC */
230
+ },
231
+ 1024
232
+ /* DYNAMIC_SLOTS */
233
+ )) : S("v-if", !0),
234
+ a(
235
+ g,
236
+ { class: "text-center" },
237
+ {
238
+ default: l(() => [
239
+ u(
240
+ m(e.$st(i.title)),
241
+ 1
242
+ /* TEXT */
243
+ )
244
+ ]),
245
+ _: 2
246
+ /* DYNAMIC */
247
+ },
248
+ 1024
249
+ /* DYNAMIC_SLOTS */
250
+ )
251
+ ]),
252
+ _: 2
253
+ /* DYNAMIC */
254
+ }, 1032, ["class", "onClick"])
255
+ ]),
256
+ _: 2
257
+ /* DYNAMIC */
258
+ },
259
+ 1024
260
+ /* DYNAMIC_SLOTS */
261
+ ))),
262
+ 128
263
+ /* KEYED_FRAGMENT */
264
+ ))
265
+ ],
266
+ 64
267
+ /* STABLE_FRAGMENT */
268
+ )) : (r(!0), h(
269
+ B,
270
+ { key: 1 },
271
+ N(e.modules, (i, f) => (r(), p(
272
+ _,
273
+ {
274
+ cols: "auto",
275
+ class: "pa-2",
276
+ key: f
277
+ },
278
+ {
279
+ default: l(() => [
280
+ a(M, {
281
+ class: T(["fixed-card hover-card", {
282
+ "selected-card": e.plugin.selectedMainModuleIndex.value === f
283
+ }]),
284
+ outlined: "",
285
+ onClick: (z) => e.selectModule(f)
286
+ }, {
287
+ default: l(() => [
288
+ i.icon ? (r(), p(
289
+ v,
290
+ {
291
+ key: 0,
292
+ size: "50"
293
+ },
294
+ {
295
+ default: l(() => [
296
+ u(
297
+ m(i.icon),
298
+ 1
299
+ /* TEXT */
300
+ )
301
+ ]),
302
+ _: 2
303
+ /* DYNAMIC */
304
+ },
305
+ 1024
306
+ /* DYNAMIC_SLOTS */
307
+ )) : S("v-if", !0),
308
+ a(
309
+ g,
310
+ { class: "text-center" },
311
+ {
312
+ default: l(() => [
313
+ u(
314
+ m(e.$st(i.title)),
315
+ 1
316
+ /* TEXT */
317
+ )
318
+ ]),
319
+ _: 2
320
+ /* DYNAMIC */
321
+ },
322
+ 1024
323
+ /* DYNAMIC_SLOTS */
324
+ )
325
+ ]),
326
+ _: 2
327
+ /* DYNAMIC */
328
+ }, 1032, ["class", "onClick"])
329
+ ]),
330
+ _: 2
331
+ /* DYNAMIC */
332
+ },
333
+ 1024
334
+ /* DYNAMIC_SLOTS */
335
+ ))),
336
+ 128
337
+ /* KEYED_FRAGMENT */
338
+ ))
339
+ ]),
340
+ _: 1
341
+ /* STABLE */
342
+ }),
343
+ a(n, { justify: "end" }, {
344
+ default: l(() => [
345
+ a(y, {
346
+ variant: "filled",
347
+ class: "ma-5",
348
+ onClick: e.startApplication,
349
+ disabled: e.isButtonDisabled
350
+ }, {
351
+ default: l(() => [
352
+ u(
353
+ m(e.$st("moduleSelector.startButton")),
354
+ 1
355
+ /* TEXT */
356
+ )
357
+ ]),
358
+ _: 1
359
+ /* STABLE */
360
+ }, 8, ["onClick", "disabled"])
361
+ ]),
362
+ _: 1
363
+ /* STABLE */
364
+ })
365
+ ]),
366
+ _: 1
367
+ /* STABLE */
368
+ })
369
+ ]),
370
+ _: 1
371
+ /* STABLE */
372
+ });
373
+ }
374
+ const te = /* @__PURE__ */ Y(X, [["render", ee], ["__scopeId", "data-v-1dca4c7d"]]), q = () => ({
375
+ windowTitle: "moduleSelector.title",
376
+ position: {
377
+ left: "33%",
378
+ right: "33%",
379
+ top: "10%"
380
+ },
381
+ modules: []
382
+ });
383
+ function se(e) {
384
+ return {
385
+ get name() {
386
+ return k;
387
+ },
388
+ get version() {
389
+ return W;
390
+ },
391
+ get mapVersion() {
392
+ return Z;
393
+ },
394
+ get config() {
395
+ return { ...q(), ...e };
396
+ },
397
+ selectedMainModuleIndex: b(void 0),
398
+ selectedNestedModuleIndex: b(void 0),
399
+ initialize(o) {
400
+ const t = {
401
+ id: x,
402
+ component: te,
403
+ slot: D.DETACHED,
404
+ position: this.config.position,
405
+ state: {
406
+ headerTitle: this.config.windowTitle
407
+ },
408
+ props: {
409
+ modules: this.config.modules,
410
+ basisModule: this.config.basisModule,
411
+ requireModuleSelection: this.config.requireModuleSelection
412
+ }
413
+ }, { action: s } = O(
414
+ {
415
+ name: "moduleSelector",
416
+ title: "moduleSelector.title",
417
+ icon: "mdi-view-grid"
418
+ },
419
+ t,
420
+ o.windowManager,
421
+ k
422
+ );
423
+ return o.navbarManager.add(
424
+ {
425
+ id: s.name,
426
+ action: s
427
+ },
428
+ k,
429
+ j.PROJECT
430
+ ), e.isActiveOnStart && o.windowManager.add(t, k), Promise.resolve();
431
+ },
432
+ getDefaultOptions: q,
433
+ toJSON() {
434
+ const o = {};
435
+ return e.windowTitle != null && (o.windowTitle = e.windowTitle), e.position != null && (o.position = e.position), e.modules != null && (o.modules = e.modules), o;
436
+ },
437
+ i18n: {
438
+ de: {
439
+ moduleSelector: {
440
+ title: "Themenkartenauswahl",
441
+ startButton: "Anwendung starten",
442
+ cardBack: "Zurück"
443
+ }
444
+ },
445
+ en: {
446
+ moduleSelector: {
447
+ title: "Theme map selection",
448
+ startButton: "Start application",
449
+ cardBack: "Back"
450
+ }
451
+ }
452
+ },
453
+ destroy() {
454
+ }
455
+ };
456
+ }
457
+ export {
458
+ se as default
459
+ };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@vcmap/module-selector",
3
+ "version": "2.0.0-rc.2",
4
+ "description": "",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "prepublishOnly": "vcmplugin build",
9
+ "build": "vcmplugin build",
10
+ "bundle": "vcmplugin bundle",
11
+ "start": "vcmplugin serve",
12
+ "preview": "vcmplugin preview",
13
+ "buildStagingApp": "vcmplugin buildStagingApp",
14
+ "lint:js": "eslint . --ext .vue,.js,.cjs,.mjs,.ts,.cts,.mts",
15
+ "lint:prettier": "prettier --check .",
16
+ "lint": "npm run lint:js && npm run lint:prettier",
17
+ "format": "prettier --write --list-different . && npm run lint:js -- --fix",
18
+ "test": "vitest",
19
+ "coverage": "vitest run --coverage",
20
+ "type-check": "vue-tsc --noEmit",
21
+ "ensure-types": "vcmplugin ensure-types"
22
+ },
23
+ "author": "pwuerstle@vc.systems",
24
+ "license": "MIT",
25
+ "keywords": [
26
+ "vcmap",
27
+ "plugin"
28
+ ],
29
+ "files": [
30
+ "src/",
31
+ "dist/",
32
+ "plugin-assets/",
33
+ "LICENSE.md",
34
+ "README.md",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "exports": {
38
+ ".": "dist/index.js",
39
+ "./dist": "./dist/index.js"
40
+ },
41
+ "eslintIgnore": [
42
+ "node_modules",
43
+ "dist",
44
+ "plugin-assets"
45
+ ],
46
+ "prettier": "@vcsuite/eslint-config/prettier.js",
47
+ "peerDependencies": {
48
+ "@vcmap/core": "^6.0.0-rc.10",
49
+ "@vcmap/ui": "^6.0.0-rc.9",
50
+ "vue": "~3.4.38",
51
+ "vuetify": "^3.7.2"
52
+ },
53
+ "devDependencies": {
54
+ "@vcmap/plugin-cli": "^4.0.0-rc.5",
55
+ "@vcsuite/eslint-config": "^3.0.8",
56
+ "@vitest/coverage-v8": "^2.1.1",
57
+ "jest-canvas-mock": "^2.5.2",
58
+ "jsdom": "^24.1.1",
59
+ "resize-observer-polyfill": "^1.5.1",
60
+ "typescript": "^5.6.2",
61
+ "vitest": "^2.1.1",
62
+ "vue-tsc": "^2.1.6"
63
+ },
64
+ "mapVersion": "^6.0"
65
+ }
@@ -0,0 +1,243 @@
1
+ <template>
2
+ <v-sheet class="module-selector" elevation="1">
3
+ <v-container>
4
+ <v-row class="justify-center align-center" no-gutters>
5
+ <v-col
6
+ cols="auto"
7
+ class="pa-2"
8
+ v-if="basisModule && !nestedCards.length"
9
+ >
10
+ <v-card class="pa-3 fixed-card selected-card" outlined>
11
+ <v-icon v-if="basisModule.icon" size="50">{{
12
+ basisModule.icon
13
+ }}</v-icon>
14
+ <v-card-title class="text-center">{{
15
+ $st(basisModule.title)
16
+ }}</v-card-title>
17
+ </v-card>
18
+ </v-col>
19
+ <template v-if="!nestedCards.length">
20
+ <v-col
21
+ cols="auto"
22
+ class="pa-2"
23
+ :key="index"
24
+ v-for="(module, index) in modules"
25
+ >
26
+ <v-card
27
+ class="fixed-card hover-card"
28
+ outlined
29
+ :class="{
30
+ 'selected-card': plugin.selectedMainModuleIndex.value === index,
31
+ }"
32
+ @click="selectModule(index)"
33
+ >
34
+ <v-icon v-if="module.icon" size="50">{{ module.icon }}</v-icon>
35
+ <v-card-title class="text-center">{{
36
+ $st(module.title)
37
+ }}</v-card-title>
38
+ </v-card>
39
+ </v-col>
40
+ </template>
41
+
42
+ <template v-else>
43
+ <v-col cols="auto" class="pa-2">
44
+ <v-card
45
+ class="fixed-card hover-card"
46
+ outlined
47
+ @click="showMainCards()"
48
+ >
49
+ <v-icon size="50">mdi-arrow-left</v-icon>
50
+ {{ $st('moduleSelector.cardBack') }}
51
+ </v-card>
52
+ </v-col>
53
+ <v-col
54
+ v-for="(card, index) in nestedCards"
55
+ :key="index"
56
+ cols="auto"
57
+ class="pa-2"
58
+ >
59
+ <v-card
60
+ class="fixed-card hover-card"
61
+ outlined
62
+ :class="{
63
+ 'selected-card':
64
+ plugin.selectedNestedModuleIndex.value === index,
65
+ }"
66
+ @click="selectNestedModule(index)"
67
+ >
68
+ <v-icon v-if="card.icon" size="50">{{ card.icon }}</v-icon>
69
+ <v-card-title class="text-center">{{
70
+ $st(card.title)
71
+ }}</v-card-title>
72
+ </v-card>
73
+ </v-col>
74
+ </template>
75
+ </v-row>
76
+ <v-row justify="end">
77
+ <VcsFormButton
78
+ variant="filled"
79
+ class="ma-5"
80
+ @click="startApplication"
81
+ :disabled="isButtonDisabled"
82
+ >
83
+ {{ $st('moduleSelector.startButton') }}
84
+ </VcsFormButton>
85
+ </v-row>
86
+ </v-container>
87
+ </v-sheet>
88
+ </template>
89
+
90
+ <script lang="ts">
91
+ import {
92
+ VSheet,
93
+ VContainer,
94
+ VRow,
95
+ VCol,
96
+ VCard,
97
+ VIcon,
98
+ VCardTitle,
99
+ } from 'vuetify/components';
100
+ import { VcsFormButton, VcsUiApp } from '@vcmap/ui';
101
+ import { computed, defineComponent, inject, PropType } from 'vue';
102
+ import { loadModule, unloadModule } from './moduleHelper.js';
103
+ import { name } from '../package.json';
104
+ import { BasisModule, ModuleSelectorPlugin, ModuleType } from './index.js';
105
+ import type { Module } from './index.ts';
106
+
107
+ export const windowIdModuleSelector = 'moduleSelector_window_id';
108
+ export default defineComponent({
109
+ name: 'ModuleSelector',
110
+ components: {
111
+ VSheet,
112
+ VContainer,
113
+ VRow,
114
+ VCol,
115
+ VCard,
116
+ VIcon,
117
+ VCardTitle,
118
+ VcsFormButton,
119
+ },
120
+ props: {
121
+ modules: {
122
+ type: Array as PropType<Module<ModuleType>[]>,
123
+ required: true,
124
+ },
125
+ basisModule: {
126
+ type: Object as PropType<BasisModule>,
127
+ required: false,
128
+ default: undefined,
129
+ },
130
+ requireModuleSelection: {
131
+ type: Boolean as PropType<boolean>,
132
+ required: false,
133
+ default: false,
134
+ },
135
+ },
136
+ setup(props) {
137
+ const app = inject('vcsApp') as VcsUiApp;
138
+ const plugin = app.plugins.getByKey(name) as ModuleSelectorPlugin;
139
+ const { modules } = props;
140
+
141
+ const nestedCards = computed(() => {
142
+ const module = props.modules[plugin.selectedMainModuleIndex.value!];
143
+ if (module?.type === 'group') {
144
+ return module.cards;
145
+ }
146
+ return [];
147
+ });
148
+
149
+ const selectModule = (index: number): void => {
150
+ if (plugin.selectedMainModuleIndex.value === index) {
151
+ plugin.selectedMainModuleIndex.value = null;
152
+ } else {
153
+ plugin.selectedMainModuleIndex.value = index;
154
+ }
155
+ };
156
+
157
+ const selectNestedModule = (index: number): void => {
158
+ if (plugin.selectedNestedModuleIndex.value === index) {
159
+ plugin.selectedNestedModuleIndex.value = null;
160
+ } else {
161
+ plugin.selectedNestedModuleIndex.value = index;
162
+ }
163
+ };
164
+
165
+ const showMainCards = (): void => {
166
+ // Reset selected module when showing main cards
167
+ plugin.selectedNestedModuleIndex.value = null;
168
+ plugin.selectedMainModuleIndex.value = null;
169
+ };
170
+
171
+ const startApplication = async (): Promise<void> => {
172
+ await unloadModule(app);
173
+
174
+ const mainModuleIndex = plugin.selectedMainModuleIndex.value;
175
+ const nestedModuleIndex = plugin.selectedNestedModuleIndex.value;
176
+
177
+ if (mainModuleIndex === null) return;
178
+
179
+ const mainModule = modules[mainModuleIndex!];
180
+ const nestedModule =
181
+ mainModule?.type === 'group'
182
+ ? mainModule.cards?.[nestedModuleIndex!]
183
+ : null;
184
+
185
+ if (nestedModule?.moduleUrl) {
186
+ await loadModule(nestedModule.moduleUrl, app);
187
+ } else if (mainModule?.type === 'url') {
188
+ await loadModule(mainModule.moduleUrl, app);
189
+ }
190
+
191
+ app.windowManager.remove(windowIdModuleSelector);
192
+ };
193
+
194
+ const isButtonDisabled = computed(() => {
195
+ if (!props.requireModuleSelection) return false;
196
+
197
+ const mainModuleIndex = plugin.selectedMainModuleIndex.value;
198
+ const nestedModuleIndex = plugin.selectedNestedModuleIndex.value;
199
+ const mainModule = modules[mainModuleIndex!];
200
+
201
+ return (
202
+ mainModuleIndex == null ||
203
+ (mainModule?.type === 'group' &&
204
+ mainModule.cards &&
205
+ nestedModuleIndex == null)
206
+ );
207
+ });
208
+
209
+ return {
210
+ nestedCards,
211
+ plugin,
212
+ selectModule,
213
+ selectNestedModule,
214
+ showMainCards,
215
+ startApplication,
216
+ isButtonDisabled,
217
+ };
218
+ },
219
+ });
220
+ </script>
221
+
222
+ <style scoped>
223
+ .v-card {
224
+ display: flex;
225
+ flex-direction: column;
226
+ justify-content: center;
227
+ align-items: center;
228
+ height: 150px;
229
+ width: 150px; /* Ensure width and height are equal */
230
+ aspect-ratio: 1; /* Maintain the square ratio */
231
+ transition: background-color 0.3s;
232
+ }
233
+ .fixed-card {
234
+ height: 150px;
235
+ width: 150px;
236
+ aspect-ratio: 1; /* Ensures the card remains square */
237
+ }
238
+
239
+ .selected-card {
240
+ background-color: rgb(var(--v-theme-primary));
241
+ color: rgb(var(--v-theme-on-primary));
242
+ }
243
+ </style>
@@ -0,0 +1,11 @@
1
+ import { ModuleSelectorConfig } from './index.js';
2
+
3
+ export default (): ModuleSelectorConfig => ({
4
+ windowTitle: 'moduleSelector.title',
5
+ position: {
6
+ left: '33%',
7
+ right: '33%',
8
+ top: '10%',
9
+ },
10
+ modules: [],
11
+ });
package/src/index.ts ADDED
@@ -0,0 +1,139 @@
1
+ import {
2
+ VcsPlugin,
3
+ VcsUiApp,
4
+ WindowSlot,
5
+ ButtonLocation,
6
+ WindowPositionOptions,
7
+ createToggleAction,
8
+ } from '@vcmap/ui';
9
+ import { Ref, ref } from 'vue';
10
+ import { name, version, mapVersion } from '../package.json';
11
+ import moduleSelector, { windowIdModuleSelector } from './ModuleSelector.vue';
12
+ import getDefaultOptions from './defaultOptions.js';
13
+
14
+ export type BasisModule = {
15
+ title: string;
16
+ icon: string;
17
+ };
18
+
19
+ export type ModuleType = 'group' | 'url';
20
+
21
+ export type Module<T extends ModuleType> = T extends 'group'
22
+ ? BasisModule & { type: T; cards: Module<'url'>[] }
23
+ : T extends 'url'
24
+ ? BasisModule & { moduleUrl: string; type: T }
25
+ : never;
26
+
27
+ type PluginConfig = Record<never, never>;
28
+ type PluginState = Record<never, never>;
29
+
30
+ export type ModuleSelectorConfig = PluginConfig & {
31
+ windowTitle?: string;
32
+ isActiveOnStart?: boolean;
33
+ requireModuleSelection?: boolean;
34
+ position?: WindowPositionOptions;
35
+ basisModule?: BasisModule;
36
+ modules: Array<Module<ModuleType>>;
37
+ };
38
+
39
+ export type ModuleSelectorPlugin = VcsPlugin<PluginConfig, PluginState> & {
40
+ readonly config: ModuleSelectorConfig;
41
+ selectedMainModuleIndex: Ref<number | undefined | null>;
42
+ selectedNestedModuleIndex: Ref<number | undefined | null>;
43
+ };
44
+
45
+ export default function plugin(
46
+ configInput: ModuleSelectorConfig,
47
+ ): ModuleSelectorPlugin {
48
+ return {
49
+ get name(): string {
50
+ return name;
51
+ },
52
+ get version(): string {
53
+ return version;
54
+ },
55
+ get mapVersion(): string {
56
+ return mapVersion;
57
+ },
58
+ get config(): ModuleSelectorConfig {
59
+ return { ...getDefaultOptions(), ...configInput } as ModuleSelectorConfig;
60
+ },
61
+ selectedMainModuleIndex: ref(undefined),
62
+ selectedNestedModuleIndex: ref(undefined),
63
+ initialize(vcsUiApp: VcsUiApp): Promise<void> {
64
+ const moduleSelectorComponent = {
65
+ id: windowIdModuleSelector,
66
+ component: moduleSelector,
67
+ slot: WindowSlot.DETACHED,
68
+ position: this.config.position,
69
+ state: {
70
+ headerTitle: this.config.windowTitle,
71
+ },
72
+ props: {
73
+ modules: this.config.modules,
74
+ basisModule: this.config.basisModule,
75
+ requireModuleSelection: this.config.requireModuleSelection,
76
+ },
77
+ };
78
+
79
+ const { action } = createToggleAction(
80
+ {
81
+ name: 'moduleSelector',
82
+ title: 'moduleSelector.title',
83
+ icon: 'mdi-view-grid',
84
+ },
85
+ moduleSelectorComponent,
86
+ vcsUiApp.windowManager,
87
+ name,
88
+ );
89
+
90
+ vcsUiApp.navbarManager.add(
91
+ {
92
+ id: action.name,
93
+ action,
94
+ },
95
+ name,
96
+ ButtonLocation.PROJECT,
97
+ );
98
+
99
+ if (configInput.isActiveOnStart) {
100
+ vcsUiApp.windowManager.add(moduleSelectorComponent, name);
101
+ }
102
+
103
+ return Promise.resolve();
104
+ },
105
+ getDefaultOptions,
106
+ toJSON(): Partial<ModuleSelectorConfig> {
107
+ const serial: Partial<ModuleSelectorConfig> = {};
108
+ if (configInput.windowTitle != null) {
109
+ serial.windowTitle = configInput.windowTitle;
110
+ }
111
+
112
+ if (configInput.position != null) {
113
+ serial.position = configInput.position;
114
+ }
115
+
116
+ if (configInput.modules != null) {
117
+ serial.modules = configInput.modules;
118
+ }
119
+ return serial;
120
+ },
121
+ i18n: {
122
+ de: {
123
+ moduleSelector: {
124
+ title: 'Themenkartenauswahl',
125
+ startButton: 'Anwendung starten',
126
+ cardBack: 'Zurück',
127
+ },
128
+ },
129
+ en: {
130
+ moduleSelector: {
131
+ title: 'Theme map selection',
132
+ startButton: 'Start application',
133
+ cardBack: 'Back',
134
+ },
135
+ },
136
+ },
137
+ destroy(): void {},
138
+ };
139
+ }
@@ -0,0 +1,31 @@
1
+ // eslint-disable-next-line import/prefer-default-export
2
+ import { VcsModule } from '@vcmap/core';
3
+ import { VcsUiApp } from '@vcmap/ui';
4
+
5
+ export async function loadModule(
6
+ moduleUrl: string,
7
+ app: VcsUiApp,
8
+ ): Promise<void> {
9
+ const config = await fetch(moduleUrl, {
10
+ method: 'GET',
11
+ }).then((response) => {
12
+ if (response.ok) {
13
+ return response.json();
14
+ }
15
+ return undefined;
16
+ });
17
+
18
+ if (config) {
19
+ config._id = `moduleSelector_module_id`;
20
+ const module = new VcsModule(config);
21
+ await app.addModule(module);
22
+ }
23
+ }
24
+
25
+ export async function unloadModule(app: VcsUiApp): Promise<void> {
26
+ const moduleId = `moduleSelector_module_id`;
27
+ const module = app.getModuleById(moduleId);
28
+ if (module) {
29
+ await app.removeModule(moduleId);
30
+ }
31
+ }