@vcmap/ui 5.0.0-rc.14 → 5.0.0-rc.16

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 (132) hide show
  1. package/README.md +33 -31
  2. package/build/build.js +9 -0
  3. package/build/buildHelpers.js +12 -10
  4. package/build/commonViteConfig.js +3 -10
  5. package/config/base.config.json +30 -24
  6. package/config/dev.config.json +13 -1
  7. package/config/www.config.json +104 -17
  8. package/dist/assets/cesium.430460.js +137226 -0
  9. package/dist/assets/cesium.js +1 -1
  10. package/dist/assets/core.5089ba.js +16024 -0
  11. package/dist/assets/core.js +1 -1
  12. package/dist/assets/index.854f8e2b.js +1 -0
  13. package/dist/assets/ol.9be53a.js +44279 -0
  14. package/dist/assets/ol.js +1 -1
  15. package/dist/assets/{ui.15ef6a.css → ui.49010a.css} +1 -1
  16. package/dist/assets/ui.49010a.js +16776 -0
  17. package/dist/assets/ui.js +1 -1
  18. package/dist/assets/vue.247c1c.js +4675 -0
  19. package/dist/assets/vue.js +5 -2
  20. package/dist/assets/{vuetify.202322.css → vuetify.735e58.css} +1 -1
  21. package/dist/assets/vuetify.735e58.js +21019 -0
  22. package/dist/assets/vuetify.js +5 -2
  23. package/dist/index.html +1 -1
  24. package/index.html +77 -0
  25. package/index.js +8 -1
  26. package/package.json +12 -10
  27. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +4 -1
  28. package/plugins/@vcmap/create-link/index.js +4 -1
  29. package/plugins/@vcmap/pluginExample/exampleActions.js +45 -0
  30. package/plugins/@vcmap/pluginExample/index.js +38 -1
  31. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +152 -98
  32. package/plugins/@vcmap/project-selector/ContextsListComponent.vue +8 -1
  33. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +27 -1
  34. package/plugins/@vcmap/search-nominatim/LICENSE.md +14 -0
  35. package/plugins/@vcmap/search-nominatim/README.md +2 -0
  36. package/plugins/@vcmap/search-nominatim/config.json +4 -0
  37. package/plugins/@vcmap/search-nominatim/index.js +26 -0
  38. package/plugins/@vcmap/search-nominatim/nominatim.js +170 -0
  39. package/plugins/@vcmap/search-nominatim/package.json +43 -0
  40. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +26 -0
  41. package/plugins/buttonExamples/ButtonExamples.vue +28 -1
  42. package/plugins/categoryTest/Categories.vue +16 -0
  43. package/plugins/categoryTest/Category.vue +30 -4
  44. package/plugins/example/mySuperComponent.vue +12 -1
  45. package/plugins/notifier/index.js +31 -0
  46. package/plugins/notifier/notifierTester.vue +88 -0
  47. package/plugins/package.json +2 -1
  48. package/plugins/simple-graph/SimpleGraphComponent.vue +5 -11
  49. package/plugins/test/allIconsComponent.vue +16 -0
  50. package/plugins/test/editor.vue +3 -0
  51. package/plugins/test/emptyComponent.vue +3 -0
  52. package/plugins/test/index.js +22 -0
  53. package/plugins/test/myCustomHeader.vue +9 -1
  54. package/plugins/test/testList.vue +287 -0
  55. package/plugins/test/vcsContent.vue +3 -0
  56. package/plugins/test/windowManagerExample.vue +3 -0
  57. package/plugins/wizardExample/index.js +41 -0
  58. package/plugins/wizardExample/wizardExample.vue +77 -0
  59. package/src/actions/actionHelper.js +103 -2
  60. package/src/actions/styleSelector.vue +9 -0
  61. package/src/application/VcsApp.vue +95 -17
  62. package/src/application/VcsAttributions.vue +63 -0
  63. package/src/application/VcsAttributionsFooter.vue +87 -0
  64. package/src/application/{Navbar.vue → VcsNavbar.vue} +35 -2
  65. package/src/application/VcsSettings.vue +4 -0
  66. package/src/application/attributionsHelper.js +150 -0
  67. package/src/application/vcsAppWrapper.vue +5 -1
  68. package/src/components/buttons/VcsActionButtonList.vue +8 -1
  69. package/src/components/buttons/VcsButton.vue +7 -1
  70. package/src/components/form-inputs-controls/VcsCheckbox.vue +7 -2
  71. package/src/components/form-inputs-controls/VcsColorPicker.vue +4 -0
  72. package/src/components/form-inputs-controls/VcsFormSection.vue +55 -9
  73. package/src/components/form-inputs-controls/VcsRadio.vue +7 -1
  74. package/src/components/form-inputs-controls/VcsSelect.vue +38 -2
  75. package/src/components/form-inputs-controls/VcsTextArea.vue +2 -0
  76. package/src/components/form-inputs-controls/VcsTextField.vue +16 -4
  77. package/src/components/form-inputs-controls/VcsWizard.vue +133 -0
  78. package/src/components/imageElementInjector.vue +22 -0
  79. package/src/components/lists/VcsActionList.vue +12 -1
  80. package/src/components/lists/VcsList.vue +466 -0
  81. package/src/components/lists/VcsTreeview.vue +7 -3
  82. package/src/components/lists/VcsTreeviewLeaf.vue +23 -51
  83. package/src/components/lists/VcsTreeviewSearchbar.vue +6 -23
  84. package/src/components/notification/VcsTooltip.vue +14 -9
  85. package/src/components/tables/VcsTable.vue +129 -38
  86. package/src/contentTree/LayerTree.vue +1 -1
  87. package/src/contentTree/contentTreeItem.js +13 -13
  88. package/src/contentTree/subContentTreeItem.js +1 -1
  89. package/src/contentTree/vcsObjectContentTreeItem.js +1 -1
  90. package/src/featureInfo/AddressBalloonComponent.vue +17 -1
  91. package/src/featureInfo/BalloonComponent.vue +63 -27
  92. package/src/featureInfo/balloonFeatureInfoView.js +14 -14
  93. package/src/featureInfo/balloonHelper.js +4 -0
  94. package/src/featureInfo/featureInfo.js +23 -2
  95. package/src/featureInfo/featureInfoInteraction.js +1 -1
  96. package/src/i18n/de.js +22 -0
  97. package/src/i18n/en.js +22 -0
  98. package/src/icons/+all.js +4 -0
  99. package/src/icons/WandIcon.vue +63 -0
  100. package/src/legend/legendHelper.js +18 -12
  101. package/src/legend/styleLegendItem.vue +20 -1
  102. package/src/legend/vcsLegend.vue +29 -3
  103. package/src/manager/toolbox/GroupToolboxComponent.vue +13 -1
  104. package/src/manager/toolbox/SelectToolboxComponent.vue +13 -1
  105. package/src/manager/toolbox/ToolboxManager.vue +3 -0
  106. package/src/manager/window/WindowComponent.vue +15 -2
  107. package/src/manager/window/WindowComponentHeader.vue +38 -7
  108. package/src/manager/window/WindowManager.vue +1 -0
  109. package/src/manager/window/windowManager.js +11 -1
  110. package/src/navigation/mapNavigation.vue +15 -36
  111. package/src/navigation/orientationToolsButton.vue +6 -1
  112. package/src/navigation/overviewMap.js +19 -47
  113. package/src/navigation/tiltSlider.vue +3 -0
  114. package/src/navigation/vcsCompass.vue +2 -0
  115. package/src/notifier/notifier.js +121 -0
  116. package/src/notifier/notifierComponent.vue +84 -0
  117. package/src/search/resultItem.vue +89 -0
  118. package/src/search/resultsComponent.vue +98 -0
  119. package/src/search/search.js +326 -0
  120. package/src/search/searchComponent.vue +90 -0
  121. package/src/styles/_typography.scss +3 -0
  122. package/src/styles/utils/_cursor.scss +4 -0
  123. package/src/styles/variables.scss +23 -4
  124. package/src/vcsUiApp.js +35 -1
  125. package/src/vuePlugins/vuetify.js +2 -0
  126. package/dist/assets/cesium.9489f8.js +0 -8699
  127. package/dist/assets/core.aa346a.js +0 -4
  128. package/dist/assets/index.3cd4fffa.js +0 -1
  129. package/dist/assets/ol.39651b.js +0 -439
  130. package/dist/assets/ui.15ef6a.js +0 -71
  131. package/dist/assets/vue.cbe9d8.js +0 -9
  132. package/dist/assets/vuetify.202322.js +0 -148
