@vcmap/ui 5.1.8 → 5.2.1

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 (99) hide show
  1. package/build/buildTypes.js +9 -0
  2. package/config/base.config.json +33 -2
  3. package/config/dev.config.json +22 -2
  4. package/config/www.config.json +16 -0
  5. package/dist/assets/cesium.js +1 -1
  6. package/dist/assets/{core.45041e.js → core.a8552c.js} +4160 -4041
  7. package/dist/assets/core.js +1 -1
  8. package/dist/assets/index-8783569c.js +1 -0
  9. package/dist/assets/ol.js +1 -1
  10. package/dist/assets/{ui.74ba2a.css → ui.aebd62.css} +2 -2
  11. package/dist/assets/{ui.74ba2a.js → ui.aebd62.js} +8031 -6985
  12. package/dist/assets/ui.js +1 -1
  13. package/dist/assets/vue.js +2 -2
  14. package/dist/assets/{vuetify.72ace9.js → vuetify.c8459d.js} +1 -1
  15. package/dist/assets/vuetify.js +2 -2
  16. package/dist/index.html +1 -1
  17. package/index.d.ts +22 -2
  18. package/index.js +29 -1
  19. package/package.json +2 -2
  20. package/plugins/@vcmap-show-case/collection-manager-example/src/CollectionManagerExample.vue +110 -37
  21. package/plugins/@vcmap-show-case/collection-manager-example/src/index.js +4 -0
  22. package/plugins/@vcmap-show-case/form-inputs-example/src/FormInputsExample.vue +10 -3
  23. package/plugins/@vcmap-show-case/panel-tester/README.md +3 -0
  24. package/plugins/@vcmap-show-case/panel-tester/package.json +5 -0
  25. package/plugins/@vcmap-show-case/panel-tester/src/IframePanelExample.vue +15 -0
  26. package/plugins/@vcmap-show-case/panel-tester/src/ImgPanelExample.vue +19 -0
  27. package/plugins/@vcmap-show-case/panel-tester/src/PanelExample.vue +128 -0
  28. package/plugins/@vcmap-show-case/panel-tester/src/TextPanelExample.vue +34 -0
  29. package/plugins/@vcmap-show-case/panel-tester/src/index.js +63 -0
  30. package/plugins/package.json +7 -3
  31. package/src/actions/actionHelper.js +1 -1
  32. package/src/actions/flightActions.d.ts +38 -2
  33. package/src/actions/flightActions.js +257 -6
  34. package/src/application/VcsApp.vue +4 -104
  35. package/src/application/VcsApp.vue.d.ts +0 -6
  36. package/src/application/VcsContainer.vue +105 -0
  37. package/src/application/VcsContainer.vue.d.ts +14 -0
  38. package/src/application/VcsMainMap.vue +68 -0
  39. package/src/application/VcsMainMap.vue.d.ts +9 -0
  40. package/src/application/markdownHelper.d.ts +7 -0
  41. package/src/application/markdownHelper.js +57 -1
  42. package/src/components/form-inputs-controls/VcsRadioGrid.vue +27 -42
  43. package/src/contentTree/LayerTree.vue +40 -14
  44. package/src/downloadHelper.d.ts +0 -2
  45. package/src/downloadHelper.js +2 -4
  46. package/src/featureInfo/BalloonComponent.vue +31 -3
  47. package/src/featureInfo/BalloonComponent.vue.d.ts +1 -0
  48. package/src/featureInfo/IframeComponent.vue +55 -0
  49. package/src/featureInfo/IframeComponent.vue.d.ts +25 -0
  50. package/src/featureInfo/MarkdownBalloonComponent.vue +24 -0
  51. package/src/featureInfo/MarkdownBalloonComponent.vue.d.ts +12 -0
  52. package/src/featureInfo/abstractFeatureInfoView.js +54 -22
  53. package/src/featureInfo/addressBalloonFeatureInfoView.d.ts +2 -2
  54. package/src/featureInfo/addressBalloonFeatureInfoView.js +1 -1
  55. package/src/featureInfo/balloonFeatureInfoView.js +1 -1
  56. package/src/featureInfo/balloonHelper.js +16 -9
  57. package/src/featureInfo/featureInfo.js +15 -0
  58. package/src/featureInfo/iframeFeatureInfoView.js +1 -20
  59. package/src/featureInfo/iframeWmsFeatureInfoView.d.ts +41 -0
  60. package/src/featureInfo/iframeWmsFeatureInfoView.js +73 -0
  61. package/src/featureInfo/markdownBalloonFeatureInfoView.d.ts +47 -0
  62. package/src/featureInfo/markdownBalloonFeatureInfoView.js +81 -0
  63. package/src/featureInfo/markdownFeatureInfoView.d.ts +47 -0
  64. package/src/featureInfo/markdownFeatureInfoView.js +95 -0
  65. package/src/i18n/de.d.ts +8 -3
  66. package/src/i18n/de.js +3 -0
  67. package/src/i18n/en.d.ts +32 -26
  68. package/src/i18n/en.js +4 -1
  69. package/src/manager/collectionManager/CollectionComponent.vue +12 -49
  70. package/src/manager/collectionManager/CollectionComponent.vue.d.ts +5 -9
  71. package/src/manager/collectionManager/CollectionComponentContent.vue +102 -0
  72. package/src/manager/collectionManager/CollectionComponentContent.vue.d.ts +17 -0
  73. package/src/manager/collectionManager/CollectionComponentList.vue +10 -2
  74. package/src/manager/collectionManager/CollectionComponentList.vue.d.ts +9 -0
  75. package/src/manager/collectionManager/CollectionComponentStandalone.vue +91 -0
  76. package/src/manager/collectionManager/CollectionComponentStandalone.vue.d.ts +9 -0
  77. package/src/manager/collectionManager/collectionManager.d.ts +2 -2
  78. package/src/manager/collectionManager/collectionManager.js +21 -19
  79. package/src/manager/panel/PanelComponent.vue +110 -0
  80. package/src/manager/panel/PanelComponent.vue.d.ts +19 -0
  81. package/src/manager/panel/PanelManagerComponent.vue +224 -0
  82. package/src/manager/panel/PanelManagerComponent.vue.d.ts +36 -0
  83. package/src/manager/panel/panelHelper.d.ts +83 -0
  84. package/src/manager/panel/panelHelper.js +272 -0
  85. package/src/manager/panel/panelManager.d.ts +338 -0
  86. package/src/manager/panel/panelManager.js +381 -0
  87. package/src/manager/window/WindowManager.vue +14 -0
  88. package/src/manager/window/windowHelper.js +1 -1
  89. package/src/manager/window/windowManager.js +4 -0
  90. package/src/search/ResultItem.vue +1 -1
  91. package/src/search/search.d.ts +2 -2
  92. package/src/search/search.js +2 -2
  93. package/src/vcsUiApp.d.ts +14 -0
  94. package/src/vcsUiApp.js +23 -1
  95. package/dist/assets/index-3cd5a3f3.js +0 -1
  96. /package/dist/assets/{cesium.035e3a.js → cesium.1b488a.js} +0 -0
  97. /package/dist/assets/{ol.eb3bee.js → ol.7488a7.js} +0 -0
  98. /package/dist/assets/{vue.17a8fa.js → vue.df3538.js} +0 -0
  99. /package/dist/assets/{vuetify.72ace9.css → vuetify.c8459d.css} +0 -0
