@eodash/eodash 5.1.0 → 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 (65) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -1
  2. package/core/client/components/EodashOverlay.vue +4 -5
  3. package/core/client/components/MobileLayout.vue +42 -21
  4. package/core/client/composables/index.js +1 -1
  5. package/core/client/eodashSTAC/EodashCollection.js +4 -15
  6. package/core/client/eodashSTAC/createLayers.js +30 -0
  7. package/core/client/eodashSTAC/helpers.js +23 -1
  8. package/dist/client/{DashboardLayout-ByVs1DrY.js → DashboardLayout-Dq9Kfe6O.js} +5 -5
  9. package/dist/client/{DynamicWebComponent-C3W7HSQm.js → DynamicWebComponent-DCBMXskE.js} +1 -1
  10. package/dist/client/{EodashDatePicker-BIAf1sMT.js → EodashDatePicker-DtngxU6s.js} +3 -3
  11. package/dist/client/{EodashItemFilter-DPznh8UB.js → EodashItemFilter-ClQebJQt.js} +1 -1
  12. package/dist/client/{EodashLayerControl-Bhxjw4V2.js → EodashLayerControl-BLBds28C.js} +2 -2
  13. package/dist/client/{EodashLayoutSwitcher-C5qTEffW.js → EodashLayoutSwitcher-DQ8SfVDd.js} +3 -3
  14. package/dist/client/EodashMapBtns-B89_YBDw.js +326 -0
  15. package/dist/client/{EodashStacInfo-CSvvF2jI.js → EodashStacInfo-Dt1nF06x.js} +1 -1
  16. package/dist/client/{EodashTools-Cv1SXQ5y.js → EodashTools-DV5ykmWc.js} +4 -4
  17. package/dist/client/{ExportState-D-iuwaad.js → ExportState-B6zZQUmE.js} +4 -5
  18. package/dist/client/{Footer-CyF0zRAk.js → Footer-DNhXs8k6.js} +1 -1
  19. package/dist/client/{Header-CgD8jDKU.js → Header-BjhN5JY4.js} +2 -3
  20. package/dist/client/MobileLayout-JelB6w1G.js +118 -0
  21. package/dist/client/{PopUp-BsYLvWch.js → PopUp-CgpvNr3o.js} +2 -3
  22. package/dist/client/{ProcessList-C2xsLU2_.js → ProcessList-vecpxThi.js} +17 -10
  23. package/dist/client/{VImg-OHe8YTs2.js → VImg-CETuikH2.js} +200 -5
  24. package/dist/client/{VMain-PryTLU4a.js → VMain-Ci9DyaGU.js} +1 -1
  25. package/dist/client/{VTooltip-DZ0fjpB3.js → VTooltip-J4ac48X7.js} +2 -3
  26. package/dist/client/{WidgetsContainer-B9LBadcC.js → WidgetsContainer-CCML4TyV.js} +1 -1
  27. package/dist/client/{asWebComponent-By_7_JjS.js → asWebComponent-ZyEzWOOf.js} +59 -160
  28. package/dist/client/{async-DkSu_u2K.js → async-B7jIrM53.js} +69 -5
  29. package/dist/client/eo-dash.js +1 -1
  30. package/dist/client/{VOverlay-yUn7p-Uf.js → forwardRefs-BQclvjMq.js} +272 -5
  31. package/dist/client/{handling-CgmFXkW6.js → handling-BS24aG1q.js} +20 -5
  32. package/dist/client/{helpers-Dy0Q13tP.js → helpers-wXK7Ywio.js} +24 -2
  33. package/dist/client/{index-skjhlH8u.js → index-4UCzZi8B.js} +5 -5
  34. package/dist/client/{index-Dqj4tbx2.js → index-9KR-G20t.js} +2 -2
  35. package/dist/client/{index-Ch_HchK3.js → index-B2XpdgR6.js} +191 -57
  36. package/dist/client/material-symbols-outlined.woff2 +0 -0
  37. package/dist/client/material-symbols-rounded.woff2 +0 -0
  38. package/dist/client/material-symbols-sharp.woff2 +0 -0
  39. package/dist/client/material-symbols-subset.woff2 +0 -0
  40. package/dist/client/templates.js +27 -37
  41. package/dist/client/{transition-C98Yn4Vo.js → transition-yBii4fu6.js} +1 -1
  42. package/dist/node/cli.js +1 -1
  43. package/dist/types/core/client/eodashSTAC/helpers.d.ts +5 -0
  44. package/dist/types/core/client/types.d.ts +1 -1
  45. package/dist/types/templates/compare.d.ts +0 -25
  46. package/dist/types/templates/expert.d.ts +16 -20
  47. package/dist/types/templates/light.d.ts +9 -0
  48. package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +4 -0
  49. package/dist/types/widgets/EodashMap/index.vue.d.ts +8 -0
  50. package/dist/types/widgets/EodashProcess/methods/async.d.ts +1 -0
  51. package/package.json +4 -2
  52. package/templates/compare.js +0 -20
  53. package/templates/expert.js +16 -15
  54. package/templates/light.js +10 -1
  55. package/widgets/EodashMap/EodashMapBtns.vue +269 -0
  56. package/widgets/EodashMap/index.vue +252 -37
  57. package/widgets/EodashProcess/ProcessList.vue +13 -1
  58. package/widgets/EodashProcess/methods/async.js +22 -1
  59. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +19 -3
  60. package/widgets/EodashProcess/methods/utils.js +45 -1
  61. package/dist/client/EodashMapBtns-WoGq8MuV.js +0 -173
  62. package/dist/client/MobileLayout-EKQ_kpSh.js +0 -1226
  63. package/dist/client/forwardRefs-BXxrv98s.js +0 -272
  64. package/dist/client/index-BuhOHXKv.js +0 -199
  65. package/widgets/EodashMapBtns.vue +0 -155
