@vcmap/ui 5.0.0-rc.15 → 5.0.0-rc.17

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 (91) hide show
  1. package/build/buildHelpers.js +7 -1
  2. package/config/base.config.json +7 -45
  3. package/config/dev.config.json +5 -1
  4. package/config/www.config.json +14 -13
  5. package/dist/assets/{cesium.2e288a.js → cesium.41de56.js} +0 -0
  6. package/dist/assets/cesium.js +1 -1
  7. package/dist/assets/{core.8014d3.js → core.af84e3.js} +6077 -4544
  8. package/dist/assets/core.js +1 -1
  9. package/dist/assets/{index.3f74fa92.js → index.5b773cad.js} +1 -1
  10. package/dist/assets/{ol.31c3a5.js → ol.5c7490.js} +0 -0
  11. package/dist/assets/ol.js +1 -1
  12. package/dist/assets/ui.dffe32.css +1 -0
  13. package/dist/assets/{ui.36f84f.js → ui.dffe32.js} +7243 -6234
  14. package/dist/assets/ui.js +1 -1
  15. package/dist/assets/{vue.a39c10.js → vue.25da17.js} +0 -0
  16. package/dist/assets/vue.js +2 -2
  17. package/dist/assets/{vuetify.378637.css → vuetify.e4ece7.css} +1 -1
  18. package/dist/assets/{vuetify.378637.js → vuetify.e4ece7.js} +5 -2
  19. package/dist/assets/vuetify.js +2 -2
  20. package/dist/index.html +1 -1
  21. package/index.html +77 -0
  22. package/index.js +18 -3
  23. package/package.json +4 -2
  24. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +4 -1
  25. package/plugins/@vcmap/create-link/index.js +4 -1
  26. package/plugins/@vcmap/pluginExample/exampleActions.js +45 -0
  27. package/plugins/@vcmap/pluginExample/index.js +26 -2
  28. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +77 -42
  29. package/plugins/@vcmap/search-nominatim/nominatim.js +1 -1
  30. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +4 -2
  31. package/plugins/categoryTest/Categories.vue +27 -13
  32. package/plugins/categoryTest/Category.vue +7 -1
  33. package/plugins/categoryTest/index.js +1 -1
  34. package/plugins/notifier/index.js +31 -0
  35. package/plugins/notifier/notifierTester.vue +88 -0
  36. package/plugins/package.json +1 -1
  37. package/plugins/test/allIconsComponent.vue +5 -5
  38. package/plugins/test/emptyComponent.vue +1 -1
  39. package/plugins/test/index.js +27 -3
  40. package/plugins/test/myCustomHeader.vue +9 -1
  41. package/plugins/test/testList.vue +290 -0
  42. package/plugins/test/vcsContent.vue +1 -1
  43. package/plugins/test/windowManagerExample.vue +12 -7
  44. package/plugins/wizardExample/index.js +41 -0
  45. package/plugins/wizardExample/wizardExample.vue +77 -0
  46. package/src/actions/actionHelper.js +10 -9
  47. package/src/application/VcsApp.vue +43 -34
  48. package/src/components/form-inputs-controls/VcsCheckbox.vue +1 -0
  49. package/src/components/form-inputs-controls/VcsFormSection.vue +23 -15
  50. package/src/components/form-inputs-controls/VcsSelect.vue +33 -1
  51. package/src/components/form-inputs-controls/VcsTextField.vue +11 -3
  52. package/src/components/form-inputs-controls/VcsWizard.vue +133 -0
  53. package/src/components/imageElementInjector.vue +22 -0
  54. package/src/components/lists/VcsList.vue +468 -0
  55. package/src/components/lists/VcsTreeview.vue +1 -2
  56. package/src/components/lists/VcsTreeviewLeaf.vue +18 -50
  57. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -23
  58. package/src/components/tables/VcsTable.vue +13 -1
  59. package/src/contentTree/LayerTree.vue +1 -1
  60. package/src/contentTree/contentTreeCollection.js +9 -0
  61. package/src/contentTree/contentTreeItem.js +13 -13
  62. package/src/contentTree/layerContentTreeItem.js +1 -1
  63. package/src/contentTree/subContentTreeItem.js +1 -1
  64. package/src/contentTree/vcsObjectContentTreeItem.js +1 -1
  65. package/src/featureInfo/BalloonComponent.vue +13 -8
  66. package/src/featureInfo/balloonFeatureInfoView.js +16 -22
  67. package/src/featureInfo/balloonHelper.js +26 -5
  68. package/src/featureInfo/featureInfo.js +14 -2
  69. package/src/featureInfo/featureInfoInteraction.js +1 -1
  70. package/src/i18n/de.js +13 -1
  71. package/src/i18n/en.js +13 -1
  72. package/src/icons/+all.js +4 -0
  73. package/src/icons/WandIcon.vue +63 -0
  74. package/src/manager/categoryManager/CategoryComponent.vue +115 -0
  75. package/src/manager/categoryManager/CategoryComponentList.vue +57 -0
  76. package/src/manager/categoryManager/CategoryManager.vue +35 -0
  77. package/src/manager/categoryManager/categoryManager.js +251 -165
  78. package/src/manager/contextMenu/contextMenuManager.js +8 -2
  79. package/src/manager/window/WindowComponent.vue +51 -70
  80. package/src/manager/window/WindowComponentHeader.vue +81 -13
  81. package/src/manager/window/WindowManager.vue +54 -30
  82. package/src/manager/window/windowHelper.js +341 -0
  83. package/src/manager/window/windowManager.js +173 -151
  84. package/src/navigation/overviewMap.js +10 -9
  85. package/src/notifier/notifier.js +120 -0
  86. package/src/notifier/notifierComponent.vue +84 -0
  87. package/src/styles/variables.scss +19 -3
  88. package/src/vcsUiApp.js +26 -2
  89. package/src/vuePlugins/vuetify.js +2 -0
  90. package/dist/assets/ui.36f84f.css +0 -1
  91. package/src/manager/categoryManager/ComponentsManager.vue +0 -30