@@ -60,17 +60,12 @@ export function setupAttributions(app: import("../vcsUiApp.js").default): {
60
60
  attributionAction: import("../actions/actionHelper.js").VcsAction;
61
61
  destroyAttributions: () => void;
62
62
  };
63
- export function setupMapNavigation(app: any): {
64
- showMapNavigation: import("vue").Ref<boolean>;
65
- destroy: () => void;
66
- };
67
63
  declare const _default: import("vue").DefineComponent<{
68
64
  appId: {
69
65
  type: StringConstructor;
70
66
  required: true;
71
67
  };
72
68
  }, {
73
- mapId: string;
74
69
  mobileLogo: import("vue").ComputedRef<any>;
75
70
  imprint: import("vue").ComputedRef<{
76
71
  title: string;
@@ -82,7 +77,6 @@ declare const _default: import("vue").DefineComponent<{
82
77
  } | undefined>;
83
78
  attributionEntries: import("vue").Ref<import("./attributionsHelper.js").AttributionEntry[]>;
84
79
  attributionAction: import("../actions/actionHelper.js").VcsAction;
85
- showMapNavigation: import("vue").Ref<boolean>;
86
80
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{
87
81
  appId: {
88
82
  type: StringConstructor;
@@ -0,0 +1,105 @@
1
+ <template>
2
+ <v-container
3
+ class="vcs-container pa-0"
4
+ :class="{ 'vcs-container-xs': $vuetify.breakpoint.xs }"
5
+ fluid
6
+ absolute
7
+ >
8
+ <template v-if="$vuetify.breakpoint.xs">
9
+ <img
10
+ v-if="mobileLogo"
11
+ :src="mobileLogo"
12
+ alt="Logo"
13
+ draggable="false"
14
+ class="mobile-logo"
15
+ />
16
+ </template>
17
+ <VcsButton
18
+ v-if="!$vuetify.breakpoint.smAndUp && $vuetify.breakpoint.mobile"
19
+ :key="attributionAction.name"
20
+ :tooltip="attributionAction.title"
21
+ :icon="attributionAction.icon"
22
+ :active="attributionAction.active"
23
+ @click.stop="attributionAction.callback($event)"
24
+ class="z-index-1 mobile-attribution-btn"
25
+ />
26
+ <PanelManagerComponent />
27
+ <ToolboxManagerComponent />
28
+ <WindowManagerComponent />
29
+ <NotifierComponent />
30
+ </v-container>
31
+ </template>
32
+
33
+ <style scoped lang="scss">
34
+ .vcs-container {
35
+ position: absolute;
36
+ top: 48px;
37
+ left: 0;
38
+ right: 0;
39
+ bottom: 22px;
40
+ }
41
+
42
+ .vcs-container-xs {
43
+ top: 0;
44
+ bottom: 56px;
45
+ }
46
+
47
+ .mobile-logo {
48
+ max-height: 40px;
49
+ max-width: 70px;
50
+ position: absolute;
51
+ top: 1rem;
52
+ left: 1rem;
53
+ z-index: 1;
54
+ }
55
+
56
+ .mobile-attribution-btn {
57
+ position: fixed;
58
+ right: 2px;
59
+ bottom: 36px;
60
+ }
61
+ </style>
62
+
63
+ <script>
64
+ import { computed, inject } from 'vue';
65
+ import { VContainer } from 'vuetify/lib';
66
+ import PanelManagerComponent from '../manager/panel/PanelManagerComponent.vue';
67
+ import WindowManagerComponent from '../manager/window/WindowManager.vue';
68
+ import ToolboxManagerComponent from '../manager/toolbox/ToolboxManager.vue';
69
+ import VcsButton from '../components/buttons/VcsButton.vue';
70
+ import NotifierComponent from '../notifier/NotifierComponent.vue';
71
+ import VcsDefaultLogoMobile from '../logo-mobile.svg';
72
+
73
+ /**
74
+ * @description The main container with map canvas
75
+ * @vue-prop {VcsAction} attributionAction
76
+ */
77
+ export default {
78
+ components: {
79
+ VcsButton,
80
+ PanelManagerComponent,
81
+ WindowManagerComponent,
82
+ ToolboxManagerComponent,
83
+ VContainer,
84
+ NotifierComponent,
85
+ },
86
+ props: {
87
+ attributionAction: {
88
+ type: Object,
89
+ required: true,
90
+ },
91
+ },
92
+ setup() {
93
+ const app = inject('vcsApp');
94
+
95
+ return {
96
+ mobileLogo: computed(
97
+ () =>
98
+ app.uiConfig.config.value.mobileLogo ??
99
+ app.uiConfig.config.value.logo ??
100
+ VcsDefaultLogoMobile,
101
+ ),
102
+ };
103
+ },
104
+ };
105
+ </script>
@@ -0,0 +1,14 @@
1
+ declare const _default: import("vue").DefineComponent<{
2
+ attributionAction: {
3
+ type: ObjectConstructor;
4
+ required: true;
5
+ };
6
+ }, {
7
+ mobileLogo: import("vue").ComputedRef<any>;
8
+ }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{
9
+ attributionAction: {
10
+ type: ObjectConstructor;
11
+ required: true;
12
+ };
13
+ }>>, {}>;
14
+ export default _default;
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <div>
3
+ <VcsMap :map-id="mapId" />
4
+ <MapNavigation v-if="showMapNavigation" />
5
+ </div>
6
+ </template>
7
+
8
+ <style lang="scss" scoped></style>
9
+
10
+ <script>
11
+ import { inject, onMounted, onUnmounted, ref } from 'vue';
12
+ import { v4 as uuid } from 'uuid';
13
+ import MapNavigation from '../navigation/MapNavigation.vue';
14
+ import VcsMap from './VcsMap.vue';
15
+
16
+ export function setupMapNavigation(app) {
17
+ const showMapNavigation = ref(app.maps.size < 1);
18
+
19
+ const listeners = [
20
+ app.maps.added.addEventListener(() => {
21
+ showMapNavigation.value = true;
22
+ }),
23
+ app.maps.removed.addEventListener(() => {
24
+ if (app.maps.size < 1) {
25
+ showMapNavigation.value = false;
26
+ }
27
+ }),
28
+ ];
29
+
30
+ return {
31
+ showMapNavigation,
32
+ destroy: () => {
33
+ listeners.forEach((cb) => cb());
34
+ },
35
+ };
36
+ }
37
+
38
+ /**
39
+ * @description Wrapper component for a VcsMap providing the map element.
40
+ * @vue-prop {string} mapId - The id of the map, which is rendered in the map element's canvas
41
+ */
42
+ export default {
43
+ name: 'VcsMainMap',
44
+ components: { MapNavigation, VcsMap },
45
+ setup() {
46
+ const app = inject('vcsApp');
47
+ const id = uuid();
48
+ const mapId = `mapCollection-${id}`;
49
+
50
+ const { showMapNavigation, destroy: destroyMapNavigationListener } =
51
+ setupMapNavigation(app);
52
+
53
+ onMounted(() => {
54
+ app.maps.setTarget(mapId);
55
+ app.mounted.raiseEvent(mapId);
56
+ });
57
+
58
+ onUnmounted(() => {
59
+ destroyMapNavigationListener();
60
+ });
61
+
62
+ return {
63
+ mapId,
64
+ showMapNavigation,
65
+ };
66
+ },
67
+ };
68
+ </script>
@@ -0,0 +1,9 @@
1
+ export function setupMapNavigation(app: any): {
2
+ showMapNavigation: import("vue").Ref<boolean>;
3
+ destroy: () => void;
4
+ };
5
+ declare const _default: import("vue").DefineComponent<{}, {
6
+ mapId: string;
7
+ showMapNavigation: import("vue").Ref<boolean>;
8
+ }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
9
+ export default _default;
@@ -3,3 +3,10 @@
3
3
  * @returns {string}
4
4
  */
5
5
  export function parseAndSanitizeMarkdown(content: string): string;
6
+ /**
7
+ * Replaces template strings by provided attributes, e.g. {{myAttribute}}
8
+ * @param {string|string[]} template
9
+ * @param {Record<string, unknown>} attributes
10
+ * @returns {string}
11
+ */
12
+ export function replaceAttributes(template: string | string[], attributes: Record<string, unknown>): string;
@@ -5,7 +5,63 @@ import DOMPurify from 'dompurify';
5
5
  * @param {string} content
6
6
  * @returns {string}
7
7
  */
8
- // eslint-disable-next-line import/prefer-default-export
9
8
  export function parseAndSanitizeMarkdown(content) {
10
9
  return DOMPurify.sanitize(marked(content));
11
10
  }
11
+
12
+ /**
13
+ * @param {Record<string, unknown>} parent
14
+ * @param {(string|number)[]} keys
15
+ * @returns {undefined|T}
16
+ * @template {*} T
17
+ */
18
+ function findRecursive(parent, keys) {
19
+ if (keys.length === 1) {
20
+ return parent[keys[0]];
21
+ } else {
22
+ const nextKey = keys.shift();
23
+ const nextParent = parent[nextKey];
24
+ if (nextParent) {
25
+ return findRecursive(nextParent, keys);
26
+ }
27
+ }
28
+ return undefined;
29
+ }
30
+
31
+ /**
32
+ * Replaces template strings by provided attributes, e.g. {{myAttribute}}
33
+ * @param {string|string[]} template
34
+ * @param {Record<string, unknown>} attributes
35
+ * @returns {string}
36
+ */
37
+ export function replaceAttributes(template, attributes) {
38
+ const templateString = Array.isArray(template)
39
+ ? template.join('\n')
40
+ : template;
41
+ return templateString.replace(/\{\{([^}]+)}}/g, (p, value) => {
42
+ const keys = value.trim().split('.');
43
+
44
+ for (let i = 0; i < keys.length; i++) {
45
+ let key = keys[i];
46
+ if (typeof key === 'string') {
47
+ const indices = [];
48
+ let arrayIndex = /\[["']?([^\]]+)["']?]$/.exec(key);
49
+ while (arrayIndex != null) {
50
+ let bracketKey = arrayIndex[1];
51
+ if (/^\d+$/.test(bracketKey)) {
52
+ bracketKey = Number(bracketKey);
53
+ }
54
+ indices.push(bracketKey);
55
+ key = key.substring(0, arrayIndex.index);
56
+ arrayIndex = /\[["']?([^\]]+)["']?]$/.exec(key);
57
+ }
58
+
59
+ if (indices.length > 0) {
60
+ keys.splice(i, 1, key, ...indices);
61
+ }
62
+ }
63
+ }
64
+
65
+ return findRecursive(attributes, keys) ?? '';
66
+ });
67
+ }
@@ -16,42 +16,22 @@
16
16
  v-bind="{ ...$attrs, ...attrs }"
17
17
  v-on="{ ...$listeners, ...on }"
18
18
  >
19
- <v-row no-gutters class="justify-center">
20
- <v-col
21
- v-for="(item, idx) in items"
22
- :key="idx"
23
- cols="1"
24
- class="mx-2"
25
- >
26
- <v-row no-gutters>
27
- <v-col>
28
- <div
29
- class="d-flex justify-center label-wrapper pt-1"
30
- :class="$attrs.disabled ? 'disabled' : ''"
31
- >
32
- <slot name="label" :value="item[itemValue]" :src="item.src">
33
- <img
34
- :src="item.src"
35
- :alt="item[itemValue]"
36
- class="image"
37
- />
38
- </slot>
39
- </div>
40
- </v-col>
41
- </v-row>
42
- <v-row no-gutters>
43
- <v-col>
44
- <v-radio
45
- :value="item[itemValue]"
46
- :ripple="false"
47
- class="ma-0"
48
- :class="isDense ? 'vcs-radio-dense' : 'vcs-radio'"
49
- :dense="isDense"
50
- />
51
- </v-col>
52
- </v-row>
53
- </v-col>
54
- </v-row>
19
+ <div class="d-flex gap-1 px-2 pt-2 pb-1 justify-center">
20
+ <div v-for="(item, idx) in items" :key="idx">
21
+ <div class="pt-1 pb-0" :class="$attrs.disabled ? 'disabled' : ''">
22
+ <slot name="label" :value="item[itemValue]" :src="item.src">
23
+ <img :src="item.src" :alt="item[itemValue]" class="image" />
24
+ </slot>
25
+ </div>
26
+ <v-radio
27
+ :value="item[itemValue]"
28
+ :ripple="false"
29
+ class="ma-0 justify-center"
30
+ :class="isDense ? 'vcs-radio-dense' : 'vcs-radio'"
31
+ :dense="isDense"
32
+ />
33
+ </div>
34
+ </div>
55
35
  </v-radio-group>
56
36
  </v-container>
57
37
  </template>
@@ -60,12 +40,22 @@
60
40
 
61
41
  <script>
62
42
  import { computed, ref } from 'vue';
63
- import { VContainer, VRow, VCol, VRadioGroup, VRadio } from 'vuetify/lib';
43
+ import { VContainer, VRadioGroup, VRadio } from 'vuetify/lib';
64
44
  import VcsTooltip from '../notification/VcsTooltip.vue';
65
45
  import { useErrorSync } from './composables.js';
66
46
 
67
47
  /**
68
48
  * @description Stylized wrapper around {@link https://vuetifyjs.com/en/api/v-radio-group/ |vuetify radio group} which places icons or raster src files above the radio buttons as labels and arranges them in a grid.
49
+ * Use figure and figcaption for labeled radio icons:
50
+ * @example
51
+ * <VcsRadioGrid v-model="model" items="items" >
52
+ * <template #label="{ src, value }">
53
+ * <figure>
54
+ * <v-icon size="24" class="d-flex justify-center">{{src}}</v-icon>
55
+ * <figcaption class="d-flex justify-center">{{ value }}</figcaption>
56
+ * </figure>
57
+ * </template>
58
+ * </VcsRadioGrid>
69
59
  * @vue-prop {{value: string, src: string}[]} items - The items to be displayed in the grid. The src is the path to the raster image.
70
60
  * @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
71
61
  * @vue-prop {string} itemValue - The key of the provided item objects that contains the value.
@@ -75,8 +65,6 @@
75
65
  name: 'VcsRadioGrid',
76
66
  components: {
77
67
  VContainer,
78
- VRow,
79
- VCol,
80
68
  VRadioGroup,
81
69
  VRadio,
82
70
  VcsTooltip,
@@ -166,9 +154,6 @@
166
154
  height: auto;
167
155
  width: auto;
168
156
  }
169
- .label-wrapper {
170
- width: 24px;
171
- }
172
157
  .disabled {
173
158
  opacity: 0.3;
174
159
  }
@@ -19,6 +19,7 @@
19
19
  import { VSheet } from 'vuetify/lib';
20
20
  import VcsTreeview from '../components/lists/VcsTreeview.vue';
21
21
 
22
+ const openStateMapSymbol = Symbol('openStateMap');
22
23
  /**
23
24
  * @description
24
25
  * Implements Treeview and shows content tree
@@ -35,23 +36,48 @@
35
36
  setup(props) {
36
37
  const app = inject('vcsApp');
37
38
  const open = app.contentTree.getTreeOpenStateRef(props.windowState.id);
38
-
39
- const initOpen = app.contentTree
40
- .getChildrenForSubTree(props.windowState.id)
41
- .filter((i) => i.initOpen)
42
- .map((i) => i.name);
43
-
44
39
  const tree = app.contentTree.getComputedVisibleTree(props.windowState.id);
45
40
 
41
+ function getWithVisibleChildren(item) {
42
+ return [
43
+ item.name,
44
+ ...item.visibleChildren.map((c) => getWithVisibleChildren(c)).flat(),
45
+ ];
46
+ }
47
+
48
+ if (!app.contentTree[openStateMapSymbol]) {
49
+ app.contentTree[openStateMapSymbol] = new Map();
50
+ }
51
+ /**
52
+ * @type {Map<string, string[]>}
53
+ */
54
+ const openStateMap = app.contentTree[openStateMapSymbol];
46
55
  // watch for new visible children, which should start init open
47
- watch(tree, (value, oldValue) => {
48
- const changed = value
49
- .filter(
50
- ({ name }) =>
51
- !oldValue.find((o) => o.name === name) && initOpen.includes(name),
52
- )
53
- .map(({ name }) => name);
54
- open.value.push(...changed);
56
+ watch(
57
+ tree,
58
+ (value, oldValue) => {
59
+ if (openStateMap.has(app.maps.activeMap?.name)) {
60
+ open.value = openStateMap.get(app.maps.activeMap?.name);
61
+ } else {
62
+ const items = [...app.contentTree]
63
+ .filter((i) => i.initOpen && i.getTreeViewItem().visible)
64
+ .map(({ name }) => name);
65
+ const oldValues = oldValue
66
+ ? oldValue.map(getWithVisibleChildren).flat()
67
+ : [];
68
+ const changed = items.filter(
69
+ (name) => !oldValues.includes(name) && !open.value.includes(name),
70
+ );
71
+ open.value.push(...changed);
72
+ }
73
+ },
74
+ { immediate: true },
75
+ );
76
+
77
+ watch(open, () => {
78
+ if (app.maps.activeMap) {
79
+ openStateMap.set(app.maps.activeMap.name, [...open.value]);
80
+ }
55
81
  });
56
82
 
57
83
  return {
@@ -2,8 +2,6 @@
2
2
  * Download a blob
3
3
  * @param {string} uri
4
4
  * @param {string} fileName
5
- * @api
6
- * @export
7
5
  */
8
6
  export function downloadURI(uri: string, fileName: string): void;
9
7
  /**
@@ -1,17 +1,15 @@
1
- import { hasSameOrigin } from '@vcmap/core';
1
+ import { isSameOrigin } from '@vcmap/core';
2
2
 
3
3
  /**
4
4
  * Download a blob
5
5
  * @param {string} uri
6
6
  * @param {string} fileName
7
- * @api
8
- * @export
9
7
  */
10
8
  export function downloadURI(uri, fileName) {
11
9
  const link = document.createElement('a');
12
10
  link.download = fileName;
13
11
  link.href = uri;
14
- if (!hasSameOrigin(uri)) {
12
+ if (!isSameOrigin(uri)) {
15
13
  link.target = '_blank';
16
14
  }
17
15
  link.click();
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-card class="mx-auto elevation-0" max-width="400" v-if="position">
2
+ <v-card class="mx-auto elevation-0" v-if="position">
3
3
  <slot name="balloon-header" :attrs="{ ...$props, ...$attrs }">
4
4
  <v-list-item class="px-2 align-center">
5
5
  <v-list-item-avatar tile size="16" class="mr-2">
@@ -28,7 +28,7 @@
28
28
 
29
29
  <v-card
30
30
  class="overflow-y-auto py-2 elevation-0"
31
- max-height="250"
31
+ :max-height="maxHeight"
32
32
  color="transparent"
33
33
  >
34
34
  <slot :attrs="{ ...$props, ...$attrs }">
@@ -56,7 +56,7 @@
56
56
  </v-card>
57
57
  </template>
58
58
  <script>
59
- import { inject, onMounted, onUnmounted, watch } from 'vue';
59
+ import { inject, onMounted, onUnmounted, ref, watch } from 'vue';
60
60
  import {
61
61
  VCard,
62
62
  VDivider,
@@ -68,6 +68,10 @@
68
68
  VListItemSubtitle,
69
69
  VListItemTitle,
70
70
  } from 'vuetify/lib';
71
+ import {
72
+ getTargetSize,
73
+ posToNumber,
74
+ } from '../manager/window/windowHelper.js';
71
75
  import { setupBalloonPositionListener } from './balloonHelper.js';
72
76
  import VcsButton from '../components/buttons/VcsButton.vue';
73
77
  import { getTag, getTagOptions } from '../components/tables/VcsTable.vue';
@@ -127,6 +131,28 @@
127
131
  setup(props, { attrs }) {
128
132
  const app = inject('vcsApp');
129
133
  const windowId = attrs['window-state'].id;
134
+ function getMaxHeight() {
135
+ if (app.windowManager.get(windowId)?.position?.maxHeight) {
136
+ return (
137
+ posToNumber(
138
+ app.windowManager.get(windowId).position.maxHeight,
139
+ 'maxHeight',
140
+ getTargetSize(app.maps.target),
141
+ ) - 49 // 44px header offset with padding 5px
142
+ );
143
+ }
144
+ if (app.windowManager.get(windowId)?.position?.height) {
145
+ return (
146
+ posToNumber(
147
+ app.windowManager.get(windowId).position.height,
148
+ 'height',
149
+ getTargetSize(app.maps.target),
150
+ ) - 49 // 44px header offset with padding 5px
151
+ );
152
+ }
153
+ return 250;
154
+ }
155
+ const maxHeight = ref(getMaxHeight());
130
156
 
131
157
  let balloonPositionListener = null;
132
158
  const destroyListener = () => {
@@ -152,6 +178,7 @@
152
178
  windowId,
153
179
  props.position,
154
180
  );
181
+ maxHeight.value = getMaxHeight();
155
182
  },
156
183
  );
157
184
 
@@ -168,6 +195,7 @@
168
195
  close,
169
196
  getTag,
170
197
  getTagOptions,
198
+ maxHeight,
171
199
  };
172
200
  },
173
201
  };
@@ -27,6 +27,7 @@ declare const _default: import("vue").DefineComponent<{
27
27
  close: () => void;
28
28
  getTag: typeof getTag;
29
29
  getTagOptions: typeof getTagOptions;
30
+ maxHeight: import("vue").Ref<number>;
30
31
  }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{
31
32
  featureId: {
32
33
  type: StringConstructor;
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <iframe :src="src" :title="title" :style="{ height, width }" />
3
+ </template>
4
+ <script>
5
+ import { inject } from 'vue';
6
+ import {
7
+ getTargetSize,
8
+ posToNumber,
9
+ } from '../manager/window/windowHelper.js';
10
+
11
+ /**
12
+ * @description An iframe component
13
+ * @vue-prop {string} src - Specifies the address of the document to embed in the <iframe>
14
+ * @vue-prop {string} [title] - optional title for the <iframe>
15
+ */
16
+ export default {
17
+ name: 'IframeComponent',
18
+ props: {
19
+ src: {
20
+ type: String,
21
+ required: true,
22
+ },
23
+ title: {
24
+ type: String,
25
+ default: undefined,
26
+ },
27
+ },
28
+ setup(props, { attrs }) {
29
+ const app = inject('vcsApp');
30
+ let { height, width } = app.windowManager.get(
31
+ attrs['window-state'].id,
32
+ ).position;
33
+
34
+ // set iframe size to absolute px values
35
+ if (app.maps.target) {
36
+ const targetSize = getTargetSize(app.maps.target);
37
+ const heightNumber = posToNumber(height, 'height', targetSize);
38
+ if (heightNumber) {
39
+ height = `${heightNumber - 36}px`; // 32px header height + 4px margin
40
+ }
41
+ const widthNumber = posToNumber(width, 'width', targetSize);
42
+ if (widthNumber) {
43
+ width = `${widthNumber}px`;
44
+ }
45
+ }
46
+
47
+ return {
48
+ height,
49
+ width,
50
+ };
51
+ },
52
+ };
53
+ </script>
54
+
55
+ <style scoped></style>
@@ -0,0 +1,25 @@
1
+ declare const _default: import("vue").DefineComponent<{
2
+ src: {
3
+ type: StringConstructor;
4
+ required: true;
5
+ };
6
+ title: {
7
+ type: StringConstructor;
8
+ default: undefined;
9
+ };
10
+ }, {
11
+ height: any;
12
+ width: any;
13
+ }, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{
14
+ src: {
15
+ type: StringConstructor;
16
+ required: true;
17
+ };
18
+ title: {
19
+ type: StringConstructor;
20
+ default: undefined;
21
+ };
22
+ }>>, {
23
+ title: string;
24
+ }>;
25
+ export default _default;