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

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/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 +26 -24
  6. package/config/dev.config.json +8 -0
  7. package/config/www.config.json +102 -17
  8. package/dist/assets/cesium.2e288a.js +137226 -0
  9. package/dist/assets/cesium.js +1 -1
  10. package/dist/assets/core.8014d3.js +14473 -0
  11. package/dist/assets/core.js +1 -1
  12. package/dist/assets/index.3f74fa92.js +1 -0
  13. package/dist/assets/ol.31c3a5.js +44279 -0
  14. package/dist/assets/ol.js +1 -1
  15. package/dist/assets/ui.36f84f.css +1 -0
  16. package/dist/assets/ui.36f84f.js +16101 -0
  17. package/dist/assets/ui.js +1 -1
  18. package/dist/assets/vue.a39c10.js +4675 -0
  19. package/dist/assets/vue.js +5 -2
  20. package/dist/assets/{vuetify.202322.css → vuetify.378637.css} +1 -1
  21. package/dist/assets/vuetify.378637.js +21019 -0
  22. package/dist/assets/vuetify.js +5 -2
  23. package/dist/index.html +1 -1
  24. package/index.js +4 -1
  25. package/package.json +10 -10
  26. package/plugins/@vcmap/pluginExample/index.js +14 -0
  27. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +93 -67
  28. package/plugins/@vcmap/project-selector/ContextsListComponent.vue +8 -1
  29. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +27 -1
  30. package/plugins/@vcmap/search-nominatim/LICENSE.md +14 -0
  31. package/plugins/@vcmap/search-nominatim/README.md +2 -0
  32. package/plugins/@vcmap/search-nominatim/config.json +4 -0
  33. package/plugins/@vcmap/search-nominatim/index.js +26 -0
  34. package/plugins/@vcmap/search-nominatim/nominatim.js +170 -0
  35. package/plugins/@vcmap/search-nominatim/package.json +43 -0
  36. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +24 -0
  37. package/plugins/buttonExamples/ButtonExamples.vue +28 -1
  38. package/plugins/categoryTest/Categories.vue +16 -0
  39. package/plugins/categoryTest/Category.vue +30 -4
  40. package/plugins/example/mySuperComponent.vue +12 -1
  41. package/plugins/package.json +2 -1
  42. package/plugins/simple-graph/SimpleGraphComponent.vue +5 -11
  43. package/plugins/test/allIconsComponent.vue +16 -0
  44. package/plugins/test/editor.vue +3 -0
  45. package/plugins/test/emptyComponent.vue +3 -0
  46. package/plugins/test/vcsContent.vue +3 -0
  47. package/src/actions/actionHelper.js +103 -2
  48. package/src/actions/styleSelector.vue +9 -0
  49. package/src/application/VcsApp.vue +77 -9
  50. package/src/application/VcsAttributions.vue +63 -0
  51. package/src/application/VcsAttributionsFooter.vue +87 -0
  52. package/src/application/{Navbar.vue → VcsNavbar.vue} +35 -2
  53. package/src/application/VcsSettings.vue +4 -0
  54. package/src/application/attributionsHelper.js +150 -0
  55. package/src/application/vcsAppWrapper.vue +5 -1
  56. package/src/components/buttons/VcsActionButtonList.vue +8 -1
  57. package/src/components/buttons/VcsButton.vue +7 -1
  58. package/src/components/form-inputs-controls/VcsCheckbox.vue +7 -2
  59. package/src/components/form-inputs-controls/VcsColorPicker.vue +4 -0
  60. package/src/components/form-inputs-controls/VcsFormSection.vue +48 -2
  61. package/src/components/form-inputs-controls/VcsRadio.vue +7 -1
  62. package/src/components/form-inputs-controls/VcsSelect.vue +5 -1
  63. package/src/components/form-inputs-controls/VcsTextArea.vue +2 -0
  64. package/src/components/form-inputs-controls/VcsTextField.vue +6 -2
  65. package/src/components/lists/VcsActionList.vue +12 -1
  66. package/src/components/lists/VcsTreeview.vue +6 -1
  67. package/src/components/lists/VcsTreeviewLeaf.vue +5 -1
  68. package/src/components/lists/VcsTreeviewSearchbar.vue +5 -0
  69. package/src/components/notification/VcsTooltip.vue +14 -9
  70. package/src/components/tables/VcsTable.vue +117 -38
  71. package/src/featureInfo/AddressBalloonComponent.vue +17 -1
  72. package/src/featureInfo/BalloonComponent.vue +57 -23
  73. package/src/featureInfo/balloonFeatureInfoView.js +2 -2
  74. package/src/featureInfo/featureInfo.js +10 -2
  75. package/src/i18n/de.js +15 -0
  76. package/src/i18n/en.js +15 -0
  77. package/src/legend/legendHelper.js +18 -12
  78. package/src/legend/styleLegendItem.vue +20 -1
  79. package/src/legend/vcsLegend.vue +29 -3
  80. package/src/manager/toolbox/GroupToolboxComponent.vue +13 -1
  81. package/src/manager/toolbox/SelectToolboxComponent.vue +13 -1
  82. package/src/manager/toolbox/ToolboxManager.vue +3 -0
  83. package/src/manager/window/WindowComponent.vue +6 -0
  84. package/src/manager/window/WindowComponentHeader.vue +6 -1
  85. package/src/navigation/mapNavigation.vue +15 -36
  86. package/src/navigation/orientationToolsButton.vue +6 -1
  87. package/src/navigation/overviewMap.js +10 -39
  88. package/src/navigation/tiltSlider.vue +3 -0
  89. package/src/navigation/vcsCompass.vue +2 -0
  90. package/src/search/resultItem.vue +89 -0
  91. package/src/search/resultsComponent.vue +98 -0
  92. package/src/search/search.js +326 -0
  93. package/src/search/searchComponent.vue +90 -0
  94. package/src/styles/_typography.scss +3 -0
  95. package/src/styles/utils/_cursor.scss +4 -0
  96. package/src/styles/variables.scss +4 -1
  97. package/src/vcsUiApp.js +16 -0
  98. package/dist/assets/cesium.9489f8.js +0 -8699
  99. package/dist/assets/core.aa346a.js +0 -4
  100. package/dist/assets/index.3cd4fffa.js +0 -1
  101. package/dist/assets/ol.39651b.js +0 -439
  102. package/dist/assets/ui.15ef6a.css +0 -1
  103. package/dist/assets/ui.15ef6a.js +0 -71
  104. package/dist/assets/vue.cbe9d8.js +0 -9
  105. package/dist/assets/vuetify.202322.js +0 -148
