@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
@@ -65,9 +65,33 @@
|
|
65
65
|
</template>
|
66
66
|
<script>
|
67
67
|
import { inject } from 'vue';
|
68
|
+
import {
|
69
|
+
VCard,
|
70
|
+
VCardText,
|
71
|
+
VChip,
|
72
|
+
VListItem,
|
73
|
+
VListItemContent,
|
74
|
+
VListItemTitle,
|
75
|
+
VListItemAction,
|
76
|
+
VAvatar,
|
77
|
+
VIcon,
|
78
|
+
VDivider,
|
79
|
+
} from 'vuetify/lib';
|
68
80
|
|
69
81
|
export default {
|
70
82
|
name: 'ThemeChanger',
|
83
|
+
components: [
|
84
|
+
VCard,
|
85
|
+
VCardText,
|
86
|
+
VChip,
|
87
|
+
VListItem,
|
88
|
+
VListItemContent,
|
89
|
+
VListItemTitle,
|
90
|
+
VListItemAction,
|
91
|
+
VAvatar,
|
92
|
+
VIcon,
|
93
|
+
VDivider,
|
94
|
+
],
|
71
95
|
setup() {
|
72
96
|
const app = inject('vcsApp');
|
73
97
|
const plugin = app.plugins.getByKey('@vcmap/theme-changer');
|
@@ -172,10 +172,37 @@
|
|
172
172
|
<script>
|
173
173
|
import { VcsButton, VcsActionButtonList } from '@vcmap/ui';
|
174
174
|
import { ref } from 'vue';
|
175
|
+
import {
|
176
|
+
VCard,
|
177
|
+
VListItem,
|
178
|
+
VListItemContent,
|
179
|
+
VListItemTitle,
|
180
|
+
VListItemAction,
|
181
|
+
VSwitch,
|
182
|
+
VDivider,
|
183
|
+
VRow,
|
184
|
+
VCol,
|
185
|
+
VCardTitle,
|
186
|
+
VCardActions,
|
187
|
+
} from 'vuetify/lib';
|
175
188
|
|
176
189
|
export default {
|
177
190
|
name: 'ButtonExamples',
|
178
|
-
components: {
|
191
|
+
components: {
|
192
|
+
VcsButton,
|
193
|
+
VcsActionButtonList,
|
194
|
+
VCard,
|
195
|
+
VListItem,
|
196
|
+
VListItemContent,
|
197
|
+
VListItemTitle,
|
198
|
+
VListItemAction,
|
199
|
+
VSwitch,
|
200
|
+
VDivider,
|
201
|
+
VRow,
|
202
|
+
VCol,
|
203
|
+
VCardTitle,
|
204
|
+
VCardActions,
|
205
|
+
},
|
179
206
|
setup() {
|
180
207
|
const active = ref(false);
|
181
208
|
const disabled = ref(false);
|
@@ -60,6 +60,15 @@
|
|
60
60
|
import { inject, ref, onUnmounted } from 'vue';
|
61
61
|
import { VcsButton } from '@vcmap/ui';
|
62
62
|
import { GeoJSONLayer } from '@vcmap/core';
|
63
|
+
import {
|
64
|
+
VDialog,
|
65
|
+
VCard,
|
66
|
+
VForm,
|
67
|
+
VSelect,
|
68
|
+
VTextField,
|
69
|
+
VBtn,
|
70
|
+
VTextarea,
|
71
|
+
} from 'vuetify/lib';
|
63
72
|
import Category from './Category.vue';
|
64
73
|
|
65
74
|
export default {
|
@@ -67,6 +76,13 @@
|
|
67
76
|
components: {
|
68
77
|
Category,
|
69
78
|
VcsButton,
|
79
|
+
VDialog,
|
80
|
+
VCard,
|
81
|
+
VForm,
|
82
|
+
VSelect,
|
83
|
+
VTextField,
|
84
|
+
VBtn,
|
85
|
+
VTextarea,
|
70
86
|
},
|
71
87
|
setup() {
|
72
88
|
const app = inject('vcsApp');
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<span class="float-right">
|
8
8
|
<vcs-button icon="$vcsPlus" @click="dialog = true" />
|
9
9
|
<vcs-button icon="mdi-download" @click="download" />
|
10
|
-
<a :href="downloadLink" target="_blank" ref="link" download="category.json"/>
|
10
|
+
<a :href="downloadLink" target="_blank" ref="link" download="category.json" />
|
11
11
|
</span>
|
12
12
|
</span>
|
13
13
|
|
@@ -26,7 +26,9 @@
|
|
26
26
|
class="mb-1"
|
27
27
|
>
|
28
28
|
<v-list-item-content>
|
29
|
-
<v-list-item-title class="subtitle-1"
|
29
|
+
<v-list-item-title class="subtitle-1">
|
30
|
+
{{ item.name }}
|
31
|
+
</v-list-item-title>
|
30
32
|
<v-list-item-subtitle>{{ item.type }}</v-list-item-subtitle>
|
31
33
|
</v-list-item-content>
|
32
34
|
</v-list-item>
|
@@ -40,7 +42,9 @@
|
|
40
42
|
@submit.prevent="addItem"
|
41
43
|
>
|
42
44
|
<v-textarea v-model="jsonString" />
|
43
|
-
<v-btn type="submit">
|
45
|
+
<v-btn type="submit">
|
46
|
+
Add
|
47
|
+
</v-btn>
|
44
48
|
</v-form>
|
45
49
|
</v-card>
|
46
50
|
</v-dialog>
|
@@ -51,10 +55,32 @@
|
|
51
55
|
import { inject, nextTick, ref } from 'vue';
|
52
56
|
import { VcsButton } from '@vcmap/ui';
|
53
57
|
import { getObjectFromClassRegistry } from '@vcmap/core';
|
58
|
+
import {
|
59
|
+
VVirtualScroll,
|
60
|
+
VListItem,
|
61
|
+
VListItemContent,
|
62
|
+
VListItemTitle,
|
63
|
+
VListItemSubtitle,
|
64
|
+
VDialog,
|
65
|
+
VCard,
|
66
|
+
VForm,
|
67
|
+
VTextarea,
|
68
|
+
} from 'vuetify/lib';
|
54
69
|
|
55
70
|
export default {
|
56
71
|
name: 'CategoryComponent',
|
57
|
-
components: {
|
72
|
+
components: {
|
73
|
+
VcsButton,
|
74
|
+
VVirtualScroll,
|
75
|
+
VListItem,
|
76
|
+
VListItemContent,
|
77
|
+
VListItemTitle,
|
78
|
+
VListItemSubtitle,
|
79
|
+
VDialog,
|
80
|
+
VCard,
|
81
|
+
VForm,
|
82
|
+
VTextarea,
|
83
|
+
},
|
58
84
|
props: {
|
59
85
|
categoryName: {
|
60
86
|
type: String,
|
@@ -107,11 +107,22 @@
|
|
107
107
|
</template>
|
108
108
|
<script>
|
109
109
|
|
110
|
-
import
|
110
|
+
import {
|
111
|
+
VSheet,
|
112
|
+
VContainer,
|
113
|
+
VRow,
|
114
|
+
VCol,
|
115
|
+
VTextField,
|
116
|
+
} from 'vuetify/lib';
|
111
117
|
|
112
118
|
export default {
|
113
119
|
name: 'MySuperComponent',
|
114
120
|
components: {
|
121
|
+
VSheet,
|
122
|
+
VContainer,
|
123
|
+
VRow,
|
124
|
+
VCol,
|
125
|
+
VTextField,
|
115
126
|
},
|
116
127
|
data() {
|
117
128
|
return {
|
package/plugins/package.json
CHANGED
@@ -14,19 +14,13 @@
|
|
14
14
|
</template>
|
15
15
|
<script>
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
* @vue-prop {Object} attributes - the feature's attributes
|
20
|
-
* @vue-prop {string[]} [labels] - optional array of strings labeling all data points
|
21
|
-
* @vue-prop {string} [graph='trend'] - Choose between a trendline or bars
|
22
|
-
* @vue-prop {string} [color='primary'] - optional color of the sparkline of the graph
|
23
|
-
* @vue-prop {string[]} [gradient] - optional array of colors to use as a linear-gradient
|
24
|
-
* @vue-prop {boolean} [fill=false] - if true, filled area below sparkline
|
25
|
-
* @vue-prop {boolean|number|string} [smooth=true] - optional number of px to use as a corner radius. true defaults to 8, false is 0
|
26
|
-
* @vue-computed {number[]} values - numeric values for the graph derived from the attributeKeys
|
27
|
-
*/
|
17
|
+
import { VSparkline } from 'vuetify/lib';
|
18
|
+
|
28
19
|
export default {
|
29
20
|
name: 'SimpleGraphComponent',
|
21
|
+
components: [
|
22
|
+
VSparkline,
|
23
|
+
],
|
30
24
|
props: {
|
31
25
|
attributes: {
|
32
26
|
type: Object,
|
@@ -16,9 +16,25 @@
|
|
16
16
|
|
17
17
|
<script>
|
18
18
|
import { Icons } from '@vcmap/ui';
|
19
|
+
import {
|
20
|
+
VSheet,
|
21
|
+
VList,
|
22
|
+
VListItem,
|
23
|
+
VListItemIcon,
|
24
|
+
VIcon,
|
25
|
+
VListItemTitle,
|
26
|
+
} from 'vuetify/lib';
|
19
27
|
|
20
28
|
export default {
|
21
29
|
name: 'AllIconsComponent',
|
30
|
+
components: [
|
31
|
+
VSheet,
|
32
|
+
VList,
|
33
|
+
VListItem,
|
34
|
+
VListItemIcon,
|
35
|
+
VIcon,
|
36
|
+
VListItemTitle,
|
37
|
+
],
|
22
38
|
computed: {
|
23
39
|
icons() {
|
24
40
|
return Object.keys(Icons).map(n => `$${n}`);
|
package/plugins/test/editor.vue
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
import { ref, inject } from 'vue';
|
22
22
|
import { VcsButton } from '@vcmap/ui';
|
23
23
|
import { Context } from '@vcmap/core';
|
24
|
+
import { VProgressCircular, VTextarea } from 'vuetify/lib';
|
24
25
|
|
25
26
|
const contextId = 'foo';
|
26
27
|
|
@@ -28,6 +29,8 @@
|
|
28
29
|
name: 'Editor',
|
29
30
|
components: {
|
30
31
|
VcsButton,
|
32
|
+
VTextarea,
|
33
|
+
VProgressCircular,
|
31
34
|
},
|
32
35
|
setup() {
|
33
36
|
/** @type {VcsUiApp} */
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import { v4 as uuid } from 'uuid';
|
2
2
|
import { check } from '@vcsuite/check';
|
3
|
-
import { Collection, MapCollection, Viewpoint } from '@vcmap/core';
|
3
|
+
import { Collection, Extent, MapCollection, mercatorProjection, Viewpoint } from '@vcmap/core';
|
4
|
+
import { Feature } from 'ol';
|
5
|
+
import { reactive, ref } from 'vue';
|
4
6
|
import { vcsAppSymbol } from '../pluginHelper.js';
|
5
|
-
import { getWindowPositionOptions } from '../manager/window/windowManager.js';
|
7
|
+
import { getWindowPositionOptions, WindowSlot } from '../manager/window/windowManager.js';
|
8
|
+
import SearchComponent from '../search/searchComponent.vue';
|
6
9
|
|
7
10
|
/**
|
8
11
|
* @typedef {Object} ActionOptions
|
@@ -88,6 +91,60 @@ export function createToggleAction(actionOptions, windowComponent, windowManager
|
|
88
91
|
return { action, destroy };
|
89
92
|
}
|
90
93
|
|
94
|
+
/**
|
95
|
+
* Creates a toggle button for the search tool, which is only available, if at least one search implementation is registered.
|
96
|
+
* @param {VcsUiApp} app
|
97
|
+
* @returns {{ searchAction: import("vue").Ref<import("vue").UnwrapRef<VcsAction>|null>, destroy: function():void }}
|
98
|
+
*/
|
99
|
+
export function createSearchButtonAction(app) {
|
100
|
+
let destroyAction = () => {};
|
101
|
+
const searchAction = ref(null);
|
102
|
+
const determineAction = () => {
|
103
|
+
if (app.windowManager.has('searchId')) {
|
104
|
+
app.windowManager.remove('searchId');
|
105
|
+
}
|
106
|
+
if (app.search.size > 0 && searchAction.value === null) {
|
107
|
+
const action = createToggleAction(
|
108
|
+
{
|
109
|
+
name: 'search.title',
|
110
|
+
icon: '$vcsSearch',
|
111
|
+
title: 'search.tooltip',
|
112
|
+
},
|
113
|
+
{
|
114
|
+
id: 'searchId',
|
115
|
+
component: SearchComponent,
|
116
|
+
state: { hideHeader: true },
|
117
|
+
slot: WindowSlot.DETACHED,
|
118
|
+
position: {
|
119
|
+
right: 0,
|
120
|
+
top: 0,
|
121
|
+
width: 440,
|
122
|
+
},
|
123
|
+
},
|
124
|
+
app.windowManager,
|
125
|
+
vcsAppSymbol,
|
126
|
+
);
|
127
|
+
destroyAction = action.destroy;
|
128
|
+
searchAction.value = reactive(action.action);
|
129
|
+
} else if (searchAction.value !== null) {
|
130
|
+
destroyAction();
|
131
|
+
destroyAction = () => {};
|
132
|
+
searchAction.value = null;
|
133
|
+
}
|
134
|
+
};
|
135
|
+
determineAction();
|
136
|
+
const listeners = [
|
137
|
+
app.search.added.addEventListener(determineAction),
|
138
|
+
app.search.removed.addEventListener(determineAction),
|
139
|
+
];
|
140
|
+
const destroy = () => {
|
141
|
+
destroyAction();
|
142
|
+
listeners.forEach((cb) => { cb(); });
|
143
|
+
};
|
144
|
+
|
145
|
+
return { searchAction, destroy };
|
146
|
+
}
|
147
|
+
|
91
148
|
/**
|
92
149
|
* Creates an action which will toggle the overview map (opening & closing the window and activating/ deactivating the overview map).
|
93
150
|
* @param {OverviewMap} overviewMap
|
@@ -260,3 +317,47 @@ export function createGoToViewpointAction(actionOptions, viewpoint, viewpointCol
|
|
260
317
|
},
|
261
318
|
};
|
262
319
|
}
|
320
|
+
|
321
|
+
/**
|
322
|
+
* calculates and returns a viewpoint using feature's extent
|
323
|
+
* @param {import("ol").Feature<import("ol/geom/Geometry").default>} feature
|
324
|
+
* @returns {Viewpoint|null}
|
325
|
+
*/
|
326
|
+
export function getViewpointFromFeature(feature) {
|
327
|
+
const extent = new Extent({
|
328
|
+
coordinates: feature.getGeometry()?.getExtent?.(),
|
329
|
+
projection: mercatorProjection,
|
330
|
+
});
|
331
|
+
|
332
|
+
if (!extent || !extent.isValid()) {
|
333
|
+
return null;
|
334
|
+
}
|
335
|
+
return Viewpoint.createViewpointFromExtent(extent);
|
336
|
+
}
|
337
|
+
|
338
|
+
/**
|
339
|
+
* Creates an action, which when clicked, zooms to the provided feature
|
340
|
+
* @param {ActionOptions} actionOptions
|
341
|
+
* @param {import("ol").Feature<import("ol/geom/Geometry").default>} feature
|
342
|
+
* @param {import("@vcmap/core").MapCollection} mapCollection
|
343
|
+
* @returns {VcsAction|null} returns null if the feature does not have a geometry with a valid extent
|
344
|
+
*/
|
345
|
+
export function createZoomToFeatureAction(actionOptions, feature, mapCollection) {
|
346
|
+
check(actionOptions, {
|
347
|
+
name: String,
|
348
|
+
icon: [undefined, String],
|
349
|
+
title: [undefined, String],
|
350
|
+
});
|
351
|
+
check(feature, Feature);
|
352
|
+
check(mapCollection, MapCollection);
|
353
|
+
|
354
|
+
const viewpoint = getViewpointFromFeature(feature);
|
355
|
+
|
356
|
+
return {
|
357
|
+
title: 'search.zoomToFeatureAction',
|
358
|
+
...actionOptions,
|
359
|
+
async callback() {
|
360
|
+
await mapCollection.activeMap.gotoViewpoint(viewpoint);
|
361
|
+
},
|
362
|
+
};
|
363
|
+
}
|
@@ -20,12 +20,21 @@
|
|
20
20
|
|
21
21
|
<script>
|
22
22
|
import { computed, inject, onUnmounted, ref } from 'vue';
|
23
|
+
import {
|
24
|
+
VChip, VList, VListItem, VListItemContent, VListItemIcon, VSheet,
|
25
|
+
} from 'vuetify/lib';
|
23
26
|
import VcsSelect from '../components/form-inputs-controls/VcsSelect.vue';
|
24
27
|
|
25
28
|
export default {
|
26
29
|
name: 'StyleSelector',
|
27
30
|
components: {
|
28
31
|
VcsSelect,
|
32
|
+
VSheet,
|
33
|
+
VList,
|
34
|
+
VListItem,
|
35
|
+
VListItemIcon,
|
36
|
+
VChip,
|
37
|
+
VListItemContent,
|
29
38
|
},
|
30
39
|
props: {
|
31
40
|
availableStyles: {
|
@@ -1,18 +1,28 @@
|
|
1
1
|
<template>
|
2
2
|
<v-container class="fill-height pa-0" absolute fluid>
|
3
|
-
<
|
3
|
+
<VcsNavbar />
|
4
4
|
<v-container class="vcs-main pa-0" :class="{ 'vcs-main-xs': $vuetify.breakpoint.xs }" fluid absolute>
|
5
5
|
<template v-if="$vuetify.breakpoint.xs">
|
6
6
|
<img v-if="mobileLogo" :src="mobileLogo" alt="Logo" draggable="false" class="mobile-logo">
|
7
7
|
<div v-else class="company-logo-mobile mobile-logo" />
|
8
8
|
</template>
|
9
|
+
<VcsButton
|
10
|
+
v-if="!$vuetify.breakpoint.smAndUp && $vuetify.breakpoint.mobile"
|
11
|
+
:key="attributionAction.name"
|
12
|
+
:tooltip="attributionAction.title"
|
13
|
+
:icon="attributionAction.icon"
|
14
|
+
:active="attributionAction.active"
|
15
|
+
@click.stop="attributionAction.callback($event)"
|
16
|
+
small
|
17
|
+
class="z-index-1 mobile-attribution-btn"
|
18
|
+
/>
|
9
19
|
<VcsMap :map-id="mapId" />
|
10
20
|
<MapNavigation />
|
11
21
|
<ToolboxManagerComponent />
|
12
22
|
<WindowManagerComponent />
|
13
23
|
</v-container>
|
14
|
-
<v-footer absolute v-if="
|
15
|
-
|
24
|
+
<v-footer absolute v-if="$vuetify.breakpoint.smAndUp" min-height="22px">
|
25
|
+
<VcsAttributionsFooter :entries="attributionEntries" :attribution-action="attributionAction" />
|
16
26
|
</v-footer>
|
17
27
|
</v-container>
|
18
28
|
</template>
|
@@ -26,7 +36,7 @@
|
|
26
36
|
top: 48px;
|
27
37
|
left: 0;
|
28
38
|
right: 0;
|
29
|
-
bottom:
|
39
|
+
bottom: 22px;
|
30
40
|
}
|
31
41
|
|
32
42
|
.vcs-main-xs {
|
@@ -43,6 +53,12 @@
|
|
43
53
|
z-index: 1;
|
44
54
|
}
|
45
55
|
|
56
|
+
.mobile-attribution-btn{
|
57
|
+
position: fixed;
|
58
|
+
right: 2px;
|
59
|
+
bottom: 56px;
|
60
|
+
}
|
61
|
+
|
46
62
|
</style>
|
47
63
|
|
48
64
|
<script>
|
@@ -56,12 +72,13 @@
|
|
56
72
|
watch,
|
57
73
|
} from 'vue';
|
58
74
|
import { getVcsAppById } from '@vcmap/core';
|
75
|
+
import { VContainer, VFooter } from 'vuetify/lib';
|
59
76
|
import WindowManagerComponent from '../manager/window/WindowManager.vue';
|
60
77
|
import ToolboxManagerComponent from '../manager/toolbox/ToolboxManager.vue';
|
61
78
|
import { ButtonLocation } from '../manager/navbarManager.js';
|
62
79
|
import { vcsAppSymbol } from '../pluginHelper.js';
|
63
80
|
import VcsMap from './VcsMap.vue';
|
64
|
-
import
|
81
|
+
import VcsNavbar from './VcsNavbar.vue';
|
65
82
|
import { createMapButtonAction, createToggleAction } from '../actions/actionHelper.js';
|
66
83
|
import MapNavigation from '../navigation/mapNavigation.vue';
|
67
84
|
import VcsSettings from './VcsSettings.vue';
|
@@ -70,6 +87,10 @@
|
|
70
87
|
import { defaultPrimaryColor } from '../vuePlugins/vuetify.js';
|
71
88
|
import VcsLegend from '../legend/vcsLegend.vue';
|
72
89
|
import { getLegendEntries } from '../legend/legendHelper.js';
|
90
|
+
import VcsAttributionsFooter from './VcsAttributionsFooter.vue';
|
91
|
+
import VcsButton from '../components/buttons/VcsButton.vue';
|
92
|
+
import VcsAttributions from './VcsAttributions.vue';
|
93
|
+
import { getAttributions } from './attributionsHelper.js';
|
73
94
|
|
74
95
|
/**
|
75
96
|
* You should call this function in the component providing the vcsUiApp to your
|
@@ -178,10 +199,10 @@
|
|
178
199
|
|
179
200
|
/**
|
180
201
|
* adds or removes the legend button, depending on the number of entries
|
181
|
-
* @param {
|
202
|
+
* @param {Array<LegendEntry>} newEntries
|
182
203
|
*/
|
183
204
|
const handleLegendButton = (newEntries) => {
|
184
|
-
if (
|
205
|
+
if (newEntries.length > 0) {
|
185
206
|
if (!app.navbarManager.has('legend')) {
|
186
207
|
app.navbarManager.add(
|
187
208
|
{
|
@@ -197,7 +218,7 @@
|
|
197
218
|
app.windowManager.remove('legend');
|
198
219
|
}
|
199
220
|
};
|
200
|
-
handleLegendButton(entries);
|
221
|
+
handleLegendButton(entries.value);
|
201
222
|
|
202
223
|
const stopWatching = watch(
|
203
224
|
entries,
|
@@ -329,6 +350,45 @@
|
|
329
350
|
};
|
330
351
|
}
|
331
352
|
|
353
|
+
/**
|
354
|
+
* This helper gets attributions of all active maps, layers and oblique collections and returns an array of entries.
|
355
|
+
* It also returns a attributionAction to toggle the attributions window and a destroy function.
|
356
|
+
* @param {VcsUiApp} app
|
357
|
+
* @returns {{attributionEntries: import("vue").Ref<Array<AttributionEntry>>, attributionAction: VcsAction, destroyAttributions: function():void}}
|
358
|
+
*/
|
359
|
+
export function setupAttributions(app) {
|
360
|
+
const { entries, destroy } = getAttributions(app);
|
361
|
+
|
362
|
+
const { action: attributionAction, destroy: attributionDestroy } = createToggleAction(
|
363
|
+
{
|
364
|
+
name: 'attributionToggle',
|
365
|
+
icon: 'mdi-chevron-double-right',
|
366
|
+
title: 'footer.attributions.tooltip',
|
367
|
+
},
|
368
|
+
{
|
369
|
+
id: 'attribution',
|
370
|
+
component: VcsAttributions,
|
371
|
+
state: {
|
372
|
+
headerTitle: 'footer.attributions.title',
|
373
|
+
headerIcon: 'mdi-copyright',
|
374
|
+
},
|
375
|
+
slot: WindowSlot.DYNAMIC_RIGHT,
|
376
|
+
props: { entries },
|
377
|
+
},
|
378
|
+
app.windowManager,
|
379
|
+
vcsAppSymbol,
|
380
|
+
);
|
381
|
+
|
382
|
+
return {
|
383
|
+
attributionEntries: entries,
|
384
|
+
attributionAction,
|
385
|
+
destroyAttributions: () => {
|
386
|
+
destroy();
|
387
|
+
attributionDestroy();
|
388
|
+
},
|
389
|
+
};
|
390
|
+
}
|
391
|
+
|
332
392
|
/**
|
333
393
|
* The base component to setup the entire application. To embed the VcsApp, use this component.
|
334
394
|
* @vue-prop {string} appId - the id of the app to inject. this will setup listeners on the app to call vcsAppMounted on plugins
|
@@ -336,11 +396,15 @@
|
|
336
396
|
*/
|
337
397
|
export default {
|
338
398
|
components: {
|
399
|
+
VcsButton,
|
400
|
+
VcsAttributionsFooter,
|
339
401
|
MapNavigation,
|
340
|
-
|
402
|
+
VcsNavbar,
|
341
403
|
VcsMap,
|
342
404
|
WindowManagerComponent,
|
343
405
|
ToolboxManagerComponent,
|
406
|
+
VContainer,
|
407
|
+
VFooter,
|
344
408
|
},
|
345
409
|
props: {
|
346
410
|
appId: {
|
@@ -360,6 +424,7 @@
|
|
360
424
|
const settingsDestroy = setupSettingsWindow(app);
|
361
425
|
const destroyComponentsWindow = setupComponentsWindow(app);
|
362
426
|
const destroyThemingListener = setupUiConfigTheming(app, getCurrentInstance().proxy.$vuetify);
|
427
|
+
const { attributionEntries, attributionAction, destroyAttributions } = setupAttributions(app);
|
363
428
|
|
364
429
|
let pluginMountedListener;
|
365
430
|
onMounted(() => {
|
@@ -376,11 +441,14 @@
|
|
376
441
|
settingsDestroy();
|
377
442
|
destroyComponentsWindow();
|
378
443
|
destroyThemingListener();
|
444
|
+
destroyAttributions();
|
379
445
|
});
|
380
446
|
|
381
447
|
return {
|
382
448
|
mapId,
|
383
449
|
mobileLogo: computed(() => app.uiConfig.config.value.mobileLogo ?? app.uiConfig.config.value.logo),
|
450
|
+
attributionEntries,
|
451
|
+
attributionAction,
|
384
452
|
};
|
385
453
|
},
|
386
454
|
};
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<template>
|
2
|
+
<v-list>
|
3
|
+
<v-list-item v-for="({key, title, attributions}) in entries" :key="key">
|
4
|
+
<v-list-item-content>
|
5
|
+
<v-list-item-title>{{ $t(title) }}</v-list-item-title>
|
6
|
+
<v-list-item-subtitle
|
7
|
+
v-for="attribution in attributions"
|
8
|
+
:key="attribution.provider"
|
9
|
+
:title="`${$t(attribution.provider)} ${attribution.year}`"
|
10
|
+
>
|
11
|
+
<a
|
12
|
+
:href="attribution.url"
|
13
|
+
target="_blank"
|
14
|
+
class="text--secondary"
|
15
|
+
>
|
16
|
+
{{ $t(attribution.provider) }} {{ attribution.year }}
|
17
|
+
</a>
|
18
|
+
</v-list-item-subtitle>
|
19
|
+
</v-list-item-content>
|
20
|
+
</v-list-item>
|
21
|
+
</v-list>
|
22
|
+
</template>
|
23
|
+
|
24
|
+
<style lang="scss" scoped>
|
25
|
+
|
26
|
+
::v-deep {
|
27
|
+
a:before {
|
28
|
+
content: '\00a9\00a0'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
</style>
|
33
|
+
|
34
|
+
<script >
|
35
|
+
import {
|
36
|
+
VList,
|
37
|
+
VListItem,
|
38
|
+
VListItemContent,
|
39
|
+
VListItemTitle,
|
40
|
+
VListItemSubtitle,
|
41
|
+
} from 'vuetify/lib';
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Lists attributions of maps, layers and oblique collections
|
45
|
+
* @vue-prop {import("vue").Ref<Array<AttributionEntry>} entries - array with one entry per active VcsObject
|
46
|
+
*/
|
47
|
+
export default {
|
48
|
+
name: 'VcsAttributions',
|
49
|
+
components: {
|
50
|
+
VList,
|
51
|
+
VListItem,
|
52
|
+
VListItemContent,
|
53
|
+
VListItemTitle,
|
54
|
+
VListItemSubtitle,
|
55
|
+
},
|
56
|
+
props: {
|
57
|
+
entries: {
|
58
|
+
type: Object,
|
59
|
+
default: () => {},
|
60
|
+
},
|
61
|
+
},
|
62
|
+
};
|
63
|
+
</script>
|