@dative-gpi/foundation-shared-components 1.0.137-maps2 → 1.0.137-override-widgets

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.
@@ -14,18 +14,20 @@
14
14
  :wrap="false"
15
15
  >
16
16
  <FSSelectField
17
+ :editable="$props.editable"
18
+ :items="actualEntityTypes"
17
19
  :hideHeader="true"
20
+ :clearable="false"
18
21
  :modelValue="$props.entityType"
19
22
  @update:modelValue="$emit('update:entityType', $event)"
20
- :items="actualEntityTypes"
21
- :clearable="false"
22
23
  />
23
24
  <template
24
25
  v-if="itemsCount > 0"
25
26
  >
26
27
  <FSButton
27
- :label="$tr('ui.common.edit', 'Edit')"
28
28
  icon="mdi-pencil"
29
+ :label="$tr('ui.common.edit', 'Edit')"
30
+ :editable="$props.editable"
29
31
  @click="$emit('click:select')"
30
32
  />
31
33
  </template>
@@ -33,8 +35,9 @@
33
35
  v-else
34
36
  >
35
37
  <FSButton
36
- :label="$tr('ui.common.select', 'Select')"
37
38
  icon="mdi-plus-circle-multiple-outline"
39
+ :label="$tr('ui.common.select', 'Select')"
40
+ :editable="$props.editable"
38
41
  @click="$emit('click:select')"
39
42
  />
40
43
  </template>
@@ -32,7 +32,9 @@
32
32
  >
33
33
  {{ $tr('translate-rich-text-field.translate-in', 'Translate in {0}', language.label) }}
34
34
  </FSSpan>
35
- <FSIcon>{{ language.icon }}</FSIcon>
35
+ <FSIcon>
36
+ {{ language.icon }}
37
+ </FSIcon>
36
38
  </FSRow>
37
39
  </template>
38
40
  </FSRichTextField>
@@ -22,7 +22,38 @@
22
22
  :color="ColorEnum.Primary"
23
23
  :latlng="gpsPosition"
24
24
  />
25
- <slot />
25
+
26
+ <FSMapFeatureGroup
27
+ v-if="$props.areas"
28
+ :expected-layers="$props.areas.length"
29
+ @update:bounds="(bounds) => areaGroupBounds = bounds"
30
+ >
31
+ <FSMapPolygon
32
+ v-for="area in areas"
33
+ :key="area.id"
34
+ :color="area.color"
35
+ :latlngs="area.coordinates.map((coord) => ({lat: coord.latitude, lng: coord.longitude}))"
36
+ @click="$emit('update:selectedAreaId', area.id)"
37
+ />
38
+ </FSMapFeatureGroup>
39
+
40
+ <FSMapMarkerClusterGroup
41
+ v-if="$props.locations"
42
+ :expected-layers="$props.locations.length"
43
+ :disableClusteringAtZoom="defaultZoom"
44
+ @update:bounds="(bounds) => locationGroupBounds = bounds"
45
+ >
46
+ <FSMapMarker
47
+ v-for="location in $props.locations"
48
+ :selected="location.id === $props.selectedLocationId"
49
+ :key="location.id"
50
+ :label="location.label"
51
+ :color="location.color ?? ColorEnum.Primary"
52
+ :icon="location.icon ?? 'mdi-map-marker'"
53
+ :latlng="{lat: location.address.latitude, lng: location.address.longitude}"
54
+ @click="$emit('update:selectedLocationId', location.id)"
55
+ />
56
+ </FSMapMarkerClusterGroup>
26
57
  </template>
27
58
  </div>
28
59
 
@@ -94,9 +125,10 @@ import type {} from "leaflet.markercluster";
94
125
  import { map as createMap, control, tileLayer, latLngBounds, latLng, type LatLng, type FitBoundsOptions, type ZoomPanOptions, type LatLngBounds } from "leaflet";
95
126
 
96
127
  import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
128
+ import { type FSArea } from '@dative-gpi/foundation-shared-domain/models';
97
129
 
98
130
  import { useBreakpoints, useColors, useSlots } from "../../composables";
99
- import { ColorEnum, type MapLayer } from "../../models";
131
+ import { ColorEnum, type FSLocation, type MapLayer } from "../../models";
100
132
 
101
133
  import FSMapLayerButton from "./FSMapLayerButton.vue";
102
134
  import FSMapOverlay from "./FSMapOverlay.vue";
@@ -106,12 +138,19 @@ import FSCol from "../FSCol.vue";
106
138
 
107
139
  import FSMapMarker from "./FSMapMarker.vue";
108
140
  import FSMapTileLayer from "./FSMapTileLayer.vue";
