@vcmap/ui 6.1.0-rc.2 → 6.1.0-rc.4

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.
Files changed (105) hide show
  1. package/config/base.config.json +6 -0
  2. package/config/clipping.config.json +384 -0
  3. package/config/cluster.config.json +106 -0
  4. package/config/concepts-show-case.config.json +4 -0
  5. package/config/projects.config.json +5 -2
  6. package/dist/assets/{cesium-57fbd309.js → cesium-bfb31a03.js} +438 -432
  7. package/dist/assets/cesium.js +1 -1
  8. package/dist/assets/{core-fd079400.js → core-28960288.js} +4913 -4516
  9. package/dist/assets/core.js +1 -1
  10. package/dist/assets/{ol-50dfef96.js → ol-338a87a3.js} +23518 -22404
  11. package/dist/assets/ol.js +1 -1
  12. package/dist/assets/ui-4ae4c67a.css +1 -0
  13. package/dist/assets/{ui-5135917c.js → ui-4ae4c67a.js} +13456 -12758
  14. package/dist/assets/ui.js +1 -1
  15. package/dist/assets/vue.js +1 -1
  16. package/dist/assets/{vuetify-f02b7bb9.css → vuetify-1f5b5c90.css} +2 -2
  17. package/dist/assets/{vuetify-f02b7bb9.js → vuetify-1f5b5c90.js} +8024 -7634
  18. package/dist/assets/vuetify.js +1 -1
  19. package/index.d.ts +38 -19
  20. package/index.js +35 -6
  21. package/lib/olLib.js +25 -3
  22. package/package.json +6 -6
  23. package/plugins/@vcmap-show-case/callback-tester/README.md +3 -0
  24. package/plugins/@vcmap-show-case/callback-tester/package.json +5 -0
  25. package/plugins/@vcmap-show-case/callback-tester/src/CallbackTester.vue +62 -0
  26. package/plugins/@vcmap-show-case/callback-tester/src/index.js +48 -0
  27. package/plugins/@vcmap-show-case/form-inputs-example/src/FormInputsExample.vue +1 -0
  28. package/src/actions/actionHelper.d.ts +1 -0
  29. package/src/actions/actionHelper.js +70 -19
  30. package/src/application/VcsApp.vue +83 -50
  31. package/src/application/VcsApp.vue.d.ts +24 -2
  32. package/src/application/VcsContainer.vue.d.ts +8 -0
  33. package/src/application/VcsObliqueFooter.vue +9 -3
  34. package/src/application/VcsSplashScreen.vue +37 -0
  35. package/src/application/VcsSplashScreen.vue.d.ts +6 -0
  36. package/src/application/positionDisplayInteraction.js +1 -1
  37. package/src/callback/activateClippingPolygonCallback.d.ts +29 -0
  38. package/src/callback/activateClippingPolygonCallback.js +54 -0
  39. package/src/callback/closeSplashScreenCallback.d.ts +8 -0
  40. package/src/callback/closeSplashScreenCallback.js +33 -0
  41. package/src/callback/deactivateClippingPolygonCallback.d.ts +29 -0
  42. package/src/callback/deactivateClippingPolygonCallback.js +54 -0
  43. package/src/callback/openSplashScreenCallback.d.ts +8 -0
  44. package/src/callback/openSplashScreenCallback.js +35 -0
  45. package/src/callback/toggleNavbarButtonCallback.d.ts +36 -0
  46. package/src/callback/toggleNavbarButtonCallback.js +62 -0
  47. package/src/components/form-inputs-controls/VcsSelect.vue +1 -1
  48. package/src/components/form-inputs-controls/VcsTextArea.vue +12 -7
  49. package/src/components/form-output/markdownHelper.d.ts +0 -25
  50. package/src/components/form-output/markdownHelper.js +1 -386
  51. package/src/components/lists/VcsGroupedList.vue +178 -0
  52. package/src/components/lists/VcsGroupedList.vue.d.ts +17 -0
  53. package/src/components/lists/VcsList.vue +142 -396
  54. package/src/components/lists/VcsList.vue.d.ts +38 -168
  55. package/src/components/lists/VcsTreeview.vue +11 -12
  56. package/src/components/lists/listHelper.d.ts +87 -0
  57. package/src/components/lists/listHelper.js +348 -0
  58. package/src/components/section/VcsFormSection.vue +7 -2
  59. package/src/components/section/VcsFormSection.vue.d.ts +9 -0
  60. package/src/components/vector-properties/VcsVectorPropertiesComponent.vue.d.ts +1 -1
  61. package/src/contentTree/LayerTree.vue +2 -1
  62. package/src/contentTree/LayerTree.vue.d.ts +2 -0
  63. package/src/contentTree/contentTreeCollection.d.ts +1 -0
  64. package/src/contentTree/contentTreeCollection.js +7 -3
  65. package/src/contentTree/contentTreeItem.js +4 -2
  66. package/src/contentTree/groupContentTreeItem.js +5 -3
  67. package/src/featureInfo/ClusterFeatureComponent.vue +58 -0
  68. package/src/featureInfo/ClusterFeatureComponent.vue.d.ts +6 -0
  69. package/src/featureInfo/abstractFeatureInfoView.js +1 -2
  70. package/src/featureInfo/featureInfo.d.ts +87 -1
  71. package/src/featureInfo/featureInfo.js +350 -34
  72. package/src/featureInfo/featureInfoInteraction.js +18 -3
  73. package/src/featureInfo/iframeFeatureInfoView.js +1 -1
  74. package/src/featureInfo/markdownBalloonFeatureInfoView.js +2 -4
  75. package/src/featureInfo/markdownFeatureInfoView.js +1 -1
  76. package/src/i18n/de.d.ts +17 -4
  77. package/src/i18n/de.js +7 -0
  78. package/src/i18n/en.d.ts +17 -4
  79. package/src/i18n/en.js +7 -0
  80. package/src/legend/legendHelper.d.ts +1 -1
  81. package/src/legend/legendHelper.js +52 -9
  82. package/src/localStorage.d.ts +21 -0
  83. package/src/localStorage.js +51 -0
  84. package/src/manager/collectionManager/CollectionComponent.vue +1 -1
  85. package/src/manager/collectionManager/CollectionComponentContent.vue +2 -3
  86. package/src/manager/collectionManager/CollectionComponentList.vue +2 -3
  87. package/src/manager/collectionManager/CollectionComponentStandalone.vue +1 -1
  88. package/src/manager/navbarManager.js +9 -4
  89. package/src/manager/toolbox/ToolboxManagerComponent.vue +10 -8
  90. package/src/manager/toolbox/ToolboxManagerComponent.vue.d.ts +11 -0
  91. package/src/manager/window/windowHelper.d.ts +7 -3
  92. package/src/manager/window/windowHelper.js +30 -10
  93. package/src/navigation/overviewMap.d.ts +1 -0
  94. package/src/navigation/overviewMap.js +4 -3
  95. package/src/pluginHelper.d.ts +7 -0
  96. package/src/pluginHelper.js +18 -4
  97. package/src/search/ResultItem.vue.d.ts +1 -1
  98. package/src/search/search.js +1 -1
  99. package/src/state.d.ts +4 -2
  100. package/src/state.js +54 -31
  101. package/src/uiConfig.d.ts +27 -0
  102. package/src/uiConfig.js +16 -1
  103. package/src/vcsUiApp.js +7 -11
  104. package/dist/assets/ui-5135917c.css +0 -1
  105. /package/dist/assets/{vue-c3c55d88.js → vue-b5c1e81a.js} +0 -0