@@ -19,19 +19,19 @@
19
19
  >
20
20
  <!-- eslint-disable-next-line -->
21
21
  <template #item.key="{ item }">
22
- <td class="vcs-table">
22
+ <td class="vcs-table px-2 overflow-max-width" :title="$t(item.key)">
23
23
  {{ $t(item.key) }}
24
24
  </td>
25
25
  </template>
26
26
  <!-- eslint-disable-next-line -->
27
27
  <template #item.value="{ item }">
28
- <td class="vcs-table">
28
+ <td class="vcs-table px-2 overflow-max-width" :title="$t(item.value)">
29
29
  {{ $t(item.value) }}
30
30
  </td>
31
31
  </template>
32
32
  <template #footer>
33
33
  <v-divider />
34
- <v-container class="pa-2" v-if="items.length > itemsPerPageRef">
34
+ <v-container class="pa-2 vcs-pagination-bar accent" v-if="items.length > itemsPerPageRef">
35
35
  <v-row
36
36
  dense
37
37
  no-gutters
@@ -55,20 +55,23 @@
55
55
  v-for="(number, index) in itemsPerPageArray"
56
56
  :key="index"
57
57
  @click="updateItemsPerPage(number)"
58
+ style="min-height: auto; height: 24px; text-align: right;"
58
59
  >
