@vcmap/module-selector 2.0.0 → 2.0.1

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.
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <v-container class="py-0 px-1">
3
+ <v-row
4
+ no-gutters
5
+ v-for="{ key, checkbox, input, placeholder } in inputs"
6
+ :key="key"
7
+ >
8
+ <v-col>
9
+ <VcsCheckbox
10
+ :label="`appConfigurator.editors.featureInfo.window.position.${key}`"
11
+ v-model="checkbox.value"
12
+ />
13
+ </v-col>
14
+ <v-col>
15
+ <VcsTextField
16
+ clearable
17
+ :disabled="!checkbox.value"
18
+ :placeholder="placeholder"
19
+ v-model.trim="input.value"
20
+ />
21
+ </v-col>
22
+ </v-row>
23
+ </v-container>
24
+ </template>
25
+ <script lang="ts">
26
+ import {
27
+ computed,
28
+ defineComponent,
29
+ PropType,
30
+ Ref,
31
+ WritableComputedRef,
32
+ } from 'vue';
33
+ import { VContainer, VRow, VCol } from 'vuetify/components';
34
+ import {
35
+ useModelHasProperty,
36
+ useProxiedComplexModel,
37
+ VcsCheckbox,
38
+ VcsTextField,
39
+ WindowPositionOptions,
40
+ } from '@vcmap/ui';
41
+
42
+ export default defineComponent({
43
+ name: 'WindowPositionSettings',
44
+ components: {
45
+ VContainer,
46
+ VRow,
47
+ VCol,
48
+ VcsCheckbox,
49
+ VcsTextField,
50
+ },
51
+ props: {
52
+ modelValue: {
53
+ type: Object as PropType<WindowPositionOptions>,
54
+ required: true,
55
+ },
56
+ positionKeys: {
57
+ type: Array as PropType<(keyof WindowPositionOptions)[]>,
58
+ default: () => ['height', 'maxHeight', 'width', 'maxWidth'],
59
+ },
60
+ },
61
+ setup(props, { emit }) {
62
+ const localValue: Ref<WindowPositionOptions> = useProxiedComplexModel(
63
+ props,
64
+ 'modelValue',
65
+ emit,
66
+ );
67
+
68
+ const inputs = computed(() => {
69
+ return props.positionKeys.reduce(
70
+ (acc, key) => {
71
+ acc[key] = {
72
+ key,
73
+ checkbox: useModelHasProperty(localValue, key, null),
74
+ input: computed({
75
+ get() {
76
+ return localValue.value[key];
77
+ },
78
+ set(value) {
79
+ localValue.value[key] = value;
80
+ },
81
+ }),
82
+ placeholder: 'auto',
83
+ };
84
+ return acc;
85
+ },
86
+ {} as Record<
87
+ keyof WindowPositionOptions,
88
+ {
89
+ key: keyof WindowPositionOptions;
90
+ checkbox: WritableComputedRef<boolean>;
91
+ input: WritableComputedRef<string | number | undefined>;
92
+ placeholder: string | null | undefined;
93
+ }
94
+ >,
95
+ );
96
+ });
97
+
98
+ return {
99
+ inputs,
100
+ };
101
+ },
102
+ });
103
+ </script>
104
+
105
+ <style scoped></style>
@@ -2,10 +2,13 @@ import { ModuleSelectorConfig } from './index.js';
2
2
 
3
3
  export default (): ModuleSelectorConfig => ({
4
4
  windowTitle: 'moduleSelector.title',
5
+ isActiveOnStart: true,
6
+ requireModuleSelection: false,
5
7
  position: {
6
8
  left: '33%',
7
9
  right: '33%',
8
10
  top: '10%',
9
11
  },
12
+ basisModule: undefined,
10
13
  modules: [],
11
14
  });
package/src/index.ts CHANGED
@@ -1,15 +1,20 @@
1
1
  import {
2
+ ButtonLocation,
3
+ createToggleAction,
4
+ PluginConfigEditor,
2
5
  VcsPlugin,
3
6
  VcsUiApp,
4
- WindowSlot,
5
- ButtonLocation,
6
7
  WindowPositionOptions,
7
- createToggleAction,
8
+ WindowSlot,
8
9
  } from '@vcmap/ui';
9
10
  import { Ref, ref } from 'vue';