@@ -8,12 +8,12 @@ import {
8
8
  Viewpoint,
9
9
  } from '@vcmap/core';
10
10
  import { Feature } from 'ol';
11
- import { reactive, ref } from 'vue';
11
+ import { nextTick, reactive, ref } from 'vue';
12
12
  import { parseBoolean } from '@vcsuite/parsers';
13
13
  import { vcsAppSymbol } from '../pluginHelper.js';
14
14
  import { WindowSlot } from '../manager/window/windowManager.js';
15
15
  import {
16
- getFittedWindowPositionOptionsFromMapEvent,
16
+ getFittedWindowPositionOptions,
17
17
  getTargetSize,
18
18
  } from '../manager/window/windowHelper.js';
19
19
  import SearchComponent from '../search/SearchComponent.vue';
@@ -142,6 +142,8 @@ export function createToggleAction(
142
142
  return { action, destroy };
143
143
  }
144
144
 
145
+ export const searchComponentId = 'searchId';
146
+
145
147
  /**
146
148
  * Creates a toggle button for the search tool, which is only available, if at least one search implementation is registered.
147
149
  * @param {import("../vcsUiApp.js").default} app
@@ -153,8 +155,8 @@ export function createSearchButtonAction(app) {
153
155
  const uiConfig = app.uiConfig.config;
154
156
 
155
157
  const determineAction = () => {
156
- if (app.windowManager.has('searchId')) {
157
- app.windowManager.remove('searchId');
158
+ if (app.windowManager.has(searchComponentId)) {
159
+ app.windowManager.remove(searchComponentId);
158
160
  }
159
161
  if (
160
162
  !uiConfig.hideSearch &&
@@ -168,7 +170,7 @@ export function createSearchButtonAction(app) {
168
170
  title: 'search.tooltip',
169
171
  },
170
172
  {
171
- id: 'searchId',
173
+ id: searchComponentId,
172
174
  component: SearchComponent,
173
175
  state: { hideHeader: true },
174
176
  slot: WindowSlot.DYNAMIC_RIGHT,
@@ -272,6 +274,7 @@ export function createModalAction(actionOptions, modalComponent, app, owner) {
272
274
  const id = uuid();
273
275
  const { position: windowPositionOptions, ...component } = modalComponent;
274
276
  let modalActivator = null;
277
+ let clickedWindowPosition = null;
275
278
 
276
279
  /**
277
280
  * Closes the modal at mousedown on an app element
@@ -285,28 +288,58 @@ export function createModalAction(actionOptions, modalComponent, app, owner) {
285
288
  }
286
289
  };
287
290
 
291
+ const getPositionOptions = (contentHeight = 0) => {
292
+ const { width, height } = modalActivator.getBoundingClientRect();
293
+ const fittedPosition = getFittedWindowPositionOptions(
294
+ clickedWindowPosition.x,
295
+ clickedWindowPosition.y,
296
+ windowPositionOptions?.width || 320,
297
+ windowPositionOptions?.height || contentHeight,
298
+ app.maps.target,
299
+ width,
300
+ height,
301
+ );
302
+ const position = {
303
+ ...fittedPosition,
304
+ ...windowPositionOptions,
305
+ };
306
+ const targetSize = getTargetSize(app.maps.target);
307
+ if (contentHeight) {
308
+ if (position.bottom) {
309
+ position.maxHeight = Math.min(
310
+ position.maxHeight ?? Infinity,
311
+ clickedWindowPosition.y - targetSize.top,
312
+ );
313
+ } else {
314
+ position.maxHeight = Math.min(
315
+ position.maxHeight ?? Infinity,
316
+ targetSize.height - (clickedWindowPosition.y - targetSize.top),
317
+ );
318
+ }
319
+ }
320
+ position.maxWidth = 320;
321
+ position.width = windowPositionOptions?.width || -1; // unset width magic. dont touch.
322
+ return position;
323
+ };
324
+
288
325
  const action = reactive({
289
326
  ...actionOptions,
290
327
  active: false,
291
328
  callback(event) {
292
329
  if (!this.active) {
293
330
  this.active = true;
294
- const { left, top, width } =
295
- event.currentTarget.getBoundingClientRect();
296
331
  modalActivator = event.currentTarget;
297
- const position = {
298
- ...getFittedWindowPositionOptionsFromMapEvent(
299
- { x: left + width, y: top - getTargetSize(app.maps.target).top },
300
- windowPositionOptions?.width || 320,
301
- windowPositionOptions?.height || 0,
302
- app.maps.target,
303
- ),
304
- ...windowPositionOptions,
305
- };
306
- position.maxWidth = 320;
307
- position.width = windowPositionOptions?.width || -1; // unset width magic. dont touch.
332
+ clickedWindowPosition = { x: event.x, y: event.y };
308
333
  const state = { ...modalComponent?.state, hideHeader: true };
309
- app.windowManager.add({ position, ...component, id, state }, owner);
334
+ app.windowManager.add(
335
+ {
336
+ position: getPositionOptions(),
337
+ ...component,
338
+ id,
339
+ state,
340
+ },
341
+ owner,
342
+ );
310
343
  document.addEventListener('mousedown', handleMouseDown);
311
344
  } else {
312
345
  this.active = false;
@@ -325,6 +358,24 @@ export function createModalAction(actionOptions, modalComponent, app, owner) {
325
358
  }),
326
359
  ];
327
360
 
361
+ // if no height is provided, update fitted window position after actual div size is available
362
+ if (!windowPositionOptions?.height) {
363
+ listeners.push(
364
+ app.windowManager.added.addEventListener(async (added) => {
365
+ if (added.id === id) {
366
+ await nextTick();
367
+ const div = document.getElementById(`window-component--${id}`);
368
+ if (div) {
369
+ app.windowManager.setWindowPositionOptions(
370
+ id,
371
+ getPositionOptions(div.offsetHeight),
372
+ );
373
+ }
374
+ }
375
+ }),
376
+ );
377
+ }
378
+
328
379
  const destroy = () => {
329
380
  listeners.forEach((cb) => {
330
381
  cb();
@@ -3,7 +3,7 @@
3
3
  <VcsSplashScreen
4
4
  v-if="splashScreen"
5
5
  :options="splashScreen"
6
- v-model="splashScreenRef"
6
+ v-model="showSplashScreen"
7
7
  ></VcsSplashScreen>
8
8
  <VcsNavbar v-if="!config.hideHeader" />
9
9
  <VcsContainer :attribution-action="attributionAction" />
@@ -42,7 +42,14 @@
42
42
  </style>
43
43
 
44
44
  <script>
45
- import { computed, onMounted, onUnmounted, provide, watch, ref } from 'vue';
45
+ import {
46
+ computed,
47
+ onMounted,
48
+ onUnmounted,
49
+ provide,
50
+ watch,
51
+ shallowRef,
52
+ } from 'vue';
46
53
  import { useDisplay } from 'vuetify';
47
54
  import { getVcsAppById, moduleIdSymbol } from '@vcmap/core';
48
55
  import { VContainer, VFooter, VSpacer } from 'vuetify/components';
@@ -69,12 +76,14 @@
69
76
  import VcsAttributionsFooter from './VcsAttributionsFooter.vue';
70
77
  import VcsObliqueFooter from './VcsObliqueFooter.vue';
71
78
  import VcsTextPageFooter from './VcsTextPageFooter.vue';
72
- import VcsSplashScreen from './VcsSplashScreen.vue';
79
+ import VcsSplashScreen, { getSplashScreenHash } from './VcsSplashScreen.vue';
73
80
  import VcsTextPage from './VcsTextPage.vue';
74
81
  import VcsAttributions from './VcsAttributions.vue';
75
82
  import { getAttributions } from './attributionsHelper.js';
76
83
  import VcsDefaultLogoMobile from '../logo-mobile.svg';
77
84
  import VcsPositionDisplay from './VcsPositionDisplay.vue';
85
+ import { getFromLocalStorage, hideSplashScreenKey } from '../localStorage.js';
86
+ import { name as packageName } from '../../package.json';
78
87
 
79
88
  /**
80
89
  * This helper checks the uiConfig and depending on the value will setup/teardown the providedSetupFunction
@@ -202,6 +211,8 @@
202
211
  };
203
212
  }
204
213
 
214
+ export const legendComponentId = 'legendId';
215
+
205
216
  /**
206
217
  * This helper function will add a legend action button to the apps NavbarManager TOOL location, if legend entries are available.
207
218
  * Watches number of legend entries.
@@ -218,7 +229,7 @@
218
229
  title: 'legend.tooltip',
219
230
  },
220
231
  {
221
- id: 'legend',
232
+ id: legendComponentId,
222
233
  component: VcsLegend,
223
234
  state: {
224
235
  headerTitle: 'legend.title',
@@ -238,10 +249,10 @@
238
249
  * Adds a legend button, if not existing
239
250
  */