@@ -118,26 +118,6 @@ export default {
118
118
  },
119
119
  },
120
120
  },
121
- {
122
- defineWidget: (selected) => {
123
- return selected
124
- ? {
125
- id: "Buttons",
126
- layout: { x: "8/8/9", y: 0, w: 1, h: 2 },
127
- title: "Buttons",
128
- type: "internal",
129
- widget: {
130
- name: "EodashMapBtns",
131
- properties: {
132
- compareIndicators: {
133
- fallbackTemplate: "expert",
134
- },
135
- },
136
- },
137
- }
138
- : null;
139
- },
140
- },
141
121
  {
142
122
  defineWidget: (selectedSTAC) => {
143
123
  return selectedSTAC
@@ -25,6 +25,22 @@ export default {
25
25
  name: "EodashMap",
26
26
  properties: {
27
27
  enableCompare: true,
28
+ zoomToExtent: true,
29
+ btns: {
30
+ enableZoom: true,
31
+ enableExportMap: true,
32
+ enableChangeProjection: true,
33
+ enableCompareIndicators: {
34
+ fallbackTemplate: "expert",
35
+ },
36
+ enableBackToPOIs: true,
37
+ enableSearch: true,
38
+ },
39
+ btnsPosition: {
40
+ x: "12/9/9",
41
+ y: 1,
42
+ gap: 16
43
+ }
28
44
  },
29
45
  },
30
46
  },
@@ -92,21 +108,6 @@ export default {
92
108
  : null;
93
109
  },
94
110
  },
