@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.
- package/README.md +33 -31
- package/build/build.js +9 -0
- package/build/buildHelpers.js +12 -10
- package/build/commonViteConfig.js +3 -10
- package/config/base.config.json +26 -24
- package/config/dev.config.json +8 -0
- package/config/www.config.json +102 -17
- package/dist/assets/cesium.2e288a.js +137226 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.8014d3.js +14473 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/index.3f74fa92.js +1 -0
- package/dist/assets/ol.31c3a5.js +44279 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.36f84f.css +1 -0
- package/dist/assets/ui.36f84f.js +16101 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.a39c10.js +4675 -0
- package/dist/assets/vue.js +5 -2
- package/dist/assets/{vuetify.202322.css → vuetify.378637.css} +1 -1
- package/dist/assets/vuetify.378637.js +21019 -0
- package/dist/assets/vuetify.js +5 -2
- package/dist/index.html +1 -1
- package/index.js +4 -1
- package/package.json +10 -10
- package/plugins/@vcmap/pluginExample/index.js +14 -0
- package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +93 -67
- package/plugins/@vcmap/project-selector/ContextsListComponent.vue +8 -1
- package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +27 -1
- package/plugins/@vcmap/search-nominatim/LICENSE.md +14 -0
- package/plugins/@vcmap/search-nominatim/README.md +2 -0
- package/plugins/@vcmap/search-nominatim/config.json +4 -0
- package/plugins/@vcmap/search-nominatim/index.js +26 -0
- package/plugins/@vcmap/search-nominatim/nominatim.js +170 -0
- package/plugins/@vcmap/search-nominatim/package.json +43 -0
- package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +24 -0
- package/plugins/buttonExamples/ButtonExamples.vue +28 -1
- package/plugins/categoryTest/Categories.vue +16 -0
- package/plugins/categoryTest/Category.vue +30 -4
- package/plugins/example/mySuperComponent.vue +12 -1
- package/plugins/package.json +2 -1
- package/plugins/simple-graph/SimpleGraphComponent.vue +5 -11
- package/plugins/test/allIconsComponent.vue +16 -0
- package/plugins/test/editor.vue +3 -0
- package/plugins/test/emptyComponent.vue +3 -0
- package/plugins/test/vcsContent.vue +3 -0
- package/src/actions/actionHelper.js +103 -2
- package/src/actions/styleSelector.vue +9 -0
- package/src/application/VcsApp.vue +77 -9
- package/src/application/VcsAttributions.vue +63 -0
- package/src/application/VcsAttributionsFooter.vue +87 -0
- package/src/application/{Navbar.vue → VcsNavbar.vue} +35 -2
- package/src/application/VcsSettings.vue +4 -0
- package/src/application/attributionsHelper.js +150 -0
- package/src/application/vcsAppWrapper.vue +5 -1
- package/src/components/buttons/VcsActionButtonList.vue +8 -1
- package/src/components/buttons/VcsButton.vue +7 -1
- package/src/components/form-inputs-controls/VcsCheckbox.vue +7 -2
- package/src/components/form-inputs-controls/VcsColorPicker.vue +4 -0
- package/src/components/form-inputs-controls/VcsFormSection.vue +48 -2
- package/src/components/form-inputs-controls/VcsRadio.vue +7 -1
- package/src/components/form-inputs-controls/VcsSelect.vue +5 -1
- package/src/components/form-inputs-controls/VcsTextArea.vue +2 -0
- package/src/components/form-inputs-controls/VcsTextField.vue +6 -2
- package/src/components/lists/VcsActionList.vue +12 -1
- package/src/components/lists/VcsTreeview.vue +6 -1
- package/src/components/lists/VcsTreeviewLeaf.vue +5 -1
- package/src/components/lists/VcsTreeviewSearchbar.vue +5 -0
- package/src/components/notification/VcsTooltip.vue +14 -9
- package/src/components/tables/VcsTable.vue +117 -38
- package/src/featureInfo/AddressBalloonComponent.vue +17 -1
- package/src/featureInfo/BalloonComponent.vue +57 -23
- package/src/featureInfo/balloonFeatureInfoView.js +2 -2
- package/src/featureInfo/featureInfo.js +10 -2
- package/src/i18n/de.js +15 -0
- package/src/i18n/en.js +15 -0
- package/src/legend/legendHelper.js +18 -12
- package/src/legend/styleLegendItem.vue +20 -1
- package/src/legend/vcsLegend.vue +29 -3
- package/src/manager/toolbox/GroupToolboxComponent.vue +13 -1
- package/src/manager/toolbox/SelectToolboxComponent.vue +13 -1
- package/src/manager/toolbox/ToolboxManager.vue +3 -0
- package/src/manager/window/WindowComponent.vue +6 -0
- package/src/manager/window/WindowComponentHeader.vue +6 -1
- package/src/navigation/mapNavigation.vue +15 -36
- package/src/navigation/orientationToolsButton.vue +6 -1
- package/src/navigation/overviewMap.js +10 -39
- package/src/navigation/tiltSlider.vue +3 -0
- package/src/navigation/vcsCompass.vue +2 -0
- package/src/search/resultItem.vue +89 -0
- package/src/search/resultsComponent.vue +98 -0
- package/src/search/search.js +326 -0
- package/src/search/searchComponent.vue +90 -0
- package/src/styles/_typography.scss +3 -0
- package/src/styles/utils/_cursor.scss +4 -0
- package/src/styles/variables.scss +4 -1
- package/src/vcsUiApp.js +16 -0
- package/dist/assets/cesium.9489f8.js +0 -8699
- package/dist/assets/core.aa346a.js +0 -4
- package/dist/assets/index.3cd4fffa.js +0 -1
- package/dist/assets/ol.39651b.js +0 -439
- package/dist/assets/ui.15ef6a.css +0 -1
- package/dist/assets/ui.15ef6a.js +0 -71
- package/dist/assets/vue.cbe9d8.js +0 -9
- package/dist/assets/vuetify.202322.js +0 -148
@@ -0,0 +1,326 @@
|
|
1
|
+
import {
|
2
|
+
IndexedCollection,
|
3
|
+
markVolatile,
|
4
|
+
maxZIndex,
|
5
|
+
mercatorProjection,
|
6
|
+
VcsEvent,
|
7
|
+
vcsLayerName,
|
8
|
+
VectorLayer,
|
9
|
+
VectorStyleItem, Viewpoint,
|
10
|
+
} from '@vcmap/core';
|
11
|
+
import { shallowRef } from 'vue';
|
12
|
+
import { check } from '@vcsuite/check';
|
13
|
+
import { Icon } from 'ol/style.js';
|
14
|
+
import { getLogger } from '@vcsuite/logger';
|
15
|
+
import { vcsAppSymbol } from '../pluginHelper.js';
|
16
|
+
import { defaultPrimaryColor } from '../vuePlugins/vuetify.js';
|
17
|
+
import { getViewpointFromFeature } from '../actions/actionHelper.js';
|
18
|
+
|
19
|
+
/**
|
20
|
+
* A readonly rendering interface of a ResultItem.
|
21
|
+
* A ResultItem must provide either a feature, a clicked handler or both.
|
22
|
+
* @typedef {Object} ResultItem
|
23
|
+
* @property {string} title
|
24
|
+
* @property {string} [icon] An optional icon
|
25
|
+
* @property {Array<VcsAction>} [actions]
|
26
|
+
* @property {function():Promise<void>} [clicked] Obligatory, if no feature is provided. Can overwrite default zoom to feature behaviour.
|
27
|
+
* @property {import("ol").Feature|undefined} [feature] If a feature is provided, the feature is added to the result layer and search zooms to the layer's extent. Default clicked handler is zoom to feature, highlight feature and select feature, if feature has a FeatureInfoView.
|
28
|
+
*/
|
29
|
+
|
30
|
+
|
31
|
+
/**
|
32
|
+
* @interface SearchImpl
|
33
|
+
* @property {string} name Name of the implementation. Must be the name of the plugin the SearchImpl is owned by
|
34
|
+
* @property {function(q:string):Array<ResultItem>} search
|
35
|
+
* @property {function(q:string):Array<string>} [suggest] // XXX currently not implemented in UI at Beta state
|
36
|
+
* @property{function():void} abort - should abort any ongoing requests to search or suggest without throwing an error
|
37
|
+
* @property {function():void} destroy
|
38
|
+
*/
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @param {string} color
|
42
|
+
* @returns {import("ol/style/Icon").Options}
|
43
|
+
*/
|
44
|
+
function getPointResultIcon(color) {
|
45
|
+
return {
|
46
|
+
src: `data:image/svg+xml,%3Csvg xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns%23' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns%23' xmlns:svg='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' id='icon_24_poi' width='24' height='23.994' viewBox='0 0 24 23.994' sodipodi:docname='mapIcon.svg'%3E%3Cg id='Gruppe_1972' transform='translate(-571 -609.477)'%3E%3Cpath id='Pfad_773' d='M583,611a8.009,8.009,0,0,0-8,8c0,5.314,6.952,13.32,7.248,13.658a1,1,0,0,0,1.5,0c.3-.338,7.248-8.344,7.248-13.658A8.009,8.009,0,0,0,583,611Zm0,19.444c-2.18-2.685-6-8.09-6-11.444a6,6,0,0,1,12,0C589,622.354,585.18,627.759,583,630.444Z' fill='currentColor' /%3E%3Cpath id='Pfad_774' d='M583,615a4,4,0,1,0,4,4A4,4,0,0,0,583,615Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,583,621Z' fill='currentColor' /%3E%3C/g%3E%3Cpath fill='${encodeURIComponent(color)}' d='M 11.672998,20.526286 C 8.5115524,16.526958 6.4310003,12.714969 6.0702695,10.260963 6.0109099,9.8571482 6.0115821,9.1201807 6.0716855,8.7084104 6.4424582,6.1682348 8.3335069,4.1603103 10.828528,3.6575721 c 1.904966,-0.383844 3.881822,0.1903514 5.289639,1.5364231 0.993092,0.9495349 1.610829,2.1488769 1.810148,3.5144152 0.0601,0.4117703 0.06077,1.1487378 0.0014,1.5525526 -0.357076,2.429138 -2.337816,6.081898 -5.487559,10.119822 -0.224045,0.287223 -0.415188,0.530536 -0.424763,0.540696 -0.0096,0.01016 -0.16456,-0.167678 -0.344411,-0.395195 z m 0.990366,-7.047968 c 0.894914,-0.146674 1.762065,-0.627065 2.349286,-1.301476 0.86707,-0.995812 1.194989,-2.3427819 0.880571,-3.6170541 -0.379849,-1.5394474 -1.596396,-2.6842781 -3.173401,-2.9863277 -0.368703,-0.070619 -1.070937,-0.070619 -1.43964,0 C 9.7056173,5.875042 8.48604,7.0227247 8.1067793,8.5597879 7.8410265,9.6368274 8.0329903,10.787029 8.6317551,11.705317 c 0.5717674,0.876885 1.4205679,1.474277 2.4457369,1.721329 0.47704,0.114961 1.079877,0.134602 1.585872,0.05167 z' id='path1432' /%3E%3C/svg%3E`,
|
47
|
+
scale: 1,
|
48
|
+
color,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* sets up result layer for displaying search results
|
54
|
+
* @param {VcsUiApp} app
|
55
|
+
* @returns {{ resultLayer: import("@vcmap/core").VectorLayer, destroy: (function(): void)}}
|
56
|
+
*/
|
57
|
+
function setupSearchResultLayer(app) {
|
58
|
+
const resultLayer = new VectorLayer({
|
59
|
+
projection: mercatorProjection.toJSON(),
|
60
|
+
vectorProperties: {
|
61
|
+
altitudeMode: 'clampToGround',
|
62
|
+
classificationType: 'both',
|
63
|
+
},
|
64
|
+
properties: {
|
65
|
+
title: 'search.title',
|
66
|
+
},
|
67
|
+
zIndex: maxZIndex,
|
68
|
+
});
|
69
|
+
markVolatile(resultLayer);
|
70
|
+
app.layers.add(resultLayer);
|
71
|
+
|
72
|
+
const style = new VectorStyleItem({
|
73
|
+
image: getPointResultIcon(defaultPrimaryColor),
|
74
|
+
fill: {
|
75
|
+
color: 'rgba(237, 237, 237, 0.1)',
|
76
|
+
},
|
77
|
+
stroke: {
|
78
|
+
color: defaultPrimaryColor,
|
79
|
+
width: 5,
|
80
|
+
},
|
81
|
+
});
|
82
|
+
resultLayer.setStyle(style);
|
83
|
+
|
84
|
+
function setResultColor(color) {
|
85
|
+
style.stroke?.setColor(color);
|
86
|
+
style.image = new Icon(getPointResultIcon(color));
|
87
|
+
resultLayer.forceRedraw();
|
88
|
+
}
|
89
|
+
|
90
|
+
const listeners = [
|
91
|
+
app.uiConfig.added.addEventListener((item) => {
|
92
|
+
if (item.name === 'primaryColor') {
|
93
|
+
setResultColor(item.value);
|
94
|
+
}
|
95
|
+
}),
|
96
|
+
app.uiConfig.removed.addEventListener((item) => {
|
97
|
+
if (item.name === 'primaryColor') {
|
98
|
+
setResultColor(defaultPrimaryColor);
|
99
|
+
}
|
100
|
+
}),
|
101
|
+
];
|
102
|
+
|
103
|
+
const destroy = () => {
|
104
|
+
resultLayer.destroy();
|
105
|
+
listeners.forEach(cb => cb());
|
106
|
+
};
|
107
|
+
|
108
|
+
return { resultLayer, destroy };
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Symbol added to search implementations to specify the implementation's owner
|
113
|
+
* @type {symbol}
|
114
|
+
*/
|
115
|
+
const searchImplOwnerSymbol = Symbol('featureInfoView');
|
116
|
+
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Collection of SearchImpl
|
120
|
+
* @extends {IndexedCollection<SearchImpl<ResultItem>>}
|
121
|
+
*/
|
122
|
+
class Search extends IndexedCollection {
|
123
|
+
/**
|
124
|
+
* @param {VcsUiApp} app
|
125
|
+
*/
|
126
|
+
constructor(app) {
|
127
|
+
super();
|
128
|
+
|
129
|
+
/**
|
130
|
+
* @type {VcsUiApp}
|
131
|
+
* @private
|
132
|
+
*/
|
133
|
+
this._app = app;
|
134
|
+
|
135
|
+
/**
|
136
|
+
* An event triggered every time the currentResults array changes,
|
137
|
+
* either by a new search providing the new results or
|
138
|
+
* on clearing, if the results array has not been empty already.
|
139
|
+
* @type {import("@vcmap/core").VcsEvent<Array<ResultItem>>}
|
140
|
+
* @private
|
141
|
+
*/
|
142
|
+
this._resultsChanged = new VcsEvent();
|
143
|
+
|
144
|
+
/**
|
145
|
+
* @type {import("vue").Ref<Array<ResultItem>>}
|
146
|
+
* @private
|
147
|
+
*/
|
148
|
+
this._currentResults = shallowRef([]);
|
149
|
+
const { resultLayer, destroy } = setupSearchResultLayer(app);
|
150
|
+
/**
|
151
|
+
* @type {import("@vcmap/core").VectorLayer}
|
152
|
+
* @private
|
153
|
+
*/
|
154
|
+
this._resultLayer = resultLayer;
|
155
|
+
/**
|
156
|
+
* @type {function():void}
|
157
|
+
* @private
|
158
|
+
*/
|
159
|
+
this._destroyResultLayer = destroy;
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* An event triggered every time the currentResults array changes,
|
164
|
+
* either by a new search providing the new results or
|
165
|
+
* on clearing, if the results array has not been empty already.
|
166
|
+
* @type {import("@vcmap/core").VcsEvent<Array<ResultItem>>}
|
167
|
+
* @readonly
|
168
|
+
*/
|
169
|
+
get resultsChanged() {
|
170
|
+
return this._resultsChanged;
|
171
|
+
}
|
172
|
+
|
173
|
+
/**
|
174
|
+
* @type {import("vue").Ref<Array<ResultItem>>}
|
175
|
+
* @readonly
|
176
|
+
*/
|
177
|
+
get currentResults() {
|
178
|
+
return this._currentResults;
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* @type {VectorLayer}
|
183
|
+
* @readonly
|
184
|
+
*/
|
185
|
+
get resultLayer() {
|
186
|
+
return this._resultLayer;
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* @param {SearchImpl} item
|
191
|
+
* @param {string|symbol} owner pluginName or vcsAppSymbol
|
192
|
+
* @param {number=} index
|
193
|
+
*/
|
194
|
+
add(item, owner, index) {
|
195
|
+
check(owner, [String, vcsAppSymbol]);
|
196
|
+
check(item.search, Function);
|
197
|
+
if (item.name !== owner) {
|
198
|
+
getLogger('Search').warning('SearchImplementations must be named as the plugin they are owned by.');
|
199
|
+
}
|
200
|
+
item[searchImplOwnerSymbol] = owner;
|
201
|
+
super.add(item, index);
|
202
|
+
}
|
203
|
+
|
204
|
+
/**
|
205
|
+
* removes all search implementations of a specific owner (plugin) and fires removed Events
|
206
|
+
* @param {string|vcsAppSymbol} owner
|
207
|
+
*/
|
208
|
+
removeOwner(owner) {
|
209
|
+
this._array.forEach((impl) => {
|
210
|
+
if (impl[searchImplOwnerSymbol] === owner) {
|
211
|
+
super.remove(impl);
|
212
|
+
}
|
213
|
+
});
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Get the results for a given query string.
|
218
|
+
* Available features are added to results layer and map is zoomed to all results (extent of result layer).
|
219
|
+
* Adds default clicked handler to result items with feature, which zooms to and highlights said feature. If feature has FeatureInfoView, feature is selected by featureInfo.
|
220
|
+
* @param {string} q
|
221
|
+
* @returns {Promise<Array<ResultItem>>}
|
222
|
+
*/
|
223
|
+
async search(q) {
|
224
|
+
this.clearResults();
|
225
|
+
const promises = await Promise.allSettled([...this._array].map(impl => impl.search(q)));
|
226
|
+
const isAborted = promises.some(r => r.status === 'rejected' && r.reason?.name === 'AbortError');
|
227
|
+
if (!isAborted) {
|
228
|
+
const results = promises
|
229
|
+
.map((o) => {
|
230
|
+
if (o.status === 'rejected') {
|
231
|
+
getLogger('Search').warning(o.reason);
|
232
|
+
return [];
|
233
|
+
}
|
234
|
+
return o.value;
|
235
|
+
})
|
236
|
+
.flat();
|
237
|
+
|
238
|
+
this._currentResults.value = results
|
239
|
+
.filter(r => r.feature || r.clicked)
|
240
|
+
.map((item) => {
|
241
|
+
if (item.feature) {
|
242
|
+
this._resultLayer.addFeatures([item.feature]);
|
243
|
+
if (!item.clicked) {
|
244
|
+
const viewpoint = getViewpointFromFeature(item.feature);
|
245
|
+
item.clicked = () => {
|
246
|
+
this._app.maps.activeMap.gotoViewpoint(viewpoint);
|
247
|
+
return this._app.featureInfo.selectFeature(item.feature);
|
248
|
+
};
|
249
|
+
}
|
250
|
+
}
|
251
|
+
return item;
|
252
|
+
});
|
253
|
+
if (this._currentResults.value.length > 0) {
|
254
|
+
await this._resultLayer.activate();
|
255
|
+
const extent = this._resultLayer.getZoomToExtent();
|
256
|
+
const viewpoint = Viewpoint.createViewpointFromExtent(extent);
|
257
|
+
await this._app.maps.activeMap.gotoViewpoint(viewpoint);
|
258
|
+
this.resultsChanged.raiseEvent(this._currentResults.value.slice(0));
|
259
|
+
}
|
260
|
+
}
|
261
|
+
return this._currentResults.value;
|
262
|
+
}
|
263
|
+
|
264
|
+
/**
|
265
|
+
* Get the suggestions for a given query string
|
266
|
+
* @param {string} q
|
267
|
+
* @returns {Promise<Array<string>>}
|
268
|
+
*/
|
269
|
+
async suggest(q) {
|
270
|
+
const promises = await Promise.allSettled([...this._array].map((impl) => {
|
271
|
+
if (impl.suggest) {
|
272
|
+
return impl.suggest(q);
|
273
|
+
}
|
274
|
+
return Promise.resolve([]);
|
275
|
+
}));
|
276
|
+
const isAborted = promises.some(r => r.status === 'rejected' && r.reason?.name === 'AbortError');
|
277
|
+
if (isAborted) {
|
278
|
+
return [];
|
279
|
+
}
|
280
|
+
const suggestions = promises
|
281
|
+
.map((o) => {
|
282
|
+
if (o.status === 'rejected') {
|
283
|
+
getLogger('Search').warning(o.reason);
|
284
|
+
return [];
|
285
|
+
}
|
286
|
+
return o.value;
|
287
|
+
})
|
288
|
+
.flat();
|
289
|
+
return suggestions;
|
290
|
+
}
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Aborting any ongoing request
|
294
|
+
*/
|
295
|
+
abort() {
|
296
|
+
[...this._array].forEach(impl => impl.abort?.());
|
297
|
+
}
|
298
|
+
|
299
|
+
/**
|
300
|
+
* Clears the results and aborts running request
|
301
|
+
*/
|
302
|
+
clearResults() {
|
303
|
+
this.abort();
|
304
|
+
if (this._currentResults.value.length > 0) {
|
305
|
+
this._currentResults.value = [];
|
306
|
+
this.resultsChanged.raiseEvent(this._currentResults.value.slice(0));
|
307
|
+
}
|
308
|
+
this._resultLayer.removeAllFeatures();
|
309
|
+
this._resultLayer.deactivate();
|
310
|
+
if (this._app.featureInfo.selectedFeature?.[vcsLayerName] === this._resultLayer.name) {
|
311
|
+
this._app.featureInfo.clear();
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* @inheritDoc
|
317
|
+
*/
|
318
|
+
destroy() {
|
319
|
+
[...this._array].forEach(impl => impl.destroy());
|
320
|
+
this.resultsChanged.destroy();
|
321
|
+
this._destroyResultLayer();
|
322
|
+
super.destroy();
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
export default Search;
|
@@ -0,0 +1,90 @@
|
|
1
|
+
<template>
|
2
|
+
<v-card>
|
3
|
+
<span class="d-flex justify-space-between align-center ma-1">
|
4
|
+
<v-icon
|
5
|
+
class="mx-2"
|
6
|
+
>
|
7
|
+
$vcsSearch
|
8
|
+
</v-icon>
|
9
|
+
<VcsTextField
|
10
|
+
class="font-size-14 d-inline-block user-select-none w-full mx-2"
|
11
|
+
autofocus
|
12
|
+
:loading="searching"
|
13
|
+
clearable
|
14
|
+
dense
|
15
|
+
:placeholder="$t('search.placeholder')"
|
16
|
+
v-model.trim="query"
|
17
|
+
@keydown.enter="search"
|
18
|
+
@keydown.esc="clear"
|
19
|
+
@input="reset"
|
20
|
+
/>
|
21
|
+
</span>
|
22
|
+
<v-divider />
|
23
|
+
<ResultsComponent :query="query" :results="results" />
|
24
|
+
</v-card>
|
25
|
+
</template>
|
26
|
+
|
27
|
+
<style>
|
28
|
+
|
29
|
+
</style>
|
30
|
+
|
31
|
+
<script>
|
32
|
+
import { inject, onUnmounted, ref } from 'vue';
|
33
|
+
import { getLogger } from '@vcsuite/logger';
|
34
|
+
import { VCard, VDivider, VIcon } from 'vuetify/lib';
|
35
|
+
import VcsTextField from '../components/form-inputs-controls/VcsTextField.vue';
|
36
|
+
import ResultsComponent from './resultsComponent.vue';
|
37
|
+
|
38
|
+
export default {
|
39
|
+
components: {
|
40
|
+
ResultsComponent,
|
41
|
+
VcsTextField,
|
42
|
+
VCard,
|
43
|
+
VIcon,
|
44
|
+
VDivider,
|
45
|
+
},
|
46
|
+
setup() {
|
47
|
+
/** @type {VcsUiApp} */
|
48
|
+
const app = inject('vcsApp');
|
49
|
+
const searching = ref(false);
|
50
|
+
const query = ref(null);
|
51
|
+
const suggestions = ref([]);
|
52
|
+
const results = app.search.currentResults;
|
53
|
+
|
54
|
+
const reset = () => {
|
55
|
+
app.search.clearResults();
|
56
|
+
suggestions.value = [];
|
57
|
+
};
|
58
|
+
|
59
|
+
const clear = () => {
|
60
|
+
reset();
|
61
|
+
searching.value = false;
|
62
|
+
query.value = null;
|
63
|
+
};
|
64
|
+
|
65
|
+
const search = async () => {
|
66
|
+
reset();
|
67
|
+
searching.value = true;
|
68
|
+
try {
|
69
|
+
await app.search.search(query.value);
|
70
|
+
} catch (e) {
|
71
|
+
getLogger('Search').error(e);
|
72
|
+
}
|
73
|
+
searching.value = false;
|
74
|
+
};
|
75
|
+
|
76
|
+
onUnmounted(() => {
|
77
|
+
clear();
|
78
|
+
});
|
79
|
+
|
80
|
+
return {
|
81
|
+
query,
|
82
|
+
searching,
|
83
|
+
results,
|
84
|
+
reset,
|
85
|
+
clear,
|
86
|
+
search,
|
87
|
+
};
|
88
|
+
},
|
89
|
+
};
|
90
|
+
</script>
|
@@ -71,8 +71,11 @@ $treeview-node-level-width: 16px;
|
|
71
71
|
$treeview-node-margin: 4px;
|
72
72
|
$treeview-node-height: 32px; // 48px !default;
|
73
73
|
|
74
|
-
/** Expansion Panel
|
74
|
+
/** Expansion Panel **/
|
75
75
|
$expansion-panel-header-min-height: 32px; // 48px !default;
|
76
76
|
$expansion-panel-active-header-min-height: 32px; // 64px !default;
|
77
77
|
$expansion-panel-header-padding: 6px 0; // 16px 24px !default;
|
78
78
|
$expansion-panel-content-padding: 0 0 4px; // 0 24px 16px !default;
|
79
|
+
|
80
|
+
/** Footer **/
|
81
|
+
$footer-padding: 0 8px; // 6px 16px !default;
|
package/src/vcsUiApp.js
CHANGED
@@ -30,6 +30,7 @@ import FeatureInfo from './featureInfo/featureInfo.js';
|
|
30
30
|
import UiConfig from './uiConfig.js';
|
31
31
|
import { createEmptyState, getStateFromURL } from './state.js';
|
32
32
|
import { version } from '../package.json';
|
33
|
+
import Search from './search/search.js';
|
33
34
|
|
34
35
|
/**
|
35
36
|
* @typedef {import("@vcmap/core").VcsAppConfig} VcsUiAppConfig
|
@@ -125,6 +126,7 @@ class VcsUiApp extends VcsApp {
|
|
125
126
|
this._toolboxManager.removeOwner(plugin.name);
|
126
127
|
this._categoryManager.removeOwner(plugin.name);
|
127
128
|
this._contextMenuManager.removeOwner(plugin.name);
|
129
|
+
this._search.removeOwner(plugin.name);
|
128
130
|
if (plugin.i18n) {
|
129
131
|
this.i18n.addPluginMessages(plugin.name, plugin[contextIdSymbol], plugin.i18n);
|
130
132
|
}
|
@@ -142,6 +144,7 @@ class VcsUiApp extends VcsApp {
|
|
142
144
|
this._toolboxManager.removeOwner(plugin.name);
|
143
145
|
this._categoryManager.removeOwner(plugin.name);
|
144
146
|
this._contextMenuManager.removeOwner(plugin.name);
|
147
|
+
this._search.removeOwner(plugin.name);
|
145
148
|
this.i18n.removePluginMessages(plugin.name, plugin[contextIdSymbol]);
|
146
149
|
}),
|
147
150
|
];
|
@@ -210,6 +213,12 @@ class VcsUiApp extends VcsApp {
|
|
210
213
|
*/
|
211
214
|
this._contextMenuManager = new ContextMenuManager(this);
|
212
215
|
|
216
|
+
/**
|
217
|
+
* @type {Search}
|
218
|
+
* @private
|
219
|
+
*/
|
220
|
+
this._search = new Search(this);
|
221
|
+
|
213
222
|
/**
|
214
223
|
* @type {AppState}
|
215
224
|
* @private
|
@@ -283,6 +292,12 @@ class VcsUiApp extends VcsApp {
|
|
283
292
|
*/
|
284
293
|
get contextMenuManager() { return this._contextMenuManager; }
|
285
294
|
|
295
|
+
/**
|
296
|
+
* @type {Search}
|
297
|
+
* @readonly
|
298
|
+
*/
|
299
|
+
get search() { return this._search; }
|
300
|
+
|
286
301
|
/**
|
287
302
|
* @type {UiConfig}
|
288
303
|
* @readonly
|
@@ -447,6 +462,7 @@ class VcsUiApp extends VcsApp {
|
|
447
462
|
destroyCollection(this._plugins);
|
448
463
|
destroyCollection(this._contentTree);
|
449
464
|
destroyCollection(this._i18n);
|
465
|
+
destroyCollection(this._search);
|
450
466
|
this._contentTreeClassRegistry.destroy();
|
451
467
|
this._featureInfo.destroy();
|
452
468
|
this._uiConfig.destroy();
|