@vcmap/ui 5.0.0-rc.10 → 5.0.0-rc.13
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 +12 -5
- package/build/build.js +6 -3
- package/build/buildHelpers.js +12 -4
- package/build/buildPreview.js +7 -0
- package/build/getPluginProxies.js +4 -0
- package/config/aerowest.config.json +13 -3
- package/config/base.config.json +398 -219
- package/config/codes.config.json +397 -0
- package/config/dev.config.json +375 -1
- package/config/graphFeatureInfo.config.json +100 -0
- package/config/www.config.json +1232 -0
- package/dist/assets/{cesium.eb5667.js → cesium.21663e.js} +0 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.63242d.js +4 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/font/OFL.txt +93 -0
- package/dist/assets/font/TitilliumWeb-Regular.woff2 +0 -0
- package/dist/assets/{index.4ccd4433.js → index.44b91cfe.js} +1 -1
- package/dist/assets/{ol.ef03b1.js → ol.88ba9d.js} +0 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.3c2933.css +1 -0
- package/dist/assets/ui.3c2933.js +71 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.c897fc.js +9 -0
- package/dist/assets/vue.js +2 -1
- package/dist/assets/{vuetify.401a29.css → vuetify.147c3a.css} +1 -1
- package/dist/assets/{vuetify.401a29.js → vuetify.147c3a.js} +72 -72
- package/dist/assets/vuetify.js +2 -2
- package/dist/index.html +1 -5
- package/index.js +39 -5
- package/lib/vue.js +1 -0
- package/map.config.json +15 -6
- package/package.json +17 -8
- package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
- package/plugins/@vcmap/create-link/index.js +83 -0
- package/plugins/@vcmap/create-link/package.json +6 -0
- package/plugins/@vcmap/pluginExample/index.js +2 -2
- package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
- package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
- package/plugins/@vcmap/project-selector/index.js +1 -1
- package/plugins/@vcmap/project-selector/package.json +1 -2
- package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
- package/plugins/@vcmap/theme-changer/index.js +1 -1
- package/plugins/@vcmap/theme-changer/package.json +1 -2
- package/plugins/categoryTest/Categories.vue +89 -1
- package/plugins/categoryTest/Category.vue +1 -1
- package/plugins/example/index.js +10 -23
- package/plugins/simple-graph/README.md +51 -0
- package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
- package/plugins/simple-graph/index.js +17 -0
- package/plugins/simple-graph/package.json +11 -0
- package/plugins/simple-graph/simpleGraphView.js +76 -0
- package/plugins/test/editor.vue +1 -1
- package/plugins/test/index.js +76 -9
- package/plugins/test/toolbox-data.js +82 -57
- package/plugins/test/windowManagerExample.vue +1 -1
- package/src/actions/stateRefAction.js +2 -2
- package/src/actions/styleSelector.vue +1 -1
- package/src/application/Navbar.vue +13 -2
- package/src/application/VcsApp.vue +301 -116
- package/src/application/VcsMap.vue +1 -1
- package/src/application/VcsSettings.vue +1 -1
- package/src/application/vcsAppWrapper.vue +1 -0
- package/src/assets/font/OFL.txt +93 -0
- package/src/assets/font/TitilliumWeb-Regular.woff2 +0 -0
- package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
- package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
- package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
- package/src/components/form-output/VcsFormattedNumber.vue +1 -1
- package/src/components/lists/VcsActionList.vue +22 -7
- package/src/components/lists/VcsTreeview.vue +4 -4
- package/src/components/lists/VcsTreeviewLeaf.vue +10 -3
- package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
- package/src/components/tables/VcsTable.vue +245 -0
- package/src/contentTree/LayerTree.vue +1 -1
- package/src/contentTree/contentTreeCollection.js +4 -4
- package/src/contentTree/contentTreeItem.js +9 -9
- package/src/contentTree/groupContentTreeItem.js +1 -1
- package/src/contentTree/layerContentTreeItem.js +15 -1
- package/src/contentTree/layerGroupContentTreeItem.js +21 -1
- package/src/contentTree/nodeContentTreeItem.js +1 -1
- package/src/featureInfo/AddressBalloonComponent.vue +47 -0
- package/src/featureInfo/BalloonComponent.vue +140 -0
- package/src/featureInfo/abstractFeatureInfoView.js +313 -0
- package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
- package/src/featureInfo/balloonFeatureInfoView.js +151 -0
- package/src/featureInfo/balloonHelper.js +132 -0
- package/src/featureInfo/featureInfo.js +457 -0
- package/src/featureInfo/featureInfoInteraction.js +42 -0
- package/src/featureInfo/iframeFeatureInfoView.js +95 -0
- package/src/featureInfo/tableFeatureInfoView.js +106 -0
- package/src/i18n/de.js +26 -0
- package/src/i18n/en.js +26 -0
- package/src/i18n/i18nCollection.js +17 -0
- package/src/icons/+all.js +80 -0
- package/src/icons/ClippingHorizontalIcon.vue +7 -0
- package/src/icons/ClippingIcon.vue +7 -0
- package/src/icons/ClippingVerticalIcon.vue +7 -0
- package/src/icons/ColorPickerIcon.vue +7 -0
- package/src/icons/ComponentsIcon.vue +2 -2
- package/src/icons/DimensionsHouseIcon.vue +11 -9
- package/src/icons/EditIcon.vue +7 -0
- package/src/icons/GlobalTerrainIcon.vue +9 -0
- package/src/icons/GroundIcon.vue +18 -0
- package/src/icons/HideIcon.vue +12 -0
- package/src/icons/LogoutIcon.vue +7 -0
- package/src/icons/ObjectAttributeIcon.vue +2 -13
- package/src/icons/PedestrianIcon.vue +2 -3
- package/src/icons/PenIcon.vue +2 -9
- package/src/icons/PoiIcon.vue +5 -2
- package/src/icons/PointSelectIcon.vue +4 -2
- package/src/icons/QueryIcon.vue +6 -7
- package/src/icons/ScreenshotIcon.vue +16 -0
- package/src/icons/ShareIcon.vue +4 -16
- package/src/icons/SkipNextIcon.vue +3 -1
- package/src/icons/TerrainBoxIcon.vue +9 -0
- package/src/icons/ToolsIcon.vue +4 -30
- package/src/icons/UploadIcon.vue +2 -9
- package/src/icons/UserProfileIcon.vue +7 -0
- package/src/icons/UserShareIcon.vue +7 -0
- package/src/icons/VideoRecorderIcon.vue +5 -9
- package/src/icons/ViewpointFlightIcon.vue +11 -0
- package/src/icons/ViewpointIcon.vue +11 -0
- package/src/icons/Viewshed360Icon.vue +7 -0
- package/src/icons/ViewshedConeIcon.vue +7 -0
- package/src/icons/ViewshedIcon.vue +7 -0
- package/src/icons/WallIcon.vue +4 -9
- package/src/legend/legendHelper.js +193 -0
- package/src/legend/styleLegendItem.vue +129 -0
- package/src/legend/vcsLegend.vue +92 -0
- package/src/manager/buttonManager.js +7 -12
- package/src/manager/categoryManager/ComponentsManager.vue +30 -0
- package/src/manager/categoryManager/categoryManager.js +500 -0
- package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
- package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
- package/src/manager/contextMenu/contextMenuManager.js +197 -0
- package/src/manager/navbarManager.js +9 -9
- package/src/manager/toolbox/GroupToolboxComponent.vue +118 -0
- package/src/manager/toolbox/SelectToolboxComponent.vue +128 -0
- package/src/manager/toolbox/ToolboxManager.vue +116 -98
- package/src/manager/toolbox/toolboxManager.js +235 -86
- package/src/manager/window/WindowComponent.vue +1 -1
- package/src/manager/window/WindowManager.vue +5 -3
- package/src/manager/window/windowManager.js +118 -14
- package/src/navigation/mapNavigation.vue +3 -5
- package/src/navigation/overviewMap.js +28 -5
- package/src/navigation/vcsCompass.vue +1 -1
- package/src/pluginHelper.js +42 -10
- package/src/setup.js +0 -2
- package/src/state.js +256 -0
- package/src/styles/_theming.scss +0 -5
- package/src/styles/variables.scss +7 -0
- package/src/styles/vcsFont.scss +17 -0
- package/src/uiConfig.js +79 -0
- package/src/vcsUiApp.js +213 -22
- package/src/vuePlugins/vuetify.js +14 -4
- package/config/berlin.config.json +0 -510
- package/dist/assets/core.216494.js +0 -4
- package/dist/assets/ui.99a1a7.css +0 -1
- package/dist/assets/ui.99a1a7.js +0 -70
- package/dist/assets/vue-composition-api.c5aca1.js +0 -14
- package/dist/assets/vue-composition-api.js +0 -2
- package/dist/assets/vue.762edd.js +0 -9
- package/lib/vue-composition-api.js +0 -2
- package/src/manager/toolbox/ToolboxGroupComponent.vue +0 -128
@@ -0,0 +1,123 @@
|
|
1
|
+
<template>
|
2
|
+
<VcsTooltip
|
3
|
+
:tooltip-position="tooltipPosition"
|
4
|
+
:tooltip="errorMessage"
|
5
|
+
color="error"
|
6
|
+
>
|
7
|
+
<template #activator="{ on, attrs }">
|
8
|
+
<span v-on="on">
|
9
|
+
<v-radio-group
|
10
|
+
hide-details
|
11
|
+
class="w-full vcs-radio-group"
|
12
|
+
:dense="$attrs.dense!==false"
|
13
|
+
:ripple="false"
|
14
|
+
v-bind="{...$attrs, ...attrs}"
|
15
|
+
v-on="{...$listeners, ...on}"
|
16
|
+
@update:error="setError"
|
17
|
+
>
|
18
|
+
<v-radio
|
19
|
+
v-for="(item, idx) in items"
|
20
|
+
:id="`radio-${idx}`"
|
21
|
+
:key="`radio-${idx}`"
|
22
|
+
:ripple="false"
|
23
|
+
:color="item.color ?? 'secondary'"
|
24
|
+
:value="item.value ?? item"
|
25
|
+
:disabled="item.disabled ?? false"
|
26
|
+
:class="$attrs.dense!==false ? 'vcs-radio-dense' : 'vcs-radio'"
|
27
|
+
>
|
28
|
+
<template #label>
|
29
|
+
<VcsLabel :html-for="`radio-${idx}`" :dense="!!$attrs.dense">
|
30
|
+
{{ $t(item.label ?? item) }}
|
31
|
+
</VcsLabel>
|
32
|
+
</template>
|
33
|
+
</v-radio>
|
34
|
+
</v-radio-group>
|
35
|
+
</span>
|
36
|
+
</template>
|
37
|
+
</VcsTooltip>
|
38
|
+
</template>
|
39
|
+
<style lang="scss" scoped>
|
40
|
+
@import "../../styles/vcsFont";
|
41
|
+
.v-input--radio-group--column .v-radio:not(:last-child):not(:only-child) {
|
42
|
+
margin-bottom: 0;
|
43
|
+
}
|
44
|
+
.v-input{
|
45
|
+
&.vcs-radio-group{
|
46
|
+
::v-deep {
|
47
|
+
margin-top: 0;
|
48
|
+
padding-top: 0;
|
49
|
+
label.v-label,
|
50
|
+
.v-icon.v-icon{
|
51
|
+
font-size: $base-font-size;
|
52
|
+
color: inherit;
|
53
|
+
}
|
54
|
+
.v-radio:not(:last-child):not(:only-child){
|
55
|
+
margin-bottom: 0;
|
56
|
+
}
|
57
|
+
.v-input--selection-controls__input{
|
58
|
+
margin: 0;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
.vcs-radio {
|
64
|
+
height: 40px;
|
65
|
+
align-items: center;
|
66
|
+
}
|
67
|
+
.vcs-radio-dense {
|
68
|
+
height: 32px;
|
69
|
+
align-items: center;
|
70
|
+
}
|
71
|
+
</style>
|
72
|
+
<script>
|
73
|
+
import VcsTooltip from '../notification/VcsTooltip.vue';
|
74
|
+
import VcsLabel from './VcsLabel.vue';
|
75
|
+
import validate from '../notification/validation.js';
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @typedef {Object} VcsRadioItem
|
79
|
+
* @property {string} label
|
80
|
+
* @property {string} [color='secondary']
|
81
|
+
* @property {any} value
|
82
|
+
* @property {boolean} [disabled=false]
|
83
|
+
*/
|
84
|
+
|
85
|
+
/**
|
86
|
+
* @description Stylized wrapper around {@link https://vuetifyjs.com/en/api/v-radio-group/ |vuetify v-radio-group} using
|
87
|
+
* {@link https://vuetifyjs.com/en/api/v-radio/ |vuetify v-radio}.
|
88
|
+
* Provides two height options depending on "dense" property:
|
89
|
+
* - if dense is set true (default), height is 24 px
|
90
|
+
* - if dense is set false, height is 32 px
|
91
|
+
* Provides VcsTooltip to show error messages
|
92
|
+
* @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
|
93
|
+
* @vue-prop {Array<string|VcsRadioItem>} items - A list of options. If strings are provided, the string is used as label and value.
|
94
|
+
*/
|
95
|
+
export default {
|
96
|
+
name: 'VcsRadio',
|
97
|
+
components: { VcsTooltip, VcsLabel },
|
98
|
+
props: {
|
99
|
+
tooltipPosition: {
|
100
|
+
type: String,
|
101
|
+
default: 'right',
|
102
|
+
},
|
103
|
+
items: {
|
104
|
+
type: Array,
|
105
|
+
required: true,
|
106
|
+
},
|
107
|
+
},
|
108
|
+
data() {
|
109
|
+
return {
|
110
|
+
errorMessage: '',
|
111
|
+
};
|
112
|
+
},
|
113
|
+
methods: {
|
114
|
+
setError() {
|
115
|
+
const rules = [...this.$attrs.rules].concat(this.$attrs.errorMessages);
|
116
|
+
this.errorMessage = validate(rules, this.$attrs.value).join('\n');
|
117
|
+
},
|
118
|
+
},
|
119
|
+
model: {
|
120
|
+
event: 'change',
|
121
|
+
},
|
122
|
+
};
|
123
|
+
</script>
|
@@ -51,20 +51,35 @@
|
|
51
51
|
* @property {boolean} [active=false] - optional state of button. If active, button is rendered in primary color
|
52
52
|
*/
|
53
53
|
|
54
|
+
/**
|
55
|
+
* pattern to check actions
|
56
|
+
* @type {Object}
|
57
|
+
*/
|
58
|
+
export const ActionPattern = {
|
59
|
+
name: String,
|
60
|
+
title: [undefined, String],
|
61
|
+
icon: [undefined, String],
|
62
|
+
callback: Function,
|
63
|
+
active: [undefined, Boolean],
|
64
|
+
};
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param {VcsAction} action
|
68
|
+
* @returns {boolean}
|
69
|
+
*/
|
70
|
+
export function validateAction(action) {
|
71
|
+
return is(action, ActionPattern);
|
72
|
+
}
|
73
|
+
|
54
74
|
/**
|
55
75
|
* @param {Array<VcsAction>} actions
|
56
76
|
* @returns {boolean}
|
57
77
|
*/
|
58
78
|
export function validateActions(actions) {
|
59
|
-
return actions.every(
|
60
|
-
name: String,
|
61
|
-
title: [undefined, String],
|
62
|
-
icon: [undefined, String],
|
63
|
-
callback: Function,
|
64
|
-
active: [undefined, Boolean],
|
65
|
-
}));
|
79
|
+
return actions.every(validateAction);
|
66
80
|
}
|
67
81
|
|
82
|
+
|
68
83
|
/**
|
69
84
|
* @description
|
70
85
|
* A component rendering a list of actions with overflow mechanic using
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="d-contents">
|
3
3
|
<VcsTreeviewSearchbar
|
4
|
-
v-if="
|
4
|
+
v-if="showSearchbar"
|
5
5
|
:placeholder="searchbarPlaceholder"
|
6
6
|
v-model="search"
|
7
7
|
/>
|
@@ -55,7 +55,7 @@
|
|
55
55
|
</style>
|
56
56
|
|
57
57
|
<script>
|
58
|
-
import { getCurrentInstance, ref } from '
|
58
|
+
import { getCurrentInstance, ref } from 'vue';
|
59
59
|
import VcsTreeviewLeaf from './VcsTreeviewLeaf.vue';
|
60
60
|
import VcsTreeviewSearchbar from './VcsTreeviewSearchbar.vue';
|
61
61
|
|
@@ -63,14 +63,14 @@
|
|
63
63
|
* @description extends API of https://vuetifyjs.com/en/api/v-treeview/
|
64
64
|
* Can render dynamic components as leaf items.
|
65
65
|
* In order to display an item needs to be registered and added to `availableComponents`.
|
66
|
-
* @vue-prop {boolean} [
|
66
|
+
* @vue-prop {boolean} [showSearchbar=false] - Whether there is a searchbar for this treeview
|
67
67
|
* @vue-prop {string} [searchbarPlaceholder] - Placeholder text for the searchbar, will be translated
|
68
68
|
*/
|
69
69
|
export default {
|
70
70
|
name: 'VcsTreeview',
|
71
71
|
components: { VcsTreeviewSearchbar, VcsTreeviewLeaf },
|
72
72
|
props: {
|
73
|
-
|
73
|
+
showSearchbar: {
|
74
74
|
type: Boolean,
|
75
75
|
default: false,
|
76
76
|
},
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<span ref="imgContainer" />
|
14
14
|
</span>
|
15
15
|
|
16
|
-
<div class="position-relative col-8 pa-0 d-flex align-center">
|
16
|
+
<div class="position-relative col-8 pa-0 d-flex align-center treeview-label" :title="$t(item.title)">
|
17
17
|
<span>{{ $t(item.title) }}</span>
|
18
18
|
</div>
|
19
19
|
<VcsActionButtonList
|
@@ -27,6 +27,13 @@
|
|
27
27
|
/>
|
28
28
|
</div>
|
29
29
|
</template>
|
30
|
+
<style lang="css" scoped>
|
31
|
+
.treeview-label span{
|
32
|
+
white-space: nowrap;
|
33
|
+
overflow: hidden;
|
34
|
+
text-overflow: ellipsis;
|
35
|
+
}
|
36
|
+
</style>
|
30
37
|
|
31
38
|
<script>
|
32
39
|
import
|
@@ -34,7 +41,7 @@
|
|
34
41
|
computed,
|
35
42
|
onMounted,
|
36
43
|
ref,
|
37
|
-
} from '
|
44
|
+
} from 'vue';
|
38
45
|
|
39
46
|
import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
|
40
47
|
|
@@ -61,7 +68,7 @@
|
|
61
68
|
const iconType = ref();
|
62
69
|
const imgContainer = ref();
|
63
70
|
|
64
|
-
const leaf = computed(() => props.item
|
71
|
+
const leaf = computed(() => props.item?.children?.length === 0);
|
65
72
|
|
66
73
|
onMounted(() => { // TODO make icon reactive
|
67
74
|
const { icon } = props.item;
|
@@ -104,7 +104,7 @@
|
|
104
104
|
|
105
105
|
|
106
106
|
<script>
|
107
|
-
import { onMounted, onUnmounted } from '
|
107
|
+
import { onMounted, onUnmounted } from 'vue';
|
108
108
|
|
109
109
|
import { Subject } from 'rxjs';
|
110
110
|
import { debounceTime } from 'rxjs/operators';
|
@@ -147,7 +147,6 @@
|
|
147
147
|
onUnmounted(() => sub.unsubscribe());
|
148
148
|
|
149
149
|
return {
|
150
|
-
sub,
|
151
150
|
handleInput: (val) => {
|
152
151
|
sub.next(val);
|
153
152
|
},
|
@@ -0,0 +1,245 @@
|
|
1
|
+
<template>
|
2
|
+
<v-card>
|
3
|
+
<VcsTreeviewSearchbar
|
4
|
+
v-if="showSearchbar"
|
5
|
+
:placeholder="$t(searchbarPlaceholder)"
|
6
|
+
v-model="search"
|
7
|
+
/>
|
8
|
+
<v-data-table
|
9
|
+
dense
|
10
|
+
item-key="key"
|
11
|
+
:headers="translatedHeaders"
|
12
|
+
:items="items"
|
13
|
+
:items-per-page.sync="itemsPerPageRef"
|
14
|
+
:page.sync="page"
|
15
|
+
:search="search"
|
16
|
+
:custom-filter="handleFilter"
|
17
|
+
hide-default-footer
|
18
|
+
class="vcs-table"
|
19
|
+
>
|
20
|
+
<!-- eslint-disable-next-line -->
|
21
|
+
<template #item.key="{ item }">
|
22
|
+
<td class="vcs-table">
|
23
|
+
{{ $t(item.key) }}
|
24
|
+
</td>
|
25
|
+
</template>
|
26
|
+
<!-- eslint-disable-next-line -->
|
27
|
+
<template #item.value="{ item }">
|
28
|
+
<td class="vcs-table">
|
29
|
+
{{ $t(item.value) }}
|
30
|
+
</td>
|
31
|
+
</template>
|
32
|
+
<template #footer>
|
33
|
+
<v-divider />
|
34
|
+
<v-container class="pa-2" v-if="items.length > itemsPerPageRef">
|
35
|
+
<v-row
|
36
|
+
dense
|
37
|
+
no-gutters
|
38
|
+
align="center"
|
39
|
+
justify="center"
|
40
|
+
>
|
41
|
+
<v-menu offset-y dense>
|
42
|
+
<template #activator="{ on, attrs }">
|
43
|
+
<VcsButton
|
44
|
+
small
|
45
|
+
color="primary"
|
46
|
+
v-bind="attrs"
|
47
|
+
v-on="on"
|
48
|
+
>
|
49
|
+
{{ itemsPerPageRef }}
|
50
|
+
<v-icon>mdi-chevron-down</v-icon>
|
51
|
+
</VcsButton>
|
52
|
+
</template>
|
53
|
+
<v-list>
|
54
|
+
<v-list-item
|
55
|
+
v-for="(number, index) in itemsPerPageArray"
|
56
|
+
:key="index"
|
57
|
+
@click="updateItemsPerPage(number)"
|
58
|
+
>
|
59
|
+
<v-list-item-title>{{ number }}</v-list-item-title>
|
60
|
+
</v-list-item>
|
61
|
+
</v-list>
|
62
|
+
</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>
|
65
|
+
<VcsButton
|
66
|
+
small
|
67
|
+
icon="mdi-chevron-left"
|
68
|
+
@click="formerPage"
|
69
|
+
tooltip="components.vcsTable.formerPage"
|
70
|
+
:disabled="page < 2"
|
71
|
+
class="mx-2"
|
72
|
+
/>
|
73
|
+
<VcsButton
|
74
|
+
small
|
75
|
+
icon="mdi-chevron-right"
|
76
|
+
@click="nextPage"
|
77
|
+
tooltip="components.vcsTable.nextPage"
|
78
|
+
:disabled="page > numberOfPages - 1"
|
79
|
+
class="mx-1"
|
80
|
+
/>
|
81
|
+
</v-row>
|
82
|
+
</v-container>
|
83
|
+
</template>
|
84
|
+
</v-data-table>
|
85
|
+
</v-card>
|
86
|
+
</template>
|
87
|
+
<script>
|
88
|
+
import { getCurrentInstance, ref, computed } from 'vue';
|
89
|
+
import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
|
90
|
+
import VcsButton from '../buttons/VcsButton.vue';
|
91
|
+
|
92
|
+
/**
|
93
|
+
* @typedef {Object} TableItem
|
94
|
+
* @property {string} key
|
95
|
+
* @property {string} value
|
96
|
+
*/
|
97
|
+
|
98
|
+
/**
|
99
|
+
* @param {Object} attributes
|
100
|
+
* @param {Array<TableItem>} [items]
|
101
|
+
* @param {string} [parent]
|
102
|
+
* @returns {Array<TableItem>}
|
103
|
+
*/
|
104
|
+
export function attributesToItems(attributes, items = [], parent = undefined) {
|
105
|
+
const nestedKey = (key, nested) => (nested ? `${nested}.${key}` : key);
|
106
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
107
|
+
if (value instanceof Object) {
|
108
|
+
attributesToItems(value, items, nestedKey(key, parent));
|
109
|
+
} else {
|
110
|
+
const item = { key: nestedKey(key, parent), value };
|
111
|
+
if (parent) { item.group = parent; }
|
112
|
+
items.push(item);
|
113
|
+
}
|
114
|
+
});
|
115
|
+
return items;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props|vuetify v-data-table }
|
120
|
+
* @vue-prop {string} featureId - feature's id
|
121
|
+
* @vue-prop {Object} attributes - feature's attributes
|
122
|
+
* @vue-prop {Array<{text: string, value: string}>} [headers] - optional array defining column names
|
123
|
+
* @vue-prop {boolean} [showSearchbar=true] - whether to show searchbar
|
124
|
+
* @vue-prop {string} [searchbarPlaceholder='Search'] - placeholder for searchbar
|
125
|
+
* @vue-computed {Array<{key:string,value:string}>} items - from attributes derived table items
|
126
|
+
*/
|
127
|
+
export default {
|
128
|
+
name: 'VcsTable',
|
129
|
+
components: { VcsButton, VcsTreeviewSearchbar },
|
130
|
+
props: {
|
131
|
+
featureId: {
|
132
|
+
type: String,
|
133
|
+
required: true,
|
134
|
+
},
|
135
|
+
attributes: {
|
136
|
+
type: Object,
|
137
|
+
required: true,
|
138
|
+
},
|
139
|
+
headers: {
|
140
|
+
type: Array,
|
141
|
+
default: () => [
|
142
|
+
{ text: 'components.vcsTable.key', value: 'key', width: '160px' },
|
143
|
+
{ text: 'components.vcsTable.value', value: 'value', width: '160px' },
|
144
|
+
],
|
145
|
+
},
|
146
|
+
itemsPerPage: {
|
147
|
+
type: Number,
|
148
|
+
default: 10,
|
149
|
+
},
|
150
|
+
itemsPerPageArray: {
|
151
|
+
type: Array,
|
152
|
+
default: () => [5, 10, 15],
|
153
|
+
},
|
154
|
+
showSearchbar: {
|
155
|
+
type: Boolean,
|
156
|
+
default: true,
|
157
|
+
},
|
158
|
+
searchbarPlaceholder: {
|
159
|
+
type: String,
|
160
|
+
default: 'components.vcsTable.searchbarPlaceholder',
|
161
|
+
},
|
162
|
+
},
|
163
|
+
setup(props) {
|
164
|
+
const vm = getCurrentInstance().proxy;
|
165
|
+
|
166
|
+
const items = computed(() => {
|
167
|
+
return attributesToItems({
|
168
|
+
featureId: props.featureId,
|
169
|
+
...props.attributes,
|
170
|
+
});
|
171
|
+
});
|
172
|
+
|
173
|
+
const translatedHeaders = computed(() => {
|
174
|
+
return props.headers.map((hd) => {
|
175
|
+
hd.text = vm.$t(hd.text);
|
176
|
+
return hd;
|
177
|
+
});
|
178
|
+
});
|
179
|
+
|
180
|
+
const itemsPerPageRef = ref(props.itemsPerPage);
|
181
|
+
const numberOfPages = computed(() => {
|
182
|
+
return Math.ceil(items.value.length / itemsPerPageRef.value);
|
183
|
+
});
|
184
|
+
|
185
|
+
const page = ref(1);
|
186
|
+
const itemsFrom = computed(() => ((page.value - 1) * itemsPerPageRef.value) + 1);
|
187
|
+
const itemsTo = computed(() => {
|
188
|
+
const last = page.value * itemsPerPageRef.value;
|
189
|
+
return last < items.value.length ? last : items.value.length;
|
190
|
+
});
|
191
|
+
|
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
|
+
return {
|
209
|
+
search: ref(''),
|
210
|
+
page,
|
211
|
+
items,
|
212
|
+
itemsPerPageRef,
|
213
|
+
itemsFrom,
|
214
|
+
itemsTo,
|
215
|
+
numberOfPages,
|
216
|
+
nextPage() {
|
217
|
+
if (page.value + 1 <= numberOfPages.value) {
|
218
|
+
page.value += 1;
|
219
|
+
}
|
220
|
+
},
|
221
|
+
formerPage() {
|
222
|
+
if (page.value - 1 >= 1) {
|
223
|
+
page.value -= 1;
|
224
|
+
}
|
225
|
+
},
|
226
|
+
updateItemsPerPage(number) {
|
227
|
+
itemsPerPageRef.value = number;
|
228
|
+
},
|
229
|
+
handleFilter,
|
230
|
+
translatedHeaders,
|
231
|
+
};
|
232
|
+
},
|
233
|
+
};
|
234
|
+
</script>
|
235
|
+
|
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;
|
243
|
+
}
|
244
|
+
}
|
245
|
+
</style>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
// eslint-disable-next-line max-classes-per-file
|
2
2
|
import { IndexedCollection, makeOverrideCollection, getObjectFromClassRegistry } from '@vcmap/core';
|
3
3
|
import { v4 as uuid } from 'uuid';
|
4
|
-
import { computed, ref } from '
|
4
|
+
import { computed, ref } from 'vue';
|
5
5
|
import ContentTreeItem from './contentTreeItem.js';
|
6
6
|
import { vcsAppSymbol } from '../pluginHelper.js';
|
7
7
|
import SubContentTreeItem, { subTreeSymbol } from './subContentTreeItem.js';
|
@@ -73,7 +73,7 @@ class ContentTreeCollection extends IndexedCollection {
|
|
73
73
|
*/
|
74
74
|
this._defaultSubtreeItem = new SubContentTreeItem({ name: 'Content', icon: '$vcsLayers', title: 'content.title' }, app);
|
75
75
|
/**
|
76
|
-
* @type {import("
|
76
|
+
* @type {import("vue").Ref<Map<string, TreeViewItem>>}
|
77
77
|
* @private
|
78
78
|
*/
|
79
79
|
this._subTreeViewItems = ref(new Map());
|
@@ -211,7 +211,7 @@ class ContentTreeCollection extends IndexedCollection {
|
|
211
211
|
|
212
212
|
/**
|
213
213
|
* @param {string} id
|
214
|
-
* @returns {import("
|
214
|
+
* @returns {import("vue").ComputedRef<Array<TreeViewItem>>}
|
215
215
|
*/
|
216
216
|
getComputedVisibleTree(id) {
|
217
217
|
return computed(() => {
|
@@ -253,7 +253,7 @@ class ContentTreeCollection extends IndexedCollection {
|
|
253
253
|
|
254
254
|
/**
|
255
255
|
* @param {string} id
|
256
|
-
* @returns {import("
|
256
|
+
* @returns {import("vue").Ref<Array<string>>}
|
257
257
|
*/
|
258
258
|
getTreeOpenStateRef(id) {
|
259
259
|
const subTree = this._getSubTree(id);
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ref, reactive, computed } from '
|
1
|
+
import { ref, reactive, computed } from 'vue';
|
2
2
|
import { check, checkMaybe } from '@vcsuite/check';
|
3
3
|
import { parseBoolean, parseNumber } from '@vcsuite/parsers';
|
4
4
|
import { ClassRegistry, VcsEvent } from '@vcmap/core';
|
@@ -63,7 +63,7 @@ class ContentTreeItem {
|
|
63
63
|
this._app = app;
|
64
64
|
|
65
65
|
/**
|
66
|
-
* @type {import("
|
66
|
+
* @type {import("vue").Ref<Array<VcsAction>>}
|
67
67
|
* @private
|
68
68
|
*/
|
69
69
|
this._actions = ref([]);
|
@@ -76,28 +76,28 @@ class ContentTreeItem {
|
|
76
76
|
|
77
77
|
/**
|
78
78
|
* Whether to display this item or not.
|
79
|
-
* @type {import("
|
79
|
+
* @type {import("vue").Ref<boolean>}
|
80
80
|
* @private
|
81
81
|
*/
|
82
82
|
this._visible = ref(true);
|
83
83
|
|
84
84
|
/**
|
85
85
|
* Whether this item reacts to click events, e.g. with visual feedback
|
86
|
-
* @type {import("
|
86
|
+
* @type {import("vue").Ref<boolean>}
|
87
87
|
* @private
|
88
88
|
*/
|
89
89
|
this._clickable = ref(true);
|
90
90
|
|
91
91
|
/**
|
92
92
|
* Whether this item should be displayed as disabled.
|
93
|
-
* @type {import("
|
93
|
+
* @type {import("vue").Ref<boolean>}
|
94
94
|
* @private
|
95
95
|
*/
|
96
96
|
this._disabled = ref(false);
|
97
97
|
|
98
98
|
/**
|
99
99
|
* The state of this item. NONE if this item cannot have a state.
|
100
|
-
* @type {import("
|
100
|
+
* @type {import("vue").Ref<StateActionState>}
|
101
101
|
* @private
|
102
102
|
*/
|
103
103
|
this._state = ref(StateActionState.NONE);
|
@@ -116,20 +116,20 @@ class ContentTreeItem {
|
|
116
116
|
this.infoUrl = options.infoUrl;
|
117
117
|
|
118
118
|
/**
|
119
|
-
* @type {import("
|
119
|
+
* @type {import("vue").Ref<Object<string, string>|string|undefined>}
|
120
120
|
* @private
|
121
121
|
*/
|
122
122
|
this._title = ref(options.title);
|
123
123
|
|
124
124
|
/**
|
125
125
|
* An optional icon to display with this item. Can be an URL or HTMLElement.
|
126
|
-
* @type {import("
|
126
|
+
* @type {import("vue").Ref<string|HTMLCanvasElement|HTMLImageElement|undefined>}
|
127
127
|
* @private
|
128
128
|
*/
|
129
129
|
this._icon = ref(options.icon);
|
130
130
|
|
131
131
|
/**
|
132
|
-
* @type {import("
|
132
|
+
* @type {import("vue").Ref<Array<TreeViewItem>>}
|
133
133
|
* @protected
|
134
134
|
*/
|
135
135
|
this._children = ref([]);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ViewPoint } from '@vcmap/core';
|
2
|
-
import { reactive } from '
|
2
|
+
import { reactive } from 'vue';
|
3
3
|
import { StateActionState } from '../actions/stateRefAction.js';
|
4
4
|
import { createGoToViewpointAction, createModalAction } from '../actions/actionHelper.js';
|
5
5
|
import component from '../actions/styleSelector.vue';
|
@@ -10,6 +10,7 @@ import { contentTreeClassRegistry } from './contentTreeItem.js';
|
|
10
10
|
/**
|
11
11
|
* @typedef {ContentTreeItemOptions} LayerContentTreeItemOptions
|
12
12
|
* @property {string} layerName
|
13
|
+
* @property {Array<string>} layerNamesToDeactivate list of LayerNames which should be deactivated if the click activates the layer
|
13
14
|
*/
|
14
15
|
|
15
16
|
/**
|
@@ -80,6 +81,14 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
|
|
80
81
|
*/
|
81
82
|
this._layerName = options.layerName;
|
82
83
|
|
84
|
+
/**
|
85
|
+
* @type {Array<string>}
|
86
|
+
* @private
|
87
|
+
*/
|
88
|
+
this._layerNamesToDeactivate = Array.isArray(options.layerNamesToDeactivate) ?
|
89
|
+
options.layerNamesToDeactivate.slice() :
|
90
|
+
[];
|
91
|
+
|
83
92
|
/**
|
84
93
|
* @type {Array<Function>}
|
85
94
|
* @private
|
@@ -210,6 +219,10 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
|
|
210
219
|
if (this._layer) {
|
211
220
|
if (this.state === StateActionState.INACTIVE) {
|
212
221
|
await this._layer.activate();
|
222
|
+
this._layerNamesToDeactivate
|
223
|
+
.map(n => this._app.layers.getByKey(n))
|
224
|
+
.filter(l => l)
|
225
|
+
.forEach(l => l.deactivate());
|
213
226
|
} else {
|
214
227
|
this._layer.deactivate();
|
215
228
|
}
|
@@ -222,6 +235,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
|
|
222
235
|
toJSON() {
|
223
236
|
const config = super.toJSON();
|
224
237
|
config.layerName = this._layerName;
|
238
|
+
config.layerNamesToDeactivate = this._layerNamesToDeactivate.slice();
|
225
239
|
return config;
|
226
240
|
}
|
227
241
|
|