@vcmap/ui 5.1.8 → 5.2.0

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 (91) 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/dist/assets/cesium.js +1 -1
  5. package/dist/assets/{core.45041e.js → core.627882.js} +4160 -4041
  6. package/dist/assets/core.js +1 -1
  7. package/dist/assets/index-76acacac.js +1 -0
  8. package/dist/assets/ol.js +1 -1
  9. package/dist/assets/{ui.74ba2a.css → ui.895896.css} +2 -2
  10. package/dist/assets/{ui.74ba2a.js → ui.895896.js} +8628 -7657
  11. package/dist/assets/ui.js +1 -1
  12. package/dist/assets/vue.js +2 -2
  13. package/dist/assets/{vuetify.72ace9.js → vuetify.1621f3.js} +1 -1
  14. package/dist/assets/vuetify.js +2 -2
  15. package/dist/index.html +1 -1
  16. package/index.d.ts +16 -0
  17. package/index.js +23 -0
  18. package/package.json +2 -2
  19. package/plugins/@vcmap-show-case/collection-manager-example/src/CollectionManagerExample.vue +110 -37
  20. package/plugins/@vcmap-show-case/collection-manager-example/src/index.js +4 -0
  21. package/plugins/@vcmap-show-case/form-inputs-example/src/FormInputsExample.vue +10 -3
  22. package/plugins/@vcmap-show-case/panel-tester/README.md +3 -0
  23. package/plugins/@vcmap-show-case/panel-tester/package.json +5 -0
  24. package/plugins/@vcmap-show-case/panel-tester/src/IframePanelExample.vue +15 -0
  25. package/plugins/@vcmap-show-case/panel-tester/src/ImgPanelExample.vue +19 -0
  26. package/plugins/@vcmap-show-case/panel-tester/src/PanelExample.vue +128 -0
  27. package/plugins/@vcmap-show-case/panel-tester/src/TextPanelExample.vue +34 -0
  28. package/plugins/@vcmap-show-case/panel-tester/src/index.js +63 -0
  29. package/src/actions/actionHelper.js +1 -1
  30. package/src/actions/flightActions.d.ts +38 -2
  31. package/src/actions/flightActions.js +257 -6
  32. package/src/application/VcsApp.vue +4 -104
  33. package/src/application/VcsApp.vue.d.ts +0 -6
  34. package/src/application/VcsContainer.vue +105 -0
  35. package/src/application/VcsContainer.vue.d.ts +14 -0
  36. package/src/application/VcsMainMap.vue +68 -0
  37. package/src/application/VcsMainMap.vue.d.ts +9 -0
  38. package/src/application/markdownHelper.d.ts +7 -0
  39. package/src/application/markdownHelper.js +57 -1
  40. package/src/components/form-inputs-controls/VcsRadioGrid.vue +27 -42
  41. package/src/contentTree/LayerTree.vue +40 -14
  42. package/src/downloadHelper.d.ts +0 -2
  43. package/src/downloadHelper.js +2 -4
  44. package/src/featureInfo/BalloonComponent.vue +31 -3
  45. package/src/featureInfo/BalloonComponent.vue.d.ts +1 -0
  46. package/src/featureInfo/MarkdownBalloonComponent.vue +24 -0
  47. package/src/featureInfo/MarkdownBalloonComponent.vue.d.ts +12 -0
  48. package/src/featureInfo/abstractFeatureInfoView.js +54 -22
  49. package/src/featureInfo/addressBalloonFeatureInfoView.d.ts +2 -2
  50. package/src/featureInfo/addressBalloonFeatureInfoView.js +1 -1
  51. package/src/featureInfo/balloonFeatureInfoView.js +1 -1
  52. package/src/featureInfo/balloonHelper.js +16 -9
  53. package/src/featureInfo/featureInfo.js +10 -0
  54. package/src/featureInfo/markdownBalloonFeatureInfoView.d.ts +47 -0
  55. package/src/featureInfo/markdownBalloonFeatureInfoView.js +81 -0
  56. package/src/featureInfo/markdownFeatureInfoView.d.ts +47 -0
  57. package/src/featureInfo/markdownFeatureInfoView.js +95 -0
  58. package/src/i18n/de.d.ts +8 -3
  59. package/src/i18n/de.js +3 -0
  60. package/src/i18n/en.d.ts +32 -26
  61. package/src/i18n/en.js +4 -1
  62. package/src/manager/collectionManager/CollectionComponent.vue +12 -49
  63. package/src/manager/collectionManager/CollectionComponent.vue.d.ts +5 -9
  64. package/src/manager/collectionManager/CollectionComponentContent.vue +102 -0
  65. package/src/manager/collectionManager/CollectionComponentContent.vue.d.ts +17 -0
  66. package/src/manager/collectionManager/CollectionComponentList.vue +10 -2
  67. package/src/manager/collectionManager/CollectionComponentList.vue.d.ts +9 -0
  68. package/src/manager/collectionManager/CollectionComponentStandalone.vue +91 -0
  69. package/src/manager/collectionManager/CollectionComponentStandalone.vue.d.ts +9 -0
  70. package/src/manager/collectionManager/collectionManager.d.ts +2 -2
  71. package/src/manager/collectionManager/collectionManager.js +21 -19
  72. package/src/manager/panel/PanelComponent.vue +110 -0
  73. package/src/manager/panel/PanelComponent.vue.d.ts +19 -0
  74. package/src/manager/panel/PanelManagerComponent.vue +224 -0
  75. package/src/manager/panel/PanelManagerComponent.vue.d.ts +36 -0
  76. package/src/manager/panel/panelHelper.d.ts +83 -0
  77. package/src/manager/panel/panelHelper.js +272 -0
  78. package/src/manager/panel/panelManager.d.ts +338 -0
  79. package/src/manager/panel/panelManager.js +381 -0
  80. package/src/manager/window/WindowManager.vue +14 -0
  81. package/src/manager/window/windowHelper.js +1 -1
  82. package/src/search/ResultItem.vue +1 -1
  83. package/src/search/search.d.ts +2 -2
  84. package/src/search/search.js +2 -2
  85. package/src/vcsUiApp.d.ts +14 -0
  86. package/src/vcsUiApp.js +18 -0
  87. package/dist/assets/index-3cd5a3f3.js +0 -1
  88. /package/dist/assets/{cesium.035e3a.js → cesium.9e39f4.js} +0 -0
  89. /package/dist/assets/{ol.eb3bee.js → ol.fe8c0e.js} +0 -0
  90. /package/dist/assets/{vue.17a8fa.js → vue.4b3319.js} +0 -0
  91. /package/dist/assets/{vuetify.72ace9.css → vuetify.1621f3.css} +0 -0