141
+ import FSMapFeatureGroup from "./FSMapFeatureGroup.vue";
142
+ import FSMapMarkerClusterGroup from "./FSMapMarkerClusterGroup.vue";
143
+ import FSMapPolygon from "./FSMapPolygon.vue";
109
144
 
110
145
  export default defineComponent({
111
146
  name: "FSMap",
112
147
  components: {
113
148
  FSMapMarker,
114
149
  FSMapTileLayer,
150
+ FSMapFeatureGroup,
151
+ FSMapMarkerClusterGroup,
152
+ FSMapPolygon,
153
+
115
154
  FSMapLayerButton,
116
155
  FSMapOverlay,
117
156
  FSButton,
@@ -155,14 +194,19 @@ export default defineComponent({
155
194
  default: false
156
195
  },
157
196
  center: {
158
- type: Array as PropType<number[] | null>,
197
+ type: Array as PropType<number[]>,
159
198
  required: false,
160
- default: null
199
+ default: () => [45.71, 5.07]
161
200
  },
162
- bounds: {
163
- type: Object as PropType<LatLngBounds | null>,
201
+ locations: {
202
+ type: Array as PropType<FSLocation[]>,
164
203
  required: false,
165
- default: null
204
+ default: () => [],
205
+ },
206
+ areas: {
207
+ type: Array as PropType<FSArea[]>,
208
+ required: false,
209
+ default: () => [],
166
210
  },
167
211
  currentLayer: {
168
212
  type: String as PropType<"map" | "imagery">,
@@ -173,6 +217,16 @@ export default defineComponent({
173
217
  type: Array as PropType<string[]>,
174
218
  required: false,
175
219
  default: () => ["map", "imagery"]
220
+ },
221
+ selectedLocationId: {
222
+ type: String as PropType<string | null>,
223
+ required: false,
224
+ default: null
225
+ },
226
+ selectedAreaId: {
227
+ type: String as PropType<string | null>,
228
+ required: false,
229
+ default: null
176
230
  }
177
231
  },
178
232
  emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId", 'update:overlayMode', 'update:currentLayer', "click:latlng"],
@@ -249,6 +303,19 @@ export default defineComponent({
249
303
  return mapLayers.find((layer) => layer.name === props.currentLayer)?.layer ?? mapLayers[0].layer;
250
304
  });
251
305
 
306
+ const bounds = computed<LatLngBounds | null>(() => {
307
+ if(!locationGroupBounds.value && !areaGroupBounds.value) {
308
+ return null;
309
+ }
310
+ let bounds = locationGroupBounds.value;
311
+ if(bounds && areaGroupBounds.value) {
312
+ bounds.extend(areaGroupBounds.value);
313
+ } else if(areaGroupBounds.value) {
314
+ bounds = areaGroupBounds.value;
315
+ }
316
+ return bounds as LatLngBounds;
317
+ });
318
+
252
319
  const overlaySlots = computed(() => {
253
320
  return Object.keys(slots).filter((slot) => slot.startsWith("overlay-")).reduce((acc, slot) => {
254
321
  acc[slot.replace("overlay-", "")] = slots[slot];
@@ -293,7 +360,7 @@ export default defineComponent({
293
360
  if(!map.value) {
294
361
  return;
295
362
  }
296
- map.value.setView(calculateTargetPosition(latLng(lat, lng), zoom), zoom);
363
+ map.value.setView(calculateTargetPosition(latLng(lat, lng)), zoom);
297
364
  }
298
365
 
299
366
  const fitBounds = (bounds: LatLngBounds, options?: FitBoundsOptions) => {
@@ -327,12 +394,11 @@ export default defineComponent({
327
394
  minZoom: 2,
328
395
  maxZoom: 22,
329
396
  maxBounds: latLngBounds(latLng(-90, -180), latLng(90, 180)),
330
- maxBoundsViscosity: 1.0,
331
- zoom: 5,
332
- center: props.center ? latLng(props.center[0], props.center[1]) : latLng(48.85782, 2.29521)
397
+ maxBoundsViscosity: 1.0
333
398
  };
334
399
 
335
400
  map.value = markRaw(createMap(leafletContainer.value, mapOptions));
401
+ setView(props.center[0], props.center[1], defaultZoom);
336
402
 
337
403
  map.value.on('click', (e: L.LeafletMouseEvent) => {
338
404
  emit('click:latlng', e.latlng);
@@ -362,18 +428,41 @@ export default defineComponent({
362
428
  mapResizeObserver.disconnect();
363
429
  });
364
430
 
365
- watch ([() => props.center, () => map.value], () => {
366
- if(!map.value || !props.center) {
431
+ watch (() => props.center, (center) => {
432
+ if(!map.value) {
367
433
  return;
368
434
  }
369
- setView(props.center[0], props.center[1], defaultZoom);
435
+ setView(center[0], center[1], defaultZoom);
436
+ });
437
+
438
+ watch (() => props.selectedLocationId, (selectedLocationId) => {
439
+ if(!map.value) {
440
+ return;
441
+ }
442
+ const selectedLocation = props.locations.find((location) => location.id === selectedLocationId);
443
+ if(!selectedLocation) {
444
+ return;
445
+ }
446
+ flyTo(selectedLocation?.address.latitude, selectedLocation?.address.longitude, defaultZoom, { animate: false });
447
+ }, { immediate: true });
448
+
449
+ watch(() => props.selectedAreaId, (selectedAreaId) => {
450
+ if(!map.value) {
451
+ return;
452
+ }
453
+ const selectedArea = props.areas.find((area) => area.id === selectedAreaId);
454
+ if(!selectedArea) {
455
+ return;
456
+ }
457
+ const bounds = latLngBounds(selectedArea.coordinates.map((coord) => latLng(coord.latitude, coord.longitude)));
458
+ fitBounds(bounds);
370
459
  }, { immediate: true });
371
460
 
372
- watch([() => props.bounds, () => map.value], () => {
373
- if(!map.value || !props.bounds) {
461
+ watch( () => bounds.value, (bounds) => {
462
+ if(!map.value || !bounds) {
374
463
  return;
375
464
  }
376
- fitBounds(props.bounds, { maxZoom: defaultZoom });
465
+ fitBounds(bounds, { maxZoom: defaultZoom });
377
466
  });
378
467
 
379
468
  return {
@@ -82,20 +82,20 @@ export default {
82
82
  iconSize: [size, size],
83
83
  iconAnchor: [size / 2, size / 2],
84
84
  });
85
- } else if(props.variant === 'location') {
85
+ } else if(props.variant === 'location' && props.icon) {
86
86
  const size = 36;
87
87
  icon = divIcon({
88
- html: locationMarkerHtml(props.icon ?? "mdi-map-marker", getColors(props.color).base, props.label),
88
+ html: locationMarkerHtml(props.icon, getColors(props.color).base, props.label),
89
89
  iconSize: [size, size],
90
- className: props.selected ? 'fs-map-marker fs-map-location fs-map-location-selected' : 'fs-map-marker fs-map-location',
90
+ className: props.selected ? 'fs-map-location fs-map-location-selected' : 'fs-map-location',
91
91
  iconAnchor: [size / 2, size / 2],
92
92
  });
93
93
  } else {
94
- const size = 16;
94
+ const size = 20;
95
95
  icon = divIcon({
96
- html: pinMarkerHtml(getColors(props.color).base, props.label),
96
+ html: pinMarkerHtml(getColors(props.color).base),
97
97
  iconSize: [size, size],
98
- className: props.selected ? 'fs-map-marker fs-map-pin fs-map-pin-selected' : 'fs-map-marker fs-map-pin',
98
+ className: props.selected ? 'fs-map-location fs-map-location-selected' : 'fs-map-location',
99
99
  iconAnchor: [size / 2, size / 2],
100
100
  });
101
101
  }
@@ -45,7 +45,7 @@ export default {
45
45
 
46
46
  return divIcon({
47
47
  html: clusterMarkerHtml(cluster.getChildCount()),
48
- className: 'fs-map-marker fs-map-cluster-marker',
48
+ className: 'fs-map-location fs-map-location-full',
49
49
  iconSize: [size, size],
50
50
  iconAnchor: [size / 2, size / 2],
51
51
  });
@@ -4,48 +4,52 @@
4
4
  :height="$props.height"
5
5
  :width="$props.width"
6
6
  >
7
- <FSClickable
8
- v-if="$props.singleSelect"
9
- padding="12px"
10
- :variant="variant"
11
- :color="color"
12
- :style="style"
13
- width="100%"
14
- height="100%"
15
- @click="() => $emit('update:modelValue', !$props.modelValue)"
16
- v-bind="$attrs"
17
- >
18
- <slot />
19
- </FSClickable>
20
- <FSClickable
21
- v-else-if="$props.href || $props.to || $attrs.onClick"
22
- variant="background"
23
- class="fs-tile"
24
- padding="12px"
25
- :color="ColorEnum.Background"
26
- :href="$props.href"
27
- width="100%"
28
- height="100%"
29
- :to="$props.to"
30
- :style="style"
31
- v-bind="$attrs"
7
+ <template
8
+ v-if="$props.editable"
32
9
  >
33
- <slot />
34
- <FSCard
35
- v-if="$props.editable"
36
- class="fs-tile-checkbox"
10
+ <FSClickable
11
+ v-if="$props.singleSelect"
12
+ padding="12px"
13
+ :variant="variant"
14
+ :color="color"
15
+ :style="style"
16
+ width="100%"
17
+ height="100%"
18
+ @click="() => $emit('update:modelValue', !$props.modelValue)"
19
+ v-bind="$attrs"
20
+ >
21
+ <slot />
22
+ </FSClickable>
23
+ <FSClickable
24
+ v-else-if="$props.href || $props.to || $attrs.onClick"
37
25
  variant="background"
38
- :height="['40px', '32px']"
39
- :width="['40px', '32px']"
40
- :border="false"
26
+ class="fs-tile"
27
+ padding="12px"
28
+ :color="ColorEnum.Background"
29
+ :href="$props.href"
30
+ width="100%"
31
+ height="100%"
32
+ :to="$props.to"
33
+ :style="style"
41
34
  v-bind="$attrs"
42
35
  >
43
- <FSCheckbox
44
- :modelValue="$props.modelValue"
45
- @update:modelValue="() => $emit('update:modelValue', !$props.modelValue)"
46
- />
47
- </FSCard>
48
- </FSClickable>
36
+ <slot />
37
+ <FSCard
38
+ v-if="$props.editable"
39
+ class="fs-tile-checkbox"
40
+ variant="background"
41
+ :height="['40px', '32px']"
42
+ :width="['40px', '32px']"
43
+ :border="false"
44
+ v-bind="$attrs"
45
+ >
46
+ <FSCheckbox
47
+ :modelValue="$props.modelValue"
48
+ @update:modelValue="() => $emit('update:modelValue', !$props.modelValue)"
49
+ />
50
+ </FSCard>
51
+ </FSClickable>
52
+ </template>
49
53
  <FSCard
50
54
  v-else
51
55
  variant="background"
package/models/map.ts CHANGED
@@ -1,8 +1,18 @@
1
1
  import { type Layer } from "leaflet";
2
2
 
3
+ import { type Address } from "@dative-gpi/foundation-shared-domain/models";
4
+
3
5
  export interface MapLayer {
4
6
  name : string;
5
7
  label: string;
6
8
  image: string;
7
9
  layer: Layer;
10
+ }
11
+
12
+ export interface FSLocation {
13
+ id: string;
14
+ label: string;
15
+ icon: string | null;
16
+ address: Address;
17
+ color: string | null;
8
18
  }
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-override-widgets",
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-override-widgets",
14
+ "@dative-gpi/foundation-shared-services": "1.0.137-override-widgets"
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": "1eff5a41302a336f7298d8198527030c6bbf4e65"
39
39
  }
@@ -77,10 +77,17 @@
77
77
  }
78
78
  }
79
79
 
80
- .fs-map-marker > div {
80
+ .fs-map-point-pin {
81
+ background-color: var(--fs-map-point-pin-color);
82
+ border-radius: 100%;
83
+ }
84
+
85
+ .fs-map-location > div {
81
86
  display: flex;
82
87
  height: 100%;
88
+ color: var(--fs-map-location-pin-color);
83
89
  border-radius: 50%;
90
+ background-color: white;
84
91
  filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.4));
85
92
  align-items: center;
86
93
  justify-content: center;
@@ -96,7 +103,7 @@
96
103
  }
97
104
  }
98
105
 
99
- .fs-map-cluster-marker > div {
106
+ .fs-map-location-full > div {
100
107
  background-color: var(--fs-map-location-pin-color);
101
108
  color: white;
102
109
  }
@@ -119,41 +126,6 @@
119
126
  }
120
127
  }
121
128
 
122
- .fs-map-location > div {
123
- color: var(--fs-map-location-pin-color);
124
- background-color: white;
125
- }
126
-
127
- .fs-map-pin > div {
128
- background-color: var(--fs-map-point-pin-color);
129
- position: relative;
130
-
131
- transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
132
- }
133
-
134
- .fs-map-pin > div::before {
135
- content: "";
136
- position: absolute;
137
- top: -4px;
138
- left: -4px;
139
- width: calc(100% + 8px);
140
- height: calc(100% + 8px);
141
- border-radius: 50%;
142
- border: 2px solid var(--fs-map-point-pin-color);
143
- opacity: 0.4;
144
-
145
- @include clickscreen {
146
- &:hover {
147
- opacity: 1;
148
- }
149
- }
150
- }
151
-
152
- .fs-map-pin-selected > div {
153
- transform: scale(1.35);
154
- }
155
-
156
-
157
129
  .fs-map-site {
158
130
  opacity: 0.6;
159
131
  transition: opacity 0.28s cubic-bezier(0.4, 0, 0.2, 1);