@vcmap/ui 5.0.0-rc.10 → 5.0.0-rc.11
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 +11 -4
- package/build/build.js +0 -3
- package/build/buildHelpers.js +0 -1
- package/build/buildPreview.js +7 -0
- package/config/aerowest.config.json +13 -3
- package/config/base.config.json +89 -64
- package/config/codes.config.json +397 -0
- package/config/dev.config.json +169 -0
- package/config/graphFeatureInfo.config.json +100 -0
- package/config/www.config.json +1232 -0
- package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.ebf665.js +4 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
- package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.fdfe0d.css +1 -0
- package/dist/assets/ui.fdfe0d.js +68 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.0bb7c6.js +9 -0
- package/dist/assets/vue.js +2 -1
- package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
- package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
- package/dist/assets/vuetify.js +2 -2
- package/dist/index.html +1 -1
- package/index.js +36 -5
- package/lib/vue.js +1 -0
- package/map.config.json +15 -6
- package/package.json +6 -7
- 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 +1 -1
- 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/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 +63 -2
- 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 +201 -92
- package/src/application/VcsMap.vue +1 -1
- package/src/application/VcsSettings.vue +1 -1
- package/src/application/vcsAppWrapper.vue +1 -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 +13 -4
- package/src/components/lists/VcsTreeview.vue +4 -4
- package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
- 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 +138 -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 +455 -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 +16 -0
- package/src/i18n/en.js +16 -0
- package/src/i18n/i18nCollection.js +17 -0
- package/src/manager/buttonManager.js +5 -5
- 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 +8 -8
- package/src/manager/toolbox/ToolboxManager.vue +2 -2
- package/src/manager/toolbox/toolboxManager.js +7 -3
- 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/setup.js +0 -2
- package/src/state.js +256 -0
- package/src/styles/_theming.scss +0 -5
- package/src/uiConfig.js +79 -0
- package/src/vcsUiApp.js +210 -20
- 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/state.js
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
import { check } from '@vcsuite/check';
|
2
|
+
import { getLogger } from '@vcsuite/logger';
|
3
|
+
import { ViewPoint } from '@vcmap/core';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @typedef {Object} LayerState
|
7
|
+
* @property {string} name
|
8
|
+
* @property {boolean} active
|
9
|
+
* @property {string} [styleName]
|
10
|
+
*/
|
11
|
+
|
12
|
+
/**
|
13
|
+
* The URL state of a layer is an array. The first entry is the layer name,
|
14
|
+
* the second its active state encoded in an integer (1 active, 0 inactive),
|
15
|
+
* the third and optional entry, is an optional styleName to set on the layer
|
16
|
+
* @typedef {[string,number,string|0]} UrlLayerState
|
17
|
+
*/
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @typedef {Object} PluginState
|
21
|
+
* @property {string} name
|
22
|
+
* @property {*} state
|
23
|
+
*/
|
24
|
+
|
25
|
+
/**
|
26
|
+
* The URL state of a plugin is an array. The first entry is the plugin name, the second entry is
|
27
|
+
* an encoded object, which is the plugins state.
|
28
|
+
* @typedef {[string, *]} UrlPluginState
|
29
|
+
*/
|
30
|
+
|
31
|
+
/**
|
32
|
+
* The URL state of a viewpoint is an array, the first entry is the camera position (or 0)
|
33
|
+
* the second is the ground position (or 0), the third is the distance, the last three are
|
34
|
+
* heading, pitch, roll in that order
|
35
|
+
* @typedef {[Array<number>|0,Array<number>|0,number,number,number,number]} UrlViewpointState
|
36
|
+
*/
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @typedef {Object} AppState
|
40
|
+
* @property {import("@vcmap/core").ViewPointOptions} [activeViewpoint]
|
41
|
+
* @property {string} [activeMap]
|
42
|
+
* @property {Array<string>} contextIds
|
43
|
+
* @property {Array<LayerState>} layers
|
44
|
+
* @property {Array<PluginState>} plugins
|
45
|
+
* @property {string} [activeObliqueCollection]
|
46
|
+
*/
|
47
|
+
|
48
|
+
/**
|
49
|
+
* The URL state of the app is an array. To null parameters, pass in 0 instead.
|
50
|
+
* The first entry is the viewpoint state, the second the active map name
|
51
|
+
* The third is an array of contexts to apply the state to
|
52
|
+
* the fourth is an array of layer states
|
53
|
+
* the fifth is an array of plugin states
|
54
|
+
* the sixth is the currently active oblique collection or 0 if not applicable
|
55
|
+
* @typedef {[UrlViewpointState,string,Array<string>,Array<UrlLayerState>,Array<UrlPluginState>,(string|0)]} UrlAppState
|
56
|
+
*/
|
57
|
+
|
58
|
+
/**
|
59
|
+
* @type {number}
|
60
|
+
*/
|
61
|
+
const MAX_URL_LENGTH = 2048;
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @returns {AppState}
|
65
|
+
*/
|
66
|
+
export function createEmptyState() {
|
67
|
+
return {
|
68
|
+
contextIds: [],
|
69
|
+
layers: [],
|
70
|
+
plugins: [],
|
71
|
+
};
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* @param {UrlViewpointState} state
|
76
|
+
* @returns {import("@vcmap/core").ViewPointOptions|null}
|
77
|
+
*/
|
78
|
+
function parseUrlViewpointState(state) {
|
79
|
+
const vp = new ViewPoint({
|
80
|
+
cameraPosition: state[0] ?? undefined,
|
81
|
+
groundPosition: state[1] ?? undefined,
|
82
|
+
distance: state[2] > 0 ? state[2] : undefined,
|
83
|
+
heading: state[3],
|
84
|
+
pitch: state[4],
|
85
|
+
roll: state[5],
|
86
|
+
});
|
87
|
+
|
88
|
+
return vp.isValid() ? vp.toJSON() : null;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* @param {UrlLayerState} state
|
93
|
+
* @returns {LayerState}
|
94
|
+
*/
|
95
|
+
function parseUrlLayerState(state) {
|
96
|
+
const layerState = {
|
97
|
+
name: state[0],
|
98
|
+
active: !!state[1],
|
99
|
+
};
|
100
|
+
if (state[2] !== 0) {
|
101
|
+
layerState.styleName = state[2];
|
102
|
+
}
|
103
|
+
return layerState;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* @param {LayerState} state
|
108
|
+
* @returns {UrlLayerState}
|
109
|
+
*/
|
110
|
+
function writeUrlLayerState(state) {
|
111
|
+
return [state.name, state.active ? 1 : 0, state.styleName ?? 0];
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* @param {UrlPluginState} state
|
116
|
+
* @returns {PluginState}
|
117
|
+
*/
|
118
|
+
function parseUrlPluginState(state) {
|
119
|
+
return {
|
120
|
+
name: state[0],
|
121
|
+
state: state[1],
|
122
|
+
};
|
123
|
+
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* @param {PluginState} state
|
127
|
+
* @returns {UrlPluginState}
|
128
|
+
*/
|
129
|
+
function writeUrlPluginState(state) {
|
130
|
+
return [state.name, state.state];
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* @param {UrlAppState} urlState
|
135
|
+
* @returns {AppState}
|
136
|
+
*/
|
137
|
+
function parseUrlAppState(urlState) {
|
138
|
+
const state = createEmptyState();
|
139
|
+
if (Array.isArray(urlState[0])) {
|
140
|
+
state.activeViewpoint = parseUrlViewpointState(urlState[0]);
|
141
|
+
}
|
142
|
+
if (typeof urlState[1] === 'string') {
|
143
|
+
state.activeMap = urlState[1];
|
144
|
+
}
|
145
|
+
if (Array.isArray(urlState[2])) {
|
146
|
+
state.contextIds = urlState[2].slice();
|
147
|
+
}
|
148
|
+
if (Array.isArray(urlState[3])) {
|
149
|
+
urlState[3].forEach((urlLayerState) => {
|
150
|
+
if (Array.isArray(urlLayerState)) {
|
151
|
+
state.layers.push(parseUrlLayerState(urlLayerState));
|
152
|
+
}
|
153
|
+
});
|
154
|
+
}
|
155
|
+
if (Array.isArray(urlState[4])) {
|
156
|
+
urlState[4].forEach((urlPluginState) => {
|
157
|
+
if (Array.isArray(urlPluginState)) {
|
158
|
+
state.plugins.push(parseUrlPluginState(urlPluginState));
|
159
|
+
}
|
160
|
+
});
|
161
|
+
}
|
162
|
+
if (typeof urlState[5] === 'string') {
|
163
|
+
state.activeObliqueCollection = urlState[5];
|
164
|
+
}
|
165
|
+
return state;
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* @param {AppState} state
|
170
|
+
* @param {number} maxLength
|
171
|
+
* @returns {UrlAppState}
|
172
|
+
*/
|
173
|
+
function writeUrlAppState(state, maxLength) {
|
174
|
+
/**
|
175
|
+
* @type {UrlAppState}
|
176
|
+
*/
|
177
|
+
const urlState = new Array(6).fill(0);
|
178
|
+
if (state.activeViewpoint) {
|
179
|
+
urlState[0] = [
|
180
|
+
state.activeViewpoint.cameraPosition?.slice() ?? 0,
|
181
|
+
state.activeViewpoint.groundPosition?.slice() ?? 0,
|
182
|
+
state.activeViewpoint.distance ?? 0,
|
183
|
+
state.activeViewpoint.heading,
|
184
|
+
state.activeViewpoint.pitch,
|
185
|
+
state.activeViewpoint.roll,
|
186
|
+
];
|
187
|
+
}
|
188
|
+
|
189
|
+
if (state.activeMap) {
|
190
|
+
urlState[1] = state.activeMap;
|
191
|
+
}
|
192
|
+
|
193
|
+
urlState[2] = state.contextIds.slice();
|
194
|
+
urlState[3] = [];
|
195
|
+
urlState[4] = [];
|
196
|
+
|
197
|
+
if (state.activeObliqueCollection) {
|
198
|
+
urlState[5] = state.activeObliqueCollection;
|
199
|
+
}
|
200
|
+
|
201
|
+
state.layers.forEach((layerState) => {
|
202
|
+
const layerUrlState = writeUrlLayerState(layerState);
|
203
|
+
if ((JSON.stringify(urlState).length + JSON.stringify(layerUrlState).length) < maxLength) {
|
204
|
+
urlState[3].push(layerUrlState);
|
205
|
+
}
|
206
|
+
});
|
207
|
+
|
208
|
+
state.plugins.forEach((pluginState) => {
|
209
|
+
const urlPluginState = writeUrlPluginState(pluginState);
|
210
|
+
if ((JSON.stringify(urlState).length + JSON.stringify(urlPluginState).length) < maxLength) {
|
211
|
+
urlState[4].push(urlPluginState);
|
212
|
+
}
|
213
|
+
});
|
214
|
+
|
215
|
+
if (urlState[3].length !== state.layers.length || urlState[4].length !== state.plugins.length) {
|
216
|
+
getLogger('StateManagement').warning('State too large for URL: Not all layers and plugins are represented');
|
217
|
+
}
|
218
|
+
|
219
|
+
return urlState;
|
220
|
+
}
|
221
|
+
|
222
|
+
/**
|
223
|
+
* @param {(URL)=} url
|
224
|
+
* @returns {AppState}
|
225
|
+
*/
|
226
|
+
export function getStateFromURL(url) {
|
227
|
+
check(url, URL);
|
228
|
+
|
229
|
+
if (url.searchParams.has('state')) {
|
230
|
+
try {
|
231
|
+
return parseUrlAppState(JSON.parse(url.searchParams.get('state')));
|
232
|
+
} catch (e) {
|
233
|
+
getLogger('StateManager').error('failed to parse the state URL parameter');
|
234
|
+
}
|
235
|
+
}
|
236
|
+
return createEmptyState();
|
237
|
+
}
|
238
|
+
|
239
|
+
/**
|
240
|
+
* @param {AppState} state
|
241
|
+
* @param {URL} url - sets the query parameter "state" on this URL
|
242
|
+
*/
|
243
|
+
export function setStateToUrl(state, url) {
|
244
|
+
check(state, {
|
245
|
+
activeMap: [String, undefined],
|
246
|
+
activeViewpoint: [Object, undefined],
|
247
|
+
activeObliqueCollection: [String, undefined],
|
248
|
+
layers: Array,
|
249
|
+
plugins: Array,
|
250
|
+
contextIds: [String],
|
251
|
+
});
|
252
|
+
check(url, URL);
|
253
|
+
|
254
|
+
const maxLength = MAX_URL_LENGTH - url.toString().length;
|
255
|
+
url.searchParams.set('state', JSON.stringify(writeUrlAppState(state, maxLength)));
|
256
|
+
}
|
package/src/styles/_theming.scss
CHANGED
@@ -7,16 +7,11 @@
|
|
7
7
|
background-image: var(--vcs-primary-logo);
|
8
8
|
height: 36px;
|
9
9
|
width: 151px;
|
10
|
-
margin: 0 auto;
|
11
10
|
}
|
12
11
|
.company-logo-mobile {
|
13
12
|
background-image: var(--vcs-mobile-logo);
|
14
13
|
height: 40px;
|
15
14
|
width: 70px;
|
16
|
-
position: absolute;
|
17
|
-
top: 1rem;
|
18
|
-
left: 1rem;
|
19
|
-
z-index: 1;
|
20
15
|
}
|
21
16
|
|
22
17
|
// Dev-Only!
|
package/src/uiConfig.js
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
import { Collection, makeOverrideCollection } from '@vcmap/core';
|
2
|
+
import { ref } from 'vue';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @typedef {Object} UiConfigurationItem
|
6
|
+
* @property {string} name
|
7
|
+
* @property {*} value
|
8
|
+
*/
|
9
|
+
|
10
|
+
/**
|
11
|
+
* @typedef {Object} UiConfigObject
|
12
|
+
* @property {string} [logo] - the company logo to display. this will override any and all css overrides.
|
13
|
+
* @property {string} [mobileLogo] - an alternative logo to display in mobile view
|
14
|
+
* @property {string} [appTitle] - an optional title to display next to the company logo
|
15
|
+
* @property {string} [primaryColor] - an optional primary color to use in all themes
|
16
|
+
*/
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @class
|
20
|
+
* @extends {Collection<UiConfigurationItem>}
|
21
|
+
* @implements {import("@vcmap/core").OverrideCollectionInterface<UiConfigurationItem>}
|
22
|
+
*/
|
23
|
+
class UiConfig extends Collection {
|
24
|
+
/**
|
25
|
+
* @param {function():string} getDynamicContextId
|
26
|
+
*/
|
27
|
+
constructor(getDynamicContextId) {
|
28
|
+
super();
|
29
|
+
|
30
|
+
makeOverrideCollection(this, getDynamicContextId);
|
31
|
+
/**
|
32
|
+
* This object just acts as a go between for reactivity until we have vue3
|
33
|
+
* @todo vue3 cleanup
|
34
|
+
* @type {Object<string, *>}
|
35
|
+
*/
|
36
|
+
const configObject = {};
|
37
|
+
/**
|
38
|
+
* @type {import("vue").Ref<Object<string, *>>}
|
39
|
+
* @private
|
40
|
+
*/
|
41
|
+
this._config = ref({});
|
42
|
+
/**
|
43
|
+
* @type {Array<function():void>}
|
44
|
+
* @private
|
45
|
+
*/
|
46
|
+
this._listeners = [
|
47
|
+
this.added.addEventListener((item) => {
|
48
|
+
if (typeof item?.name === 'string') {
|
49
|
+
configObject[item.name] = item.value;
|
50
|
+
this._config.value = { ...configObject }; // shallow clone to trip reactivity
|
51
|
+
}
|
52
|
+
}),
|
53
|
+
this.removed.addEventListener((item) => {
|
54
|
+
if (typeof item?.name === 'string') {
|
55
|
+
delete configObject[item.name];
|
56
|
+
this._config.value = { ...configObject }; // shallow clone to trip reactivity
|
57
|
+
}
|
58
|
+
}),
|
59
|
+
];
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* @returns {import("vue").Ref<Object<string, *>|UiConfigObject>}
|
64
|
+
*/
|
65
|
+
get config() {
|
66
|
+
return this._config;
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* @inheritDoc
|
71
|
+
*/
|
72
|
+
destroy() {
|
73
|
+
this._listeners.forEach((cb) => { cb(); });
|
74
|
+
this._listeners = [];
|
75
|
+
super.destroy();
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
export default UiConfig;
|
package/src/vcsUiApp.js
CHANGED
@@ -5,6 +5,10 @@ import {
|
|
5
5
|
makeOverrideCollection,
|
6
6
|
destroyCollection,
|
7
7
|
OverrideClassRegistry,
|
8
|
+
defaultDynamicContextId,
|
9
|
+
ObliqueMap,
|
10
|
+
ViewPoint,
|
11
|
+
volatileContextId,
|
8
12
|
} from '@vcmap/core';
|
9
13
|
import { getLogger as getLoggerByName } from '@vcsuite/logger';
|
10
14
|
import {
|
@@ -20,6 +24,11 @@ import { createContentTreeCollection } from './contentTree/contentTreeCollection
|
|
20
24
|
import { contentTreeClassRegistry } from './contentTree/contentTreeItem.js';
|
21
25
|
import OverviewMap from './navigation/overviewMap.js';
|
22
26
|
import I18nCollection from './i18n/i18nCollection.js';
|
27
|
+
import CategoryManager from './manager/categoryManager/categoryManager.js';
|
28
|
+
import ContextMenuManager from './manager/contextMenu/contextMenuManager.js';
|
29
|
+
import FeatureInfo from './featureInfo/featureInfo.js';
|
30
|
+
import UiConfig from './uiConfig.js';
|
31
|
+
import { createEmptyState, getStateFromURL } from './state.js';
|
23
32
|
|
24
33
|
/**
|
25
34
|
* @typedef {import("@vcmap/core").VcsAppConfig} VcsUiAppConfig
|
@@ -44,21 +53,25 @@ import I18nCollection from './i18n/i18nCollection.js';
|
|
44
53
|
/**
|
45
54
|
* @interface VcsPlugin
|
46
55
|
* @template {Object} P
|
56
|
+
* @template {Object} S
|
47
57
|
* @property {string} version
|
48
58
|
* @property {string} name
|
49
|
-
* @property {
|
59
|
+
* @property {Object<string, *>} [i18n] - the i18n messages of this plugin
|
60
|
+
* @property {function(VcsUiApp, S=)} initialize - called on plugin added. Is passed the VcsUiApp and optionally, the state for the plugin
|
50
61
|
* @property {function(VcsUiApp)} onVcsAppMounted - called on mounted of VcsApp.vue
|
51
62
|
* @property {function():P} [toJSON] - serialization
|
52
63
|
* @property {function():Promise<void>} destroy
|
64
|
+
* @property {function(boolean=):S|Promise<S>} [getState] - should return the plugins state or a promise for said state. is passed a "for url" flag. If true, only the state relevant for sharing a URL should be passed and short keys shall be used
|
53
65
|
* @api
|
54
66
|
*/
|
55
67
|
|
68
|
+
|
56
69
|
/**
|
57
70
|
* @interface VcsComponentManager
|
58
71
|
* @template {Object} T - the component type
|
59
72
|
* @template {Object} O - component options
|
60
|
-
* @property {VcsEvent<T>} added
|
61
|
-
* @property {VcsEvent<T>} removed
|
73
|
+
* @property {import("@vcmap/core").VcsEvent<T>} added
|
74
|
+
* @property {import("@vcmap/core").VcsEvent<T>} removed
|
62
75
|
* @property {string[]} componentIds - all registered component ids as reactive array
|
63
76
|
* @property {function(string):T} get - get component by id
|
64
77
|
* @property {function(string):boolean} has - has component with id
|
@@ -84,7 +97,7 @@ class VcsUiApp extends VcsApp {
|
|
84
97
|
constructor() {
|
85
98
|
super();
|
86
99
|
/**
|
87
|
-
* @type {OverrideCollection<VcsPlugin>}
|
100
|
+
* @type {import("@vcmap/core").OverrideCollection<VcsPlugin>}
|
88
101
|
* @private
|
89
102
|
*/
|
90
103
|
this._plugins = makeOverrideCollection(
|
@@ -93,11 +106,37 @@ class VcsUiApp extends VcsApp {
|
|
93
106
|
serializePlugin,
|
94
107
|
deserializePlugin,
|
95
108
|
);
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
109
|
+
/**
|
110
|
+
* @type {Array<function():void>}
|
111
|
+
* @private
|
112
|
+
*/
|
113
|
+
this._pluginListeners = [
|
114
|
+
this._plugins.added.addEventListener((plugin) => {
|
115
|
+
this._windowManager.removeOwner(plugin.name);
|
116
|
+
this._navbarManager.removeOwner(plugin.name);
|
117
|
+
this._toolboxManager.removeOwner(plugin.name);
|
118
|
+
this._categoryManager.removeOwner(plugin.name);
|
119
|
+
this._contextMenuManager.removeOwner(plugin.name);
|
120
|
+
if (plugin.i18n) {
|
121
|
+
this.i18n.addPluginMessages(plugin.name, plugin[contextIdSymbol], plugin.i18n);
|
122
|
+
}
|
123
|
+
if (plugin.initialize) {
|
124
|
+
let state;
|
125
|
+
if (this._cachedAppState.contextIds.includes(plugin[contextIdSymbol])) {
|
126
|
+
state = this._cachedAppState.plugins.find(s => s.name === plugin.name);
|
127
|
+
}
|
128
|
+
plugin.initialize(this, state?.state);
|
129
|
+
}
|
130
|
+
}),
|
131
|
+
this._plugins.removed.addEventListener(async (plugin) => {
|
132
|
+
this._windowManager.removeOwner(plugin.name);
|
133
|
+
this._navbarManager.removeOwner(plugin.name);
|
134
|
+
this._toolboxManager.removeOwner(plugin.name);
|
135
|
+
this._categoryManager.removeOwner(plugin.name);
|
136
|
+
this._contextMenuManager.removeOwner(plugin.name);
|
137
|
+
this.i18n.removePluginMessages(plugin.name, plugin[contextIdSymbol]);
|
138
|
+
}),
|
139
|
+
];
|
101
140
|
|
102
141
|
/**
|
103
142
|
* @type {OverrideClassRegistry<ContentTreeItem>}
|
@@ -124,10 +163,20 @@ class VcsUiApp extends VcsApp {
|
|
124
163
|
*/
|
125
164
|
this._windowManager = new WindowManager();
|
126
165
|
/**
|
127
|
-
* @type {
|
166
|
+
* @type {NavbarManager}
|
128
167
|
* @private
|
129
168
|
*/
|
130
169
|
this._navbarManager = new NavbarManager();
|
170
|
+
/**
|
171
|
+
* @type {UiConfig}
|
172
|
+
* @private
|
173
|
+
*/
|
174
|
+
this._uiConfig = new UiConfig(() => this.dynamicContextId);
|
175
|
+
/**
|
176
|
+
* @type {FeatureInfo}
|
177
|
+
* @private
|
178
|
+
*/
|
179
|
+
this._featureInfo = new FeatureInfo(this);
|
131
180
|
|
132
181
|
/**
|
133
182
|
* @type {OverviewMap}
|
@@ -140,16 +189,34 @@ class VcsUiApp extends VcsApp {
|
|
140
189
|
* @private
|
141
190
|
*/
|
142
191
|
this._i18n = new I18nCollection(() => this.dynamicContextId);
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @type {CategoryManager}
|
195
|
+
* @private
|
196
|
+
*/
|
197
|
+
this._categoryManager = new CategoryManager(this);
|
198
|
+
|
199
|
+
/**
|
200
|
+
* @type {ContextMenuManager}
|
201
|
+
* @private
|
202
|
+
*/
|
203
|
+
this._contextMenuManager = new ContextMenuManager(this);
|
204
|
+
|
205
|
+
/**
|
206
|
+
* @type {AppState}
|
207
|
+
* @private
|
208
|
+
*/
|
209
|
+
this._cachedAppState = getStateFromURL(new URL(window.location.href));
|
143
210
|
}
|
144
211
|
|
145
212
|
/**
|
146
|
-
* @type {OverrideCollection<VcsPlugin>}
|
213
|
+
* @type {import("@vcmap/core").OverrideCollection<VcsPlugin>}
|
147
214
|
* @readonly
|
148
215
|
*/
|
149
216
|
get plugins() { return this._plugins; }
|
150
217
|
|
151
218
|
/**
|
152
|
-
* @type {
|
219
|
+
* @type {OverrideContentTreeCollection}
|
153
220
|
* @readonly
|
154
221
|
*/
|
155
222
|
get contentTree() { return this._contentTree; }
|
@@ -173,11 +240,17 @@ class VcsUiApp extends VcsApp {
|
|
173
240
|
get windowManager() { return this._windowManager; }
|
174
241
|
|
175
242
|
/**
|
176
|
-
* @returns {
|
243
|
+
* @returns {NavbarManager}
|
177
244
|
* @readonly
|
178
245
|
*/
|
179
246
|
get navbarManager() { return this._navbarManager; }
|
180
247
|
|
248
|
+
/**
|
249
|
+
* @returns {FeatureInfo}
|
250
|
+
* @readonly
|
251
|
+
*/
|
252
|
+
get featureInfo() { return this._featureInfo; }
|
253
|
+
|
181
254
|
/**
|
182
255
|
* @type {OverviewMap}
|
183
256
|
* @readonly
|
@@ -190,6 +263,77 @@ class VcsUiApp extends VcsApp {
|
|
190
263
|
*/
|
191
264
|
get i18n() { return this._i18n; }
|
192
265
|
|
266
|
+
/**
|
267
|
+
* @returns {CategoryManager}
|
268
|
+
* @readonly
|
269
|
+
*/
|
270
|
+
get categoryManager() { return this._categoryManager; }
|
271
|
+
|
272
|
+
/**
|
273
|
+
* @type {ContextMenuManager}
|
274
|
+
* @readonly
|
275
|
+
*/
|
276
|
+
get contextMenuManager() { return this._contextMenuManager; }
|
277
|
+
|
278
|
+
/**
|
279
|
+
* @type {UiConfig}
|
280
|
+
* @readonly
|
281
|
+
*/
|
282
|
+
get uiConfig() { return this._uiConfig; }
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Get the state of the application. When passed the forUrl flag, only a minimal set of states shall be provided for a sharable link to the current state (to ensure
|
286
|
+
* the maximum URL length is not exceeded). This includes: layer active state & styling, active map, active viewpoint,
|
287
|
+
* currently selected feature info & any state deemed required for a sharable URL by the currently loaded plugins.
|
288
|
+
* @param {boolean=} forUrl
|
289
|
+
* @returns {Promise<AppState>}
|
290
|
+
*/
|
291
|
+
async getState(forUrl) {
|
292
|
+
const state = createEmptyState();
|
293
|
+
state.contextIds = this.contexts
|
294
|
+
.filter(({ id }) => id !== defaultDynamicContextId)
|
295
|
+
.map(({ id }) => id);
|
296
|
+
|
297
|
+
state.activeMap = this.maps.activeMap.name;
|
298
|
+
const viewPoint = await this.maps.activeMap.getViewPoint();
|
299
|
+
state.activeViewpoint = viewPoint?.isValid?.() ? viewPoint.toJSON() : undefined;
|
300
|
+
state.layers = [...this.layers]
|
301
|
+
.filter(l => l.isSupported(this.maps.activeMap) &&
|
302
|
+
l[contextIdSymbol] !== defaultDynamicContextId &&
|
303
|
+
l[contextIdSymbol] !== volatileContextId &&
|
304
|
+
(
|
305
|
+
((l.active || l.loading) && !l.activeOnStartup) ||
|
306
|
+
(!l.active && l.activeOnStartup) ||
|
307
|
+
((l.active || l.loading) && l.style !== l.defaultStyle && this.styles.has(l.style))
|
308
|
+
))
|
309
|
+
.map((l) => {
|
310
|
+
const layerState = {
|
311
|
+
name: l.name,
|
312
|
+
active: l.active || l.loading,
|
313
|
+
};
|
314
|
+
if (
|
315
|
+
l.style.name !== l.defaultStyle.name &&
|
316
|
+
this.styles.has(l.style) &&
|
317
|
+
l.style[contextIdSymbol] !== defaultDynamicContextId &&
|
318
|
+
l.style[contextIdSymbol] !== volatileContextId
|
319
|
+
) {
|
320
|
+
layerState.styleName = l.style.name;
|
321
|
+
}
|
322
|
+
return layerState;
|
323
|
+
});
|
324
|
+
|
325
|
+
state.plugins = await Promise.all([...this.plugins]
|
326
|
+
.filter(p => p[contextIdSymbol] !== defaultDynamicContextId &&
|
327
|
+
p[contextIdSymbol] !== volatileContextId &&
|
328
|
+
typeof p.getState === 'function')
|
329
|
+
.map(async p => ({ name: p.name, state: await p.getState(forUrl) })));
|
330
|
+
|
331
|
+
if (this.maps.activeMap instanceof ObliqueMap) {
|
332
|
+
state.activeObliqueCollection = this.maps.activeMap.collection.name;
|
333
|
+
}
|
334
|
+
return state;
|
335
|
+
}
|
336
|
+
|
193
337
|
/**
|
194
338
|
* @param {import("@vcmap/core").Context} context
|
195
339
|
* @returns {Promise<void>}
|
@@ -212,17 +356,56 @@ class VcsUiApp extends VcsApp {
|
|
212
356
|
|
213
357
|
plugins
|
214
358
|
.filter(p => p)
|
215
|
-
.map(p => this._plugins.override(p))
|
216
|
-
.filter(p => p.i18n)
|
217
|
-
.forEach((p) => {
|
218
|
-
this.i18n.addPluginMessages(p.name, context.id, p.i18n);
|
219
|
-
});
|
359
|
+
.map(p => this._plugins.override(p));
|
220
360
|
}
|
221
361
|
if (Array.isArray(config.i18n)) {
|
222
362
|
await this.i18n.parseItems(config.i18n, context.id);
|
223
363
|
}
|
224
364
|
await super._parseContext(context);
|
225
365
|
await this._contentTree.parseItems(config.contentTree, context.id);
|
366
|
+
await this._uiConfig.parseItems(config.uiConfig, context.id);
|
367
|
+
await this._featureInfo.collection.parseItems(config.featureInfo, context.id);
|
368
|
+
}
|
369
|
+
|
370
|
+
/**
|
371
|
+
* @param {import("@vcmap/core").Context} context
|
372
|
+
* @returns {Promise<void>}
|
373
|
+
* @protected
|
374
|
+
*/
|
375
|
+
async _setContextState(context) {
|
376
|
+
await super._setContextState(context);
|
377
|
+
if (this._cachedAppState.contextIds.includes(context.id)) {
|
378
|
+
this._cachedAppState.layers.forEach((layerState) => {
|
379
|
+
const layer = this.layers.getByKey(layerState.name);
|
380
|
+
if (layer) {
|
381
|
+
if (layerState.active) {
|
382
|
+
layer.activate();
|
383
|
+
} else {
|
384
|
+
layer.deactivate();
|
385
|
+
}
|
386
|
+
|
387
|
+
if (layerState.styleName && this.styles.hasKey(layerState.styleName) && layer.setStyle) {
|
388
|
+
layer.setStyle(this.styles.getByKey(layerState.styleName));
|
389
|
+
}
|
390
|
+
}
|
391
|
+
});
|
392
|
+
if (this._cachedAppState.activeMap && this.maps.hasKey(this._cachedAppState.activeMap)) {
|
393
|
+
await this.maps.setActiveMap(this._cachedAppState.activeMap);
|
394
|
+
}
|
395
|
+
if (
|
396
|
+
this._cachedAppState.activeObliqueCollection &&
|
397
|
+
this.maps.activeMap instanceof ObliqueMap &&
|
398
|
+
this.obliqueCollections.hasKey(this._cachedAppState.activeObliqueCollection)
|
399
|
+
) {
|
400
|
+
await this.maps.activeMap.setCollection(
|
401
|
+
this.obliqueCollections.getByKey(this._cachedAppState.activeObliqueCollection),
|
402
|
+
this._cachedAppState.activeViewpoint,
|
403
|
+
);
|
404
|
+
} else if (this._cachedAppState.activeViewpoint && this.maps.activeMap) {
|
405
|
+
await this.maps.activeMap.gotoViewPoint(new ViewPoint(this._cachedAppState.activeViewpoint));
|
406
|
+
}
|
407
|
+
this._cachedAppState.contextIds.splice(this._cachedAppState.contextIds.indexOf(context.id), 1);
|
408
|
+
}
|
226
409
|
}
|
227
410
|
|
228
411
|
/**
|
@@ -236,6 +419,8 @@ class VcsUiApp extends VcsApp {
|
|
236
419
|
this._plugins.removeContext(contextId),
|
237
420
|
this._i18n.removeContext(contextId),
|
238
421
|
this._contentTree.removeContext(contextId),
|
422
|
+
this._featureInfo.collection.removeContext(contextId),
|
423
|
+
this._uiConfig.removeContext(contextId),
|
239
424
|
]);
|
240
425
|
}
|
241
426
|
|
@@ -245,13 +430,18 @@ class VcsUiApp extends VcsApp {
|
|
245
430
|
destroy() {
|
246
431
|
this.windowManager.destroy();
|
247
432
|
this.navbarManager.destroy();
|
433
|
+
this.toolboxManager.destroy();
|
434
|
+
this.categoryManager.destroy();
|
435
|
+
this.contextMenuManager.destroy();
|
248
436
|
this._overviewMap.destroy();
|
249
|
-
|
250
|
-
this.
|
437
|
+
this._pluginListeners.forEach((cb) => { cb(); });
|
438
|
+
this._pluginListeners = [];
|
251
439
|
destroyCollection(this._plugins);
|
252
440
|
destroyCollection(this._contentTree);
|
253
441
|
destroyCollection(this._i18n);
|
254
442
|
this._contentTreeClassRegistry.destroy();
|
443
|
+
this._featureInfo.destroy();
|
444
|
+
this._uiConfig.destroy();
|
255
445
|
super.destroy();
|
256
446
|
}
|
257
447
|
}
|