@dative-gpi/foundation-shared-components 1.0.137-maps2 → 1.0.137-maps4

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.
Binary file
@@ -14,7 +14,7 @@
14
14
  v-if="map"
15
15
  >
16
16
  <FSMapTileLayer
17
- :layer="actualLayer"
17
+ :layers="actualLayer"
18
18
  />
19
19
  <FSMapMarker
20
20
  v-if="gpsPosition"
@@ -69,6 +69,7 @@
69
69
  </FSCol>
70
70
 
71
71
  <FSMapOverlay
72
+ v-if="overlaySlots && Object.keys(overlaySlots).length"
72
73
  :mode="$props.overlayMode"
73
74
  @update:mode="$emit('update:overlayMode', $event)"
74
75
  @update:height="(height) => overlayHeight = height"
@@ -96,7 +97,7 @@ import { map as createMap, control, tileLayer, latLngBounds, latLng, type LatLng
96
97
  import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
97
98
 
98
99
  import { useBreakpoints, useColors, useSlots } from "../../composables";
99
- import { ColorEnum, type MapLayer } from "../../models";
100
+ import { ColorEnum, MapLayers, MapOverlayPositions, type MapLayer } from "../../models";
100
101
 
101
102
  import FSMapLayerButton from "./FSMapLayerButton.vue";
102
103
  import FSMapOverlay from "./FSMapOverlay.vue";
@@ -135,9 +136,9 @@ export default defineComponent({
135
136
  default: false
136
137
  },
137
138
  overlayMode: {
138
- type: String as PropType<'collapse' | 'half' | 'expand'>,
139
+ type: String as PropType<MapOverlayPositions>,
139
140
  required: false,
140
- default: 'collapse'
141
+ default: MapOverlayPositions.Collapse
141
142
  },
142
143
  showMyLocation: {
143
144
  type: Boolean,
@@ -165,14 +166,19 @@ export default defineComponent({
165
166
  default: null
166
167
  },
167
168
  currentLayer: {
168
- type: String as PropType<"map" | "imagery">,
169
+ type: String as PropType<MapLayers>,
169
170
  required: false,
170
- default: "map"
171
+ default: MapLayers.Map
171
172
  },
172
173
  allowedLayers: {
173
- type: Array as PropType<string[]>,
174
+ type: Array as PropType<MapLayers[]>,
174
175
  required: false,
175
- default: () => ["map", "imagery"]
176
+ default: () => [MapLayers.Map, MapLayers.Imagery]
177
+ },
178
+ dirtyZoom: {
179
+ type: Number,
180
+ required: false,
181
+ default: 16
176
182
  }
177
183
  },
178
184
  emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId", 'update:overlayMode', 'update:currentLayer', "click:latlng"],
@@ -192,7 +198,7 @@ export default defineComponent({
192
198
 
193
199
  provide('map', map);
194
200
 
195
- const defaultZoom = 16;
201
+ const defaultZoom = ref(props.dirtyZoom);
196
202
  const mapResizeObserver = new ResizeObserver(() => {
197
203
  if(!map.value) {
198
204
  return;
@@ -202,29 +208,53 @@ export default defineComponent({
202
208
 
203
209
  const mapLayers: MapLayer[] = [
204
210
  {
205
- name: "map",
211
+ name: MapLayers.Map,
206
212
  label: $tr("ui.map-layer.map", "Map"),
207
213
  image: new URL("../../assets/images/map/map.png", import.meta.url).href,
208
- layer: tileLayer(`https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
209
- maxZoom: 22,
210
- subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
211
- attribution: '© Google Map Data'
212
- })
214
+ layers: [
215
+ tileLayer(`https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
216
+ maxZoom: 22,
217
+ subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
218
+ attribution: '© Google Map Data',
219
+ className: 'fs-map-tile-base-layer'
220
+ })
221
+ ]
213
222
  },
214
223
  {
215
- name: "imagery",
224
+ name: MapLayers.Imagery,
216
225
  label: $tr("ui.map-layer.imagery", "Imagery"),
217
226
  image: new URL("../../assets/images/map/imagery.png", import.meta.url).href,
218
- layer: tileLayer(`https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
219
- maxZoom: 22,
220
- subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
221
- attribution: '© Google Map Data'
222
- })
227
+ layers: [
228
+ tileLayer(`https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
229
+ maxZoom: 22,
230
+ subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
231
+ attribution: '© Google Map Data',
232
+ className: 'fs-map-tile-base-layer'
233
+ })
234
+ ]
235
+ },
236
+ {
237
+ name: MapLayers.Snow,
238
+ label: $tr("ui.map-layer.snow", "Snow ski map"),
239
+ image: new URL("../../assets/images/map/snow.png", import.meta.url).href,
240
+ layers: [
241
+ tileLayer(`https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
242
+ maxZoom: 22,
243
+ subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
244
+ attribution: '© Google Map Data',
245
+ className: 'fs-map-tile-base-layer fs-map-tile-grayscale-layer'
246
+ }),
247
+ tileLayer(`https://tiles.opensnowmap.org/pistes/{z}/{x}/{y}.png`, {
248
+ maxZoom: 18,
249
+ attribution: 'Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors & ODbL, &copy; <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
250
+ className: 'fs-map-tile-base-layer'
251
+ })
252
+ ]
223
253
  }
224
254
  ];
225
255
 
226
256
  const bottomOffset = computed(() => {
227
- if (props.overlayMode !== 'expand' && overlayHeight.value && isExtraSmall.value) {
257
+ if (props.overlayMode !== MapOverlayPositions.Expand && overlayHeight.value && isExtraSmall.value) {
228
258
  return overlayHeight.value;
229
259
  }
230
260
  return 0;
@@ -246,7 +276,7 @@ export default defineComponent({
246
276
  }));
247
277
 
248
278
  const actualLayer = computed(() => {
249
- return mapLayers.find((layer) => layer.name === props.currentLayer)?.layer ?? mapLayers[0].layer;
279
+ return mapLayers.find((mapLayer) => mapLayer.name === props.currentLayer)?.layers ?? mapLayers[0].layers;
250
280
  });
251
281
 
252
282
  const overlaySlots = computed(() => {
@@ -265,7 +295,7 @@ export default defineComponent({
265
295
  return map.value.unproject(targetPoint, zoom);
266
296
  }
267
297
 
268
- const flyTo = (lat: number, lng: number, zoom: number = defaultZoom, options?: ZoomPanOptions) => {
298
+ const flyTo = (lat: number, lng: number, zoom: number = defaultZoom.value, options?: ZoomPanOptions) => {
269
299
  if(!map.value) {
270
300
  return;
271
301
  }
@@ -328,7 +358,7 @@ export default defineComponent({
328
358
  maxZoom: 22,
329
359
  maxBounds: latLngBounds(latLng(-90, -180), latLng(90, 180)),
330
360
  maxBoundsViscosity: 1.0,
331
- zoom: 5,
361
+ zoom: defaultZoom.value,
332
362
  center: props.center ? latLng(props.center[0], props.center[1]) : latLng(48.85782, 2.29521)
333
363
  };
334
364
 
@@ -366,16 +396,23 @@ export default defineComponent({
366
396
  if(!map.value || !props.center) {
367
397
  return;
368
398
  }
369
- setView(props.center[0], props.center[1], defaultZoom);
399
+ setView(props.center[0], props.center[1], defaultZoom.value);
370
400
  }, { immediate: true });
371
401
 
372
402
  watch([() => props.bounds, () => map.value], () => {
373
403
  if(!map.value || !props.bounds) {
374
404
  return;
375
405
  }
376
- fitBounds(props.bounds, { maxZoom: defaultZoom });
406
+ fitBounds(props.bounds, { maxZoom: defaultZoom.value });
377
407
  });
378
408
 
409
+ watch(() => props.dirtyZoom, (newZoom) => {
410
+ defaultZoom.value = newZoom;
411
+ if(map.value) {
412
+ map.value.setZoom(newZoom);
413
+ }
414
+ }, { immediate: true });
415
+
379
416
  return {
380
417
  ColorEnum,
381
418
  defaultZoom,
@@ -1,16 +1,16 @@
1
1
  <template>
2
2
  <v-overlay
3
3
  v-if="isExtraSmall"
4
- :modelValue="$props.mode === 'expand'"
4
+ :modelValue="$props.mode === MapOverlayPositions.Expand"
5
5
  :contained="true"
6
- @click="$emit('update:mode', 'collapse')"
6
+ @click="$emit('update:mode', MapOverlayPositions.Collapse)"
7
7
  zIndex="0"
8
8
  />
9
9
  <div
10
10
  v-show="isExtraSmall"
11
11
  ref="mobileOverlayWrapper"
12
12
  class="fs-map-overlay-mobile"
13
- :style="{ height: $props.mode === 'expand' ? '90%' : ($props.mode === 'half' ? '60%' : 'auto') }"
13
+ :style="{ height: $props.mode === MapOverlayPositions.Expand ? '90%' : ($props.mode === MapOverlayPositions.Half ? '60%' : 'auto') }"
14
14
  >
15
15
  <FSCard
16
16
  width="100%"
@@ -29,15 +29,15 @@
29
29
  @mousedown="onClick"
30
30
  >
31
31
  <FSIcon>
32
- {{ $props.mode === 'expand' ? 'mdi-chevron-down' : 'mdi-chevron-up' }}
32
+ {{ $props.mode === MapOverlayPositions.Expand ? 'mdi-chevron-down' : 'mdi-chevron-up' }}
33
33
  </FSIcon>
34
34
  </FSRow>
35
35
  <slot
36
- v-if="$props.mode === 'collapse'"
36
+ v-if="$props.mode === MapOverlayPositions.Collapse"
37
37
  name="collapsed"
38
38
  />
39
39
  <FSCol
40
- v-if="$props.mode !== 'collapse'"
40
+ v-if="$props.mode !== MapOverlayPositions.Collapse"
41
41
  height="fill"
42
42
  style="min-height: 0;"
43
43
  >
@@ -70,6 +70,8 @@ import { defineComponent, type PropType, onUnmounted, onMounted, ref } from "vue
70
70
 
71
71
  import { useBreakpoints } from "../../composables";
72
72
 
73
+ import { MapOverlayPositions } from '@dative-gpi/foundation-shared-components/models';
74
+
73
75
  import FSCard from "../FSCard.vue";
74
76
  import FSIcon from "../FSIcon.vue";
75
77
  import FSCol from "../FSCol.vue";
@@ -85,9 +87,9 @@ export default defineComponent({
85
87
  },
86
88
  props: {
87
89
  mode: {
88
- type: String as PropType<"collapse" | "half" | "expand">,
90
+ type: String as PropType<MapOverlayPositions>,
89
91
  required: false,
90
- default: "collapse"
92
+ default: MapOverlayPositions.Collapse
91
93
  }
92
94
  },
93
95
  emits: ["update:mode", "update:height", "update:width"],
@@ -101,11 +103,11 @@ export default defineComponent({
101
103
  const desktopResizeObserver = ref<ResizeObserver | null>(null);
102
104
 
103
105
  const onClick = (): void => {
104
- if (props.mode === "expand") {
105
- emit("update:mode", "collapse");
106
+ if (props.mode === MapOverlayPositions.Expand) {
107
+ emit("update:mode", MapOverlayPositions.Collapse);
106
108
  return;
107
109
  }
108
- emit("update:mode", "expand");
110
+ emit("update:mode", MapOverlayPositions.Expand);
109
111
  }
110
112
 
111
113
  onMounted(() => {
@@ -144,6 +146,7 @@ export default defineComponent({
144
146
  return {
145
147
  mobileOverlayWrapper,
146
148
  isTouchScreenEnabled,
149
+ MapOverlayPositions,
147
150
  desktopOverlay,
148
151
  isExtraSmall,
149
152
  onClick
@@ -12,15 +12,15 @@ import { MAP } from './keys';
12
12
  export default {
13
13
  name: 'FSMapTileLayer',
14
14
  props: {
15
- layer: {
16
- type: Object as PropType<Layer>,
15
+ layers: {
16
+ type: Object as PropType<Layer[]>,
17
17
  required: false
18
18
  }
19
19
  },
20
20
  setup(props) {
21
21
  const map = inject<Ref<Map | null>>(MAP);
22
22
 
23
- const lastLayer = props.layer;
23
+ let lastLayers = props.layers;
24
24
 
25
25
  if(!map) {
26
26
  throw new Error('FSMapTileLayer must be used inside a FSMap component');
@@ -31,20 +31,26 @@ export default {
31
31
  }
32
32
 
33
33
  const updateLayer = () => {
34
- if (!props.layer || !map.value) {
34
+ if (!props.layers || !map.value) {
35
35
  return;
36
36
  }
37
37
 
38
- if(lastLayer) {
39
- map.value.removeLayer(lastLayer);
38
+ if(lastLayers) {
39
+ lastLayers.forEach(layer => {
40
+ layer.removeFrom(map.value!);
41
+ });
40
42
  }
41
43
 
42
- props.layer.addTo(map.value);
44
+ lastLayers = [];
45
+
46
+ props.layers.forEach(layer => {
47
+ lastLayers?.push(layer.addTo(map.value!));
48
+ });
43
49
  };
44
50
 
45
51
  onMounted(updateLayer);
46
52
 
47
- watch(() => props.layer, updateLayer);
53
+ watch(() => props.layers, updateLayer);
48
54
  }
49
55
  };
50
56
  </script>
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <FSSelectField
3
+ :items="items"
4
+ :modelValue="$props.modelValue"
5
+ @update:modelValue="$emit('update:modelValue', $event)"
6
+ v-bind="$attrs"
7
+ >
8
+ <template
9
+ #item-prepend="{ item }"
10
+ >
11
+ <FSIcon
12
+ :icon="item.icon"
13
+ />
14
+ </template>
15
+ </FSSelectField>
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ import type { PropType} from "vue";
20
+ import { defineComponent } from "vue";
21
+
22
+ import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
23
+
24
+ import { MapLayers } from '@dative-gpi/foundation-shared-components/models';
25
+
26
+ import FSSelectField from "../fields/FSSelectField.vue";
27
+ import FSIcon from '@dative-gpi/foundation-shared-components/components/FSIcon.vue';
28
+
29
+ export default defineComponent({
30
+ name: "FSSelectMapLayer",
31
+ components: {
32
+ FSIcon,
33
+ FSSelectField
34
+ },
35
+ props: {
36
+ modelValue: {
37
+ type: [String, Array] as PropType<MapLayers | MapLayers[]>,
38
+ required: false
39
+ }
40
+ },
41
+ emits: ["update:modelValue"],
42
+ setup() {
43
+ const { $tr } = useTranslationsProvider();
44
+
45
+ const items = [
46
+ {
47
+ id: MapLayers.Map,
48
+ icon: 'mdi-map',
49
+ label: $tr("ui.map-layer.map", "Map")
50
+ },
51
+ {
52
+ id: MapLayers.Imagery,
53
+ icon: 'mdi-satellite',
54
+ label: $tr("ui.map-layer.imagery", "Imagery")
55
+ },
56
+ {
57
+ id: MapLayers.Snow,
58
+ icon: 'mdi-snowflake',
59
+ label: $tr("ui.map-layer.snow", "Snow ski map")
60
+ }
61
+ ]
62
+
63
+ return {
64
+ items
65
+ };
66
+ }
67
+ });
68
+ </script>
package/models/map.ts CHANGED
@@ -1,8 +1,20 @@
1
1
  import { type Layer } from "leaflet";
2
2
 
3
3
  export interface MapLayer {
4
- name : string;
4
+ name : MapLayers;
5
5
  label: string;
6
6
  image: string;
7
- layer: Layer;
7
+ layers: Layer[];
8
+ }
9
+
10
+ export enum MapLayers {
11
+ Map = "map",
12
+ Imagery = "imagery",
13
+ Snow= "snow"
14
+ }
15
+
16
+ export enum MapOverlayPositions {
17
+ Expand = "expand",
18
+ Half = "half",
19
+ Collapse = "collapse",
8
20
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dative-gpi/foundation-shared-components",
3
3
  "sideEffects": false,
4
- "version": "1.0.137-maps2",
4
+ "version": "1.0.137-maps4",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -10,8 +10,8 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@dative-gpi/foundation-shared-domain": "1.0.137-maps2",
14
- "@dative-gpi/foundation-shared-services": "1.0.137-maps2"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.137-maps4",
14
+ "@dative-gpi/foundation-shared-services": "1.0.137-maps4"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@dative-gpi/bones-ui": "^1.0.0",
@@ -35,5 +35,5 @@
35
35
  "sass": "1.71.1",
36
36
  "sass-loader": "13.3.2"
37
37
  },
38
- "gitHead": "0e9da9645c2c83684220f99135cf12e4c339897c"
38
+ "gitHead": "2ff4d8aa1783b9f845daae1e8e6c9b9d23820b72"
39
39
  }
@@ -6,7 +6,15 @@
6
6
  .fs-leaflet-container {
7
7
  width: 100%;
8
8
  height: 100%;
9
- filter: grayscale(var(--fs-map-container-grayscale));
9
+ z-index: 0;
10
+
11
+ .fs-map-tile-base-layer {
12
+ filter: grayscale(var(--fs-map-container-grayscale));
13
+ }
14
+
15
+ .fs-map-tile-grayscale-layer {
16
+ filter: grayscale(100%);
17
+ }
10
18
  }
11
19
 
12
20
  .fs-map-overlay-mobile {