59
60
  <v-list-item-title>{{ number }}</v-list-item-title>
60
61
  </v-list-item>
61
62
  </v-list>
62
63
  </v-menu>
63
- <span class="grey--text mx-2">{{ $t('components.vcsTable.itemsPerPage') }}</span>
64
- <span class="grey--text mx-2">{{ itemsFrom }} - {{ itemsTo }} of {{ items.length }}</span>
64
+ <span class="mx-2">{{ $t('components.vcsTable.itemsPerPage') }}</span>
65
+ <span class="mx-2">
66
+ {{ itemsFrom }} - {{ itemsTo }} {{ $t('components.vcsTable.ofItems') }} {{ numberOfItems }}
67
+ </span>
65
68
  <VcsButton
66
69
  small
67
70
  icon="mdi-chevron-left"
68
71
  @click="formerPage"
69
72
  tooltip="components.vcsTable.formerPage"
70
73
  :disabled="page < 2"
71
- class="mx-2"
74
+ class="ml-1"
72
75
  />
73
76
  <VcsButton
74
77
  small
@@ -76,7 +79,7 @@
76
79
  @click="nextPage"
77
80
  tooltip="components.vcsTable.nextPage"
78
81
  :disabled="page > numberOfPages - 1"
79
- class="mx-1"
82
+ class="ml-1"
80
83
  />
81
84
  </v-row>
82
85
  </v-container>
@@ -86,6 +89,18 @@
86
89
  </template>
87
90
  <script>
88
91
  import { getCurrentInstance, ref, computed } from 'vue';
92
+ import {
93
+ VCard,
94
+ VDivider,
95
+ VContainer,
96
+ VDataTable,
97
+ VList,
98
+ VListItem,
99
+ VListItemTitle,
100
+ VMenu,
101
+ VIcon,
102
+ VRow,
103
+ } from 'vuetify/lib';
89
104
  import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
90
105
  import VcsButton from '../buttons/VcsButton.vue';
91
106
 
@@ -116,17 +131,35 @@
116
131
  }
117
132
 
