@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.
Files changed (116) hide show
  1. package/README.md +11 -4
  2. package/build/build.js +0 -3
  3. package/build/buildHelpers.js +0 -1
  4. package/build/buildPreview.js +7 -0
  5. package/config/aerowest.config.json +13 -3
  6. package/config/base.config.json +89 -64
  7. package/config/codes.config.json +397 -0
  8. package/config/dev.config.json +169 -0
  9. package/config/graphFeatureInfo.config.json +100 -0
  10. package/config/www.config.json +1232 -0
  11. package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
  12. package/dist/assets/cesium.js +1 -1
  13. package/dist/assets/core.ebf665.js +4 -0
  14. package/dist/assets/core.js +1 -1
  15. package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
  16. package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
  17. package/dist/assets/ol.js +1 -1
  18. package/dist/assets/ui.fdfe0d.css +1 -0
  19. package/dist/assets/ui.fdfe0d.js +68 -0
  20. package/dist/assets/ui.js +1 -1
  21. package/dist/assets/vue.0bb7c6.js +9 -0
  22. package/dist/assets/vue.js +2 -1
  23. package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
  24. package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
  25. package/dist/assets/vuetify.js +2 -2
  26. package/dist/index.html +1 -1
  27. package/index.js +36 -5
  28. package/lib/vue.js +1 -0
  29. package/map.config.json +15 -6
  30. package/package.json +6 -7
  31. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
  32. package/plugins/@vcmap/create-link/index.js +83 -0
  33. package/plugins/@vcmap/create-link/package.json +6 -0
  34. package/plugins/@vcmap/pluginExample/index.js +1 -1
  35. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
  36. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
  37. package/plugins/@vcmap/project-selector/index.js +1 -1
  38. package/plugins/@vcmap/project-selector/package.json +1 -2
  39. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
  40. package/plugins/@vcmap/theme-changer/index.js +1 -1
  41. package/plugins/@vcmap/theme-changer/package.json +1 -2
  42. package/plugins/categoryTest/Categories.vue +89 -1
  43. package/plugins/categoryTest/Category.vue +1 -1
  44. package/plugins/simple-graph/README.md +51 -0
  45. package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
  46. package/plugins/simple-graph/index.js +17 -0
  47. package/plugins/simple-graph/package.json +11 -0
  48. package/plugins/simple-graph/simpleGraphView.js +76 -0
  49. package/plugins/test/editor.vue +1 -1
  50. package/plugins/test/index.js +63 -2
  51. package/plugins/test/windowManagerExample.vue +1 -1
  52. package/src/actions/stateRefAction.js +2 -2
  53. package/src/actions/styleSelector.vue +1 -1
  54. package/src/application/Navbar.vue +13 -2
  55. package/src/application/VcsApp.vue +201 -92
  56. package/src/application/VcsMap.vue +1 -1
  57. package/src/application/VcsSettings.vue +1 -1
  58. package/src/application/vcsAppWrapper.vue +1 -0
  59. package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
  60. package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
  61. package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
  62. package/src/components/form-output/VcsFormattedNumber.vue +1 -1
  63. package/src/components/lists/VcsActionList.vue +13 -4
  64. package/src/components/lists/VcsTreeview.vue +4 -4
  65. package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
  66. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
  67. package/src/components/tables/VcsTable.vue +245 -0
  68. package/src/contentTree/LayerTree.vue +1 -1
  69. package/src/contentTree/contentTreeCollection.js +4 -4
  70. package/src/contentTree/contentTreeItem.js +9 -9
  71. package/src/contentTree/groupContentTreeItem.js +1 -1
  72. package/src/contentTree/layerContentTreeItem.js +15 -1
  73. package/src/contentTree/layerGroupContentTreeItem.js +21 -1
  74. package/src/contentTree/nodeContentTreeItem.js +1 -1
  75. package/src/featureInfo/AddressBalloonComponent.vue +47 -0
  76. package/src/featureInfo/BalloonComponent.vue +138 -0
  77. package/src/featureInfo/abstractFeatureInfoView.js +313 -0
  78. package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
  79. package/src/featureInfo/balloonFeatureInfoView.js +151 -0
  80. package/src/featureInfo/balloonHelper.js +132 -0
  81. package/src/featureInfo/featureInfo.js +455 -0
  82. package/src/featureInfo/featureInfoInteraction.js +42 -0
  83. package/src/featureInfo/iframeFeatureInfoView.js +95 -0
  84. package/src/featureInfo/tableFeatureInfoView.js +106 -0
  85. package/src/i18n/de.js +16 -0
  86. package/src/i18n/en.js +16 -0
  87. package/src/i18n/i18nCollection.js +17 -0
  88. package/src/manager/buttonManager.js +5 -5
  89. package/src/manager/categoryManager/ComponentsManager.vue +30 -0
  90. package/src/manager/categoryManager/categoryManager.js +500 -0
  91. package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
  92. package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
  93. package/src/manager/contextMenu/contextMenuManager.js +197 -0
  94. package/src/manager/navbarManager.js +8 -8
  95. package/src/manager/toolbox/ToolboxManager.vue +2 -2
  96. package/src/manager/toolbox/toolboxManager.js +7 -3
  97. package/src/manager/window/WindowComponent.vue +1 -1
  98. package/src/manager/window/WindowManager.vue +5 -3
  99. package/src/manager/window/windowManager.js +118 -14
  100. package/src/navigation/mapNavigation.vue +3 -5
  101. package/src/navigation/overviewMap.js +28 -5
  102. package/src/navigation/vcsCompass.vue +1 -1
  103. package/src/setup.js +0 -2
  104. package/src/state.js +256 -0
  105. package/src/styles/_theming.scss +0 -5
  106. package/src/uiConfig.js +79 -0
  107. package/src/vcsUiApp.js +210 -20
  108. package/src/vuePlugins/vuetify.js +14 -4
  109. package/config/berlin.config.json +0 -510
  110. package/dist/assets/core.216494.js +0 -4
  111. package/dist/assets/ui.99a1a7.css +0 -1
  112. package/dist/assets/ui.99a1a7.js +0 -70
  113. package/dist/assets/vue-composition-api.c5aca1.js +0 -14
  114. package/dist/assets/vue-composition-api.js +0 -2
  115. package/dist/assets/vue.762edd.js +0 -9
  116. package/lib/vue-composition-api.js +0 -2