@@ -3,21 +3,22 @@
3
3
  class="d-flex flex-row align-center"
4
4
  v-if="item"
5
5
  >
6
- <span v-if="item.icon" class="d-flex align-center">
7
- <v-icon
8
- v-if="iconType === iconTypes.string"
9
- v-text="item.icon"
10
- :size="16"
11
- class="mr-1"
12
- />
13
- <span ref="imgContainer" />
14
- </span>
15
-
16
6
  <div
17
- class="position-relative col-8 pa-0 d-flex align-center treeview-label"
7
+ class="position-relative col-8 pa-0 d-flex align-center vcs-treeview-leaf"
18
8
  :title="$t(item.tooltip || item.title)"
19
9
  >
20
- <span>{{ $t(item.title) }}</span>
10
+ <span
11
+ v-if="item.icon"
12
+ >
13
+ <v-icon
14
+ v-if="isStringIcon"
15
+ v-text="item.icon"
16
+ :size="16"
17
+ class="mr-1"
18
+ />
19
+ <ImageElementInjector :element="item.icon" v-else />
20
+ </span>
21
+ <span class="vcs-treeview-item-title">{{ $t(item.title) }}</span>
21
22
  </div>
22
23
  <VcsActionButtonList
23
24
  v-if="item.actions.length > 0"
@@ -31,7 +32,7 @@
31
32
  </div>
32
33
  </template>
33
34
  <style lang="css" scoped>