240
251
  const addLegend = () => {
241
- if (!app.navbarManager.has('legend')) {
252
+ if (!app.navbarManager.has(legendComponentId)) {
242
253
  app.navbarManager.add(
243
254
  {
244
- id: 'legend',
255
+ id: legendComponentId,
245
256
  action: legendAction,
246
257
  },
247
258
  vcsAppSymbol,
@@ -255,11 +266,11 @@
255
266
  if (
256
267
  app.uiConfig.config.openLegendOnAdd &&
257
268
  newValue.length > currentEntryLength &&
258
- !app.windowManager.has('legend')
269
+ !app.windowManager.has(legendComponentId)
259
270
  ) {
260
271
  app.windowManager.add(
261
272
  {
262
- id: 'legend',
273
+ id: legendComponentId,
263
274
  component: VcsLegend,
264
275
  state: {
265
276
  headerTitle: 'legend.title',
@@ -289,8 +300,8 @@
289
300
  (style) => style?.properties?.legend,
290
301
  );
291
302
  if (layersWithLegend < 1 && stylesWithLegend < 1) {
292
- app.navbarManager.remove('legend');
293
- app.windowManager.remove('legend');
303
+ app.navbarManager.remove(legendComponentId);
304
+ app.windowManager.remove(legendComponentId);
294
305
  } else {
295
306
  addLegend();
296
307
  }
@@ -314,14 +325,16 @@
314
325
 
315
326
  return () => {
316
327
  watchEntries();
317
- app.navbarManager.remove('legend');
318
- app.windowManager.remove('legend');
328
+ app.navbarManager.remove(legendComponentId);
329
+ app.windowManager.remove(legendComponentId);
319
330
  destroy();
320
331
  legendDestroy();
321
332
  listeners.forEach((cb) => cb());
322
333
  };
323
334
  }
324
335
 
336
+ export const customScreenComponentId = 'customScreenId';
337
+
325
338
  /**
326
339
  * This helper function will add a customScreen action button to the apps NavbarManager MENU location.
327
340
  * @param {import("../vcsUiApp.js").default} app
@@ -338,7 +351,7 @@
338
351
  title: customScreen.title,
339
352
  },
340
353
  {
341
- id: 'customScreenId',
354
+ id: customScreenComponentId,
342
355
  component: VcsTextPage,
343
356
  state: {
344
357
  headerIcon: customScreen.icon,
@@ -355,7 +368,7 @@
355
368
  );
356
369
  app.navbarManager.add(
357
370
  {
358
- id: 'customScreenToggle',
371
+ id: customScreenComponentId,
359
372
  action: customScreenAction,
360
373
  },
361
374
  vcsAppSymbol,
@@ -369,8 +382,8 @@
369
382
  const stopCustomScreenWatcher = watch(
370
383
  () => app.uiConfig.config.customScreen,
371
384
  (newCustomScreen) => {
372
- if (app.navbarManager.has('customScreenToggle')) {
373
- app.navbarManager.remove('customScreenToggle');
385
+ if (app.navbarManager.has(customScreenComponentId)) {
386
+ app.navbarManager.remove(customScreenComponentId);
374
387
  }
375
388
  if (newCustomScreen) {
376
389
  customScreen = setupCustomScreenAction();
@@ -385,17 +398,19 @@
385
398
  stopCustomScreenWatcher();
386
399
  };
387
400
  }
401
+
402
+ export const splashScreenComponentId = 'splashScreenToggle';
388
403
  /**
389
404
  * This helper function will add a Splash Screen action button to the apps NavbarManager MENU location.
390
405
  * @param {import("../vcsUiApp.js").default} app
391
- * @param {import("vue").Ref} splashScreenRef
392
406
  * @returns {WatchStopHandle}
393
407
  */
394
- function setupSplashScreen(app, splashScreenRef) {
408
+ function setupSplashScreen(app) {
395
409
  function setupSplashScreenAction(moduleId) {
396
- const { splashScreen } = app.uiConfig.config;
410
+ const { config, showSplashScreen } = app.uiConfig;
411
+ const { splashScreen } = config;
397
412
  if (splashScreen && moduleId !== app.dynamicModuleId) {
398
- splashScreenRef.value = true;
413
+ showSplashScreen.value = true;
399
414
  }
400
415
  if (splashScreen && splashScreen.menuEntry) {
401
416
  const splashScreenAction = {
@@ -403,12 +418,12 @@
403
418
  icon: splashScreen.icon || 'mdi-alert-box',
404
419
  title: splashScreen.title,
405
420
  callback() {
406
- splashScreenRef.value = !splashScreenRef.value;
421
+ showSplashScreen.value = !showSplashScreen.value;
407
422
  },
408
423
  };
409
424
  app.navbarManager.add(
410
425
  {
411
- id: 'splashScreenToggle',
426
+ id: splashScreenComponentId,
412
427
  action: splashScreenAction,
413
428
  },
414
429
  vcsAppSymbol,
@@ -419,8 +434,8 @@
419
434
  setupSplashScreenAction();
420
435
  const removeAddedListener = app.uiConfig.added.addEventListener((item) => {
421
436
  if (item.name === 'splashScreen') {
422
- if (app.navbarManager.has('splashScreenToggle')) {
423
- app.navbarManager.remove('splashScreenToggle');
437
+ if (app.navbarManager.has(splashScreenComponentId)) {
438
+ app.navbarManager.remove(splashScreenComponentId);
424
439
  }
425
440
  setupSplashScreenAction(item[moduleIdSymbol]);
426
441
  }
@@ -428,8 +443,8 @@
428
443
  const removeRemovedListener = app.uiConfig.removed.addEventListener(
429
444
  (item) => {
430
445
  if (item.name === 'splashScreen') {
431
- if (app.navbarManager.has('splashScreenToggle')) {
432
- app.navbarManager.remove('splashScreenToggle');
446
+ if (app.navbarManager.has(splashScreenComponentId)) {
447
+ app.navbarManager.remove(splashScreenComponentId);
433
448
  }
434
449
  }
435
450
  },
@@ -439,13 +454,14 @@
439
454
  removeRemovedListener();
440
455
  };
441
456
  }
457
+
458
+ export const settingsComponentId = 'vcsSettings';
442
459
  /**
443
460
  * This helper function will add a settings action button to the apps NavbarManager MENU location.
444
461
  * @param {import("../vcsUiApp.js").default} app
445
462
  * @returns {function():void}
446
463
  */
447
464
  export function setupSettingsWindow(app) {
448
- const settingsWindowId = 'vcsSettings';
449
465
  const { action: settingsAction, destroy: settingsDestroy } =
450
466
  createToggleAction(
451
467
  {
@@ -454,7 +470,7 @@
454
470
  title: 'settings.tooltip',
455
471
  },
456
472
  {
457
- id: settingsWindowId,
473
+ id: settingsComponentId,
458
474
  component: VcsSettings,
459
475
  state: { headerIcon: 'mdi-cog', headerTitle: 'settings.title' },
460
476
  slot: WindowSlot.DYNAMIC_RIGHT,
@@ -464,19 +480,20 @@
464
480
  );
465
481
  app.navbarManager.add(
466
482
  {
467
- id: settingsWindowId,
483
+ id: settingsComponentId,
468
484
  action: settingsAction,
469
485
  },
470
486
  vcsAppSymbol,
471
487
  ButtonLocation.MENU,
472
488
  );
473
489
  return () => {
474
- app.navbarManager.remove(settingsWindowId);
475
- app.windowManager.remove(settingsWindowId);
490
+ app.navbarManager.remove(settingsComponentId);
491
+ app.windowManager.remove(settingsComponentId);
476
492
  settingsDestroy();
477
493
  };
478
494
  }
479
495
 
496
+ export const helpComponentId = 'helpButton';
480
497
  /**
481
498
  * This helper function will add a help action button referencing VC Map help page to the apps NavbarManager MENU location.
482
499
  * @param {import("../vcsUiApp.js").default} app
@@ -492,7 +509,7 @@
492
509
  );
493
510
  app.navbarManager.add(
494
511
  {
495
- id: 'helpButton',
512
+ id: helpComponentId,
496
513
  action: helpAction,
497
514
  },
498
515
  vcsAppSymbol,
@@ -674,6 +691,8 @@
674
691
  };
675
692
  }
676
693
 
694
+ export const attributionsComponentId = 'attributionId';
695
+
677
696
  /**
678
697
  * This helper gets attributions of all active maps, layers and oblique collections and returns an array of entries.
679
698
  * It also returns a attributionAction to toggle the attributions window and a destroy function.
@@ -691,7 +710,7 @@
691
710
  title: 'footer.attributions.tooltip',
692
711
  },
693
712
  {
694
- id: 'attribution',
713
+ id: attributionsComponentId,
695
714
  component: VcsAttributions,
696
715
  state: {
697
716
  headerTitle: 'footer.attributions.title',
@@ -758,8 +777,8 @@
758
777
  'hideSettings',
759
778
  );
760
779
  const stopCustomScreen = setupCustomScreen(app);
761
- const splashScreenRef = ref(false);
762
- const stopSplashScreen = setupSplashScreen(app, splashScreenRef);
780
+ const { showSplashScreen } = app.uiConfig;
781
+ const stopSplashScreen = setupSplashScreen(app);
763
782
  setupHelpButton(app);
764
783
  const destroyMyWorkspace = setupUIConfigDependency(
765
784
  app,
@@ -776,6 +795,33 @@
776
795
  pluginMountedListener = setupPluginMountedListeners(app);
777
796
  });
778
797
 
798
+ function getSplashScreenConfig() {
799
+ if (app.uiConfig.config.splashScreen) {
800
+ const config = app.uiConfig.getByKey('splashScreen');
801
+ const hash = getSplashScreenHash(app);
802
+ const moduleId = config[moduleIdSymbol];
803
+ const storedHash = getFromLocalStorage(
804
+ `${packageName}_${moduleId}`,
805
+ hideSplashScreenKey,
806
+ );
807
+ if (hash !== storedHash) {
808
+ return {
809
+ title: 'components.splashScreen.name',
810
+ tooltip: 'components.splashScreen.tooltip',
811
+ position: { width: '800px', height: '400px' },
812
+ ...app.uiConfig.config.splashScreen,
813
+ };
814
+ }
815
+ }
816
+ return undefined;
817
+ }
818
+ const splashScreen = shallowRef(getSplashScreenConfig());
819
+ app.uiConfig.added.addEventListener(({ name }) => {
820
+ if (name === 'splashScreen') {
821
+ splashScreen.value = getSplashScreenConfig();
822
+ }
823
+ });
824
+
779
825
  onUnmounted(() => {
780
826
  if (pluginMountedListener) {
781
827
  pluginMountedListener();
@@ -829,21 +875,8 @@
829
875
  }
830
876
  return undefined;
831
877
  }),
832
- splashScreenRef,
833
- splashScreen: computed(() => {
834
- if (app.uiConfig.config.splashScreen) {
835
- return {
836
- title: 'components.splashScreen.name',
837
- tooltip: 'components.splashScreen.tooltip',
838
- position: {
839
- width: '800px',
840
- height: '400px',
841
- },
842
- ...app.uiConfig.config.splashScreen,
843
- };
844
- }
845
- return undefined;
846
- }),
878
+ showSplashScreen,
879
+ splashScreen,
847
880
  attributionEntries,
848
881
  attributionAction,
849
882
  };
@@ -74,10 +74,16 @@ export function setupAttributions(app: import("../vcsUiApp.js").default): {
74
74
  attributionAction: import("../actions/actionHelper.js").VcsAction;
75
75
  destroyAttributions: () => void;
76
76
  };
77
+ export const legendComponentId: "legendId";
78
+ export const customScreenComponentId: "customScreenId";
79
+ export const splashScreenComponentId: "splashScreenToggle";
80
+ export const settingsComponentId: "vcsSettings";
81
+ export const helpComponentId: "helpButton";
77
82
  /**
78
83
  * @type {string}
79
84
  */
80
85
  export const categoryManagerWindowId: string;
86
+ export const attributionsComponentId: "attributionId";
81
87
  declare const _default: import("vue").DefineComponent<{
82
88
  appId: {
83
89
  type: StringConstructor;
@@ -183,6 +189,14 @@ declare const _default: import("vue").DefineComponent<{
183
189
  }[] | undefined;
184
190
  readonly menuEntry?: boolean | undefined;
185
191
  readonly acceptInput?: boolean | undefined;
192
+ /**
193
+ * Whether the Secondary Button is disabled as well as long as the checkbox is not checked.
194
+ */
195
+ readonly requireInputForSecondary?: boolean | undefined;
196
+ /**
197
+ * Whether to display a checkbox allowing the user not to see the SplashScreen again. This parameter is relative to moduleId and configuration; the SplashScreen will be shown again in case of any change.
198
+ */
199
+ readonly enableDontShowAgain?: boolean | undefined;
186
200
  readonly position?: {
187
201
  readonly width?: string | undefined;
188
202
  readonly height?: string | undefined;
@@ -408,8 +422,8 @@ declare const _default: import("vue").DefineComponent<{
408
422
  content?: string | undefined;
409
423
  tooltip: string;
410
424
  } | undefined>;
411
- splashScreenRef: import("vue").Ref<boolean>;
412
- splashScreen: import("vue").ComputedRef<{
425
+ showSplashScreen: import("vue").Ref<boolean>;
426
+ splashScreen: import("vue").ShallowRef<{
413
427
  title: string;
414
428
  icon?: string | undefined;
415
429
  content?: string | undefined;
@@ -425,6 +439,14 @@ declare const _default: import("vue").DefineComponent<{
425
439
  }[] | undefined;
426
440
  menuEntry?: boolean | undefined;
427
441
  acceptInput?: boolean | undefined;
442
+ /**
443
+ * Whether the Secondary Button is disabled as well as long as the checkbox is not checked.
444
+ */
445
+ requireInputForSecondary?: boolean | undefined;
446
+ /**
447
+ * Whether to display a checkbox allowing the user not to see the SplashScreen again. This parameter is relative to moduleId and configuration; the SplashScreen will be shown again in case of any change.
448
+ */
449
+ enableDontShowAgain?: boolean | undefined;
428
450
  position: {
429
451
  readonly width?: string | undefined;
430
452
  readonly height?: string | undefined;
@@ -103,6 +103,14 @@ declare const _default: import("vue").DefineComponent<{
103
103
  }[] | undefined;
104
104
  readonly menuEntry?: boolean | undefined;
105
105
  readonly acceptInput?: boolean | undefined;
106
+ /**
107
+ * Whether the Secondary Button is disabled as well as long as the checkbox is not checked.
108
+ */
109
+ readonly requireInputForSecondary?: boolean | undefined;
110
+ /**
111
+ * Whether to display a checkbox allowing the user not to see the SplashScreen again. This parameter is relative to moduleId and configuration; the SplashScreen will be shown again in case of any change.
112
+ */
113
+ readonly enableDontShowAgain?: boolean | undefined;
106
114
  readonly position?: {
107
115
  readonly width?: string | undefined;
108
116
  readonly height?: string | undefined;
@@ -17,8 +17,12 @@
17
17
 
18
18
  <script>
19
19
  import { computed, inject, onUnmounted, shallowRef } from 'vue';
20
- import { ObliqueMap, ObliqueViewDirection } from '@vcmap/core';
21
- import { renderTemplate } from '../components/form-output/markdownHelper.js';
20
+ import {
21
+ ObliqueMap,
22
+ ObliqueViewDirection,
23
+ renderTemplate,
24
+ } from '@vcmap/core';
25
+ import { parseAndSanitizeMarkdown } from '../components/form-output/markdownHelper.js';
22
26
 
23
27
  const i18nViewDirection = {
24
28
  [ObliqueViewDirection.NORTH]: 'footer.oblique.north',
@@ -95,7 +99,9 @@
95
99
  app.uiConfig.config.obliqueFooterTemplate ??
96
100
  'footer.oblique.template';
97
101
  return renderTemplate(
98
- app.vueI18n.te(template) ? app.vueI18n.tm(template) : template,
102
+ parseAndSanitizeMarkdown(
103
+ app.vueI18n.te(template) ? app.vueI18n.tm(template) : template,
104
+ ),
99
105
  getTranslatedImageInfo(currentImage.value),
100
106
  );
101
107
  }
@@ -25,6 +25,13 @@
25
25
  />
26
26
  </template>
27
27
  </VcsCheckbox>
28
+ <VcsCheckbox v-if="options.enableDontShowAgain" v-model="dontShowAgain">
29
+ <template #label>
30
+ <div class="pl-2">
31
+ {{ $t('components.splashScreen.dontShowAgain') }}
32
+ </div>
33
+ </template>
34
+ </VcsCheckbox>
28
35
  </v-card-text>
29
36
 
30
37
  <v-card-actions>
@@ -33,6 +40,11 @@
33
40
  v-if="
34
41
  options.secondaryButtonTitle && options.secondaryCallbackOptions
35
42
  "
43
+ :disabled="
44
+ option.requireInputForSecondary &&
45
+ options.acceptInput &&
46
+ !checkBox
47
+ "
36
48
  @click="secondaryButtonClicked"
37
49
  >
38
50
  {{ $st(options.secondaryButtonTitle) }}
@@ -55,11 +67,27 @@
55
67
  <script>
56
68
  import { VDialog, VCard, VCardText, VCardActions } from 'vuetify/components';
57
69
  import { computed, ref, inject } from 'vue';
70
+ import { moduleIdSymbol } from '@vcmap/core';
71
+ import { v5 as uuidv5 } from 'uuid';
72
+ import { hideSplashScreenKey, setToLocalStorage } from '../localStorage.js';
58
73
  import { executeCallbacks } from '../callback/vcsCallback.js';
59
74
  import VcsFormButton from '../components/buttons/VcsFormButton.vue';
60
75
  import VcsCheckbox from '../components/form-inputs-controls/VcsCheckbox.vue';
61
76
  import VcsMarkdown from '../components/form-output/VcsMarkdown.vue';
62
77
  import { useProxiedAtomicModel } from '../components/modelHelper.js';
78
+ import { name } from '../../package.json';
79
+
80
+ /**
81
+ * @param {import("@vcmap/ui").VcsUiApp} app
82
+ * @returns {Promise<string>} The hash of the SplashScreen config.
83
+ */
84
+ export function getSplashScreenHash(app) {
85
+ const config = app.uiConfig.getByKey('splashScreen');
86
+ const string = JSON.stringify(
87
+ Object.entries(config.value).sort((a, b) => a[0].localeCompare(b[0])),
88
+ );
89
+ return uuidv5(string, uuidv5.URL);
90
+ }
63
91
 
64
92
  export default {
65
93
  name: 'VcsSplashScreen',
@@ -87,9 +115,17 @@
87
115
  const localValue = useProxiedAtomicModel(props, 'modelValue', emit);
88
116
 
89
117
  const checkBox = ref(false);
118
+ const dontShowAgain = ref(false);
119
+
90
120
  function exitScreen() {
91
121
  localValue.value = false;
92
122
  checkBox.value = false;
123
+ if (dontShowAgain.value) {
124
+ const config = app.uiConfig.getByKey('splashScreen');
125
+ const hash = getSplashScreenHash(app);
126
+ const moduleId = config[moduleIdSymbol];
127
+ setToLocalStorage(`${name}_${moduleId}`, hideSplashScreenKey, hash);
128
+ }
93
129
  if (Array.isArray(props.options.exitCallbackOptions)) {
94
130
  executeCallbacks(app, props.options.exitCallbackOptions);
95
131
  }
@@ -113,6 +149,7 @@
113
149
  exitScreen,
114
150
  secondaryButtonClicked,
115
151
  checkBox,
152
+ dontShowAgain,
116
153
  position,
117
154
  };
118
155
  },
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @param {import("@vcmap/ui").VcsUiApp} app
3
+ * @returns {Promise<string>} The hash of the SplashScreen config.
4
+ */
5
+ export function getSplashScreenHash(app: import("@vcmap/ui").VcsUiApp): Promise<string>;
1
6
  declare const _default: import("vue").DefineComponent<{
2
7
  modelValue: {
3
8
  type: BooleanConstructor;
@@ -12,6 +17,7 @@ declare const _default: import("vue").DefineComponent<{
12
17
  exitScreen: () => void;
13
18
  secondaryButtonClicked: () => void;
14
19
  checkBox: import("vue").Ref<boolean>;
20
+ dontShowAgain: import("vue").Ref<boolean>;
15
21
  position: import("vue").ComputedRef<{
16
22
  width: any;
17
23
  height: any;