95
- {
96
- defineWidget: (selected) => {
97
- return selected
98
- ? {
99
- id: "Buttons",
100
- layout: { x: "8/8/9", y: 0, w: 1, h: 2 },
101
- title: "Buttons",
102
- type: "internal",
103
- widget: {
104
- name: "EodashMapBtns",
105
- },
106
- }
107
- : null;
108
- },
109
- },
110
111
  {
111
112
  defineWidget: (selectedSTAC) =>
112
113
  includesProcess(selectedSTAC) && {
@@ -19,12 +19,21 @@ export default {
19
19
  },
20
20
  },
21
21
  background: {
22
- id: "background-map",
22
+ id: "lite-map",
23
23
  type: "internal",
24
24
  widget: {
25
25
  name: "EodashMap",
26
26
  properties: {
27
27
  enableCompare: true,
28
+ enableCursorCoordinates: false,
29
+ enableScaleLine: false,
30
+ btns: {
31
+ enableExportMap: false,
32
+ enableChangeProjection: false,
33
+ enableCompareIndicators: false,
34
+ enableBackToPOIs: false,
35
+ enableSearch: true,
36
+ },
28
37
  },
29
38
  },
30
39
  },
@@ -0,0 +1,269 @@
1
+ <template>
2
+ <div ref="rootRef" class="map-buttons d-flex flex-column align-end">
3
+ <button
4
+ v-if="enableZoom"
5
+ class="primary small circle small-elevate"
6
+ @click="onMapZoomIn"
7
+ >
8
+ <i class="small"
9
+ ><svg viewBox="0 0 24 24"><path :d="mdiPlus" /></svg
10
+ ></i>
11
+ <div class="tooltip left">Zoom in</div>
12
+ </button>
13
+
14
+ <button
15
+ v-if="enableZoom"
16
+ class="primary small circle small-elevate"
17
+ @click="onMapZoomOut"
18
+ >
19
+ <i class="small"
20
+ ><svg viewBox="0 0 24 24"><path :d="mdiMinus" /></svg
21
+ ></i>
22
+ <div class="tooltip left">Zoom out</div>
23
+ </button>
24
+
25
+ <button
26
+ v-if="exportMap"
27
+ class="primary small circle small-elevate"
28
+ @click="showMapState = !showMapState"
29
+ >
30
+ <i class="small"
31
+ ><svg viewBox="0 0 24 24"><path :d="mdiMapPlus" /></svg
32
+ ></i>
33
+ <div class="tooltip left">Extract storytelling configuration</div>
34
+ </button>
35
+ <ExportState v-if="exportMap" v-model="showMapState" />
36
+
37
+ <button
38
+ v-if="changeProjection && !!availableMapProjection"
39
+ class="primary small circle small-elevate"
40
+ @click="changeMapProjection(availableMapProjection)"
41
+ >
42
+ <i class="small"
43
+ ><svg viewBox="0 0 24 24"><path :d="mdiEarthBox" /></svg
44
+ ></i>
45
+ <div class="tooltip left">Change map projection</div>
46
+ </button>
47
+ <button
48
+ v-if="compareIndicators"
49
+ class="primary small circle small-elevate"
50
+ @click="onCompareClick"
51
+ >
52
+ <i class="small"
53
+ ><svg viewBox="0 0 24 24"><path :d="compareIcon" /></svg
54
+ ></i>
55
+ <div class="tooltip left">Compare mode</div>
56
+ </button>
57
+ <button
58
+ v-if="backToPOIs && (poi || comparePoi)"
59
+ class="primary small circle small-elevate"
60
+ @click="loadPOiIndicator()"
61
+ >
62
+ <i class="small"
63
+ ><svg viewBox="0 0 24 24">
64
+ <path :d="mdiStarFourPointsCircleOutline" /></svg
65
+ ></i>
66
+ <div class="tooltip left">Back to POIs</div>
67
+ </button>
68
+ <eox-geosearch
69
+ v-if="mapEl && enableSearch"
70
+ :for="mapEl"
71
+ :endpoint="opencageUrl"
72
+ class="geosearch-detached"
73
+ label="Search"
74
+ small
75
+ button
76
+ list-direction="left"
77
+ results-direction="down"
78
+ tooltip="Search"
79
+ tooltip-direction="left"
80
+ ></eox-geosearch>
81
+ <PopUp
82
+ v-model="showCompareIndicators"
83
+ :maxWidth="popupWidth"
84
+ :width="popupWidth"
85
+ :max-height="popupHeight"
86
+ :height="popupHeight"
87
+ >
88
+ <EodashItemFilter
89
+ :enableCompare="true"
90
+ :enableHighlighting="false"
91
+ resultType="cards"
92
+ style="--select-filter-max-items: 8"
93
+ filters-title="Select an indicator to compare"
94
+ subTitleProperty="subtitle"
95
+ imageProperty="thumbnail"
96
+ aggregateResults="collection_group"
97
+ results-title=""
98
+ @select="onSelectCompareIndicator"
99
+ />
100
+ </PopUp>
101
+ </div>
102
+ </template>
103
+ <script setup>
104
+ import { useTransparentPanel } from "@/composables";
105
+ import { changeMapProjection, setActiveTemplate } from "@/store/actions";
106
+ import {
107
+ activeTemplate,
108
+ availableMapProjection,
109
+ comparePoi,
110
+ mapEl,
111
+ poi,
112
+ } from "@/store/states";
113
+ import {
114
+ mdiCompare,
115
+ mdiCompareRemove,
116
+ mdiEarthBox,
117
+ mdiMapPlus,
118
+ mdiMinus,
119
+ mdiPlus,
120
+ mdiStarFourPointsCircleOutline,
121
+ } from "@mdi/js";
122
+ import ExportState from "^/ExportState.vue";
123
+ import { computed, ref, triggerRef } from "vue";
124
+ import PopUp from "^/PopUp.vue";
125
+ import EodashItemFilter from "^/EodashItemFilter.vue";
126
+ import { useDisplay } from "vuetify";
127
+ import { useSTAcStore } from "@/store/stac";
128
+ import { storeToRefs } from "pinia";
129
+ import { loadPOiIndicator } from "^/EodashProcess/methods/handling";
130
+ import { easeOut } from "ol/easing.js";
131
+
132
+ import "@eox/geosearch";
133
+
134
+ const {
135
+ compareIndicators,
136
+ changeProjection,
137
+ exportMap,
138
+ backToPOIs,
139
+ enableSearch,
140
+ enableZoom,
141
+ } = defineProps({
142
+ exportMap: {
143
+ type: Boolean,
144
+ default: true,
145
+ },
146
+ changeProjection: {
147
+ type: Boolean,
148
+ default: true,
149
+ },
150
+ compareIndicators: {
151
+ /** @type {import("vue").PropType<boolean | {compareTemplate?:string;fallbackTemplate?:string}> }*/
152
+ type: [Boolean, Object],
153
+ default: true,
154
+ },
155
+ backToPOIs: {
156
+ type: Boolean,
157
+ default: true,
158
+ },
159
+ enableSearch: {
160
+ type: Boolean,
161
+ default: true,
162
+ },
163
+ enableZoom: {
164
+ type: Boolean,
165
+ default: true,
166
+ },
167
+ });
168
+ const { selectedStac, selectedCompareStac } = storeToRefs(useSTAcStore());
169
+ const { resetSelectedCompareSTAC } = useSTAcStore();
170
+ const { smAndDown } = useDisplay();
171
+ const popupWidth = computed(() => (smAndDown.value ? "80%" : "70%"));
172
+ const popupHeight = computed(() => (smAndDown.value ? "90%" : "70%"));
173
+
174
+ const showMapState = ref(false);
175
+ const showCompareIndicators = ref(false);
176
+ const compareIcon = computed(() =>
177
+ activeTemplate.value ===
178
+ ((typeof compareIndicators === "object" &&
179
+ compareIndicators?.compareTemplate) ||
180
+ "compare")
181
+ ? mdiCompareRemove
182
+ : mdiCompare,
183
+ );
184
+
185
+ const onCompareClick = () => {
186
+ showCompareIndicators.value =
187
+ activeTemplate.value !==
188
+ ((typeof compareIndicators === "object" &&
189
+ compareIndicators.compareTemplate) ||
190
+ "compare");
191
+
192
+ const fallbackTemplate =
193
+ (typeof compareIndicators === "object" &&
194
+ compareIndicators.fallbackTemplate) ||
195
+ "expert";
196
+ selectedCompareStac.value = null;
197
+ resetSelectedCompareSTAC();
198
+ setActiveTemplate(fallbackTemplate);
199
+ triggerRef(selectedStac);
200
+ };
201
+
202
+ /** @type {import("vue").Ref<HTMLDivElement|null>} */
203
+ const rootRef = ref(null);
204
+
205
+ const onSelectCompareIndicator = () => {
206
+ const compareTemplate =
207
+ (typeof compareIndicators === "object" &&
208
+ compareIndicators.compareTemplate) ||
209
+ "compare";
210
+ setActiveTemplate(compareTemplate);
211
+ showCompareIndicators.value = !showCompareIndicators.value;
212
+ };
213
+
214
+ useTransparentPanel(rootRef);
215
+
216
+ const onMapZoomOut = () => {
217
+ const map = mapEl.value?.map;
218
+ const currentZoom = map?.getView().getZoom();
219
+ if (currentZoom !== undefined && currentZoom !== null) {
220
+ const view = map?.getView();
221
+
222
+ if (view !== undefined && view.getZoom()) {
223
+ view.animate({
224
+ zoom: currentZoom - 1,
225
+ duration: 250,
226
+ easing: easeOut,
227
+ });
228
+ }
229
+ }
230
+ };
231
+
232
+ const onMapZoomIn = () => {
233
+ const map = mapEl.value?.map;
234
+ const currentZoom = map?.getView().getZoom();
235
+ if (currentZoom !== undefined && currentZoom !== null) {
236
+ const view = map?.getView();
237
+
238
+ if (view !== undefined && view.getZoom()) {
239
+ view.animate({
240
+ zoom: currentZoom + 1,
241
+ duration: 250,
242
+ easing: easeOut,
243
+ });
244
+ }
245
+ }
246
+ };
247
+ const opencageApiKey = process.env.EODASH_OPENCAGE || "NO_KEY_FOUND";
248
+ const opencageUrl = `https://api.opencagedata.com/geocode/v1/json?key=${opencageApiKey}`;
249
+
250
+ /*const menu = document
251
+ .querySelector("eox-geosearch")
252
+ .renderRoot.querySelector("menu");*/
253
+ </script>
254
+
255
+ <style scoped>
256
+ @import url("@eox/ui/style.css");
257
+
258
+ .map-buttons button {
259
+ margin-bottom: 5px;
260
+ background-color: var(--primary);
261
+ }
262
+
263
+ /* Container constraints removal */
264
+ eox-geosearch {
265
+ position: relative !important;
266
+ overflow: visible !important;
267
+ z-index: 10;
268
+ }
269
+ </style>
@@ -1,45 +1,88 @@
1
1
  <template>
2
- <eox-map-compare
3
- class="fill-height fill-width overflow-none"
4
- .enabled="showCompare"
5
- >
6
- <eox-map
2
+ <span>
3
+ <eox-map-compare
7
4
  class="fill-height fill-width overflow-none"
8
- slot="first"
9
- ref="eoxMap"
10
- id="main"
11
- .animationOptions="animationOptions"
12
- .center="initialCenter"
13
- .zoom="initialZoom"
14
- .layers="eoxMapLayers"
15
- .controls="controls"
5
+ .enabled="showCompare"
16
6
  >
17
- <eox-map-tooltip
18
- :style="mainTooltipStyles"
19
- .propertyTransform="tooltipPropertyTransform('main')"
20
- />
21
- </eox-map>
22
- <eox-map
23
- class="fill-height fill-width overflow-none"
24
- id="compare"
25
- slot="second"
26
- ref="compareMap"
27
- .layers="eoxMapCompareLayers"
7
+ <eox-map
8
+ class="fill-height fill-width overflow-none"
9
+ slot="first"
10
+ ref="eoxMap"
11
+ id="main"
12
+ .animationOptions="animationOptions"
13
+ .center="initialCenter"
14
+ .zoom="initialZoom"
15
+ .layers="eoxMapLayers"
16
+ .controls="controls"
17
+ >
18
+ <eox-map-tooltip
19
+ :style="mainTooltipStyles"
20
+ .propertyTransform="tooltipPropertyTransform('main')"
21
+ />
22
+ </eox-map>
23
+ <eox-map
24
+ class="fill-height fill-width overflow-none"
25
+ id="compare"
26
+ slot="second"
27
+ ref="compareMap"
28
+ .layers="eoxMapCompareLayers"
29
+ >
30
+ <eox-map-tooltip
31
+ :style="compareTooltipStyles"
32
+ .propertyTransform="tooltipPropertyTransform('compare')"
33
+ />
34
+ </eox-map>
35
+ </eox-map-compare>
36
+ <div
37
+ v-if="enableCursorCoordinates"
38
+ id="cursor-coordinates"
39
+ ref="cursor-coords"
40
+ />
41
+ <span v-if="enableScaleLine" id="scale-line" ref="scale-line" />
42
+ <div
43
+ class="map-buttons-container"
44
+ :style="`margin: ${btnsPosition.gap}px 0 ${btnsPosition.gap}px 0`"
28
45
  >
29
- <eox-map-tooltip
30
- :style="compareTooltipStyles"
31
- .propertyTransform="tooltipPropertyTransform('compare')"
46
+ <EodashMapBtns
47
+ :style="{
48
+ gridColumn: indicator || compareIndicator ? responsiveX : '12',
49
+ gridRow: responsiveY,
50
+ }"
51
+ :exportMap="(indicator || compareIndicator) ? btnsProps.exportMap : false"
52
+ :changeProjection="
53
+ (indicator || compareIndicator) ? btnsProps.changeProjection : false
54
+ "
55
+ :compareIndicators="
56
+ (indicator || compareIndicator) ? btnsProps.compareIndicators : false
57
+ "
58
+ :backToPOIs="
59
+ (indicator || compareIndicator) ? btnsProps.backToPOIs : false
60
+ "
61
+ :enableSearch="
62
+ (indicator || compareIndicator) ? btnsProps.enableSearch : false
63
+ "
64
+ :enableZoom="
65
+ (indicator || compareIndicator) ? btnsProps.enableZoom : false
66
+ "
32
67
  />