34
- .treeview-label span{
35
+ .vcs-treeview-leaf .vcs-treeview-item-title{
35
36
  white-space: nowrap;
36
37
  overflow: hidden;
37
38
  text-overflow: ellipsis;
@@ -39,22 +40,10 @@
39
40
  </style>
40
41
 
41
42
  <script>
42
- import
43
- {
44
- computed,
45
- onMounted,
46
- ref,
47
- } from 'vue';
48
-
43
+ import { computed } from 'vue';
49
44
  import { VIcon } from 'vuetify/lib';
50
45
  import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
51
-
52
-
53
- const iconTypes = {
54
- image: 'HTMLImageElement',
55
- canvas: 'HTMLCanvasElement',
56
- string: 'StringIcon',
57
- };
46
+ import ImageElementInjector from '../imageElementInjector.vue';
58
47
 
59
48
  /**
60
49
  * @description
@@ -66,6 +55,7 @@
66
55
  components: {
67
56
  VcsActionButtonList,
68
57
  VIcon,
58
+ ImageElementInjector,
69
59
  },
70
60
  props: {
71
61
  item: {
@@ -74,32 +64,10 @@
74
64
  },
75
65
  },
76
66
  setup(props) {
77
- const iconType = ref();
78
- const imgContainer = ref();
79
-
80
67
  const leaf = computed(() => props.item?.children?.length === 0);
81
68
 
82
- onMounted(() => { // TODO make icon reactive
83
- const { icon } = props.item;
84
- if (icon) {
85
- if (icon instanceof HTMLImageElement) {
86
- imgContainer.value.appendChild(icon);
87
- iconType.value = iconTypes.image;
88
- }
89
- if (icon instanceof HTMLCanvasElement) {
90
- imgContainer.value.appendChild(icon);
91
- iconType.value = iconTypes.canvas;
92
- }
93
- if (typeof icon === 'string') {
94
- iconType.value = iconTypes.string;
95
- }
96
- }
97
- });
98
-
99
69
  return {
100
- iconTypes,
101
- iconType,
102
- imgContainer,
70
+ isStringIcon: computed(() => typeof props.item.icon === 'string'),
103
71
  leaf,
104
72
  };
105
73
  },
@@ -17,7 +17,7 @@
17
17
  class="searchbar outlined rounded-xl align-center d-flex justify-center white pa-1 pl-6"
18
18
  :placeholder="$t(placeholder)"
19
19
  :value="value"
20
- @input="$event => handleInput($event)"
20
+ v-on="$listeners"
21
21
  clearable
22
22
  />
23
23
  </slot>
@@ -104,13 +104,8 @@
104
104
 
105
105
 
106
106
  <script>
107
- import { onMounted, onUnmounted } from 'vue';
108
-
109
- import { Subject } from 'rxjs';
110
- import { debounceTime } from 'rxjs/operators';
111
107
  import { VIcon, VTextField } from 'vuetify/lib';
112
108
 
113
-
114
109
  /**
115
110
  * @description Stylized wrapper around vuetify divider
116
111
  * @vue-prop {number} height - Height of the component.
@@ -140,22 +135,5 @@
140
135
  default: false,
141
136
  },
142
137
  },
143
- setup(props, context) {
144
- const sub = new Subject();
145
- onMounted(() => {
146
- sub.pipe(debounceTime(330)).subscribe(
147
- (value) => {
148
- context.emit('input', value);
149
- },
150
- );
151
- });
152
- onUnmounted(() => sub.unsubscribe());
153
-
154
- return {
155
- handleInput: (val) => {
156
- sub.next(val);
157
- },
158
- };
159
- },
160
138
  };
161
139
  </script>
@@ -90,7 +90,16 @@
90
90
  <script>
91
91
  import { getCurrentInstance, ref, computed } from 'vue';
92
92
  import {
93
- VContainer, VDataTable, VList, VListItem, VListItemTitle, VMenu, VRow,
93
+ VCard,
94
+ VDivider,
95
+ VContainer,
96
+ VDataTable,
97
+ VList,
98
+ VListItem,
99
+ VListItemTitle,
100
+ VMenu,
101
+ VIcon,
102
+ VRow,
94
103
  } from 'vuetify/lib';
95
104
  import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
96
105
  import VcsButton from '../buttons/VcsButton.vue';
@@ -140,10 +149,13 @@
140
149
  components: {
141
150
  VcsButton,
142
151
  VcsTreeviewSearchbar,
152
+ VCard,
143
153
  VDataTable,
144
154
  VContainer,
155
+ VDivider,
145
156
  VRow,
146
157
  VMenu,
158
+ VIcon,
147
159
  VList,
148
160
  VListItem,
149
161
  VListItemTitle,
@@ -3,7 +3,7 @@
3
3
  v-if="tree && tree.length"
4
4
  :items="tree"
5
5
  :open.sync="open"
6
- :has-searchbar="true"
6
+ :show-searchbar="true"
7
7
  :searchbar-placeholder="'content.search.placeholder'"
8
8
  item-children="visibleChildren"
9
9
  />
@@ -198,6 +198,15 @@ class ContentTreeCollection extends IndexedCollection {
198
198
  this._subTreeListeners = subTrees.map(subTree => this._createSubtreeActionButton(subTree));
199
199
  }
200
200
 
201
+ /**
202
+ * Returns all managed subtrees. Entries are not persisted and will change, if the trees get recalculated.
203
+ * @type {import("vue").Ref<Map<string, TreeViewItem>>}
204
+ * @readonly
205
+ */
206
+ get subTreeViewItems() {
207
+ return this._subTreeViewItems;
208
+ }
209
+
201
210
  /**
202
211
  * All ids of the currently managed subtrees. Ids are not persisted and will change if
203
212
  * the trees get recalculated. The first ID is always the default tree. Other ids are subtree ids.
@@ -7,30 +7,30 @@ import { createStateRefAction, StateActionState } from '../actions/stateRefActio
7
7
 
8
8
  /**
9
9
  * @typedef {Object} ContentTreeItemOptions
10
- * @property {string} name
10
+ * @property {string} name - name of the item defining the structure within the tree using dot notation.
11
11
  * @property {string} [title] - may be unset, if set from object properties later on. required otherwise
12
12
  * @property {string} [tooltip] - may be unset or set from object properties later on.
13
- * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} icon - an icon URL or element to display
14
- * @property {number} [weight]
15
- * @property {string} [infoUrl]
16
- * @property {boolean} [initOpen=false]
13
+ * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} icon - an icon URL or element to display.
14
+ * @property {number} [weight] - optional weighting of the item. higher weights come first.
15
+ * @property {string} [infoUrl] - optional info url providing link with additional information.
16
+ * @property {boolean} [initOpen=false] - groups being initially open or not.
17
17
  */
18
18
 
19
19
  /**
20
20
  * A readonly rendering interface of a ContentTreeItem.
21
21
  * @typedef {Object} TreeViewItem
22
22
  * @property {string} name
23
- * @property {boolean} visible
24
- * @property {boolean} clickable
25
- * @property {boolean} disabled
26
- * @property {StateActionState} state
27
- * @property {string} title
23
+ * @property {boolean} visible - Whether to display this item or not.
24
+ * @property {boolean} clickable - Whether this item reacts to click events, e.g. with visual feedback
25
+ * @property {boolean} disabled - Whether this item should be displayed as disabled.
26
+ * @property {StateActionState} state - The state of this item. NONE if this item cannot have a state.
27
+ * @property {string} title - The title to be displayed
28
28
  * @property {string} [tooltip]
29
- * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} icon
29
+ * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} [icon] - An optional icon to display with this item. Can be an URL or HTMLElement.
30
30
  * @property {Array<VcsAction>} actions
31
31
  * @property {Array<TreeViewItem>} children
32
32
  * @property {Array<TreeViewItem>} visibleChildren - computed property
33
- * @property {function():Promise<void>} clicked
33
+ * @property {function(PointerEvent):Promise<void>} clicked - A callback called once the item is clicked.
34
34
  */
35
35
 
36
36
  /**
@@ -376,7 +376,7 @@ class ContentTreeItem {
376
376
  * GoToExtent: 8
377
377
  * The default weight is set to always push new actions past these.
378
378
  * @param {VcsAction} action
379
- * @param {number} [weight=0]
379
+ * @param {number} [weight=11]
380
380
  */
381
381
  addAction(action, weight = 11) {
382
382
  check(action.name, String);
@@ -127,7 +127,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
127
127
  layerName: this._layerName,
128
128
  }),
129
129
  },
