@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.
Files changed (165) hide show
  1. package/README.md +12 -5
  2. package/build/build.js +6 -3
  3. package/build/buildHelpers.js +12 -4
  4. package/build/buildPreview.js +7 -0
  5. package/build/getPluginProxies.js +4 -0
  6. package/config/aerowest.config.json +13 -3
  7. package/config/base.config.json +398 -219
  8. package/config/codes.config.json +397 -0
  9. package/config/dev.config.json +375 -1
  10. package/config/graphFeatureInfo.config.json +100 -0
  11. package/config/www.config.json +1232 -0
  12. package/dist/assets/{cesium.eb5667.js → cesium.21663e.js} +0 -0
  13. package/dist/assets/cesium.js +1 -1
  14. package/dist/assets/core.63242d.js +4 -0
  15. package/dist/assets/core.js +1 -1
  16. package/dist/assets/font/OFL.txt +93 -0
  17. package/dist/assets/font/TitilliumWeb-Regular.woff2 +0 -0
  18. package/dist/assets/{index.4ccd4433.js → index.44b91cfe.js} +1 -1
  19. package/dist/assets/{ol.ef03b1.js → ol.88ba9d.js} +0 -0
  20. package/dist/assets/ol.js +1 -1
  21. package/dist/assets/ui.3c2933.css +1 -0
  22. package/dist/assets/ui.3c2933.js +71 -0
  23. package/dist/assets/ui.js +1 -1
  24. package/dist/assets/vue.c897fc.js +9 -0
  25. package/dist/assets/vue.js +2 -1
  26. package/dist/assets/{vuetify.401a29.css → vuetify.147c3a.css} +1 -1
  27. package/dist/assets/{vuetify.401a29.js → vuetify.147c3a.js} +72 -72
  28. package/dist/assets/vuetify.js +2 -2
  29. package/dist/index.html +1 -5
  30. package/index.js +39 -5
  31. package/lib/vue.js +1 -0
  32. package/map.config.json +15 -6
  33. package/package.json +17 -8
  34. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
  35. package/plugins/@vcmap/create-link/index.js +83 -0
  36. package/plugins/@vcmap/create-link/package.json +6 -0
  37. package/plugins/@vcmap/pluginExample/index.js +2 -2
  38. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
  39. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
  40. package/plugins/@vcmap/project-selector/index.js +1 -1
  41. package/plugins/@vcmap/project-selector/package.json +1 -2
  42. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
  43. package/plugins/@vcmap/theme-changer/index.js +1 -1
  44. package/plugins/@vcmap/theme-changer/package.json +1 -2
  45. package/plugins/categoryTest/Categories.vue +89 -1
  46. package/plugins/categoryTest/Category.vue +1 -1
  47. package/plugins/example/index.js +10 -23
  48. package/plugins/simple-graph/README.md +51 -0
  49. package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
  50. package/plugins/simple-graph/index.js +17 -0
  51. package/plugins/simple-graph/package.json +11 -0
  52. package/plugins/simple-graph/simpleGraphView.js +76 -0
  53. package/plugins/test/editor.vue +1 -1
  54. package/plugins/test/index.js +76 -9
  55. package/plugins/test/toolbox-data.js +82 -57
  56. package/plugins/test/windowManagerExample.vue +1 -1
  57. package/src/actions/stateRefAction.js +2 -2
  58. package/src/actions/styleSelector.vue +1 -1
  59. package/src/application/Navbar.vue +13 -2
  60. package/src/application/VcsApp.vue +301 -116
  61. package/src/application/VcsMap.vue +1 -1
  62. package/src/application/VcsSettings.vue +1 -1
  63. package/src/application/vcsAppWrapper.vue +1 -0
  64. package/src/assets/font/OFL.txt +93 -0
  65. package/src/assets/font/TitilliumWeb-Regular.woff2 +0 -0
  66. package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
  67. package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
  68. package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
  69. package/src/components/form-output/VcsFormattedNumber.vue +1 -1
  70. package/src/components/lists/VcsActionList.vue +22 -7
  71. package/src/components/lists/VcsTreeview.vue +4 -4
  72. package/src/components/lists/VcsTreeviewLeaf.vue +10 -3
  73. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
  74. package/src/components/tables/VcsTable.vue +245 -0
  75. package/src/contentTree/LayerTree.vue +1 -1
  76. package/src/contentTree/contentTreeCollection.js +4 -4
  77. package/src/contentTree/contentTreeItem.js +9 -9
  78. package/src/contentTree/groupContentTreeItem.js +1 -1
  79. package/src/contentTree/layerContentTreeItem.js +15 -1
  80. package/src/contentTree/layerGroupContentTreeItem.js +21 -1
  81. package/src/contentTree/nodeContentTreeItem.js +1 -1
  82. package/src/featureInfo/AddressBalloonComponent.vue +47 -0
  83. package/src/featureInfo/BalloonComponent.vue +140 -0
  84. package/src/featureInfo/abstractFeatureInfoView.js +313 -0
  85. package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
  86. package/src/featureInfo/balloonFeatureInfoView.js +151 -0
  87. package/src/featureInfo/balloonHelper.js +132 -0
  88. package/src/featureInfo/featureInfo.js +457 -0
  89. package/src/featureInfo/featureInfoInteraction.js +42 -0
  90. package/src/featureInfo/iframeFeatureInfoView.js +95 -0
  91. package/src/featureInfo/tableFeatureInfoView.js +106 -0
  92. package/src/i18n/de.js +26 -0
  93. package/src/i18n/en.js +26 -0
  94. package/src/i18n/i18nCollection.js +17 -0
  95. package/src/icons/+all.js +80 -0
  96. package/src/icons/ClippingHorizontalIcon.vue +7 -0
  97. package/src/icons/ClippingIcon.vue +7 -0
  98. package/src/icons/ClippingVerticalIcon.vue +7 -0
  99. package/src/icons/ColorPickerIcon.vue +7 -0
  100. package/src/icons/ComponentsIcon.vue +2 -2
  101. package/src/icons/DimensionsHouseIcon.vue +11 -9
  102. package/src/icons/EditIcon.vue +7 -0
  103. package/src/icons/GlobalTerrainIcon.vue +9 -0
  104. package/src/icons/GroundIcon.vue +18 -0
  105. package/src/icons/HideIcon.vue +12 -0
  106. package/src/icons/LogoutIcon.vue +7 -0
  107. package/src/icons/ObjectAttributeIcon.vue +2 -13
  108. package/src/icons/PedestrianIcon.vue +2 -3
  109. package/src/icons/PenIcon.vue +2 -9
  110. package/src/icons/PoiIcon.vue +5 -2
  111. package/src/icons/PointSelectIcon.vue +4 -2
  112. package/src/icons/QueryIcon.vue +6 -7
  113. package/src/icons/ScreenshotIcon.vue +16 -0
  114. package/src/icons/ShareIcon.vue +4 -16
  115. package/src/icons/SkipNextIcon.vue +3 -1
  116. package/src/icons/TerrainBoxIcon.vue +9 -0
  117. package/src/icons/ToolsIcon.vue +4 -30
  118. package/src/icons/UploadIcon.vue +2 -9
  119. package/src/icons/UserProfileIcon.vue +7 -0
  120. package/src/icons/UserShareIcon.vue +7 -0
  121. package/src/icons/VideoRecorderIcon.vue +5 -9
  122. package/src/icons/ViewpointFlightIcon.vue +11 -0
  123. package/src/icons/ViewpointIcon.vue +11 -0
  124. package/src/icons/Viewshed360Icon.vue +7 -0
  125. package/src/icons/ViewshedConeIcon.vue +7 -0
  126. package/src/icons/ViewshedIcon.vue +7 -0
  127. package/src/icons/WallIcon.vue +4 -9
  128. package/src/legend/legendHelper.js +193 -0
  129. package/src/legend/styleLegendItem.vue +129 -0
  130. package/src/legend/vcsLegend.vue +92 -0
  131. package/src/manager/buttonManager.js +7 -12
  132. package/src/manager/categoryManager/ComponentsManager.vue +30 -0
  133. package/src/manager/categoryManager/categoryManager.js +500 -0
  134. package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
  135. package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
  136. package/src/manager/contextMenu/contextMenuManager.js +197 -0
  137. package/src/manager/navbarManager.js +9 -9
  138. package/src/manager/toolbox/GroupToolboxComponent.vue +118 -0
  139. package/src/manager/toolbox/SelectToolboxComponent.vue +128 -0
  140. package/src/manager/toolbox/ToolboxManager.vue +116 -98
  141. package/src/manager/toolbox/toolboxManager.js +235 -86
  142. package/src/manager/window/WindowComponent.vue +1 -1
  143. package/src/manager/window/WindowManager.vue +5 -3
  144. package/src/manager/window/windowManager.js +118 -14
  145. package/src/navigation/mapNavigation.vue +3 -5
  146. package/src/navigation/overviewMap.js +28 -5
  147. package/src/navigation/vcsCompass.vue +1 -1
  148. package/src/pluginHelper.js +42 -10
  149. package/src/setup.js +0 -2
  150. package/src/state.js +256 -0
  151. package/src/styles/_theming.scss +0 -5
  152. package/src/styles/variables.scss +7 -0
  153. package/src/styles/vcsFont.scss +17 -0
  154. package/src/uiConfig.js +79 -0
  155. package/src/vcsUiApp.js +213 -22
  156. package/src/vuePlugins/vuetify.js +14 -4
  157. package/config/berlin.config.json +0 -510
  158. package/dist/assets/core.216494.js +0 -4
  159. package/dist/assets/ui.99a1a7.css +0 -1
  160. package/dist/assets/ui.99a1a7.js +0 -70
  161. package/dist/assets/vue-composition-api.c5aca1.js +0 -14
  162. package/dist/assets/vue-composition-api.js +0 -2
  163. package/dist/assets/vue.762edd.js +0 -9
  164. package/lib/vue-composition-api.js +0 -2
  165. package/src/manager/toolbox/ToolboxGroupComponent.vue +0 -128