118
133
  /**
119
- * @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props|vuetify v-data-table }
134
+ * @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props v-data-table }
120
135
  * @vue-prop {string} featureId - feature's id
121
136
  * @vue-prop {Object} attributes - feature's attributes
122
137
  * @vue-prop {Array<{text: string, value: string}>} [headers] - optional array defining column names
123
138
  * @vue-prop {boolean} [showSearchbar=true] - whether to show searchbar
124
139
  * @vue-prop {string} [searchbarPlaceholder='Search'] - placeholder for searchbar
125
- * @vue-computed {Array<{key:string,value:string}>} items - from attributes derived table items
140
+ * @vue-computed {Array<TableItem>} items - from attributes derived table items
141
+ * @vue-computed {Array<TableItem>} filteredItems - array of items with search filter applied on. If search string is empty, same as items array.
142
+ * @vue-computed {number} numberOfItems - number of filtered items (depending on search).
143
+ * @vue-computed {number} numberOfPages - number of pages depending on number of items, search and itemsPerPage.
144
+ * @vue-computed {number} itemsFrom - index of first item shown on current page.
145
+ * @vue-computed {number} itemsTo - index of last item shown on current page.
126
146
  */
127
147
  export default {
128
148
  name: 'VcsTable',
129
- components: { VcsButton, VcsTreeviewSearchbar },
149
+ components: {
150
+ VcsButton,
151
+ VcsTreeviewSearchbar,
152
+ VCard,
153
+ VDataTable,
154
+ VContainer,
155
+ VDivider,
156
+ VRow,
157
+ VMenu,
158
+ VIcon,
159
+ VList,
160
+ VListItem,
161
+ VListItemTitle,
162
+ },
130
163
  props: {
131
164
  featureId: {
132
165
  type: String,
@@ -162,7 +195,13 @@
162
195
  },
163
196
  setup(props) {
164
197
  const vm = getCurrentInstance().proxy;
165
-
198
+ /**
199
+ * @type {Ref<UnwrapRef<string>>}
200
+ */
201
+ const search = ref('');
202
+ /**
203
+ * @type {ComputedRef<Array<TableItem>>}
204
+ */
166
205
  const items = computed(() => {
167
206
  return attributesToItems({
168
207
  featureId: props.featureId,
@@ -170,6 +209,33 @@
170
209
  });
171
210
  });
172
211
 
212
+ /**
213
+ * @param {any} value
214
+ * @param {string|undefined} filter
215
+ * @param {TableItem} item
216
+ * @returns {boolean}
217
+ */
218
+ const handleFilter = (value, filter, item) => {
219
+ if (filter) {
220
+ const q = filter.toLocaleLowerCase();
221
+ return [item.key, item.value].some((i) => {
222
+ const content = i.toString();
223
+ const translated = vm.$t(content);
224
+ return translated.toLowerCase().includes(q) || content.toLowerCase().includes(q);
225
+ });
226
+ }
227
+ return true;
228
+ };
229
+
230
+ /**
231
+ * @type {ComputedRef<TableItem[]>}
232
+ */
233
+ const filteredItems = computed(() => items.value.filter(item => handleFilter(item.value, search.value, item)));
234
+ const numberOfItems = computed(() => filteredItems.value.length);
235
+
236
+ /**
237
+ * @type {ComputedRef<Array<{text: string, value: string}>>}
238
+ */
173
239
  const translatedHeaders = computed(() => {
174
240
  return props.headers.map((hd) => {
175
241
  hd.text = vm.$t(hd.text);
@@ -177,42 +243,33 @@
177
243
  });
178
244
  });
179
245
 
246
+ /**
247
+ * @type {Ref<UnwrapRef<number>>}
248
+ */
180
249
  const itemsPerPageRef = ref(props.itemsPerPage);
181
250
  const numberOfPages = computed(() => {
182
- return Math.ceil(items.value.length / itemsPerPageRef.value);
251
+ return Math.ceil(numberOfItems.value / itemsPerPageRef.value);
183
252
  });
184
-
253
+ /**
254
+ * @type {Ref<UnwrapRef<number>>}
255
+ */
185
256
  const page = ref(1);
186
257
  const itemsFrom = computed(() => ((page.value - 1) * itemsPerPageRef.value) + 1);
187
258
  const itemsTo = computed(() => {
188
259
  const last = page.value * itemsPerPageRef.value;
189
- return last < items.value.length ? last : items.value.length;
260
+ return last < numberOfItems.value ? last : numberOfItems.value;
190
261
  });
191
262
 
192
- /**
193
- * @param {any} value
194
- * @param {string} search
195
- * @param {TableItem} item
196
- * @returns {boolean}
197
- */
198
- // eslint-disable-next-line default-param-last
199
- const handleFilter = (value, search = '', item) => {
200
- const q = search.toLocaleLowerCase();
201
- return [item.key, item.value].some((i) => {
202
- const content = i.toString();
203
- const translated = vm.$t(content);
204
- return translated.toLowerCase().includes(q) || content.toLowerCase().includes(q);
205
- });
206
- };
207
-
208
263
  return {
209
- search: ref(''),
264
+ search,
210
265
  page,
211
266
  items,
267
+ filteredItems,
212
268
  itemsPerPageRef,
213
269
  itemsFrom,
214
270
  itemsTo,
215
271
  numberOfPages,
272
+ numberOfItems,
216
273
  nextPage() {
217
274
  if (page.value + 1 <= numberOfPages.value) {
218
275
  page.value += 1;
@@ -233,13 +290,47 @@
233
290
  };
234
291
  </script>
235
292
 
236
- <style lang="scss" scoped>
237
- .vcs-table {
238
- td {
239
- max-width: 160px;
240
- overflow: hidden;
241
- text-overflow: ellipsis;
242
- white-space: nowrap;
293
+ <style lang="scss" scoped>
294
+ ::v-deep{
295
+ .vcs-table {
296
+ td {
297
+ overflow: hidden;
298
+ text-overflow: ellipsis;
299
+ white-space: nowrap;
300
+ &.overflow-max-width{
301
+ max-width: 160px;
302
+ }
303
+ &.v-data-table__mobile-row{
304
+ justify-content: left;
305
+ height: 27px;
306
+ min-height: auto;
307
+ }
308
+ }
309
+ th.sortable{
310
+ padding: 0 8px;
311
+ span{
312
+ vertical-align: middle;
313
+ padding: 0 4px 0 0;
314
+ }
315
+ }
316
+ }
317
+ .v-data-table__mobile-row__cell{
318
+ td.vcs-table.overflow-max-width{
319
+ max-width: 260px;
320
+ }
321
+ }
322
+ .v-btn.vcs-button--small{
323
+ height: 100% !important;
324
+ display: block;
325
+ }
326
+ }
327
+ .vcs-pagination-bar{
328
+ .vcs-button-wrap{
329
+ height: 25px;
330
+ border: 1px solid lightgrey;
331
+ padding: 0 4px;
332
+ background-color: var(--v-basic-base);
333
+ border-radius: 4px;
243
334
  }
244
335
  }
245
336
  </style>
@@ -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
  />
@@ -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);
@@ -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
@@ -31,6 +31,14 @@
31
31
  </template>
32
32
  <script>
33
33
 
34
+ import {
35
+ VIcon,
36
+ VListItem,
37
+ VListItemAvatar,
38
+ VListItemContent,
39
+ VListItemSubtitle,
40
+ VListItemTitle,
41
+ } from 'vuetify/lib';
34
42
  import BalloonComponent from './BalloonComponent.vue';
35
43
 
36
44
  /**
@@ -38,7 +46,15 @@
38
46
  */
39
47
  export default {
40
48
  name: 'AddressBalloonComponent',
41
- components: { BalloonComponent },
49
+ components: {
50
+ BalloonComponent,
51
+ VListItem,
52
+ VListItemAvatar,
53
+ VIcon,
54
+ VListItemContent,
55
+ VListItemTitle,
56
+ VListItemSubtitle,
57
+ },
42
58
  };
43
59
  </script>
44
60
 
@@ -1,40 +1,43 @@
1
1
  <template>
2
2
  <v-card
3
- class="mx-auto"
3
+ class="mx-auto mb-1 elevation-0"
4
4
  max-width="400"
5
5
  v-if="position"
6
6
  >
7
7
  <slot name="balloon-header" :attrs="{...$props, ...$attrs}">
8
- <v-list-item two-line>
8
+ <v-list-item class="px-2 pr-1 align-start">
9
9
  <v-list-item-avatar
10
10
  tile
11
- size="40"
11
+ size="20"
12
+ class="mr-2 align-self-start"
12
13
  >
13
14
  <v-icon color="primary">
14
15
  $vcsInfo
15
16
  </v-icon>
16
17
  </v-list-item-avatar>
17
- <v-list-item-content>
18
- <v-list-item-title class="text-h5">
19
- {{ title }}
18
+ <v-list-item-content class="py-2 pr-1 align-self-start">
19
+ <v-list-item-title class="text-h6">
20
+ {{ $t(balloonTitle) }}
20
21
  </v-list-item-title>
21
- <v-list-item-subtitle>{{ subtitle }}</v-list-item-subtitle>
22
+ <v-list-item-subtitle v-if="balloonSubtitle">
23
+ {{ $t(balloonSubtitle) }}
24
+ </v-list-item-subtitle>
22
25
  </v-list-item-content>
23
26
  <VcsButton
24
27
  @click.stop="close"
25
28
  small
26
29
  icon="mdi-close-thick"
27
- class="mb-8"
30
+ tooltip="components.close"
28
31
  />
29
32
  </v-list-item>
30
33
  </slot>
31
34
 
32
35
  <v-divider />
33
36
 
34
- <v-card class="overflow-y-auto" max-height="250">
37
+ <v-card class="overflow-y-auto py-2 elevation-0" max-height="250">
35
38
  <slot :attrs="{...$props, ...$attrs}">
36
39
  <v-list v-for="(value, name, index) in attributes" :key="`attribute-${index}`">
37
- <v-list-item>
40
+ <v-list-item class="px-2">
38
41
  <v-list-item-content>
39
42
  <v-list-item-title>
40
43
  {{ name }}
@@ -50,6 +53,16 @@
50
53
  <script>
51
54
 
52
55
  import { inject, onMounted, onUnmounted, watch } from 'vue';
56
+ import {
57
+ VCard, VDivider,
58
+ VIcon,
59
+ VList,
60
+ VListItem,
61
+ VListItemAvatar,
62
+ VListItemContent,
63
+ VListItemSubtitle,
64
+ VListItemTitle,
65
+ } from 'vuetify/lib';
53
66
  import { setupBalloonPositionListener } from './balloonHelper.js';
54
67
  import VcsButton from '../components/buttons/VcsButton.vue';
55
68
 
@@ -57,8 +70,8 @@
57
70
  * @description A balloon viewing feature attributes. Size dynamic dependent on number of attributes.
58
71
  * Scrollable, if more than 6 attributes are provided.
59
72
  * @vue-prop {string} featureId - feature's id
60
- * @vue-prop {string} title - balloon title
61
- * @vue-prop {string} subtitle - balloon subtitle
73
+ * @vue-prop {string} balloonTitle - balloon title
74
+ * @vue-prop {string} balloonSubtitle - balloon subtitle
62
75
  * @vue-prop {Object} attributes - feature's attributes
63
76
  * @vue-prop {Array<import("ol/coordinate").Coordinate>} position - clicked position balloon is rendered at
64
77
  * @vue-data {slot} [#balloon-header] - slot to override balloon header, $props and $attrs are passed to `attrs`
@@ -66,17 +79,28 @@
66
79
  */
67
80
  export default {
68
81
  name: 'BalloonComponent',
69
- components: { VcsButton },
82
+ components: {
83
+ VcsButton,
84
+ VCard,
85
+ VList,
86
+ VListItem,
87
+ VListItemAvatar,
88
+ VIcon,
89
+ VListItemContent,
90
+ VListItemTitle,
91
+ VListItemSubtitle,
92
+ VDivider,
93
+ },
70
94
  props: {
71
95
  featureId: {
72
96
  type: String,
73
97
  required: true,
74
98
  },
75
- title: {
99
+ balloonTitle: {
76
100
  type: String,
77
101
  required: true,
78
102
  },
79
- subtitle: {
103
+ balloonSubtitle: {
80
104
  type: String,
81
105
  required: true,
82
106
  },
@@ -125,16 +149,28 @@
125
149
  };
126
150
  </script>
127
151
 
128
- <style>
129
- .balloon:before {
130
- content: "";
131
- position: absolute;
132
- bottom: -20px;
133
- left: 40px;
134
- border-width: 20px 20px 0;
135
- border-style: solid;
136
- border-color: white transparent;
137
- display: block;
138
- width: 0;
139
- }
152
+ <style lang="scss">
153
+ .balloon hr:first-child {
154
+ display: none;
155
+ }
156
+ .balloon:after {
157
+ content: "";
158
+ position: absolute;
159
+ bottom: -20px;
160
+ left: 40px;
161
+ border-width: 20px 20px 0;
162
+ border-style: solid;
163
+ display: block;
164
+ width: 0;
165
+ }
166
+ .v-sheet.theme--light.balloon:after{
167
+ border-color: #FFFFFF transparent;
168
+ }
169
+ .v-sheet.theme--dark.balloon:after{
170
+ border-color: #1E1E1E transparent;
171
+ }
172
+ .balloon .v-list-item .v-list-item__title,
173
+ .balloon .v-list-item .v-list-item__subtitle {
174
+ line-height: 18px;
175
+ }
140
176
  </style>
@@ -26,14 +26,14 @@ export function extractNestedKey(key, attrs, defaultValue = null) {
26
26
 
27
27
  /**
28
28
  * @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
29
+ * @property {string} [balloonTitle] - optional title to overwrite default (layerName). Can be attribute key (nested key using '.'), i18n key or text
30
+ * @property {string} [balloonSubtitle] - optional window title to overwrite default (featureId). Can be attribute key (nested key using '.'), i18n key or text
31
31
  */
32
32
 
33
33
  /**
34
34
  * @typedef {FeatureInfoProps} BalloonFeatureInfoViewProps
35
- * @property {string} title
36
- * @property {string} subtitle
35
+ * @property {string} balloonTitle
36
+ * @property {string} balloonSubtitle
37
37
  * @property {import("ol/coordinate").Coordinate} position
38
38
  */
39
39
 
@@ -87,12 +87,12 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
87
87
  /**
88
88
  * @type {string}
89
89
  */
90
- this.title = options.title;
90
+ this.balloonTitle = options.balloonTitle;
91
91
 
92
92
  /**
93
93
  * @type {string}
94
94
  */
95
- this.subtitle = options.subtitle;
95
+ this.balloonSubtitle = options.balloonSubtitle;
96
96
  }
97
97
 
98
98
  /**
@@ -105,11 +105,11 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
105
105
  return {
106
106
  ...properties,
107
107
  position: featureInfo.position ?? getPositionFromFeature(featureInfo.feature),
108
- title: this.title ?
109
- extractNestedKey(this.title, properties.attributes, this.title) :
108
+ balloonTitle: this.balloonTitle != null ?
109
+ extractNestedKey(this.balloonTitle, properties.attributes, this.balloonTitle) :
110
110
  properties.layerProperties.title,
111
- subtitle: this.subtitle ?
112
- extractNestedKey(this.subtitle, properties.attributes, this.subtitle) :
111
+ balloonSubtitle: this.balloonSubtitle != null ?
112
+ extractNestedKey(this.balloonSubtitle, properties.attributes, this.balloonSubtitle) :
113
113
  properties.featureId,
114
114
  };
115
115
  }
@@ -138,11 +138,11 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
138
138
  */
139
139
  toJSON() {
140
140
  const config = super.toJSON();
141
- if (this.title) {
142
- config.title = this.title;
141
+ if (this.balloonTitle) {
142
+ config.balloonTitle = this.balloonTitle;
143
143
  }
144
- if (this.subtitle) {
145
- config.subtitle = this.subtitle;
144
+ if (this.balloonSubtitle) {
145
+ config.balloonSubtitle = this.balloonSubtitle;
146
146
  }
147
147
  return config;
148
148
  }
@@ -99,6 +99,10 @@ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosi
99
99
 
100
100
  const map = app.maps.activeMap;
101
101
  if (map instanceof CesiumMap) {
102
+ if (!position[2]) {
103
+ const [position3D] = await map.getHeightFromTerrain([position]);
104
+ position[2] = position3D[2];
105
+ }
102
106
  const wgs84Position = Projection.mercatorToWgs84(position);
103
107
  const cartesian = Cartographic.toCartesian(Cartographic.fromDegrees(...wgs84Position));
104
108
  listeners.push(map.getScene().postRender.addEventListener((scene) => {