130
- this._app.windowManager,
130
+ this._app,
131
131
  vcsAppSymbol,
132
132
  );
133
133
  this.addAction(action, 4);
@@ -6,7 +6,7 @@ import ContentTreeItem, { contentTreeClassRegistry } from './contentTreeItem.js'
6
6
  export const subTreeSymbol = Symbol('SubTree');
7
7
 
8
8
  /**
9
- * A sub tree item. Subtrees are rendered in their own (not the main content tree).
9
+ * A subtree item. Subtrees are rendered in their own (not the main content tree).
10
10
  * They will receive their own toggle button in the nav bar.
11
11
  * Only toplevel items can be content tree items (with a name which does not have a .)
12
12
  * @class
@@ -9,7 +9,7 @@ import ContentTreeItem, { contentTreeClassRegistry } from './contentTreeItem.js'
9
9
 
10
10
  /**
11
11
  * An abstract class for VcsObject based items.
12
- * It handle the overriding/setting of its own values based on
12
+ * It handles the overriding/setting of its own values based on
13
13
  * the VcsObjects properties bag.
14
14
  * @class
15
15
  * @template {VcsObjectContentTreeItemProperties} T
@@ -17,16 +17,17 @@
17
17
  </v-list-item-avatar>
18
18
  <v-list-item-content class="py-2 pr-1 align-self-start">
19
19
  <v-list-item-title class="text-h6">
20
- {{ $t(title) }}
20
+ {{ $t(balloonTitle) }}
21
21
  </v-list-item-title>
22
- <v-list-item-subtitle v-if="subtitle">
23
- {{ $t(subtitle) }}
22
+ <v-list-item-subtitle v-if="balloonSubtitle">
23
+ {{ $t(balloonSubtitle) }}
24
24
  </v-list-item-subtitle>
25
25
  </v-list-item-content>
26
26
  <VcsButton
27
27
  @click.stop="close"
28
28
  small
29
29
  icon="mdi-close-thick"
30
+ tooltip="components.close"
30
31
  />
31
32
  </v-list-item>
32
33
  </slot>
@@ -58,7 +59,8 @@
58
59
  VList,
59
60
  VListItem,
60
61
  VListItemAvatar,
61
- VListItemContent, VListItemSubtitle,
62
+ VListItemContent,
63
+ VListItemSubtitle,
62
64
  VListItemTitle,
63
65
  } from 'vuetify/lib';
64
66
  import { setupBalloonPositionListener } from './balloonHelper.js';
@@ -68,8 +70,8 @@
68
70
  * @description A balloon viewing feature attributes. Size dynamic dependent on number of attributes.
69
71
  * Scrollable, if more than 6 attributes are provided.
70
72
  * @vue-prop {string} featureId - feature's id
71
- * @vue-prop {string} title - balloon title
72
- * @vue-prop {string} subtitle - balloon subtitle
73
+ * @vue-prop {string} balloonTitle - balloon title
74
+ * @vue-prop {string} balloonSubtitle - balloon subtitle
73
75
  * @vue-prop {Object} attributes - feature's attributes
74
76
  * @vue-prop {Array<import("ol/coordinate").Coordinate>} position - clicked position balloon is rendered at
75
77
  * @vue-data {slot} [#balloon-header] - slot to override balloon header, $props and $attrs are passed to `attrs`
@@ -94,11 +96,11 @@
94
96
  type: String,
95
97
  required: true,
96
98
  },
97
- title: {
99
+ balloonTitle: {
98
100
  type: String,
99
101
  required: true,
100
102
  },
101
- subtitle: {
103
+ balloonSubtitle: {
102
104
  type: String,
103
105
  required: true,
104
106
  },
@@ -148,6 +150,9 @@
148
150
  </script>
149
151
 
150
152
  <style lang="scss">
153
+ .balloon {
154
+ z-index: 0 !important;
155
+ }
151
156
  .balloon hr:first-child {
152
157
  display: none;
153
158
  }
@@ -4,9 +4,8 @@ import { Cartographic, Entity, Math as CesiumMath } from '@vcmap/cesium';
4
4
  import { Projection } from '@vcmap/core';
5
5
  import { check } from '@vcsuite/check';
6
6
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
7
- import { getWindowPositionOptions, WindowAlignment, WindowSlot } from '../manager/window/windowManager.js';
7
+ import { WindowSlot } from '../manager/window/windowManager.js';
8
8
  import BalloonComponent from './BalloonComponent.vue';
9
- import { balloonOffset } from './balloonHelper.js';
10
9
 
11
10
  /**
12
11
  * derive value from attributes
@@ -26,14 +25,14 @@ export function extractNestedKey(key, attrs, defaultValue = null) {
26
25
 
27
26
  /**
28
27
  * @typedef {FeatureInfoViewOptions} BalloonFeatureInfoViewOptions
29
- * @property {string} [title] - optional title to overwrite default (layerName). Can be attribute key (nested key using '.'), i18n key or text
30
- * @property {string} [subtitle] - optional window title to overwrite default (featureId). Can be attribute key (nested key using '.'), i18n key or text
28
+ * @property {string} [balloonTitle] - optional title to overwrite default (layerName). Can be attribute key (nested key using '.'), i18n key or text
29
+ * @property {string} [balloonSubtitle] - optional window title to overwrite default (featureId). Can be attribute key (nested key using '.'), i18n key or text
31
30
  */