@@ -92,6 +92,16 @@
92
92
  <script>
93
93
 
94
94
  import { getStringColor } from '@vcmap/core';
95
+ import {
96
+ VFlex,
97
+ VImg,
98
+ VLayout,
99
+ VList,
100
+ VListItem,
101
+ VListItemContent,
102
+ VListItemIcon,
103
+ VListItemTitle,
104
+ } from 'vuetify/lib';
95
105
  import { StyleRowType, getImageSrcFromShape } from './legendHelper.js';
96
106
 
97
107
  /**
@@ -100,7 +110,16 @@
100
110
  */
101
111
  export default {
102
112
  name: 'StyleLegendItem',
103
- components: { },
113
+ components: {
114
+ VList,
115
+ VLayout,
116
+ VFlex,
117
+ VListItem,
118
+ VListItemIcon,
119
+ VImg,
120
+ VListItemContent,
121
+ VListItemTitle,
122
+ },
104
123
  props: {
105
124
  item: {
106
125
  type: Object,
@@ -50,17 +50,38 @@
50
50
 
51
51
  <script>
52
52
 
53
+ import {
54
+ VCard,
55
+ VExpansionPanels,
56
+ VExpansionPanel,
57
+ VExpansionPanelHeader,
58
+ VExpansionPanelContent,
59
+ VIcon,
60
+ VList,
61
+ VImg,
62
+ } from 'vuetify/lib';
53
63
  import { LegendType } from './legendHelper.js';
54
64
  import StyleLegendItem from './styleLegendItem.vue';
55
65
  import VcsTreeviewLeaf from '../components/lists/VcsTreeviewLeaf.vue';
56
66
 
57
67
  /**
58
68
  * @description A component rendering configured legend information for active layers.
59
- * @vue-prop {import("vue").Reactive<{string,LegendEntry}>} entries - legend entries to be displayed
69
+ * @vue-prop {import("vue").Ref<Array<LegendEntry>>} entries - legend entries to be displayed
60
70
  */
61
71
  export default {
62
72
  name: 'VcsLegend',
63
- components: { VcsTreeviewLeaf, StyleLegendItem },
73
+ components: {
74
+ VcsTreeviewLeaf,
75
+ StyleLegendItem,
76
+ VCard,
77
+ VExpansionPanels,
78
+ VExpansionPanel,
79
+ VExpansionPanelHeader,
80
+ VExpansionPanelContent,
81
+ VIcon,
82
+ VList,
83
+ VImg,
84
+ },
64
85
  props: {
65
86
  entries: {
66
87
  type: Object,
@@ -85,8 +106,13 @@
85
106
  };
86
107
  </script>
87
108
 
88
- <style scoped>
109
+ <style lang="scss" scoped>
89
110
  .v-list-item--dense {
90
111
  height: 32px;
91
112
  }
113
+ ::v-deep {
114
+ .treeview-label {
115
+ max-width: 189px;
116
+ }
117
+ }
92
118
  </style>
@@ -62,6 +62,12 @@
62
62
  </style>
63
63
  <script>
64
64
  import { computed, ref } from 'vue';
65
+ import {
66
+ VMenu,
67
+ VIcon,
68
+ VToolbar,
69
+ VToolbarItems,
70
+ } from 'vuetify/lib';
65
71
  import VcsButton from '../../components/buttons/VcsButton.vue';
66
72
  import { getComponentsByOrder } from './toolboxManager.js';
67
73
 
@@ -78,7 +84,13 @@
78
84
  */
79
85
  export default {
80
86
  name: 'ToolboxActionGroup',
81
- components: { VcsButton },
87
+ components: {
88
+ VcsButton,
89
+ VMenu,
90
+ VIcon,
91
+ VToolbar,
92
+ VToolbarItems,
93
+ },
82
94
  props: {
83
95
  group: {
84
96
  type: Object,
@@ -88,6 +88,12 @@
88
88
  </style>
89
89
  <script>
90
90
  import { ref, computed } from 'vue';
91
+ import {
92
+ VMenu,
93
+ VIcon,
94
+ VToolbar,
95
+ VToolbarItems,
96
+ } from 'vuetify/lib';
91
97
  import VcsButton from '../../components/buttons/VcsButton.vue';
92
98
 
93
99
  /**
@@ -98,7 +104,13 @@
98
104
  */
99
105
  export default {
100
106
  name: 'ToolboxActionSelect',
101
- components: { VcsButton },
107
+ components: {
108
+ VcsButton,
109
+ VMenu,
110
+ VIcon,
111
+ VToolbar,
112
+ VToolbarItems,
113
+ },
102
114
  props: {
103
115
  group: {
104
116
  type: Object,
@@ -72,6 +72,7 @@
72
72
  <script>
73
73
  import { inject, ref, computed, watch, onUnmounted } from 'vue';
74
74
  import { ButtonLocation, vcsAppSymbol } from '@vcmap/ui';
75
+ import { VToolbar, VToolbarItems } from 'vuetify/lib';
75
76
  import { getComponentsByOrder, ToolboxType } from './toolboxManager.js';
76
77
  import ToolboxActionSelect from './SelectToolboxComponent.vue';
77
78
  import ToolboxActionGroup from './GroupToolboxComponent.vue';
@@ -104,6 +105,8 @@
104
105
  ToolboxActionSelect,
105
106
  ToolboxActionGroup,
106
107
  VcsButton,
108
+ VToolbar,
109
+ VToolbarItems,
107
110
  },
108
111
  setup() {
109
112
  const app = inject('vcsApp');
@@ -44,9 +44,15 @@
44
44
  } from 'vue';
45
45
  import { fromEvent } from 'rxjs';
46
46
  import { switchMap, take, map, tap } from 'rxjs/operators';
47
+ import { VDivider, VSheet } from 'vuetify/lib';
47
48
  import { WindowSlot } from './windowManager.js';
48
49
 
49
50
  export default {
51
+ name: 'WindowComponent',
52
+ components: {
53
+ VSheet,
54
+ VDivider,
55
+ },
50
56
  props: {
51
57
  windowState: {
52
58
  type: Object,
@@ -25,10 +25,15 @@
25
25
  </style>
26
26
 
27
27
  <script>
28
+ import { VIcon } from 'vuetify/lib';
28
29
  import VcsButton from '../../components/buttons/VcsButton.vue';
29
30
 
30
31
  export default {
31
- components: { VcsButton },
32
+ name: 'WindowComponentHeader',
33
+ components: {
34
+ VcsButton,
35
+ VIcon,
36
+ },
32
37
  props: {
33
38
  windowState: {
34
39
  type: Object,
@@ -37,8 +37,8 @@
37
37
 
38
38
  <script>
39
39
  import { computed, inject, ref, reactive, onUnmounted } from 'vue';
40
- import { ObliqueMap, CesiumMap, OpenlayersMap } from '@vcmap/core';
41
- import { unByKey } from 'ol/Observable.js';
40
+ import { ObliqueMap, CesiumMap } from '@vcmap/core';
41
+ import { VContainer, VRow } from 'vuetify/lib';
42
42
  import { createOverviewMapAction } from '../actions/actionHelper.js';
43
43
  import { getWindowComponentOptions } from './overviewMap.js';
44
44
  import VcsCompass from './vcsCompass.vue';
@@ -65,32 +65,6 @@
65
65
  return OrientationToolsViewMode.TWO_D;
66
66
  }
67
67
 
68
- /**
69
- * @param {VcsMap} map
70
- * @param {Ref<number>} headingRef
71
- * @param {Ref<number>} tiltRef
72
- * @returns {function():void}
73
- */
74
- function mapPostRender(map, headingRef, tiltRef) {
75
- const handler = () => {
76
- const vp = map.getViewpointSync();
77
- if (vp) {
78
- headingRef.value = vp.heading;
79
- tiltRef.value = vp.pitch;
80
- }
81
- };
82
-
83
- if (map instanceof CesiumMap) {
84
- return map.getScene().postRender.addEventListener(handler);
85
- } else if (map instanceof ObliqueMap || map instanceof OpenlayersMap) {
86
- const key = map.olMap.on('postrender', handler);
87
- return () => {
88
- unByKey(key);
89
- };
90
- }
91
- return () => {};
92
- }
93
-
94
68
  /**
95
69
  * @param {VcsMap} map
96
70
  * @param {boolean} [out=false]
@@ -117,6 +91,8 @@
117
91
  TiltSlider,
118
92
  VcsZoomButton,
119
93
  VcsCompass,
94
+ VContainer,
95
+ VRow,
120
96
  },
121
97
  setup() {
122
98
  /** @type {VcsUiApp} */
@@ -125,17 +101,19 @@
125
101
  const headingRef = ref(0);
126
102
  const tiltRef = ref(0);
127
103
 
128
- let postRenderHandler = () => {};
129
-
130
- const setActiveMap = (map) => {
104
+ const handleRenderEvent = ({ map }) => {
131
105
  viewMode.value = getViewModeForMap(map);
132
- postRenderHandler();
133
- postRenderHandler = mapPostRender(map, headingRef, tiltRef);
106
+ const vp = map.getViewpointSync();
107
+ if (vp) {
108
+ headingRef.value = vp.heading;
109
+ tiltRef.value = vp.pitch;
110
+ }
134
111
  };
135
112
 
136
- app.maps.mapActivated.addEventListener(setActiveMap);
137
- setActiveMap(app.maps.activeMap);
138
-
113
+ const postRenderHandler = app.maps.postRender.addEventListener(handleRenderEvent);
114
+ if (app.maps.activeMap) {
115
+ handleRenderEvent({ map: app.maps.activeMap });
116
+ }
139
117
  const heading = computed({
140
118
  get() { return headingRef.value; },
141
119
  async set(headingValue) {
@@ -169,6 +147,7 @@
169
147
  if (destroy) {
170
148
  destroy();
171
149
  }
150
+ postRenderHandler();
172
151
  });
173
152
 
174
153
  return {
@@ -23,6 +23,7 @@
23
23
  }
24
24
  </style>
25
25
  <script>
26
+ import { VCard, VIcon } from 'vuetify/lib';
26
27
  import VcsTooltip from '../components/notification/VcsTooltip.vue';
27
28
 
28
29
  /**
@@ -33,7 +34,11 @@
33
34
  */
34
35
  export default {
35
36
  name: 'OrientationToolsButton',
36
- components: { VcsTooltip },
37
+ components: {
38
+ VcsTooltip,
39
+ VCard,
40
+ VIcon,
41
+ },
37
42
  props: {
38
43
  icon: {
39
44
  type: String,
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  OpenlayersMap,
3
- CesiumMap,
4
3
  ObliqueMap,
5
4
  VectorLayer,
6
5
  VectorStyleItem,
@@ -308,12 +307,10 @@ class OverviewMap {
308
307
  }
309
308
  this._active = true;
310
309
  const { activeMap } = this._app.maps;
311
- if (activeMap instanceof CesiumMap) {
312
- await this._initializeForCesium(activeMap);
313
- } else if (activeMap instanceof OpenlayersMap) {
314
- await this._initializeForOpenlayers(activeMap);
315
- } else if (activeMap instanceof ObliqueMap) {
310
+ if (activeMap instanceof ObliqueMap) {
316
311
  await this._initializeForOblique(activeMap);
312
+ } else {
313
+ await this._initializePostRenderHandler(activeMap);
317
314
  }
318
315
  }
319
316
 
@@ -343,45 +340,19 @@ class OverviewMap {
343
340
  }
344
341
 
345
342
  /**
346
- * @param {import("@vcmap/core").CesiumMap} cesiumMap
343
+ * @param {import("@vcmap/core").VcsMap} map
347
344
  * @returns {Promise<void>}
348
345
  * @private
349
346
  */
350
- async _initializeForCesium(cesiumMap) {
347
+ async _initializePostRenderHandler(map) {
351
348
  if (!this._cameraIconLayer) {
352
349
  this._setupCameraIconLayer();
353
350
  }
354
- if (cesiumMap.initialized) {
355
- const cesiumViewer = cesiumMap.getCesiumWidget();
356
- const cesiumScene = cesiumViewer.scene;
357
- const navRemover = this._addNavigationListener(cesiumMap);
358
- const prRemover = cesiumScene.postRender.addEventListener(this._addCameraFeature, this);
359
- const cleanupTasks = () => {
360
- prRemover();
361
- navRemover();
362
- this._cameraIconLayer.deactivate();
363
- };
364
- this._listeners.push(cleanupTasks);
365
- await this._cameraIconLayer.activate();
366
- }
367
- }
368
-
369
- /**
370
- * @param {import("@vcmap/core").OpenlayersMap} map
371
- * @returns {Promise<void>}
372
- * @private
373
- */
374
- async _initializeForOpenlayers(map) {
375
- if (!this._cameraIconLayer) {
376
- this._setupCameraIconLayer();
377
- }
378
- const { olMap } = map;
379
- const navListener = this._addNavigationListener(map);
380
- const prUnKey = olMap.on('postrender', this._addCameraFeature.bind(this));
381
-
351
+ const navRemover = this._addNavigationListener(map);
352
+ const prRemover = map.postRender.addEventListener(this._addCameraFeature.bind(this));
382
353
  const cleanupTasks = () => {
383
- unByKey(prUnKey);
384
- navListener();
354
+ prRemover();
355
+ navRemover();
385
356
  this._cameraIconLayer.deactivate();
386
357
  };
387
358
  this._listeners.push(cleanupTasks);
@@ -543,7 +514,7 @@ class OverviewMap {
543
514
  * @private
544
515
  */
545
516
  _addCameraFeature() {
546
- const viewpoint = this._app.maps.activeMap.getViewpointSync();
517
+ const viewpoint = this._app.maps.activeMap?.getViewpointSync();
547
518
  if (!viewpoint || !viewpoint.isValid() || viewpoint.equals(this._cachedViewpoint)) {
548
519
  return;
549
520
  }
@@ -47,6 +47,7 @@
47
47
  </style>
48
48
  <script>
49
49
  import { clamp } from 'ol/math.js';
50
+ import { VCard, VSlider } from 'vuetify/lib';
50
51
  import VcsTooltip from '../components/notification/VcsTooltip.vue';
51
52
 
52
53
  /**
@@ -58,6 +59,8 @@
58
59
  name: 'TiltSlider',
59
60
  components: {
60
61
  VcsTooltip,
62
+ VCard,
63
+ VSlider,
61
64
  },
62
65
  props: {
63
66
  value: {
@@ -33,6 +33,7 @@
33
33
  import { fromEvent, merge, of, Subject } from 'rxjs';
34
34
  import { takeUntil, tap } from 'rxjs/operators';
35
35
 
36
+ import { VSheet } from 'vuetify/lib';
36
37
  import MapNavCompass from './mapNavCompass.vue';
37
38
 
38
39
  /**
@@ -45,6 +46,7 @@
45
46
  name: 'VcsCompass',
46
47
  components: {
47
48
  MapNavCompass,
49
+ VSheet,
48
50
  },
49
51
  props: {
50
52
  viewMode: {
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div
3
+ class="ma-1 d-flex flex-row align-center"
4
+ v-if="item"
5
+ >
6
+ <v-list-item-icon v-if="item.icon" class="px-1">
7
+ <v-icon>
8
+ {{ item.icon }}
9
+ </v-icon>
10
+ </v-list-item-icon>
11
+ <div
12
+ class="px-2 d-flex align-center"
13
+ :title="$t('search.select')"
14
+ >
15
+ <span v-html="marked" />
16
+ </div>
17
+ <VcsActionButtonList
18
+ v-if="hasActions"
19
+ :actions="item.actions"
20
+ :block-overflow="true"
21
+ :overflow-count="2"
22
+ small
23
+ right
24
+ />
25
+ </div>
26
+ </template>
27
+
28
+ <script>
29
+ import { computed } from 'vue';
30
+ import { VIcon, VListItemIcon } from 'vuetify/lib';
31
+ import VcsActionButtonList from '../components/buttons/VcsActionButtonList.vue';
32
+
33
+ /**
34
+ * @param {string} text
35
+ * @param {string} query
36
+ * @returns {string}
37
+ */
38
+ function markText(text, query) {
39
+ let replacement = text;
40
+ if (query) {
41
+ const partials = query.split(/[.,\s]/)
42
+ .filter(partial => partial.trim());
43
+ partials.forEach((partial) => {
44
+ replacement = replacement
45
+ .replaceAll(new RegExp(`(^|[^>])(${partial})`, 'ig'), '<span class="primary--text">$2</span>');
46
+ });
47
+ }
48
+ return replacement;
49
+ }
50
+
51
+ /**
52
+ * ResultItem with optional icon or image, title and optional actions
53
+ * @vue-prop {string} query - The query string to mark results
54
+ * @vue-prop {ResultItem} resultItem
55
+ * @vue-computed {boolean} hasActions - Whether result item has actions or not
56
+ * @vue-computed {string} marked - The result item's title with highlighted query string
57
+ */
58
+ export default {
59
+ name: 'ResultItem',
60
+ components: {
61
+ VcsActionButtonList,
62
+ VListItemIcon,
63
+ VIcon,
64
+ },
65
+ props: {
66
+ query: {
67
+ type: String,
68
+ default: '',
69
+ },
70
+ item: {
71
+ type: Object,
72
+ required: true,
73
+ },
74
+ },
75
+ setup(props) {
76
+ const hasActions = computed(() => props.item?.actions?.length > 0);
77
+ const marked = computed(() => markText(props.item.title, props.query));
78
+
79
+ return {
80
+ hasActions,
81
+ marked,
82
+ };
83
+ },
84
+ };
85
+ </script>
86
+
87
+ <style lang="scss" scoped>
88
+
89
+ </style>
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <v-list dense class="ma-0 overflow-y-auto vcs-search-results">
3
+ <v-list-item-group
4
+ v-model="highlighted"
5
+ >
6
+ <v-list-item
7
+ v-for="(item, index) in results"
8
+ :key="index"
9
+ color="secondary"
10
+ class="px-0"
11
+ >
12
+ <v-list-item-content>
13
+ <ResultItem
14
+ :item="item"
15
+ :query="query"
16
+ class="cursor-pointer"
17
+ />
18
+ <v-divider
19
+ v-if="index < results.length - 1"
20
+ :key="index"
21
+ />
22
+ </v-list-item-content>
23
+ </v-list-item>
24
+ </v-list-item-group>
25
+ </v-list>
26
+ </template>
27
+
28
+ <script>
29
+ import { inject, onUnmounted, ref, computed } from 'vue';
30
+ import { VDivider, VList, VListItem, VListItemContent, VListItemGroup } from 'vuetify/lib';
31
+ import ResultItem from './resultItem.vue';
32
+
33
+ /**
34
+ * ResultsComponent listing all available result items in a scrollable list
35
+ * @vue-prop {string} query - The query string forwarded to mark results within resultItem component.
36
+ * @vue-prop {Array<ResultItem>} results - Array of results.
37
+ * @vue-computed {import("vue").Ref<string>} highlighted - The highlighted result item. Updates also on feature select.
38
+ */
39
+ export default {
40
+ name: 'ResultsComponent',
41
+ components: {
42
+ ResultItem,
43
+ VList,
44
+ VListItemGroup,
45
+ VListItem,
46
+ VListItemContent,
47
+ VDivider,
48
+ },
49
+ props: {
50
+ query: {
51
+ type: String,
52
+ default: '',
53
+ },
54
+ results: {
55
+ type: Array,
56
+ required: true,
57
+ },
58
+ },
59
+ setup(props) {
60
+ const highlightedRef = ref(-1);
61
+ /** @type {VcsUiApp} */
62
+ const app = inject('vcsApp');
63
+ const selectedListener = app.featureInfo.featureChanged.addEventListener((feature) => {
64
+ if (highlightedRef.value >= 0) {
65
+ if (feature && props.results[highlightedRef.value].feature === feature) {
66
+ return;
67
+ }
68
+ highlightedRef.value = -1;
69
+ } else if (feature) {
70
+ highlightedRef.value = props.results.findIndex(r => r.feature === feature);
71
+ }
72
+ });
73
+
74
+ onUnmounted(() => {
75
+ selectedListener();
76
+ });
77
+
78
+ return {
79
+ highlighted: computed({
80
+ get() { return highlightedRef.value; },
81
+ set(value) {
82
+ highlightedRef.value = value;
83
+ if (value >= 0) {
84
+ const item = props.results[value];
85
+ item.clicked();
86
+ }
87
+ },
88
+ }),
89
+ };
90
+ },
91
+ };
92
+ </script>
93
+
94
+ <style scoped>
95
+ .vcs-search-results {
96
+ max-height: 400px;
97
+ }
98
+ </style>