@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
@@ -12,6 +12,12 @@ import {
12
12
  VectorStyleItem,
13
13
  markVolatile,
14
14
  maxZIndex,
15
+ vectorClusterGroupName,
16
+ hidden,
17
+ isProvidedClusterFeature,
18
+ alreadyTransformedToImage,
19
+ ObliqueMap,
20
+ originalFeatureSymbol,
15
21
  } from '@vcmap/core';
16
22
  import { getLogger as getLoggerByName } from '@vcsuite/logger';
17
23
  import {
@@ -24,6 +30,7 @@ import { Feature } from 'ol';
24
30
  import { check, maybe, oneOf } from '@vcsuite/check';
25
31
 
26
32
  import { reactive } from 'vue';
33
+ import { WindowSlot } from '../manager/window/windowManager.js';
27
34
  import { vcsAppSymbol } from '../pluginHelper.js';
28
35
  import FeatureInfoInteraction from './featureInfoInteraction.js';
29
36
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
@@ -36,6 +43,7 @@ import { getDefaultPrimaryColor } from '../vuePlugins/vuetify.js';
36
43
  import { ToolboxType } from '../manager/toolbox/toolboxManager.js';
37
44
  import MarkdownBalloonFeatureInfoView from './markdownBalloonFeatureInfoView.js';
38
45
  import IframeWmsFeatureInfoView from './iframeWmsFeatureInfoView.js';
46
+ import ClusterFeatureComponent from './ClusterFeatureComponent.vue';
39
47
 
40
48
  /** @typedef {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity} FeatureType */
41
49
 
@@ -64,6 +72,43 @@ export const featureInfoClassRegistry = new ClassRegistry();
64
72
  */
65
73
  export const featureInfoViewSymbol = Symbol('featureInfoView');
66
74
 
75
+ /**
76
+ *
77
+ * @param {import("ol/style/Style.js").default?} style
78
+ * @param {import("@vcmap-cesium/engine").Color} fillColor
79
+ * @returns {import("ol/style/Style.js").default}
80
+ */
81
+ export function getHighlightStyleFromStyle(style, fillColor) {
82
+ const highlightStyle =
83
+ style?.clone?.() ??
84
+ new VectorStyleItem(getDefaultVectorStyleItemOptions()).style;
85
+ if (highlightStyle.getText()) {
86
+ if (highlightStyle.getText().getFill()) {
87
+ highlightStyle.getText().getFill().setColor(fillColor.toCssColorString());
88
+ }
89
+ highlightStyle
90
+ .getText()
91
+ .setScale((highlightStyle.getText().getScale() ?? 1) * 2);
92
+ }
93
+ if (highlightStyle.getImage()) {
94
+ highlightStyle
95
+ .getImage()
96
+ .setScale(highlightStyle.getImage().getScale() * 2);
97
+ }
98
+ if (highlightStyle.getStroke()) {
99
+ highlightStyle.getStroke().setColor(fillColor.toCssColorString());
100
+ highlightStyle
101
+ .getStroke()
102
+ .setWidth(highlightStyle.getStroke().getWidth() * 2);
103
+ }
104
+ if (highlightStyle.getFill()) {
105
+ const color = fillColor.toBytes();
106
+ color[3] /= 255;
107
+ highlightStyle.getFill().setColor(color);
108
+ }
109
+ return highlightStyle;
110
+ }
111
+
67
112
  /**
68
113
  * @param {FeatureType} feature
69
114
  * @param {import("@vcmap/core").Layer} layer
@@ -71,7 +116,7 @@ export const featureInfoViewSymbol = Symbol('featureInfoView');
71
116
  * @returns {import("ol/style/Style.js").default|import("@vcmap/core").VectorStyleItem}
72
117
  */
73
118
  export function getHighlightStyle(feature, layer, defaultFillColor) {
74
- if (layer && layer.highlightStyle) {
119
+ if (layer?.highlightStyle) {
75
120
  return layer.highlightStyle;
76
121
  }
77
122
 
@@ -81,32 +126,32 @@ export function getHighlightStyle(feature, layer, defaultFillColor) {
81
126
  if (typeof style === 'function') {
82
127
  style = style(feature, 1);
83
128
  }
84
- style =
85
- style?.clone?.() ??
86
- new VectorStyleItem(getDefaultVectorStyleItemOptions()).style;
87
- if (style.getText()) {
88
- if (style.getText().getFill()) {
89
- style.getText().getFill().setColor(fillColor.toCssColorString());
90
- }
91
- style.getText().setScale((style.getText().getScale() ?? 1) * 2);
92
- }
93
- if (style.getImage()) {
94
- style.getImage().setScale(style.getImage().getScale() * 2);
95
- }
96
- if (style.getStroke()) {
97
- style.getStroke().setColor(fillColor.toCssColorString());
98
- style.getStroke().setWidth(style.getStroke().getWidth() * 2);
99
- }
100
- if (style.getFill()) {
101
- const color = fillColor.toBytes();
102
- color[3] /= 255;
103
- style.getFill().setColor(color);
104
- }
105
- return style;
129
+ return getHighlightStyleFromStyle(style, fillColor);
106
130
  }
107
131
  return fromCesiumColor(fillColor);
108
132
  }
109
133
 
134
+ /**
135
+ * @param {import("ol").Feature} clusterFeature
136
+ * @param {import("@vcmap/core").VectorClusterGroup} clusterGroup
137
+ * @param {import("ol/style/Style.js").default} clusterStyle
138
+ * @param {string} defaultFillColor
139
+ * @returns {import("ol/style/Style.js").default}
140
+ */
141
+ export function getClusterHighlightStyle(
142
+ clusterFeature,
143
+ clusterGroup,
144
+ clusterStyle,
145
+ defaultFillColor,
146
+ ) {
147
+ if (clusterGroup?.highlightStyle) {
148
+ return clusterGroup.getHighlightStyleForFeature(clusterFeature);
149
+ }
150
+
151
+ const fillColor = Color.fromCssColorString(defaultFillColor).withAlpha(0.8);
152
+ return getHighlightStyleFromStyle(clusterStyle, fillColor);
153
+ }
154
+
110
155
  /**
111
156
  * @param {import("../vcsUiApp.js").default} app
112
157
  * @returns {FeatureInfoSession}
@@ -158,7 +203,7 @@ function setupFeatureInfoTool(app) {
158
203
  session.stopped.addEventListener(() => {
159
204
  action.active = false;
160
205
  session = null;
161
- app.featureInfo.clear();
206
+ app.featureInfo.clearSelection();
162
207
  action.title = 'featureInfo.activateToolTitle';
163
208
  });
164
209
  this.active = true;
@@ -266,7 +311,12 @@ class FeatureInfo extends Collection {
266
311
  */
267
312
  this._windowId = null;
268
313
  /**
269
- * @type {VcsEvent<FeatureType>}
314
+ * @type {string|null}
315
+ * @private
316
+ */
317
+ this._clusterWindowId = null;
318
+ /**
319
+ * @type {VcsEvent<FeatureType|null>}
270
320
  * @private
271
321
  */
272
322
  this._featureChanged = new VcsEvent();
@@ -280,6 +330,21 @@ class FeatureInfo extends Collection {
280
330
  * @private
281
331
  */
282
332
  this._selectedFeatureId = null;
333
+ /**
334
+ * @type {VcsEvent<import("ol").Feature|null>}
335
+ * @private
336
+ */
337
+ this._clusterFeatureChanged = new VcsEvent();
338
+ /**
339
+ * @type {import("ol").Feature|null}
340
+ * @private
341
+ */
342
+ this._selectedClusterFeature = null;
343
+ /**
344
+ * @type {string|null}
345
+ * @private
346
+ */
347
+ this._selectedClusterFeatureId = null;
283
348
  /**
284
349
  * @type {Array<function():void>}
285
350
  * @private
@@ -305,18 +370,39 @@ class FeatureInfo extends Collection {
305
370
  ) {
306
371
  this._app.windowManager.remove(this._windowId);
307
372
  }
373
+
374
+ if (
375
+ this._clusterWindowId &&
376
+ this._app.windowManager.has(this._clusterWindowId)
377
+ ) {
378
+ const { props } = this._app.windowManager.get(this._clusterWindowId);
379
+ if (props.items.some((item) => item.group === layer.name)) {
380
+ props.items = props.items.filter(
381
+ (item) => item.group !== layer.name,
382
+ );
383
+ props.groups = props.groups.filter(
384
+ (group) => group.name !== layer.name,
385
+ );
386
+ if (props.items.length === 0) {
387
+ this._app.windowManager.remove(this._clusterWindowId);
388
+ }
389
+ }
390
+ }
308
391
  }),
309
392
  this._app.windowManager.removed.addEventListener(({ id }) => {
310
393
  if (id === this._windowId) {
311
- this.clear();
394
+ this.clearFeature();
395
+ }
396
+ if (id === this._clusterWindowId) {
397
+ this.clearCluster();
312
398
  }
313
399
  }),
314
400
  this._app.moduleAdded.addEventListener(() => {
315
- this.clear();
401
+ this.clearSelection();
316
402
  this._destroyFeatureInfoTool();
317
403
  this._destroyFeatureInfoTool = setupFeatureInfoTool(this._app);
318
404
  }),
319
- this._app.moduleRemoved.addEventListener(() => this.clear()),
405
+ this._app.moduleRemoved.addEventListener(() => this.clearSelection()),
320
406
  ];
321
407
  /**
322
408
  * A vector layer to render provided features on
@@ -332,6 +418,8 @@ class FeatureInfo extends Collection {
332
418
  }
333
419
 
334
420
  /**
421
+ * Emitted whenever a feature is selected or cleared.
422
+ * Does not reflect cluster feature changes!
335
423
  * @type {VcsEvent<null|FeatureType>}
336
424
  */
337
425
  get featureChanged() {
@@ -352,6 +440,28 @@ class FeatureInfo extends Collection {
352
440
  return this._selectedFeatureId;
353
441
  }
354
442
 
443
+ /**
444
+ * Emitted whenever a cluster feature is selected or cleared.
445
+ * @type {VcsEvent<null|import("ol").Feature>}
446
+ */
447
+ get clusterFeatureChanged() {
448
+ return this._clusterFeatureChanged;
449
+ }
450
+
451
+ /**
452
+ * @type {null|import("ol").Feature}
453
+ */
454
+ get selectedClusterFeature() {
455
+ return this._selectedClusterFeature;
456
+ }
457
+
458
+ /**
459
+ * @type {null|string}
460
+ */
461
+ get selectedClusterFeatureId() {
462
+ return this._selectedClusterFeatureId;
463
+ }
464
+
355
465
  /**
356
466
  * The window id of the current features FeatureInfoView window
357
467
  * @type {string|null}
@@ -360,6 +470,14 @@ class FeatureInfo extends Collection {
360
470
  return this._windowId;
361
471
  }
362
472
 
473
+ /**
474
+ * The window id of the current cluster feature window
475
+ * @type {string|null}
476
+ */
477
+ get clusterWindowId() {
478
+ return this._clusterWindowId;
479
+ }
480
+
363
481
  /**
364
482
  * @private
365
483
  */
@@ -426,10 +544,22 @@ class FeatureInfo extends Collection {
426
544
 
427
545
  if (usedFeatureInfoView && layer) {
428
546
  this._clearInternal();
547
+ if (
548
+ this._selectedClusterFeature &&
549
+ !this._selectedClusterFeature
550
+ .get('features')
551
+ .map((f) => f[originalFeatureSymbol] ?? f)
552
+ .includes(feature)
553
+ ) {
554
+ this.clearCluster();
555
+ }
429
556
  if (feature[isProvidedFeature]) {
430
557
  this._ensureScratchLayer();
431
- this._scratchLayer.addFeatures([feature]);
432
- const featureId = feature.getId(); // make sure to grab ID after adding it to the layer
558
+ // we need to clone the feature to avoid changing vcsLayerNameSymbol on the original feature
559
+ const clonedFeature = feature.clone();
560
+ clonedFeature.setId(feature.getId());
561
+ this._scratchLayer.addFeatures([clonedFeature]);
562
+ const featureId = clonedFeature.getId(); // make sure to grab ID after adding it to the layer
433
563
  this._scratchLayer.featureVisibility.highlight({
434
564
  [featureId]: getHighlightStyle(
435
565
  feature,
@@ -440,6 +570,29 @@ class FeatureInfo extends Collection {
440
570
  });
441
571
  this._clearHighlightingCb = () =>
442
572
  this._scratchLayer.featureVisibility.unHighlight([featureId]);
573
+ } else if (layer.vectorClusterGroup) {
574
+ this._ensureScratchLayer();
575
+ const clone = feature.clone();
576
+ const featureId = feature.getId();
577
+ this._scratchLayer.vectorProperties.setValuesForFeatures(
578
+ layer.vectorProperties.getValuesForFeatures([clone]),
579
+ [clone],
580
+ );
581
+ const eyeOffset = clone.get('olcs_eyeOffset') ?? [0, 0, 0];
582
+ eyeOffset[2] -= 10;
583
+ clone.set('olcs_eyeOffset', eyeOffset);
584
+ clone.setId(featureId);
585
+ this._scratchLayer.addFeatures([clone]);
586
+ this._scratchLayer.featureVisibility.highlight({
587
+ [featureId]: getHighlightStyle(
588
+ feature,
589
+ layer,
590
+ this._app.uiConfig.config.primaryColor ??
591
+ getDefaultPrimaryColor(this._app),
592
+ ),
593
+ });
594
+ this._clearHighlightingCb = () =>
595
+ this._scratchLayer.featureVisibility.unHighlight([clone]);
443
596
  } else if (layer.featureVisibility) {
444
597
  const featureId = feature.getId();
445
598
  layer.featureVisibility.highlight({
@@ -470,10 +623,121 @@ class FeatureInfo extends Collection {
470
623
  this._selectedFeatureId = feature.getId();
471
624
  this._featureChanged.raiseEvent(this._selectedFeature);
472
625
  } else {
473
- this.clear();
626
+ this.clearSelection();
474
627
  }
475
628
  }
476
629
 
630
+ /**
631
+ * Selecting a cluster feature opens a window listing the features belonging to the cluster feature.
632
+ * To be listed the feature must meet the following criteria: a) the feature must be part of a layer, b) said layer must be managed in
633
+ * the same VcsApp as provided to the FeatureInfo on construction. if not providing a feature info view, then c) said layer must have a featureInfo property set on
634
+ * its properties bag and d) said featureInfo property must provide the name of a FeatureInfoView present on this FeatureInfos
635
+ * collection.
636
+ * The cluster feature will be cloned, highlighted and added on an internal scratch layer to ensure availability until deselection.
637
+ * The original cluster feature will be hidden until deselection.
638
+ * @param {import("ol").Feature} clusterFeature
639
+ * @returns {Promise<void>}
640
+ */
641
+ async selectClusterFeature(clusterFeature) {
642
+ this.clearFeature();
643
+ this._clearClusterInternal();
644
+ const id = `cluster-at-${clusterFeature.getGeometry().getCoordinates().join('-')}`;
645
+
646
+ this._ensureScratchLayer();
647
+ const feature = clusterFeature.clone();
648
+ feature.setId(id);
649
+
650
+ clusterFeature[hidden] = true;
651
+ clusterFeature.changed();
652
+
653
+ const fillColor =
654
+ this._app.uiConfig.config.primaryColor ??
655
+ getDefaultPrimaryColor(this._app);
656
+
657
+ if (clusterFeature[vectorClusterGroupName]) {
658
+ const clusterGroup = this._app.vectorClusterGroups.getByKey(
659
+ clusterFeature[vectorClusterGroupName],
660
+ );
661
+ this._scratchLayer.vectorProperties.setValuesForFeatures(
662
+ clusterGroup.vectorProperties.getValuesForFeatures([feature]),
663
+ [feature],
664
+ );
665
+ const clusterStyle = clusterGroup.style.createStyleFunction((layerName) =>
666
+ this._app.layers.getByKey(layerName),
667
+ )(clusterFeature, 1);
668
+ const highlightStyle = getClusterHighlightStyle(
669
+ clusterFeature,
670
+ clusterGroup,
671
+ clusterStyle,
672
+ fillColor,
673
+ );
674
+ feature.setStyle(highlightStyle);
675
+ } else if (clusterFeature[isProvidedClusterFeature]) {
676
+ feature.setStyle(
677
+ fromCesiumColor(Color.fromCssColorString(fillColor)).style,
678
+ );
679
+ }
680
+
681
+ if (this._app.maps.activeMap instanceof ObliqueMap) {
682
+ feature.getGeometry()[alreadyTransformedToImage] = true;
683
+ }
684
+ this._scratchLayer.addFeatures([feature]);
685
+
686
+ const features = clusterFeature.get('features');
687
+ const groups = {};
688
+ const items = features.map((f) => {
689
+ const oFeature = f[originalFeatureSymbol] ?? f;
690
+ const listItem = reactive({
691
+ name: oFeature.getId(),
692
+ title:
693
+ oFeature.getAttributes()?.title ||
694
+ oFeature.getAttributes()?.name ||
695
+ oFeature.getId(),
696
+ disabled: !this._getFeatureInfoViewForFeature(oFeature),
697
+ selectionChanged: (value) => {
698
+ if (value) {
699
+ this.selectFeature(oFeature);
700
+ } else {
701
+ this.clearFeature();
702
+ }
703
+ },
704
+ });
705
+ const layerName = oFeature[vcsLayerName];
706
+ if (layerName) {
707
+ if (!groups[layerName]) {
708
+ const title = this._app.layers.getByKey(layerName)?.properties?.title;
709
+ groups[layerName] = {
710
+ name: layerName,
711
+ title: title || layerName,
712
+ };
713
+ }
714
+ listItem.group = layerName;
715
+ }
716
+ return listItem;
717
+ });
718
+
719
+ this._clusterWindowId = id;
720
+ this._app.windowManager.add(
721
+ {
722
+ id,
723
+ component: ClusterFeatureComponent,
724
+ props: reactive({
725
+ items,
726
+ groups: Object.values(groups),
727
+ }),
728
+ state: {
729
+ headerTitle: 'featureInfo.cluster.headerTitle',
730
+ },
731
+ slot: WindowSlot.DYNAMIC_LEFT,
732
+ },
733
+ vcsAppSymbol,
734
+ );
735
+
736
+ this._selectedClusterFeature = clusterFeature;
737
+ this._selectedClusterFeatureId = id;
738
+ this._clusterFeatureChanged.raiseEvent(this._selectedClusterFeature);
739
+ }
740
+
477
741
  /**
478
742
  * Clears the current feature. remove window, highlighting and provided feature.
479
743
  * @private
@@ -487,16 +751,35 @@ class FeatureInfo extends Collection {
487
751
  this._app.windowManager.remove(this._windowId);
488
752
  this._windowId = null;
489
753
  }
754
+ if (this._scratchLayer && this._selectedFeatureId) {
755
+ this._scratchLayer.removeFeaturesById([this._selectedFeatureId]);
756
+ }
757
+ }
490
758
 
491
- if (this._scratchLayer) {
492
- this._scratchLayer.removeAllFeatures();
759
+ /**
760
+ * Clears the current cluster feature. remove window, highlighting and provided cluster feature.
761
+ * @private
762
+ */
763
+ _clearClusterInternal() {
764
+ if (this._clusterWindowId) {
765
+ this._app.windowManager.remove(this._clusterWindowId);
766
+ this._clusterWindowId = null;
767
+ }
768
+
769
+ if (this._selectedClusterFeature) {
770
+ this._selectedClusterFeature[hidden] = false;
771
+ this._selectedClusterFeature.changed();
772
+
773
+ if (this._scratchLayer) {
774
+ this._scratchLayer.removeFeaturesById([this._selectedClusterFeatureId]);
775
+ }
493
776
  }
494
777
  }
495
778
 
496
779
  /**
497
780
  * Deselecting feature clears highlighting and closes FeatureInfoView. fires feature changed with null
498
781
  */
499
- clear() {
782
+ clearFeature() {
500
783
  this._clearInternal();
501
784
  if (this._selectedFeature) {
502
785
  this._selectedFeature = null;
@@ -505,12 +788,45 @@ class FeatureInfo extends Collection {
505
788
  }
506
789
  }
507
790
 
791
+ /**
792
+ * Deselecting and removing cluster feature. Closing cluster window and fires cluster feature changed with null
793
+ */
794
+ clearCluster() {
795
+ this._clearClusterInternal();
796
+ if (this._selectedClusterFeature) {
797
+ this._selectedClusterFeature[hidden] = false;
798
+ this._selectedClusterFeature = null;
799
+ this._clusterFeatureChanged.raiseEvent(this._selectedClusterFeature);
800
+ }
801
+ }
802
+
803
+ /**
804
+ * @deprecated
805
+ */
806
+ clear() {
807
+ getLogger().deprecate(
808
+ 'clear',
809
+ 'Use clearSelection instead. Clear will clear the FeatureInfo collection removing all registered FeatureInfoViews in feature.',
810
+ );
811
+ this.clearSelection();
812
+ }
813
+
814
+ /**
815
+ * Clears selection by deselecting current feature and cluster and closing all related windows.
816
+ * Fires feature changed and cluster feature changed events with null.
817
+ */
818
+ clearSelection() {
819
+ this.clearFeature();
820
+ this.clearCluster();
821
+ }
822
+
508
823
  /**
509
824
  * Destroys the feature info and all its events & listeners
510
825
  */
511
826
  destroy() {
512
827
  super.destroy();
513
828
  this._clearInternal();
829
+ this._clearClusterInternal();
514
830
  this._featureChanged.destroy();
515
831
  this._destroyFeatureInfoTool();
516
832
  if (this._scratchLayer) {
@@ -2,6 +2,8 @@ import {
2
2
  AbstractInteraction,
3
3
  EventType,
4
4
  ModificationKeyType,
5
+ vectorClusterGroupName,
6
+ isProvidedClusterFeature,
5
7
  } from '@vcmap/core';
6
8
 
7
9
  /**
@@ -28,9 +30,22 @@ class FeatureInfoInteraction extends AbstractInteraction {
28
30
  */
29
31
  async pipe(event) {
30
32
  if (event.feature) {
31
- if (
33
+ const featureId = event.feature.getId();
34
+ const isClusterFeature = !!(
35
+ event.feature[vectorClusterGroupName] ||
36
+ event.feature[isProvidedClusterFeature]
37
+ );
38
+ if (isClusterFeature) {
39
+ if (
40
+ !this._featureInfo.selectedClusterFeature ||
41
+ featureId !== this._featureInfo.selectedClusterFeatureId
42
+ ) {
43
+ event.stopPropagation = true;
44
+ await this._featureInfo.selectClusterFeature(event.feature);
45
+ }
46
+ } else if (
32
47
  !this._featureInfo.selectedFeature ||
33
- event.feature.getId() !== this._featureInfo.selectedFeatureId
48
+ featureId !== this._featureInfo.selectedFeatureId
34
49
  ) {
35
50
  event.stopPropagation = true;
36
51
  await this._featureInfo.selectFeature(event.feature, event.position, [
@@ -39,7 +54,7 @@ class FeatureInfoInteraction extends AbstractInteraction {
39
54
  ]);
40
55
  }
41
56
  } else {
42
- await this._featureInfo.clear();
57
+ await this._featureInfo.clearSelection();
43
58
  }
44
59
  return event;
45
60
  }
@@ -1,4 +1,4 @@
1
- import { renderTemplate } from '../components/form-output/markdownHelper.js';
1
+ import { renderTemplate } from '@vcmap/core';
2
2
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
3
3
  import IframeComponent from './IframeComponent.vue';
4
4
 
@@ -1,7 +1,5 @@
1
- import {
2
- parseAndSanitizeMarkdown,
3
- renderTemplate,
4
- } from '../components/form-output/markdownHelper.js';
1
+ import { renderTemplate } from '@vcmap/core';
2
+ import { parseAndSanitizeMarkdown } from '../components/form-output/markdownHelper.js';
5
3
  import BalloonFeatureInfoView from './balloonFeatureInfoView.js';
6
4
  import MarkdownBalloonComponent from './MarkdownBalloonComponent.vue';
7
5
 
@@ -1,4 +1,4 @@
1
- import { renderTemplate } from '../components/form-output/markdownHelper.js';
1
+ import { renderTemplate } from '@vcmap/core';
2
2
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
3
3
  import VcsMarkdown from '../components/form-output/VcsMarkdown.vue';
4
4
 
package/src/i18n/de.d.ts CHANGED
@@ -73,6 +73,11 @@ declare namespace messages {
73
73
  counter: string;
74
74
  counterSize: string;
75
75
  };
76
+ fileUpload: {
77
+ title: string;
78
+ divider: string;
79
+ browse: string;
80
+ };
76
81
  timePicker: {
77
82
  am: string;
78
83
  pm: string;
@@ -278,6 +283,7 @@ declare namespace messages {
278
283
  export { name_2 as name };
279
284
  export let checkBoxText: string;
280
285
  export let buttonTitle: string;
286
+ export let dontShowAgain: string;
281
287
  }
282
288
  export namespace customScreen {
283
289
  let name_3: string;
@@ -457,14 +463,21 @@ declare namespace messages {
457
463
  export namespace featureInfo {
458
464
  let activateToolTitle: string;
459
465
  let deactivateToolTitle: string;
466
+ namespace cluster {
467
+ export let headerTitle: string;
468
+ export let expand: string;
469
+ export let collapse: string;
470
+ let empty_2: string;
471
+ export { empty_2 as empty };
472
+ }
460
473
  }
461
474
  export namespace legend {
462
475
  let title_17: string;
463
476
  export { title_17 as title };
464
477
  let tooltip_5: string;
465
478
  export { tooltip_5 as tooltip };
466
- let empty_2: string;
467
- export { empty_2 as empty };
479
+ let empty_3: string;
480
+ export { empty_3 as empty };
468
481
  export let openInNew: string;
469
482
  export let defaultLabelText: string;
470
483
  }
@@ -495,8 +508,8 @@ declare namespace messages {
495
508
  export { title_21 as title };
496
509
  let tooltip_7: string;
497
510
  export { tooltip_7 as tooltip };
498
- let empty_3: string;
499
- export { empty_3 as empty };
511
+ let empty_4: string;
512
+ export { empty_4 as empty };
500
513
  }
501
514
  export namespace imprint {
502
515
  let title_22: string;
package/src/i18n/de.js CHANGED
@@ -172,6 +172,7 @@ const messages = {
172
172
  checkBoxText:
173
173
  'Bitte akzeptieren sie die [Bedingungen](https://vc.systems/) um Fortzufahren',
174
174
  buttonTitle: 'Weiter',
175
+ dontShowAgain: 'Diese Nachricht nicht erneut anzeigen',
175
176
  },
176
177
  customScreen: {
177
178
  name: 'Benutzerdefinierter Screen',
@@ -325,6 +326,12 @@ const messages = {
325
326
  featureInfo: {
326
327
  activateToolTitle: 'Informationen abfragen',
327
328
  deactivateToolTitle: 'Informationen abfragen',
329
+ cluster: {
330
+ headerTitle: 'Gruppeninformationen',
331
+ expand: 'Informationsliste anzeigen',
332
+ collapse: 'Informationsliste minimieren',
333
+ empty: 'Keine Informationen verfügbar',
334
+ },
328
335
  },
329
336
  legend: {
330
337
  title: 'Legende',