32
31
 
33
32
  /**
34
33
  * @typedef {FeatureInfoProps} BalloonFeatureInfoViewProps
35
- * @property {string} title
36
- * @property {string} subtitle
34
+ * @property {string} balloonTitle
35
+ * @property {string} balloonSubtitle
37
36
  * @property {import("ol/coordinate").Coordinate} position
38
37
  */
39
38
 
@@ -87,12 +86,12 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
87
86
  /**
88
87
  * @type {string}
89
88
  */
90
- this.title = options.title;
89
+ this.balloonTitle = options.balloonTitle;
91
90
 
92
91
  /**
93
92
  * @type {string}
94
93
  */
95
- this.subtitle = options.subtitle;
94
+ this.balloonSubtitle = options.balloonSubtitle;
96
95
  }
97
96
 
98
97
  /**
@@ -105,11 +104,11 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
105
104
  return {
106
105
  ...properties,
107
106
  position: featureInfo.position ?? getPositionFromFeature(featureInfo.feature),
108
- title: this.title != null ?
109
- extractNestedKey(this.title, properties.attributes, this.title) :
107
+ balloonTitle: this.balloonTitle != null ?
108
+ extractNestedKey(this.balloonTitle, properties.attributes, this.balloonTitle) :
110
109
  properties.layerProperties.title,
111
- subtitle: this.subtitle != null ?
112
- extractNestedKey(this.subtitle, properties.attributes, this.subtitle) :
110
+ balloonSubtitle: this.balloonSubtitle != null ?
111
+ extractNestedKey(this.balloonSubtitle, properties.attributes, this.balloonSubtitle) :
113
112
  properties.featureId,
114
113
  };
115
114
  }
@@ -124,12 +123,7 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
124
123
  options.state.hideHeader = true;
125
124
  options.state.classes = ['balloon'];
126
125
  options.slot = WindowSlot.DETACHED;
127
- options.position = getWindowPositionOptions(
128
- (featureInfo.windowPosition?.[0] ?? 0) - balloonOffset.x, // if we do not have a windowPosition, let the next render handle it
129
- (featureInfo.windowPosition?.[1] ?? 0) - balloonOffset.y,
130
- null,
131
- WindowAlignment.BOTTOM_LEFT,
132
- );
126
+ // windowPosition is handled by next render
133
127
  return options;
134
128
  }
135
129
 
@@ -138,11 +132,11 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
138
132
  */