@@ -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!
@@ -55,6 +55,7 @@ $list-padding: 0 0; // 8px 0 !default;
55
55
  $list-item-title-font-size: $font-size; // map-deep-get($headings, 'subtitle-1', 'size') !default;
56
56
  $list-item-dense-title-font-size: $font-size; // 0.8125rem !default;
57
57
  $list-item-dense-title-line-height: 24px; // 1rem !default;
58
+ $list-dense-subheader-height: 32px; // 40px !default;
58
59
  $list-item-min-height: 32px; // 48px !default;
59
60
  $list-dense-min-height: 24px; // 40px !default;
60
61
  $list-item-content-padding: 4px 0; // 12px 0 !default;
@@ -68,3 +69,9 @@ $list-item-icon-margin: auto 0; // 16px 0 !default;
68
69
  $treeview-node-level-width: 16px;
69
70
  $treeview-node-margin: 4px;
70
71
  $treeview-node-height: 32px; // 48px !default;
72
+
73
+ /** Expansion Panel */
74
+ $expansion-panel-header-min-height: 32px; // 48px !default;
75
+ $expansion-panel-active-header-min-height: 32px; // 64px !default;
76
+ $expansion-panel-header-padding: 6px 0; // 16px 24px !default;
77
+ $expansion-panel-content-padding: 0 0 4px; // 0 24px 16px !default;
@@ -1,3 +1,20 @@
1
+ /* latin-ext */
2
+ @font-face {
3
+ font-family: 'Titillium Web';
4
+ font-style: normal;
5
+ font-weight: 400;
6
+ src: url(../assets/font/TitilliumWeb-Regular.woff2) format('woff2');
7
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
8
+ }
9
+ /* latin */
10
+ @font-face {
11
+ font-family: 'Titillium Web';
12
+ font-style: normal;
13
+ font-weight: 400;
14
+ src: url(../assets/font/TitilliumWeb-Regular.woff2) format('woff2');
15
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
16
+ }
17
+
1
18
  // fonts