@@ -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,24 @@
1
+ <template>
2
+ <BalloonComponent v-bind="{ ...$attrs }">
3
+ <div class="pa-2" v-html="html" />
4
+ </BalloonComponent>
5
+ </template>
6
+ <script>
7
+ import BalloonComponent from './BalloonComponent.vue';
8
+
9
+ /**
10
+ * @description A balloon showing markdown content
11
+ */
12
+ export default {
13
+ name: 'MarkdownBalloonComponent',
14
+ props: {
15
+ html: {
16
+ type: String,
17
+ required: true,
18
+ },
19
+ },
20
+ components: {
21
+ BalloonComponent,
22
+ },
23
+ };
24
+ </script>
@@ -0,0 +1,12 @@
1
+ declare const _default: import("vue").DefineComponent<{
2
+ html: {
3
+ type: StringConstructor;
4
+ required: true;
5
+ };
6
+ }, {}, {}, {}, {}, import("vue/types/v3-component-options.js").ComponentOptionsMixin, import("vue/types/v3-component-options.js").ComponentOptionsMixin, {}, string, Readonly<import("vue").ExtractPropTypes<{
7
+ html: {
8
+ type: StringConstructor;
9
+ required: true;
10
+ };
11
+ }>>, {}>;
12
+ export default _default;