139
133
  toJSON() {
140
134
  const config = super.toJSON();
141
- if (this.title) {
142
- config.title = this.title;
135
+ if (this.balloonTitle) {
136
+ config.balloonTitle = this.balloonTitle;
143
137
  }
144
- if (this.subtitle) {
145
- config.subtitle = this.subtitle;
138
+ if (this.balloonSubtitle) {
139
+ config.balloonSubtitle = this.balloonSubtitle;
146
140
  }
147
141
  return config;
148
142
  }
@@ -4,7 +4,7 @@ import { unByKey } from 'ol/Observable.js';
4
4
  import {
5
5
  getWindowPositionOptionsFromMapEvent,
6
6
  WindowAlignment,
7
- } from '../manager/window/windowManager.js';
7
+ } from '../manager/window/windowHelper.js';
8
8
 
9
9
  /**
10
10
  * balloon offset from location or click position in pixel
@@ -63,8 +63,9 @@ export async function getBalloonPosition(app, position) {
63
63
  * @param {WindowManager} windowManager
64
64
  * @param {string} id - windowId of balloon
65
65
  * @param {import("@vcmap/cesium").Cartesian2|undefined} windowPosition
66
+ * @param {HTMLElement} target - the map's target { @link @import("@vcmap/core").MapCollection }
66
67
  */
67
- export function setBalloonPosition(windowManager, id, windowPosition) {
68
+ export function setBalloonPosition(windowManager, id, windowPosition, target) {
68
69
  if (!windowPosition) {
69
70
  return;
70
71
  }
@@ -72,6 +73,7 @@ export function setBalloonPosition(windowManager, id, windowPosition) {
72
73
  id,
73
74
  getWindowPositionOptionsFromMapEvent(
74
75
  new Cartesian2(windowPosition.x - balloonOffset.x, windowPosition.y - balloonOffset.y),
76
+ target,
75
77
  WindowAlignment.BOTTOM_LEFT,
76
78
  ),
77
79
  );
@@ -99,13 +101,27 @@ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosi
99
101
 
100
102
  const map = app.maps.activeMap;
101
103
  if (map instanceof CesiumMap) {
104
+ if (!position[2]) {
105
+ const [position3D] = await map.getHeightFromTerrain([position]);
106
+ position[2] = position3D[2];
107
+ }
102
108
  const wgs84Position = Projection.mercatorToWgs84(position);
103
109
  const cartesian = Cartographic.toCartesian(Cartographic.fromDegrees(...wgs84Position));
104
110
  listeners.push(map.getScene().postRender.addEventListener((scene) => {
105
- setBalloonPosition(app.windowManager, windowId, getBalloonPositionCesium(scene, cartesian));
111
+ setBalloonPosition(
112
+ app.windowManager,
113
+ windowId,
114
+ getBalloonPositionCesium(scene, cartesian),
115
+ app.maps.target,
116
+ );
106
117
  }));
107
118
  } else if (map instanceof OpenlayersMap) {
108
- const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, position));
119
+ const handler = () => setBalloonPosition(
120
+ app.windowManager,
121
+ windowId,
122
+ getBalloonPositionOL(map.olMap, position),
123
+ app.maps.target,
124
+ );
109
125
  const key = map.olMap.on(
110
126
  'postrender',
111
127
  handler,
@@ -117,7 +133,12 @@ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosi
117
133
  position,
118
134
  );
119
135
  listeners.push(map.imageChanged.addEventListener(setup.bind(null, app, windowId, position)));
120
- const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, coords));
136
+ const handler = () => setBalloonPosition(
137
+ app.windowManager,
138
+ windowId,
139
+ getBalloonPositionOL(map.olMap, coords),
140
+ app.maps.target,
141
+ );
121
142
  const key = map.olMap.on(
122
143
  'postrender',
123
144
  handler,
@@ -25,7 +25,6 @@ import {
25
25
  } from '@vcmap/cesium';
26
26
  import { Feature } from 'ol';
27
27
  import { check, checkMaybe } from '@vcsuite/check';
28
- import { v4 as uuidv4 } from 'uuid';
29
28
 
30
29
  import { vcsAppSymbol } from '../pluginHelper.js';
31
30
  import FeatureInfoInteraction from './featureInfoInteraction.js';
@@ -231,6 +230,11 @@ class FeatureInfo {
231
230
  * @private
232
231
  */
233
232
  this._selectedFeature = null;
233
+ /**
234
+ * @type {string|null}
235
+ * @private
236
+ */
237
+ this._selectedFeatureId = null;
234
238
  /**
235
239
  * @type {Array<function():void>}
236
240
  * @private
@@ -299,6 +303,12 @@ class FeatureInfo {
299
303
  */
300
304
  get selectedFeature() { return this._selectedFeature; }
301
305
 
306
+ /**
307
+ * @type {null|string}
308
+ * @readonly
309
+ */
310
+ get selectedFeatureId() { return this._selectedFeatureId; }
311
+
302
312
  /**
303
313
  * The window id of the current features FeatureInfoView window
304
314
  * @type {string|null}
@@ -391,7 +401,7 @@ class FeatureInfo {
391
401
  });
392
402
  this._clearHighlightingCb = () => layer.featureVisibility.unHighlight([featureId]);
393
403
  }
394
- this._windowId = uuidv4(); // we need to create a uuid, otherwise the window manager gets confused if we recreate a window in the same thread with the same id
404
+ this._windowId = usedFeatureInfoView.className; // use className for a type based position caching
395
405
  this._app.windowManager.add(
396
406
  {
397
407
  id: this._windowId,
@@ -404,6 +414,7 @@ class FeatureInfo {
404
414
  );
405
415
 
406
416
  this._selectedFeature = feature;
417
+ this._selectedFeatureId = feature.getId();
407
418
  this._featureChanged.raiseEvent(this._selectedFeature);
408
419
  } else {
409
420
  this.clear();
@@ -436,6 +447,7 @@ class FeatureInfo {
436
447
  this._clearInternal();
437
448
  if (this._selectedFeature) {
438
449
  this._selectedFeature = null;
450
+ this._selectedFeatureId = null;
439
451
  this._featureChanged.raiseEvent(this._selectedFeature);
440
452
  }
441
453
  }
@@ -24,7 +24,7 @@ class FeatureInfoInteraction extends AbstractInteraction {
24
24
  */
25
25
  async pipe(event) {
26
26
  if (event.feature) {
27
- if (!this._featureInfo.selectedFeature || event.feature.getId() !== this._featureInfo.selectedFeature.getId()) {
27
+ if (!this._featureInfo.selectedFeature || event.feature.getId() !== this._featureInfo.selectedFeatureId) {
28
28
  event.stopPropagation = true;
29
29
  await this._featureInfo.selectFeature(
30
30
  event.feature,
package/src/i18n/de.js CHANGED
@@ -41,9 +41,15 @@ const messages = {
41
41
  pitchTooltip: 'Kamera pitch: {0}°',
42
42
  overviewMapTooltip: 'Übersichtskarte',
43
43
  },
44
- components: {
44
+ categoryManager: {
45
45
  title: 'Komponenten',
46
46
  tooltip: 'Komponenten',
47
+ more: 'Weitere anzeigen...',
48
+ empty: 'Es gibt noch keine Einträge.',
49
+ },
50
+ components: {
51
+ pin: 'Fenster andocken.',
52
+ close: 'Fenster schließen.',
47
53
  vcsFormSection: {
48
54
  help: 'Hilfe anzeigen.',
49
55
  },
@@ -91,6 +97,12 @@ const messages = {
91
97
  tooltip: 'Öffne Attribution Fenster',
92
98
  },
93
99
  },
100
+ notification: {
101
+ error: 'Fehler',
102
+ warning: 'Warnung',
103
+ information: 'Information',
104
+ success: 'Erfolg',
105
+ },
94
106
  };
95
107
 
96
108
  export default messages;
package/src/i18n/en.js CHANGED
@@ -41,9 +41,15 @@ const messages = {
41
41
  pitchTooltip: 'Camera pitch: {0}°',
42
42
  overviewMapTooltip: 'Overview Map',
43
43
  },
44
- components: {
44
+ categoryManager: {
45
45
  title: 'Components',
46
46
  tooltip: 'Components',
47
+ more: 'Show more...',
48
+ empty: 'There are no entries yet.',
49
+ },
50
+ components: {
51
+ pin: 'Pin window.',
52
+ close: 'Close window.',
47
53
  vcsFormSection: {
48
54
  help: 'Show help.',
49
55
  },
@@ -91,6 +97,12 @@ const messages = {
91
97
  tooltip: 'Open Attributions Window',
92
98
  },
93
99
  },
100
+ notification: {
101
+ error: 'Error',
102
+ warning: 'Warning',
103
+ information: 'Information',
104
+ success: 'Success',
105
+ },
94
106
  };
95
107
 
96
108
  export default messages;
package/src/icons/+all.js CHANGED
@@ -100,6 +100,7 @@ import Viewshed360Icon from './Viewshed360Icon.vue';
100
100
  import ViewshedConeIcon from './ViewshedConeIcon.vue';
101
101
  import WalkingIcon from './WalkingIcon.vue';
102
102
  import WallIcon from './WallIcon.vue';
103
+ import WandIcon from './WandIcon.vue';
103
104
 
104
105
  // * // IconMap.boundingBox
105
106
  // * <v-icon size="16" v-text="'$vcsBoundingBox'" />
@@ -431,6 +432,9 @@ const IconMap = {
431
432
  walking: {
432
433
  component: WalkingIcon,
433
434
  },
435
+ wand: {
436
+ component: WandIcon,
437
+ },
434
438
  };
435
439
 
436
440
  const nameCapitalized = (name) => { return name.charAt(0).toUpperCase() + name.slice(1); };