@@ -0,0 +1,197 @@
1
+ import { CesiumMap, BaseOLMap } from '@vcmap/core';
2
+ import { unByKey } from 'ol/Observable.js';
3
+ import { check } from '@vcsuite/check';
4
+ import ContextMenuInteraction from './contextMenuInteraction.js';
5
+ import { vcsAppSymbol } from '../../pluginHelper.js';
6
+ import { validateAction } from '../../components/lists/VcsActionList.vue';
7
+ import { getFittedWindowPositionOptionsFromMapEvent, WindowSlot } from '../window/windowManager.js';
8
+ import ContextMenuComponent, { contextMenuWindowId } from './contextMenuComponent.vue';
9
+ import { sortByOwner } from '../navbarManager.js';
10
+
11
+ /**
12
+ * @typedef {Object} ContextMenuEventHandler
13
+ * @property {string|symbol} owner
14
+ * @property {function(import("@vcmap/core").InteractionEvent):Promise<Array<VcsAction>>|Array<VcsAction>} handler
15
+ */
16
+
17
+ /**
18
+ * @param {import("@vcmap/core").VcsMap} map
19
+ * @param {function():void} clear
20
+ * @returns {function():void} - returns stop listener function.
21
+ */
22
+ function setupViewPointChanged(map, clear) {
23
+ const currentViewpoint = map.getViewPointSync();
24
+ const postRenderHandler = () => {
25
+ if (!currentViewpoint.equals(map.getViewPointSync(), 0.001)) {
26
+ clear();
27
+ }
28
+ };
29
+
30
+ if (map instanceof CesiumMap) {
31
+ return map.getScene().postRender.addEventListener(postRenderHandler);
32
+ } else if (map instanceof BaseOLMap) {
33
+ const key = map.olMap.on('postrender', postRenderHandler);
34
+ return () => {
35
+ unByKey(key);
36
+ };
37
+ }
38
+ return () => {};
39
+ }
40
+
41
+ /**
42
+ * The context menu manager handles right click events in the current map and displays a
43
+ * context menu based on registered action providers.
44
+ * @class
45
+ */
46
+ class ContextMenuManager {
47
+ /**
48
+ * @param {VcsUiApp} app
49
+ */
50
+ constructor(app) {
51
+ /**
52
+ * @type {VcsUiApp}
53
+ * @private
54
+ */
55
+ this._app = app;
56
+ /**
57
+ * @type {ContextMenuInteraction}
58
+ * @private
59
+ */
60
+ this._interaction = new ContextMenuInteraction(
61
+ this._handleRightClick.bind(this),
62
+ async () => {
63
+ this.clear();
64
+ },
65
+ );
66
+ /**
67
+ * @type {Array<ContextMenuEventHandler>}
68
+ * @private
69
+ */
70
+ this._eventHandlers = [];
71
+ /**
72
+ * @type {function():void|null}
73
+ * @private
74
+ */
75
+ this._interactionListener = null;
76
+ /**
77
+ * @type {Array<function():void>}
78
+ * @private
79
+ */
80
+ this._listeners = [];
81
+ }
82
+
83
+ _ensureInteraction() {
84
+ if (!this._interactionListener) {
85
+ this._interactionListener = this._app.maps.eventHandler.addPersistentInteraction(this._interaction);
86
+ }
87
+ }
88
+
89
+ _setupListeners() {
90
+ const clear = this.clear.bind(this);
91
+ this._listeners = [
92
+ this._app.layers.stateChanged.addEventListener(clear),
93
+ this._app.maps.mapActivated.addEventListener(clear),
94
+ setupViewPointChanged(this._app.maps.activeMap, clear),
95
+ ];
96
+ }
97
+
98
+ /**
99
+ * @param {import("@vcmap/core").InteractionEvent} event
100
+ * @private
101
+ */
102
+ async _handleRightClick(event) {
103
+ this.clear();
104
+ const actionArrays = await Promise.all(this._eventHandlers.map(({ handler }) => handler(event)));
105
+ const actions = actionArrays
106
+ .filter(i => Array.isArray(i))
107
+ .flatMap(i => i)
108
+ .filter(validateAction);
109
+
110
+ if (actions.length > 0) {
111
+ const position = getFittedWindowPositionOptionsFromMapEvent(event.windowPosition, 320, actions.length * 32);
112
+ if (position.left) { // ensure we nudge the window, so it does not trigger the default right click.
113
+ position.left += 1;
114
+ } else {
115
+ position.right += 1;
116
+ }
117
+
118
+ this._app.windowManager.add({
119
+ id: contextMenuWindowId,
120
+ component: ContextMenuComponent,
121
+ state: {
122
+ hideHeader: true,
123
+ },
124
+ props: {
125
+ actions,
126
+ showIcon: true,
127
+ },
128
+ position,
129
+ slow: WindowSlot.DETACHED,
130
+ }, vcsAppSymbol);
131
+
132
+ this._setupListeners();
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Adds a handler to the context menu. A handler is called with the interaction event on each right click.
138
+ * If the handler returns an array of valid actions, said actions will be displayed in the context menu
139
+ * @param {function(import("@vcmap/core").InteractionEvent):Promise<Array<VcsAction>>|Array<VcsAction>} handler
140
+ * @param {string|symbol} owner
141
+ */
142
+ addEventHandler(handler, owner) {
143
+ check(handler, Function);
144
+ check(owner, [String, vcsAppSymbol]);
145
+
146
+ this._ensureInteraction();
147
+ this._eventHandlers.push({ owner, handler });
148
+ const order = [...this._app.plugins].map(p => p.name);
149
+ this._eventHandlers.sort((a, b) => {
150
+ return sortByOwner(a.owner, b.owner, order);
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Remove a single handler
156
+ * @param {function(import("@vcmap/core").InteractionEvent):Promise<Array<VcsAction>>|Array<VcsAction>} handler
157
+ */
158
+ removeHandler(handler) {
159
+ this._eventHandlers = this._eventHandlers.filter(({ handler: itemHandler }) => itemHandler !== handler);
160
+ if (this._eventHandlers.length === 0 && this._interactionListener) {
161
+ this._interactionListener();
162
+ this._interactionListener = null;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Remove all handlers associated with this owner
168
+ * @param {string|symbol} owner
169
+ */
170
+ removeOwner(owner) {
171
+ this._eventHandlers = this._eventHandlers.filter(({ owner: handlerOwner }) => handlerOwner !== owner);
172
+ if (this._eventHandlers.length === 0 && this._interactionListener) {
173
+ this._interactionListener();
174
+ this._interactionListener = null;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Clear any currently opened context menus
180
+ */
181
+ clear() {
182
+ this._listeners.forEach((cb) => { cb(); });
183
+ this._listeners = [];
184
+ this._app.windowManager.remove(contextMenuWindowId);
185
+ }
186
+
187
+ destroy() {
188
+ this._interaction.destroy();
189
+ if (this._interactionListener) {
190
+ this._interactionListener();
191
+ this._interactionListener = null;
192
+ }
193
+ this._eventHandlers = [];
194
+ }
195
+ }
196
+
197
+ export default ContextMenuManager;
@@ -1,20 +1,20 @@
1
1
  import { check } from '@vcsuite/check';
2
- import { ButtonManager } from './buttonManager.js';
2
+ import ButtonManager from './buttonManager.js';
3
3
  import { vcsAppSymbol } from '../pluginHelper.js';
4
4
 
5
5
  export const locationSymbol = Symbol('location');
6
6
 
7
7
  /**
8
8
  * sorts by owner and optionally plugin order
9
- * @param {ButtonComponent} a
10
- * @param {ButtonComponent} b
9
+ * @param {string | symbol} ownerA
10
+ * @param {string | symbol} ownerB
11
11
  * @param {string[]} [order] order of owners to sort by
12
12
  * @returns {number}
13
13
  */
14
- function sortByOwner(a, b, order = []) {
14
+ export function sortByOwner(ownerA, ownerB, order = []) {
15
15
  const sorted = [vcsAppSymbol, ...order];
16
- const indexA = sorted.indexOf(a.owner);
17
- const indexB = sorted.indexOf(b.owner);
16
+ const indexA = sorted.indexOf(ownerA);
17
+ const indexB = sorted.indexOf(ownerB);
18
18
 
19
19
  if (indexA === indexB) {
20
20
  return 0;
@@ -35,13 +35,13 @@ function sortByOwner(a, b, order = []) {
35
35
  * @param {Array<ButtonComponent>} buttonComponents
36
36
  * @param {ButtonLocation} location Button render position
37
37
  * @param {string[]} [order] optional order to sort by (plugin names)
38
- * @param {function(a: ButtonComponent, b: ButtonComponent, order: string[]):number} [compareFn=sortByOwner] Per default components are sorted by owner: app first, then plugins
38
+ * @param {function(ownerA:string, ownerB:string, order: string[]):number} [compareFn=sortByOwner] Per default components are sorted by owner: app first, then plugins
39
39
  * @returns {Array<VcsAction>}
40
40
  */
41
41
  export function getActionsByLocation(buttonComponents, location, order = [], compareFn = sortByOwner) {
42
42
  return [...buttonComponents]
43
43
  .filter(b => b[locationSymbol] === location)
44
- .sort((a, b) => compareFn(a, b, order))
44
+ .sort((a, b) => compareFn(a.owner, b.owner, order))
45
45
  .map(b => b.action);
46
46
  }
47
47
 
@@ -49,7 +49,7 @@
49
49
 
50
50
  <script>
51
51
 
52
- import { inject, ref, computed, watch, onUnmounted } from '@vue/composition-api';
52
+ import { inject, ref, computed, watch, onUnmounted } from 'vue';
53
53
  import ToolboxGroupComponent from './ToolboxGroupComponent.vue';
54
54
  import {ButtonLocation, vcsAppSymbol} from '@vcmap/ui';
55
55
 
@@ -143,7 +143,7 @@ export default {
143
143
  actionGroups,
144
144
  width,
145
145
  getPosition,
146
- groupId: null,
146
+ groupId: ref(null),
147
147
  toggleGroup(groupId) {
148
148
  if (this.groupId === groupId) {
149
149
  this.groupId = null;
@@ -1,9 +1,8 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import { VcsEvent } from '@vcmap/core';
3
- import { reactive } from '@vue/composition-api';
4
3
  import { check, checkMaybe } from '@vcsuite/check';
5
4
  import { v4 as uuidv4 } from 'uuid';
6
- import { ButtonManager } from '../buttonManager.js';
5
+ import ButtonManager from '../buttonManager.js';
7
6
 
8
7
  /**
9
8
  * @typedef ToolboxGroupComponentOptions
@@ -24,6 +23,11 @@ import { ButtonManager } from '../buttonManager.js';
24
23
  * @type {Array<ToolboxGroupComponentOptions>}
25
24
  */
26
25
  const defaultGroups = [
26
+ {
27
+ id: 'featureInfo',
28
+ icon: '$vcsInfo',
29
+ title: 'Feature Info',
30
+ },
27
31
  {
28
32
  id: 'select',
29
33
  icon: '$vcsPen',
@@ -70,7 +74,7 @@ export class ToolboxManager {
70
74
  * reactive ordered array of ids,
71
75
  * @type {Array<string>}
72
76
  */
73
- this.componentIds = reactive([]);
77
+ this.componentIds = [];
74
78
 
75
79
  /**
76
80
  * @type {Map<string, ToolboxGroupComponent>}
@@ -41,7 +41,7 @@
41
41
  <script>
42
42
  import {
43
43
  onMounted, onUnmounted, computed, ref, nextTick,
44
- } from '@vue/composition-api';
44
+ } from 'vue';
45
45
  import { fromEvent } from 'rxjs';
46
46
  import { switchMap, take, map, tap } from 'rxjs/operators';
47
47
  import { WindowSlot } from './windowManager.js';
@@ -1,5 +1,7 @@
1
1
  <template>
2
- <div :class="$vuetify.breakpoint.xs ? 'win-container-mobile' : 'unset'">
2
+ <div
3
+ :class="$vuetify.breakpoint.xs ? 'win-container-mobile' : 'unset'"
4
+ >
3
5
  <WindowComponent
4
6
  v-for="(id, zIndex) in componentIds"
5
7
  :key="id"
@@ -46,7 +48,7 @@
46
48
  </style>
47
49
 
48
50
  <script>
49
- import { inject } from '@vue/composition-api';
51
+ import { inject, ref } from 'vue';
50
52
 
51
53
  import WindowComponent from './WindowComponent.vue';
52
54
  import WindowComponentHeader from './WindowComponentHeader.vue';
@@ -109,7 +111,7 @@
109
111
  };
110
112
 
111
113
  return {
112
- componentIds,
114
+ componentIds: ref(componentIds),
113
115
  getComponent: id => windowManager.get(id).component,
114
116
  getHeaderComponent: id => windowManager.get(id).headerComponent || WindowComponentHeader,
115
117
  getStyles,
@@ -1,4 +1,4 @@
1
- import { reactive, ref } from '@vue/composition-api';
1
+ import { reactive, ref } from 'vue';
2
2
  import { VcsEvent } from '@vcmap/core';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import { parseEnumValue } from '@vcsuite/parsers';
@@ -72,8 +72,8 @@ export const WindowPositions = {
72
72
  /**
73
73
  * @typedef WindowComponentOptions
74
74
  * @property {string} [id] Optional ID, If not provided an uuid will be generated.
75
- * @property {VueComponent} component Main Component which is shown below the header.
76
- * @property {VueComponent} [headerComponent] Replaces the Header Component.
75
+ * @property {import("vue").Component} component Main Component which is shown below the header.
76
+ * @property {import("vue").Component} [headerComponent] Replaces the Header Component.
77
77
  * @property {WindowPositionOptions} [position] Will be ignored if WindowSlot !== DETACHED, can be given otherwise or default will be used
78
78
  * @property {WindowState} [state]
79
79
  * @property {WindowSlot} [slot] If WindowSlot is not detached the position will be ignored
@@ -94,8 +94,8 @@ export const WindowPositions = {
94
94
  /**
95
95
  * @typedef WindowComponent
96
96
  * @property {string} id
97
- * @property {VueComponent} component
98
- * @property {VueComponent} [headerComponent]
97
+ * @property {import("vue").Component} component
98
+ * @property {import("vue").Component} [headerComponent]
99
99
  * @property {WindowPosition} position
100
100
  * @property {WindowState} state
101
101
  * @property {Ref<UnwrapRef<WindowSlot>>} slot
@@ -150,20 +150,124 @@ export function windowPositionFromOptions(windowPositionOptions, windowPosition
150
150
  return Object.assign(windowPosition, result);
151
151
  }
152
152
 
153
+ /**
154
+ * @enum {number}
155
+ * @property {number} TOP_LEFT
156
+ * @property {number} TOP_RIGHT
157
+ * @property {number} BOTTOM_LEFT
158
+ * @property {number} BOTTOM_RIGHT
159
+ */
160
+ export const WindowAlignment = {
161
+ TOP_LEFT: 1,
162
+ TOP_RIGHT: 2,
163
+ BOTTOM_LEFT: 3,
164
+ BOTTOM_RIGHT: 4,
165
+ };
166
+
167
+ /**
168
+ * @returns {HTMLElement|null}
169
+ */
170
+ function getActiveMapElement() {
171
+ const mapElements = document.getElementsByClassName('mapElement');
172
+ for (let i = 0; i < mapElements.length; i++) {
173
+ const element = mapElements.item(i);
174
+ if (element.style.display !== 'none') {
175
+ return element;
176
+ }
177
+ }
178
+ return null;
179
+ }
180
+
153
181
  /**
154
182
  * WindowPositionOptions from client position relative to a HTMLElement
155
- * @param {number} x
156
- * @param {number} y
157
- * @param {string|HTMLElement} [element='mapElement']
183
+ * @param {number} x - client pixel position
184
+ * @param {number} y - client pixel position
185
+ * @param {HTMLElement} [element='mapElement'] - the element. if none is provided, the currently active mapElement will be taken
186
+ * @param {WindowAlignment} [alignment=WindowAlignment.TOP_LEFT]
158
187
  * @returns {WindowPositionOptions}
159
188
  */
160
- export function getWindowPositionOptions(x, y, element = 'mapElement') {
161
- let mapElement = element;
162
- if (typeof mapElement === 'string') {
163
- mapElement = document.getElementsByClassName(element).item(0);
189
+ export function getWindowPositionOptions(x, y, element, alignment = WindowAlignment.TOP_LEFT) {
190
+ const mapElement = element ?? getActiveMapElement();
191
+ if (!mapElement) {
192
+ return { left: x, top: y };
164
193
  }
194
+
195
+ const { left, top, width, height } = mapElement.getBoundingClientRect();
196
+ if (alignment === WindowAlignment.TOP_LEFT) {
197
+ return { left: x - left, top: y - top };
198
+ } else if (alignment === WindowAlignment.TOP_RIGHT) {
199
+ return { right: (left + width) - x, top: y - top };
200
+ } else if (alignment === WindowAlignment.BOTTOM_LEFT) {
201
+ return { left: x - left, bottom: (height + top) - y };
202
+ }
203
+ return { right: (left + width) - x, bottom: (height + top) - y };
204
+ }
205
+
206
+ /**
207
+ * Get window position options based on a pixel in the map
208
+ * @param {import("@vcmap/cesium").Cartesian2} windowPosition - the window position, as retrieved from an InteractionEvent
209
+ * @param {WindowAlignment} [alignment]
210
+ * @returns {WindowPositionOptions}
211
+ */
212
+ export function getWindowPositionOptionsFromMapEvent(windowPosition, alignment) {
213
+ const mapElement = getActiveMapElement();
214
+ if (!mapElement) {
215
+ return { left: windowPosition.x, top: windowPosition.y };
216
+ }
217
+
218
+ const { left, top } = mapElement.getBoundingClientRect();
219
+ return getWindowPositionOptions(windowPosition.x + left, windowPosition.y + top, mapElement, alignment);
220
+ }
221
+
222
+
223
+ /**
224
+ * Fits a window aligned top left so it fits into the parent. this will change the alignment to be bottom or right depending
225
+ * on if the window would not fit into the parent.
226
+ * @param {number} x - client pixel position
227
+ * @param {number} y - client pixel position
228
+ * @param {number} width - window width to fit
229
+ * @param {number} height - window height to fit
230
+ * @param {HTMLElement} [element='mapElement'] - the element. if none is provided, the currently active mapElement will be taken
231
+ * @returns {WindowPositionOptions}
232
+ */
233
+ export function getFittedWindowPositionOptions(x, y, width, height, element) {
234
+ const mapElement = element ?? getActiveMapElement();
235
+ if (!mapElement) {
236
+ return { left: x, top: y };
237
+ }
238
+
239
+ const { width: parentWidth, height: parentHeight } = mapElement.getBoundingClientRect();
240
+ const bottom = y + height > parentHeight;
241
+ const right = x + width > parentWidth;
242
+ let alignment = WindowAlignment.TOP_LEFT;
243
+ if (bottom) {
244
+ if (right) {
245
+ alignment = WindowAlignment.BOTTOM_RIGHT;
246
+ } else {
247
+ alignment = WindowAlignment.BOTTOM_LEFT;
248
+ }
249
+ } else if (right) {
250
+ alignment = WindowAlignment.TOP_RIGHT;
251
+ }
252
+ return getWindowPositionOptions(x, y, mapElement, alignment);
253
+ }
254
+
255
+ /**
256
+ * Fits a window aligned top left so it fits into currently active map. this will change the alignment to be bottom or right depending
257
+ * on if the window would not fit into active map element.
258
+ * @param {import("@vcmap/cesium").Cartesian2} windowPosition - the window position, as retrieved from an InteractionEvent
259
+ * @param {number} width
260
+ * @param {number} height
261
+ * @returns {WindowPositionOptions}
262
+ */
263
+ export function getFittedWindowPositionOptionsFromMapEvent(windowPosition, width, height) {
264
+ const mapElement = getActiveMapElement();
265
+ if (!mapElement) {
266
+ return { left: windowPosition.x, top: windowPosition.y };
267
+ }
268
+
165
269
  const { left, top } = mapElement.getBoundingClientRect();
166
- return { left: x - left, top: y - top };
270
+ return getFittedWindowPositionOptions(windowPosition.x + left, windowPosition.y + top, width, height, mapElement);
167
271
  }
168
272
 
169
273
  /**
@@ -185,7 +289,7 @@ export class WindowManager {
185
289
  * reactive ordered array of ids,
186
290
  * @type {Array<string>}
187
291
  */
188
- this.componentIds = reactive([]);
292
+ this.componentIds = [];
189
293
 
190
294
  /**
191
295
  * @type {Map<string, WindowComponent>}
@@ -36,7 +36,7 @@
36
36
  </template>
37
37
 
38
38
  <script>
39
- import { computed, inject, ref, onUnmounted } from '@vue/composition-api';
39
+ import { computed, inject, ref, reactive, onUnmounted } from 'vue';
40
40
  import { ObliqueMap, CesiumMap, OpenlayersMap } from '@vcmap/core';
41
41
  import { unByKey } from 'ol/Observable.js';
42
42
  import { createOverviewMapAction } from '../actions/actionHelper.js';
@@ -119,7 +119,7 @@
119
119
  VcsCompass,
120
120
  },
121
121
  setup() {
122
- /** @type {VcsApp} */
122
+ /** @type {VcsUiApp} */
123
123
  const app = inject('vcsApp');
124
124
  const viewMode = ref(OrientationToolsViewMode.TWO_D);
125
125
  const headingRef = ref(0);
@@ -179,9 +179,7 @@
179
179
  isOblique: computed(() => viewMode.value === OrientationToolsViewMode.OBLIQUE),
180
180
  zoomIn() { zoom(app.maps.activeMap); }, // debounce?
181
181
  zoomOut() { zoom(app.maps.activeMap, true); },
182
- left: () => {},
183
- right: () => {},
184
- overviewAction: ref(action),
182
+ overviewAction: reactive(action),
185
183
  };
186
184
  },
187
185
  };
@@ -20,7 +20,7 @@ import { unByKey } from 'ol/Observable.js';
20
20
  import VectorSource from 'ol/source/Vector.js';
21
21
  import { WindowSlot } from '../manager/window/windowManager.js';
22
22
  import OverviewMapClickedInteraction from './overviewMapClickedInteraction.js';
23
- import { vuetify } from '../vuePlugins/vuetify.js';
23
+ import { defaultPrimaryColor } from '../vuePlugins/vuetify.js';
24
24
  import { vcsAppSymbol } from '../pluginHelper.js';
25
25
  import VcsMap from '../application/VcsMap.vue';
26
26
 
@@ -99,8 +99,8 @@ class OverviewMap {
99
99
  */
100
100
  this._obliqueSelectedImageLayer = null;
101
101
 
102
- const { primary, accent } = vuetify.userPreset.theme.themes.light;
103
- const fillColor = Color.fromCssColorString(accent);
102
+ const primary = app.uiConfig.config.value.primaryColor ?? defaultPrimaryColor;
103
+ const fillColor = Color.fromCssColorString('#EDEDED');
104
104
 
105
105
  /**
106
106
  * @type {VectorStyleItem}
@@ -199,7 +199,7 @@ class OverviewMap {
199
199
  * @type {Array<function():void>}
200
200
  * @private
201
201
  */
202
- this._layerCollectionListener = [
202
+ this._collectionListeners = [
203
203
  this._app.maps.layerCollection.added.addEventListener((layer) => {
204
204
  if (layer.properties.showInOverviewMap) {
205
205
  const clone = deserializeLayer(this._app, layer.toJSON());
@@ -219,6 +219,16 @@ class OverviewMap {
219
219
  this._map.layerCollection.remove(clone);
220
220
  }
221
221
  }),
222
+ this._app.uiConfig.added.addEventListener((item) => {
223
+ if (item?.name === 'primaryColor') {
224
+ this._setObliqueColor(item.value);
225
+ }
226
+ }),
227
+ this._app.uiConfig.removed.addEventListener((item) => {
228
+ if (item?.name === 'primaryColor') {
229
+ this._setObliqueColor(defaultPrimaryColor);
230
+ }
231
+ }),
222
232
  ];
223
233
  }
224
234
 
@@ -254,6 +264,18 @@ class OverviewMap {
254
264
  return this._mapClicked;
255
265
  }
256
266
 
267
+ /**
268
+ * @param {string} color
269
+ * @private
270
+ */
271
+ _setObliqueColor(color) {
272
+ this.obliqueUnselectedStyle?.stroke?.setColor(color);
273
+ this.obliqueSelectedStyle?.stroke?.setColor(color);
274
+ this._obliqueTileLayer?.forceRedraw?.();
275
+ this._obliqueImageLayer?.forceRedraw?.();
276
+ this._obliqueSelectedImageLayer?.forceRedraw?.();
277
+ }
278
+
257
279
  /**
258
280
  * @private
259
281
  */
@@ -574,7 +596,8 @@ class OverviewMap {
574
596
 
575
597
  destroy() {
576
598
  this._clearListeners();
577
- this._layerCollectionListener.forEach(cb => cb());
599
+ this._collectionListeners.forEach(cb => cb());
600
+ this._collectionListeners = [];
578
601
  if (this._mapPointerListener) {
579
602
  this._mapPointerListener();
580
603
  this._mapPointerListener = null;
@@ -28,7 +28,7 @@
28
28
 
29
29
 
30
30
  <script>
31
- import { computed, onUnmounted, ref } from '@vue/composition-api';
31
+ import { computed, onUnmounted, ref } from 'vue';
32
32
 
33
33
  import { fromEvent, merge, of, Subject } from 'rxjs';
34
34
  import { takeUntil, tap } from 'rxjs/operators';
package/src/setup.js CHANGED
@@ -1,11 +1,9 @@
1
1
  import Vue from 'vue';
2
- import VueCompositionAPI from '@vue/composition-api';
3
2
 
4
3
  // eslint-disable-next-line no-unused-vars
5
4
  import * as core from '@vcmap/core';
6
5
  // pull in entire core for vcsClassRegistry
7
6
 
8
7
  Vue.config.productionTip = false;
9
- Vue.use(VueCompositionAPI);
10
8
 
11
9
  window.CESIUM_BASE_URL = '/node_modules/@vcmap/cesium/Source/';