2
19
  $base-font-size: 14px; // base font-size minimum size
3
20
 
@@ -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 {
@@ -13,13 +17,18 @@ import {
13
17
  serializePlugin,
14
18
  deserializePlugin,
15
19
  } from './pluginHelper.js';
16
- import { setupDefaultGroups, ToolboxManager } from './manager/toolbox/toolboxManager.js';
20
+ import ToolboxManager, { setupDefaultGroups } from './manager/toolbox/toolboxManager.js';
17
21
  import { WindowManager } from './manager/window/windowManager.js';
18
22
  import { NavbarManager } from './manager/navbarManager.js';
19
23
  import { createContentTreeCollection } from './contentTree/contentTreeCollection.js';
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
@@ -38,27 +47,32 @@ import I18nCollection from './i18n/i18nCollection.js';
38
47
  * @callback createPlugin
39
48
  * @template {Object} P
40
49
  * @param {P} config
50
+ * @param {string} pluginBaseUrl
41
51
  * @returns {VcsPlugin<P>}
42
52
  */
43
53
 
44
54
  /**
45
55
  * @interface VcsPlugin
46
56
  * @template {Object} P
57
+ * @template {Object} S
47
58
  * @property {string} version
48
59
  * @property {string} name
49
- * @property {function(VcsUiApp)} initialize - called on plugin added
60
+ * @property {Object<string, *>} [i18n] - the i18n messages of this plugin
61
+ * @property {function(VcsUiApp, S=)} initialize - called on plugin added. Is passed the VcsUiApp and optionally, the state for the plugin
50
62
  * @property {function(VcsUiApp)} onVcsAppMounted - called on mounted of VcsApp.vue
51
63
  * @property {function():P} [toJSON] - serialization
52
64
  * @property {function():Promise<void>} destroy
65
+ * @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
66
  * @api
54
67
  */
55
68
 
69
+
56
70
  /**
57
71
  * @interface VcsComponentManager
58
72
  * @template {Object} T - the component type
59
73
  * @template {Object} O - component options
60
- * @property {VcsEvent<T>} added
61
- * @property {VcsEvent<T>} removed
74
+ * @property {import("@vcmap/core").VcsEvent<T>} added
75
+ * @property {import("@vcmap/core").VcsEvent<T>} removed
62
76
  * @property {string[]} componentIds - all registered component ids as reactive array
63
77
  * @property {function(string):T} get - get component by id
64
78
  * @property {function(string):boolean} has - has component with id
@@ -84,7 +98,7 @@ class VcsUiApp extends VcsApp {
84
98
  constructor() {
85
99
  super();
86
100
  /**
87
- * @type {OverrideCollection<VcsPlugin>}
101
+ * @type {import("@vcmap/core").OverrideCollection<VcsPlugin>}
88
102
  * @private
89
103
  */
90
104
  this._plugins = makeOverrideCollection(
@@ -93,11 +107,37 @@ class VcsUiApp extends VcsApp {
93
107
  serializePlugin,
94
108
  deserializePlugin,
95
109
  );
96
- this._pluginAddedListener = this._plugins.added.addEventListener((plugin) => {
97
- if (plugin.initialize) {
98
- plugin.initialize(this);
99
- }
100
- });
110
+ /**
111
+ * @type {Array<function():void>}
112
+ * @private
113
+ */
114
+ this._pluginListeners = [
115
+ this._plugins.added.addEventListener((plugin) => {
116
+ this._windowManager.removeOwner(plugin.name);
117
+ this._navbarManager.removeOwner(plugin.name);
118
+ this._toolboxManager.removeOwner(plugin.name);
119
+ this._categoryManager.removeOwner(plugin.name);
120
+ this._contextMenuManager.removeOwner(plugin.name);
121
+ if (plugin.i18n) {
122
+ this.i18n.addPluginMessages(plugin.name, plugin[contextIdSymbol], plugin.i18n);
123
+ }
124
+ if (plugin.initialize) {
125
+ let state;
126
+ if (this._cachedAppState.contextIds.includes(plugin[contextIdSymbol])) {
127
+ state = this._cachedAppState.plugins.find(s => s.name === plugin.name);
128
+ }
129
+ plugin.initialize(this, state?.state);
130
+ }
131
+ }),
132
+ this._plugins.removed.addEventListener(async (plugin) => {
133
+ this._windowManager.removeOwner(plugin.name);
134
+ this._navbarManager.removeOwner(plugin.name);
135
+ this._toolboxManager.removeOwner(plugin.name);
136
+ this._categoryManager.removeOwner(plugin.name);
137
+ this._contextMenuManager.removeOwner(plugin.name);
138
+ this.i18n.removePluginMessages(plugin.name, plugin[contextIdSymbol]);
139
+ }),
140
+ ];
101
141
 
102
142
  /**
103
143
  * @type {OverrideClassRegistry<ContentTreeItem>}
@@ -124,10 +164,20 @@ class VcsUiApp extends VcsApp {
124
164
  */
125
165
  this._windowManager = new WindowManager();
126
166
  /**
127
- * @type {ButtonManager}
167
+ * @type {NavbarManager}
128
168
  * @private
129
169
  */
130
170
  this._navbarManager = new NavbarManager();
171
+ /**
172
+ * @type {UiConfig}
173
+ * @private
174
+ */
175
+ this._uiConfig = new UiConfig(() => this.dynamicContextId);
176
+ /**
177
+ * @type {FeatureInfo}
178
+ * @private
179
+ */
180
+ this._featureInfo = new FeatureInfo(this);
131
181
 
132
182
  /**
133
183
  * @type {OverviewMap}
@@ -140,16 +190,34 @@ class VcsUiApp extends VcsApp {
140
190
  * @private
141
191
  */
142
192
  this._i18n = new I18nCollection(() => this.dynamicContextId);
193
+
194
+ /**
195
+ * @type {CategoryManager}
196
+ * @private
197
+ */
198
+ this._categoryManager = new CategoryManager(this);
199
+
200
+ /**
201
+ * @type {ContextMenuManager}
202
+ * @private
203
+ */
204
+ this._contextMenuManager = new ContextMenuManager(this);
205
+
206
+ /**
207
+ * @type {AppState}
208
+ * @private
209
+ */
210
+ this._cachedAppState = getStateFromURL(new URL(window.location.href));
143
211
  }
144
212
 
145
213
  /**
146
- * @type {OverrideCollection<VcsPlugin>}
214
+ * @type {import("@vcmap/core").OverrideCollection<VcsPlugin>}
147
215
  * @readonly
148
216
  */
149
217
  get plugins() { return this._plugins; }
150
218
 
151
219
  /**
152
- * @type {OverrideCollection<ContentTreeItem>}
220
+ * @type {OverrideContentTreeCollection}
153
221
  * @readonly
154
222
  */
155
223
  get contentTree() { return this._contentTree; }
@@ -173,11 +241,17 @@ class VcsUiApp extends VcsApp {
173
241
  get windowManager() { return this._windowManager; }
174
242
 
175
243
  /**
176
- * @returns {ButtonManager}
244
+ * @returns {NavbarManager}
177
245
  * @readonly
178
246
  */
179
247
  get navbarManager() { return this._navbarManager; }
180
248
 
249
+ /**
250
+ * @returns {FeatureInfo}
251
+ * @readonly
252
+ */
253
+ get featureInfo() { return this._featureInfo; }
254
+
181
255
  /**
182
256
  * @type {OverviewMap}
183
257
  * @readonly
@@ -190,6 +264,77 @@ class VcsUiApp extends VcsApp {
190
264
  */
191
265
  get i18n() { return this._i18n; }
192
266
 
267
+ /**
268
+ * @returns {CategoryManager}
269
+ * @readonly
270
+ */
271
+ get categoryManager() { return this._categoryManager; }
272
+
273
+ /**
274
+ * @type {ContextMenuManager}
275
+ * @readonly
276
+ */
277
+ get contextMenuManager() { return this._contextMenuManager; }
278
+
279
+ /**
280
+ * @type {UiConfig}
281
+ * @readonly
282
+ */
283
+ get uiConfig() { return this._uiConfig; }
284
+
285
+ /**
286
+ * 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
287
+ * the maximum URL length is not exceeded). This includes: layer active state & styling, active map, active viewpoint,
288
+ * currently selected feature info & any state deemed required for a sharable URL by the currently loaded plugins.
289
+ * @param {boolean=} forUrl
290
+ * @returns {Promise<AppState>}
291
+ */
292
+ async getState(forUrl) {
293
+ const state = createEmptyState();
294
+ state.contextIds = this.contexts
295
+ .filter(({ id }) => id !== defaultDynamicContextId)
296
+ .map(({ id }) => id);
297
+
298
+ state.activeMap = this.maps.activeMap.name;
299
+ const viewPoint = await this.maps.activeMap.getViewPoint();
300
+ state.activeViewpoint = viewPoint?.isValid?.() ? viewPoint.toJSON() : undefined;
301
+ state.layers = [...this.layers]
302
+ .filter(l => l.isSupported(this.maps.activeMap) &&
303
+ l[contextIdSymbol] !== defaultDynamicContextId &&
304
+ l[contextIdSymbol] !== volatileContextId &&
305
+ (
306
+ ((l.active || l.loading) && !l.activeOnStartup) ||
307
+ (!l.active && l.activeOnStartup) ||
308
+ ((l.active || l.loading) && l.style !== l.defaultStyle && this.styles.has(l.style))
309
+ ))
310
+ .map((l) => {
311
+ const layerState = {
312
+ name: l.name,
313
+ active: l.active || l.loading,
314
+ };
315
+ if (
316
+ l.style.name !== l.defaultStyle.name &&
317
+ this.styles.has(l.style) &&
318
+ l.style[contextIdSymbol] !== defaultDynamicContextId &&
319
+ l.style[contextIdSymbol] !== volatileContextId
320
+ ) {
321
+ layerState.styleName = l.style.name;
322
+ }
323
+ return layerState;
324
+ });
325
+
326
+ state.plugins = await Promise.all([...this.plugins]
327
+ .filter(p => p[contextIdSymbol] !== defaultDynamicContextId &&
328
+ p[contextIdSymbol] !== volatileContextId &&
329
+ typeof p.getState === 'function')
330
+ .map(async p => ({ name: p.name, state: await p.getState(forUrl) })));
331
+
332
+ if (this.maps.activeMap instanceof ObliqueMap) {
333
+ state.activeObliqueCollection = this.maps.activeMap.collection.name;
334
+ }
335
+ return state;
336
+ }
337
+
193
338
  /**
194
339
  * @param {import("@vcmap/core").Context} context
195
340
  * @returns {Promise<void>}
@@ -199,7 +344,7 @@ class VcsUiApp extends VcsApp {
199
344
  const { config } = context;
200
345
  if (Array.isArray(config.plugins)) {
201
346
  const plugins = await Promise.all(config.plugins.map(async (pluginConfig) => {
202
- const plugin = await loadPlugin(this, pluginConfig.name, pluginConfig);
347
+ const plugin = await loadPlugin(pluginConfig.name, pluginConfig);
203
348
  if (!plugin) {
204
349
  return null;
205
350
  }
@@ -212,17 +357,56 @@ class VcsUiApp extends VcsApp {
212
357
 
213
358
  plugins
214
359
  .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
- });
360
+ .map(p => this._plugins.override(p));
220
361
  }
221
362
  if (Array.isArray(config.i18n)) {
222
363
  await this.i18n.parseItems(config.i18n, context.id);
223
364
  }
224
365
  await super._parseContext(context);
225
366
  await this._contentTree.parseItems(config.contentTree, context.id);
367
+ await this._uiConfig.parseItems(config.uiConfig, context.id);
368
+ await this._featureInfo.collection.parseItems(config.featureInfo, context.id);
369
+ }
370
+
371
+ /**
372
+ * @param {import("@vcmap/core").Context} context
373
+ * @returns {Promise<void>}
374
+ * @protected
375
+ */
376
+ async _setContextState(context) {
377
+ await super._setContextState(context);
378
+ if (this._cachedAppState.contextIds.includes(context.id)) {
379
+ this._cachedAppState.layers.forEach((layerState) => {
380
+ const layer = this.layers.getByKey(layerState.name);
381
+ if (layer) {
382
+ if (layerState.active) {
383
+ layer.activate();
384
+ } else {
385
+ layer.deactivate();
386
+ }
387
+
388
+ if (layerState.styleName && this.styles.hasKey(layerState.styleName) && layer.setStyle) {
389
+ layer.setStyle(this.styles.getByKey(layerState.styleName));
390
+ }
391
+ }
392
+ });
393
+ if (this._cachedAppState.activeMap && this.maps.hasKey(this._cachedAppState.activeMap)) {
394
+ await this.maps.setActiveMap(this._cachedAppState.activeMap);
395
+ }
396
+ if (
397
+ this._cachedAppState.activeObliqueCollection &&
398
+ this.maps.activeMap instanceof ObliqueMap &&
399
+ this.obliqueCollections.hasKey(this._cachedAppState.activeObliqueCollection)
400
+ ) {
401
+ await this.maps.activeMap.setCollection(
402
+ this.obliqueCollections.getByKey(this._cachedAppState.activeObliqueCollection),
403
+ this._cachedAppState.activeViewpoint,
404
+ );
405
+ } else if (this._cachedAppState.activeViewpoint && this.maps.activeMap) {
406
+ await this.maps.activeMap.gotoViewPoint(new ViewPoint(this._cachedAppState.activeViewpoint));
407
+ }
408
+ this._cachedAppState.contextIds.splice(this._cachedAppState.contextIds.indexOf(context.id), 1);
409
+ }
226
410
  }
227
411
 
228
412
  /**
@@ -236,6 +420,8 @@ class VcsUiApp extends VcsApp {
236
420
  this._plugins.removeContext(contextId),
237
421
  this._i18n.removeContext(contextId),
238
422
  this._contentTree.removeContext(contextId),
423
+ this._featureInfo.collection.removeContext(contextId),
424
+ this._uiConfig.removeContext(contextId),
239
425
  ]);
240
426
  }
241
427
 
@@ -245,13 +431,18 @@ class VcsUiApp extends VcsApp {
245
431
  destroy() {
246
432
  this.windowManager.destroy();
247
433
  this.navbarManager.destroy();
434
+ this.toolboxManager.destroy();
435
+ this.categoryManager.destroy();
436
+ this.contextMenuManager.destroy();
248
437
  this._overviewMap.destroy();
249
- // TODO destroy other manager
250
- this._pluginAddedListener();
438
+ this._pluginListeners.forEach((cb) => { cb(); });
439
+ this._pluginListeners = [];
251
440
  destroyCollection(this._plugins);
252
441
  destroyCollection(this._contentTree);
253
442
  destroyCollection(this._i18n);
254
443
  this._contentTreeClassRegistry.destroy();
444
+ this._featureInfo.destroy();
445
+ this._uiConfig.destroy();
255
446
  super.destroy();
256
447
  }
257
448
  }
@@ -5,8 +5,16 @@ import Icons from '../icons/+all.js';
5
5
 
6
6
  Vue.use(Vuetify);
7
7
 
8
+ /**
9
+ * @type {string}
10
+ */
11
+ export const defaultPrimaryColor = '#409D76';
12
+
13
+ /**
14
+ * @returns {import("vuetify").default}
15
+ */
8
16
  export function createVuetify() {
9
- const vuetify = new Vuetify({
17
+ return new Vuetify({
10
18
  treeShake: false,
11
19
  defaultAssets: {
12
20
  font: {
@@ -20,7 +28,7 @@ export function createVuetify() {
20
28
  themes: {
21
29
  light: {
22
30
  basic: '#FFFFFF',
23
- primary: '#409D76',
31
+ primary: defaultPrimaryColor,
24
32
  accent: '#EDEDED',
25
33
  warning: '#FFCE00',
26
34
  'gray-200': '#DEDEDE',
@@ -33,7 +41,7 @@ export function createVuetify() {
33
41
  },
34
42
  dark: {
35
43
  basic: '#000000',
36
- primary: '#409D76',
44
+ primary: defaultPrimaryColor,
37
45
  secondary: '#FFFFFF',
38
46
  accent: '#757575',
39
47
  warning: '#FFCE00',
@@ -48,7 +56,9 @@ export function createVuetify() {
48
56
  },
49
57
  },
50
58
  });
51
- return vuetify;
52
59
  }
53
60
 
61
+ /**
62
+ * @type {import("vuetify").default}
63
+ */
54
64
  export const vuetify = createVuetify();