33
- </eox-map>
34
- </eox-map-compare>
68
+ </div>
69
+ </span>
35
70
  </template>
36
71
  <script setup>
37
72
  import "@eox/map";
38
73
  import "@eox/map/src/plugins/advancedLayersAndSources";
39
- import { computed, onMounted, ref, toRaw } from "vue";
40
- import { datetime, mapEl, mapPosition, mapCompareEl } from "@/store/states";
74
+ import { computed, onMounted, ref, toRaw, useTemplateRef } from "vue";
75
+ import {
76
+ datetime,
77
+ mapEl,
78
+ mapPosition,
79
+ mapCompareEl,
80
+ indicator,
81
+ compareIndicator,
82
+ } from "@/store/states";
41
83
  import { storeToRefs } from "pinia";
42
84
  import { useSTAcStore } from "@/store/stac";
85
+ import { useDisplay } from "vuetify";
43
86
  import {
44
87
  eodashCollections,
45
88
  eodashCompareCollections,
@@ -53,6 +96,7 @@ import {
53
96
  } from "^/EodashMap/methods";
54
97
  import { inAndOut } from "ol/easing.js";
55
98
  import mustache from "mustache";
99
+ import EodashMapBtns from "^/EodashMap/EodashMapBtns.vue";
56
100
 
57
101
  const props = defineProps({
58
102
  enableCompare: {
@@ -73,18 +117,118 @@ const props = defineProps({
73
117
  type: Boolean,
74
118
  default: true,
75
119
  },
120
+ enableCursorCoordinates: {
121
+ type: Boolean,
122
+ default: true,
123
+ },
124
+ enableScaleLine: {
125
+ type: Boolean,
126
+ default: true,
127
+ },
128
+ btnsPosition: {
129
+ type: Object,
130
+ default: () => ({
131
+ x: "12/9/10",
132
+ y: 1,
133
+ gap: 16,
134
+ }),
135
+ },
136
+ btns: {
137
+ type: Object,
138
+ default: () => ({
139
+ enableExportMap: true,
140
+ enableChangeProjection: true,
141
+ enableCompareIndicators: true,
142
+ enableBackToPOIs: true,
143
+ enableSearch: true,
144
+ enableZoom: true,
145
+ }),
146
+ },
76
147
  });
77
148
 
149
+ // Responsive positioning logic
150
+ const { width } = useDisplay();
151
+
152
+ /**
153
+ * Parse responsive string values (e.g., "1/5/10") into values for different screen sizes
154
+ * Breakpoints: [0, 960, 1920] based on properties passed to eox-layout in DashboardLayout.vue
155
+ * @param {string | number} value
156
+ * @returns {number}
157
+ */
158
+ const parseResponsiveValue = (value) => {
159
+ if (typeof value === "number") {
160
+ return value;
161
+ }
162
+ if (typeof value === "string") {
163
+ const parts = value.split("/");
164
+ const currentWidth = width.value;
165
+
166
+ if (currentWidth < 960) {
167
+ return parseInt(parts[0]) || 1;
168
+ } else if (currentWidth < 1920) {
169
+ return parseInt(parts[1] || parts[0]) || 1;
170
+ } else {
171
+ return parseInt(parts[2] || parts[1] || parts[0]) || 1;
172
+ }
173
+ }
174
+ return 1;
175
+ };
176
+
177
+ const responsiveX = computed(() => parseResponsiveValue(props.btnsPosition.x));
178
+ const responsiveY = computed(() => parseResponsiveValue(props.btnsPosition.y));
179
+ const btnsProps = computed(() => ({
180
+ exportMap: props.btns.enableExportMap ?? true,
181
+ changeProjection: props.btns.enableChangeProjection ?? true,
182
+ compareIndicators: props.btns.enableCompareIndicators ?? true,
183
+ backToPOIs: props.btns.enableBackToPOIs ?? true,
184
+ enableSearch: props.btns.enableSearch ?? true,
185
+ enableZoom: props.btns.enableZoom ?? true,
186
+ }));
187
+
188
+ // Prepare containers for scale line and cursor coordinates
189
+ const scaleLineRef = useTemplateRef("scale-line");
190
+ const cursorCoordsRef = useTemplateRef("cursor-coords");
191
+
78
192
  /** @type {import("vue").Ref<Exclude<import("@/types").EodashStyleJson["tooltip"], undefined>>} */
79
193
  const tooltipProperties = ref([]);
80
194
  /** @type {import("vue").Ref<Exclude<import("@/types").EodashStyleJson["tooltip"], undefined>>} */
81
195
  const compareTooltipProperties = ref([]);
82
- /** @type {import("@eox/map").EOxMap["controls"]} */
83
- const controls = {
84
- Attribution: {
85
- collapsible: true,
86
- },
87
- };
196
+ /** @type {import("vue").ComputedRef<{
197
+ Attribution: { collapsible: boolean };
198
+ ScaleLine?: { target: HTMLElement };
199
+ MousePosition?: { projection: string; coordinateFormat: (c: [number, number]) => string; target: HTMLElement };
200
+ }>} */
201
+ const controls = computed(() => {
202
+ /** @type {{
203
+ Attribution: { collapsible: boolean };
204
+ ScaleLine?: { target: HTMLElement };
205
+ MousePosition?: { projection: string; coordinateFormat: (c: [number, number]) => string; target: HTMLElement };
206
+ }} */
207
+ const controlsObj = {
208
+ Attribution: {
209
+ collapsible: true,
210
+ },
211
+ };
212
+
213
+ if (props.enableScaleLine && scaleLineRef.value) {
214
+ controlsObj.ScaleLine = {
215
+ target: scaleLineRef.value,
216
+ };
217
+ }
218
+
219
+ if (props.enableCursorCoordinates && cursorCoordsRef.value) {
220
+ controlsObj.MousePosition = {
221
+ projection: "EPSG:4326",
222
+ coordinateFormat: (/** @type {[number, number]} */ c) => {
223
+ return `${c[1].toFixed(3)} °N, ${c[0].toFixed(3)} °E`;
224
+ },
225
+ target: cursorCoordsRef.value,
226
+ };
227
+ }
228
+
229
+ return controlsObj;
230
+ });
231
+
88
232
  const initialCenter = toRaw(props.center);
89
233
  const initialZoom = toRaw(mapPosition.value?.[2] ?? props.zoom);
90
234
  /** @type {import("vue").Ref<Record<string,any>[]>} */
@@ -210,3 +354,74 @@ const tooltipPropertyTransform = (map) => {
210
354
  };
211
355
  };
212
356
  </script>
357
+
358
+ <style scoped>
359
+ #cursor-coordinates {
360
+ position: fixed;
361
+ left: 24px;
362
+ bottom: 54px; /* Tighter spacing: watermark at 6px + ~48px */
363
+ color: rgba(0, 0, 0, 0.9);
364
+ font-size: 11px;
365
+ font-family: var(--eox-body-font-family);
366
+ background: #fffe;
367
+ border-radius: 4px;
368
+ border: none;
369
+ padding: 0px 3px;
370
+ max-height: 24px;
371
+ }
372
+
373
+ @media (max-width: 959px) {
374
+ #cursor-coordinates {
375
+ display: none; /* Hidden in mobile mode */
376
+ }
377
+ }
378
+
379
+ #scale-line {
380
+ position: fixed;
381
+ left: 24px;
382
+ bottom: 28px; /* Tighter spacing: watermark at 6px + ~22px */
383
+ color: #fff;
384
+ }
385
+
386
+ @media (max-width: 959px) {
387
+ #scale-line {
388
+ bottom: 102px; /* Adjusted for mobile bottom nav - closer to coordinates */
389
+ }
390
+ }
391
+
392
+ :deep(.ol-scale-line) {
393
+ background: #fffe !important;
394
+ border-radius: 4px !important;
395
+ border: none !important;
396
+ padding: 0px 3px 3px 3px !important;
397
+ font-size: 10px !important;
398
+ font-family: var(--eox-body-font-family);
399
+ max-height: 20px;
400
+ }
401
+ :deep(.ol-scale-line-inner) {
402
+ display: flex;
403
+ justify-content: center;
404
+ border: 1px solid rgba(0, 0, 0, 0.5) !important;
405
+ border-top: none !important;
406
+ color: #333 !important;
407
+ font-weight: 500 !important;
408
+ transform: translateY(1px);
409
+ }
410
+
411
+ .map-buttons-container {
412
+ position: fixed;
413
+ top: 0;
414
+ left: 0;
415
+ width: 100%;
416
+ height: 100%;
417
+ display: grid;
418
+ grid-template-columns: repeat(12, 1fr);
419
+ grid-template-rows: repeat(12, 1fr);
420
+ pointer-events: none;
421
+ z-index: 1;
422
+ }
423
+
424
+ .map-buttons-container > * {
425
+ pointer-events: auto;
426
+ }
427
+ </style>