10
- import { name, version, mapVersion } from '../package.json';
11
- import moduleSelector, { windowIdModuleSelector } from './ModuleSelector.vue';
11
+ import { parseBoolean, parseInteger } from '@vcsuite/parsers';
12
+ import equal from 'fast-deep-equal';
13
+ import { mapVersion, name, version } from '../package.json';
14
+ import moduleSelector from './ModuleSelector.vue';
12
15
  import getDefaultOptions from './defaultOptions.js';
16
+ import ModuleSelectorConfigEditor from './config/ModuleSelectorConfigEditor.vue';
17
+ import { startApplication, windowIdModuleSelector } from './moduleHelper';
13
18
 
14
19
  export type BasisModule = {
15
20
  title: string;
@@ -24,19 +29,29 @@ export type Module<T extends ModuleType> = T extends 'group'
24
29
  ? BasisModule & { moduleUrl: string; type: T }
25
30
  : never;
26
31
 
27
- type PluginConfig = Record<never, never>;
28
- type PluginState = Record<never, never>;
29
-
30
- export type ModuleSelectorConfig = PluginConfig & {
32
+ export type ModuleSelectorConfig = {
31
33
  windowTitle?: string;
32
34
  isActiveOnStart?: boolean;
33
35
  requireModuleSelection?: boolean;
34
36
  position?: WindowPositionOptions;
35
37
  basisModule?: BasisModule;
36
- modules: Array<Module<ModuleType>>;
38
+ modules?: Array<Module<ModuleType>>;
37
39
  };
38
40
 
39
- export type ModuleSelectorPlugin = VcsPlugin<PluginConfig, PluginState> & {
41
+ export type PluginState = {
42
+ // selectedMainModuleIndex
43
+ smi?: number | undefined | null;
44
+ // selectedNestedModuleIndex
45
+ snmi?: number | undefined | null;
46
+ // module elector window open
47
+ w?: boolean;
48
+ modules?: Array<Module<ModuleType>>;
49
+ };
50
+
51
+ export type ModuleSelectorPlugin = VcsPlugin<
52
+ Partial<ModuleSelectorConfig>,
53
+ PluginState
54
+ > & {
40
55
  readonly config: ModuleSelectorConfig;
41
56
  selectedMainModuleIndex: Ref<number | undefined | null>;
42
57
  selectedNestedModuleIndex: Ref<number | undefined | null>;
@@ -45,6 +60,7 @@ export type ModuleSelectorPlugin = VcsPlugin<PluginConfig, PluginState> & {
45
60
  export default function plugin(
46
61
  configInput: ModuleSelectorConfig,
47
62
  ): ModuleSelectorPlugin {
63
+ let app: VcsUiApp;
48
64
  return {
49
65
  get name(): string {
50
66
  return name;
@@ -60,7 +76,11 @@ export default function plugin(
60
76
  },
61
77
  selectedMainModuleIndex: ref(undefined),
62
78
  selectedNestedModuleIndex: ref(undefined),
63
- initialize(vcsUiApp: VcsUiApp): Promise<void> {
79
+ async initialize(
80
+ vcsUiApp: VcsUiApp,
81
+ state: PluginState | undefined,
82
+ ): Promise<void> {
83
+ app = vcsUiApp;
64
84
  const moduleSelectorComponent = {
65
85
  id: windowIdModuleSelector,
66
86
  component: moduleSelector,
@@ -68,6 +88,7 @@ export default function plugin(
68
88
  position: this.config.position,
69
89
  state: {
70
90
  headerTitle: this.config.windowTitle,
91
+ infoUrlCallback: app.getHelpUrlCallback('/tools/moduleSelector.html'),
71
92
  },
72
93
  props: {
73
94
  modules: this.config.modules,
@@ -75,6 +96,35 @@ export default function plugin(
75
96
  requireModuleSelection: this.config.requireModuleSelection,
76
97
  },
77
98
  };
99
+ if (state) {
100
+ const smi = parseInteger(state.smi);
101
+ const snmi = parseInteger(state.snmi);
102
+ const w = parseBoolean(state.w);
103
+ const { modules } = this.config;
104
+
105
+ if (smi !== undefined && smi < modules?.length) {
106
+ this.selectedMainModuleIndex.value = smi;
107
+ const selectedModule = modules[smi];
108
+
109
+ if (
110
+ snmi !== undefined &&
111
+ selectedModule?.type === 'group' &&
112
+ snmi < selectedModule?.cards?.length
113
+ ) {
114
+ this.selectedNestedModuleIndex.value = snmi;
115
+ }
116
+ }
117
+
118
+ if (!w) {
119
+ await startApplication(
120
+ this.selectedMainModuleIndex.value,
121
+ this.selectedNestedModuleIndex.value,
122
+ app,
123
+ configInput.modules!,
124
+ configInput.basisModule,
125
+ );
126
+ }
127
+ }
78
128
 
79
129
  const { action } = createToggleAction(
80
130
  {
@@ -83,11 +133,11 @@ export default function plugin(
83
133
  icon: 'mdi-view-grid',
84
134
  },
85
135
  moduleSelectorComponent,
86
- vcsUiApp.windowManager,
136
+ app.windowManager,
87
137
  name,
88
138
  );
89
139
 
90
- vcsUiApp.navbarManager.add(
140
+ app.navbarManager.add(
91
141
  {
92
142
  id: action.name,
93
143
  action,
@@ -96,41 +146,132 @@ export default function plugin(
96
146
  ButtonLocation.PROJECT,
97
147
  );
98
148
 
99
- if (configInput.isActiveOnStart) {
100
- vcsUiApp.windowManager.add(moduleSelectorComponent, name);
149
+ if ((state && state.w) || (!state && configInput.isActiveOnStart)) {
150
+ app.windowManager.add(moduleSelectorComponent, name);
101
151
  }
102
152
 
103
153
  return Promise.resolve();
104
154
  },
105
155
  getDefaultOptions,
156
+ getState(): PluginState {
157
+ const smi = this.selectedMainModuleIndex?.value;
158
+ const snmi = this.selectedNestedModuleIndex?.value;
159
+ const windowState = app.windowManager.has(windowIdModuleSelector);
160
+ const isActive = this.config.isActiveOnStart;
161
+
162
+ return smi !== undefined || snmi !== undefined || windowState !== isActive
163
+ ? { smi, snmi, w: windowState }
164
+ : {};
165
+ },
166
+ getConfigEditors(): PluginConfigEditor<object>[] {
167
+ return [
168
+ {
169
+ component: ModuleSelectorConfigEditor,
170
+ title: 'moduleSelector.name',
171
+ infoUrlCallback: app.getHelpUrlCallback(
172
+ '/components/plugins/moduleSelectorConfig.html',
173
+ 'app-configurator',
174
+ ),
175
+ },
176
+ ];
177
+ },
106
178
  toJSON(): Partial<ModuleSelectorConfig> {
107
179
  const serial: Partial<ModuleSelectorConfig> = {};
108
- if (configInput.windowTitle != null) {
109
- serial.windowTitle = configInput.windowTitle;
180
+ const defaultOptions = getDefaultOptions();
181
+ if (this.config.windowTitle !== defaultOptions.windowTitle) {
182
+ serial.windowTitle = this.config.windowTitle;
183
+ }
184
+
185
+ if (this.config.isActiveOnStart !== defaultOptions.isActiveOnStart) {
186
+ serial.isActiveOnStart = this.config.isActiveOnStart;
187
+ }
188
+
189
+ if (
190
+ this.config.requireModuleSelection !==
191
+ defaultOptions.requireModuleSelection
192
+ ) {
193
+ serial.requireModuleSelection = this.config.requireModuleSelection;
110
194
  }
111
195
 
112
- if (configInput.position != null) {
113
- serial.position = configInput.position;
196
+ if (!equal(this.config.position, defaultOptions.position)) {
197
+ serial.position = this.config.position;
114
198
  }
115
199
 
116
- if (configInput.modules != null) {
117
- serial.modules = configInput.modules;
200
+ if (!equal(this.config.modules, defaultOptions.modules)) {
201
+ serial.modules = this.config.modules;
118
202
  }
203
+
204
+ if (!equal(this.config.basisModule, defaultOptions.basisModule)) {
205
+ serial.basisModule = this.config.basisModule;
206
+ }
207
+
119
208
  return serial;
120
209
  },
121
210
  i18n: {
122
211
  de: {
123
212
  moduleSelector: {
213
+ name: 'Modul Auswahl Editor',
124
214
  title: 'Themenkartenauswahl',
125
215
  startButton: 'Anwendung starten',
126
216
  cardBack: 'Zurück',
217
+ configEditor: {
218
+ TooltipAddModule: 'Modul hinzufügen',
219
+ TooltipEditModule: 'Modul bearbeiten',
220
+ TooltipEditGroup: 'Modul-Gruppe bearbeiten',
221
+ TooltipDeleteMG: 'Modul/Gruppe löschen',
222
+ error: 'Bitte tragen Sie einen Namen ein',
223
+ editorError: 'Bitte füllen Sie das Feld aus',
224
+ breadcrumbs: { overview: 'Übersicht', group: 'Gruppe' },
225
+ heading: 'Themen- & Modulzuordnung',
226
+ baseModule: 'Basismodul',
227
+ baseModuleCheckbox: 'Basismodul anzeigen',
228
+ title: 'Titel',
229
+ add: 'Module hinzufügen',
230
+ addGroup: 'Module Gruppe hinzufügen',
231
+ moduleName: 'Module Name',
232
+ moduleIcon: 'Icon Name',
233
+ moduleUrl: 'Module URL',
234
+ groupName: 'Gruppen Name',
235
+ back: 'Zurück',
236
+ general: 'Allgemeine Einstellungen',
237
+ isActiveOnStart: 'Themenkartenauswahl beim Start der Karte zeigen',
238
+ requireModuleSelection: 'Modulauswahl erforderlich machen',
239
+ windowTitle: 'Fenster Titel',
240
+ windowPosition: 'Fenster Position',
241
+ },
127
242
  },
128
243
  },
129
244
  en: {
130
245
  moduleSelector: {
246
+ name: 'Module Selector Editor',
131
247
  title: 'Theme map selection',
132
248
  startButton: 'Start application',
133
249
  cardBack: 'Back',
250
+ configEditor: {
251
+ TooltipAddModule: 'Add module',
252
+ TooltipEditModule: 'Edit module',
253
+ TooltipEditGroup: 'Edit module group',
254
+ TooltipDeleteMG: 'Delete module/group',
255
+ error: 'Please add a name',
256
+ editorError: 'Please fill in the field',
257
+ breadcrumbs: { overview: 'Overview', group: 'Group' },
258
+ heading: 'Topic & module assignment',
259
+ baseModule: 'Base module',
260
+ baseModuleCheckbox: 'Show base module',
261
+ title: 'Title',
262
+ add: 'Add module',
263
+ addGroup: 'Add module Group',
264
+ moduleName: 'Module name',
265
+ moduleIcon: 'Icon name',
266
+ moduleUrl: 'Module URL',
267
+ groupName: 'Group name',
268
+ back: 'Back',
269
+ general: 'General settings',
270
+ isActiveOnStart: 'Show theme map selection on map start',
271
+ requireModuleSelection: 'Require module selection',
272
+ windowTitle: 'Window title',
273
+ windowPosition: 'Window position',
274
+ },
134
275
  },
135
276
  },
136
277
  },
@@ -1,6 +1,7 @@
1
1
  // eslint-disable-next-line import/prefer-default-export
2
2
  import { VcsModule } from '@vcmap/core';
3
3
  import { VcsUiApp } from '@vcmap/ui';
4
+ import type { BasisModule, Module, ModuleType } from './index';
4
5
 
5
6
  export async function loadModule(
6
7
  moduleUrl: string,
@@ -33,3 +34,33 @@ export async function unloadModule(app: VcsUiApp): Promise<void> {
33
34
  await app.maps.setActiveMap(activeMapName || [...app.maps][0]?.name);
34
35
  }
35
36
  }
37
+
38
+ export const windowIdModuleSelector = 'moduleSelector_window_id';
39
+
40
+ export async function startApplication(
41
+ mainModuleIndex: number | undefined | null,
42
+ nestedModuleIndex: number | undefined | null,
43
+ app: VcsUiApp,
44
+ modules: Module<ModuleType>[],
45
+ basisModule?: BasisModule,
46
+ ): Promise<void> {
47
+ await unloadModule(app);
48
+
49
+ if (mainModuleIndex === null && !basisModule) {
50
+ app.windowManager.remove(windowIdModuleSelector);
51
+ return;
52
+ }
53
+
54
+ const mainModule = modules[mainModuleIndex!];
55
+ const nestedModule =
56
+ mainModule?.type === 'group'
57
+ ? mainModule.cards?.[nestedModuleIndex!]
58
+ : null;
59
+
60
+ if (nestedModule?.moduleUrl) {
61
+ await loadModule(nestedModule.moduleUrl, app);
62
+ } else if (mainModule?.type === 'url') {
63
+ await loadModule(mainModule.moduleUrl, app);
64
+ }
65
+ app.windowManager.remove(windowIdModuleSelector);
66
+ }