@dative-gpi/foundation-shared-components 0.0.205 → 0.0.207

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 (31) hide show
  1. package/assets/images/map/imagery.png +0 -0
  2. package/assets/images/map/osm.png +0 -0
  3. package/components/FSCalendar.vue +1 -0
  4. package/components/FSCalendarTwin.vue +2 -1
  5. package/components/FSClock.vue +18 -6
  6. package/components/FSImageCard.vue +72 -0
  7. package/components/autocompletes/FSAutoCompleteAddress.vue +6 -6
  8. package/components/autocompletes/FSAutocompleteLanguage.vue +4 -5
  9. package/components/autocompletes/FSAutocompleteTimeZone.vue +4 -5
  10. package/components/fields/FSColorField.vue +9 -11
  11. package/components/fields/FSDateField.vue +10 -4
  12. package/components/fields/FSDateRangeField.vue +10 -4
  13. package/components/fields/FSDateTimeField.vue +20 -11
  14. package/components/fields/FSDateTimeRangeField.vue +35 -25
  15. package/components/fields/FSRichTextField.vue +132 -54
  16. package/components/fields/FSTermField.vue +190 -186
  17. package/components/fields/FSTimeField.vue +17 -12
  18. package/components/fields/FSTranslateField.vue +7 -14
  19. package/components/fields/FSTranslateRichTextField.vue +185 -0
  20. package/components/map/FSMap.vue +129 -65
  21. package/components/map/FSMapEditPointAddressOverlay.vue +19 -18
  22. package/components/map/FSMapLayerButton.vue +71 -0
  23. package/models/map.ts +1 -0
  24. package/models/richTextVariable.ts +5 -0
  25. package/models/variableNode.ts +105 -0
  26. package/package.json +4 -4
  27. package/styles/components/fs_image_card.scss +18 -0
  28. package/styles/components/fs_map.scss +10 -14
  29. package/styles/components/fs_rich_text_field.scss +16 -4
  30. package/styles/components/index.scss +1 -0
  31. package/utils/lexical.ts +2 -0
@@ -53,8 +53,7 @@
53
53
  </template>
54
54
 
55
55
  <script lang="ts">
56
- import type { PropType} from "vue";
57
- import { computed, defineComponent, ref } from "vue";
56
+ import { computed, defineComponent, type PropType, ref, watch } from "vue";
58
57
 
59
58
  import { useColors, useRules, useSlots } from "@dative-gpi/foundation-shared-components/composables";
60
59
  import { getTimeScaleIndex, timeScale } from "@dative-gpi/foundation-shared-components/utils";
@@ -131,16 +130,6 @@ export default defineComponent({
131
130
  const innerTime = ref(0);
132
131
  const selectedUnit = ref(timeScale[0]);
133
132
 
134
- if (props.modelValue) {
135
- if (getTimeScaleIndex(props.modelValue) !== 0) {
136
- selectedUnit.value = timeScale[getTimeScaleIndex(props.modelValue)];
137
- innerTime.value = props.modelValue / selectedUnit.value.id;
138
- }
139
- else {
140
- innerTime.value = props.modelValue;
141
- }
142
- }
143
-
144
133
  const style = computed((): { [key: string] : string | null | undefined } => {
145
134
  if (!props.editable) {
146
135
  return {
@@ -186,6 +175,22 @@ export default defineComponent({
186
175
  emit("update:modelValue", innerTime.value * selectedUnit.value.id);
187
176
  };
188
177
 
178
+ const reset = (): void => {
179
+ if (props.modelValue) {
180
+ if (getTimeScaleIndex(props.modelValue) !== 0) {
181
+ selectedUnit.value = timeScale[getTimeScaleIndex(props.modelValue)];
182
+ innerTime.value = props.modelValue / selectedUnit.value.id;
183
+ }
184
+ else {
185
+ innerTime.value = props.modelValue;
186
+ }
187
+ }
188
+ };
189
+
190
+ watch(() => props.modelValue, () => {
191
+ reset();
192
+ }, { immediate: true });
193
+
189
194
  return {
190
195
  selectedUnit,
191
196
  numberSlots,
@@ -19,11 +19,10 @@
19
19
  >
20
20
  <FSButton
21
21
  :prependIcon="$props.buttonPrependIcon"
22
- :label="$props.buttonLabel"
23
22
  :appendIcon="$props.buttonAppendIcon"
24
23
  :variant="$props.buttonVariant"
25
24
  :color="$props.buttonColor"
26
- :load="fetchingLanguages"
25
+ :label="$props.buttonLabel"
27
26
  @click="dialog = true"
28
27
  />
29
28
  <slot
@@ -84,12 +83,10 @@
84
83
  </template>
85
84
 
86
85
  <script lang="ts">
87
- import type { PropType} from "vue";
88
- import { computed, defineComponent, onMounted, ref } from "vue";
86
+ import { computed, defineComponent, type PropType, ref } from "vue";
89
87
 
90
- import type { ColorBase} from "@dative-gpi/foundation-shared-components/models";
91
- import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
92
- import { useLanguages } from "@dative-gpi/foundation-shared-services/composables";
88
+ import { type ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
89
+ import { useAppLanguages } from "@dative-gpi/foundation-shared-services/composables";
93
90
 
94
91
  import { useColors, useSlots } from "../../composables";
95
92
 
@@ -159,14 +156,15 @@ export default defineComponent({
159
156
  },
160
157
  emits: ["update:modelValue", "update:translations"],
161
158
  setup(props, { emit }) {
162
- const { getMany: getManyLanguages, fetching: fetchingLanguages, entities: languages } = useLanguages();
159
+ const { languages } = useAppLanguages();
163
160
  const { getColors } = useColors();
164
161
  const { slots } = useSlots();
165
162
 
166
163
  delete slots.append;
164
+
165
+ const dialog = ref(false);
167
166
 
168
167
  const innerTranslations = ref(props.translations);
169
- const dialog = ref(false);
170
168
 
171
169
  const lights = getColors(ColorEnum.Light);
172
170
  const darks = getColors(ColorEnum.Dark);
@@ -220,12 +218,7 @@ export default defineComponent({
220
218
  }
221
219
  };
222
220
 
223
- onMounted(() => {
224
- getManyLanguages();
225
- });
226
-
227
221
  return {
228
- fetchingLanguages,
229
222
  innerTranslations,
230
223
  ColorEnum,
231
224
  languages,
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <FSCol
3
+ v-if="$props.translationsExpanded"
4
+ >
5
+ <FSCol
6
+ gap="16px"
7
+ >
8
+ <FSRichTextField
9
+ :editable="false"
10
+ :label="$tr('ui.translateRichTextField.defaultValue', 'Default value')"
11
+ :modelValue="$props.modelValue"
12
+ v-bind="$attrs"
13
+ />
14
+ <FSRichTextField
15
+ v-for="(language, index) in languages"
16
+ :editable="$props.editable"
17
+ :key="index"
18
+ :modelValue="getTranslation(language.code)"
19
+ @update:modelValue="setTranslation(language.code, $event)"
20
+ v-bind="$attrs"
21
+ >
22
+ <template
23
+ #label
24
+ >
25
+ <FSRow
26
+ :wrap="false"
27
+ >
28
+ <FSSpan
29
+ class="fs-translate-field-label"
30
+ font="text-overline"
31
+ >
32
+ {{ $tr('ui.translateRichTextField.translateIn', 'Translate in {0}', language.label) }}
33
+ </FSSpan>
34
+ <FSIcon>{{ language.icon }}</FSIcon>
35
+ </FSRow>
36
+ </template>
37
+ </FSRichTextField>
38
+ </FSCol>
39
+ <FSRow
40
+ :wrap="false"
41
+ >
42
+ <FSButton
43
+ prependIcon="mdi-cancel"
44
+ :label="$tr('ui.translateRichTextField.cancelButton.label', 'Cancel')"
45
+ :fullWidth="true"
46
+ @click="onCancelTranslations"
47
+ />
48
+ <FSButton
49
+ v-if="$props.editable"
50
+ prependIcon="mdi-check"
51
+ color="primary"
52
+ :label="$tr('ui.translateRichTextField.validateButton.label', 'Validate translations')"
53
+ :fullWidth="true"
54
+ @click="onSubmitTranslations"
55
+ />
56
+ </FSRow>
57
+ </FSCol>
58
+ <FSRichTextField
59
+ v-else
60
+ :editable="$props.editable"
61
+ :modelValue="$props.modelValue"
62
+ @update:modelValue="$emit('update:modelValue', $event)"
63
+ v-bind="$attrs"
64
+ >
65
+ <template
66
+ #append-inner
67
+ >
68
+ <FSButton
69
+ prependIcon="mdi-translate"
70
+ color="primary"
71
+ :label="$tr('ui.translateRichTextField.translateButton.label', 'Manage translations')"
72
+ :fullWidth="true"
73
+ @click="() => $emit('update:translationsExpanded', true)"
74
+ />
75
+ </template>
76
+ </FSRichTextField>
77
+ </template>
78
+
79
+ <script lang="ts">
80
+ import { defineComponent, type PropType, ref } from 'vue';
81
+
82
+ import { useAppLanguages } from "@dative-gpi/foundation-shared-services/composables";
83
+
84
+ import { emptyLexicalState } from '../../utils';
85
+
86
+ import FSRichTextField from './FSRichTextField.vue';
87
+ import FSButton from '../FSButton.vue';
88
+ import FSIcon from '../FSIcon.vue';
89
+ import FSSpan from '../FSSpan.vue';
90
+ import FSCol from '../FSCol.vue';
91
+ import FSRow from '../FSRow.vue';
92
+
93
+ export default defineComponent({
94
+ name: 'FSTranslateRichTextField',
95
+ components: {
96
+ FSRichTextField,
97
+ FSButton,
98
+ FSIcon,
99
+ FSSpan,
100
+ FSCol,
101
+ FSRow
102
+ },
103
+ props: {
104
+ translationsExpanded: {
105
+ type: Boolean,
106
+ default: false,
107
+ },
108
+ editable: {
109
+ type: Boolean,
110
+ default: true,
111
+ },
112
+ modelValue: {
113
+ type: String as PropType<string | null>,
114
+ required: false,
115
+ default: null
116
+ },
117
+ translations: {
118
+ type: Array as PropType<{ languageCode: string; [key: string]: string }[]>,
119
+ required: false,
120
+ default: () => []
121
+ },
122
+ property: {
123
+ type: String as PropType<string>,
124
+ required: false,
125
+ default: "label"
126
+ }
127
+ },
128
+ emits: ['update:translationsExpanded', 'update:modelValue', 'update:translations'],
129
+ setup(props, { emit }) {
130
+ const { languages } = useAppLanguages();
131
+
132
+ const innerTranslations = ref(props.translations);
133
+
134
+ const getTranslation = (languageCode: string): string => {
135
+ if (!innerTranslations.value) {
136
+ return emptyLexicalState;
137
+ }
138
+ const translation = innerTranslations.value.find((t) => t.languageCode === languageCode);
139
+ if (!translation || !translation[props.property]) {
140
+ return emptyLexicalState;
141
+ }
142
+ return translation[props.property].toString();
143
+ };
144
+
145
+ const setTranslation = (languageCode: string, value: string): void => {
146
+ if (!innerTranslations.value) {
147
+ innerTranslations.value = [{
148
+ languageCode,
149
+ [props.property]: value
150
+ }]
151
+ return;
152
+ }
153
+ const translation = innerTranslations.value.find((t) => t.languageCode === languageCode);
154
+ if (translation) {
155
+ translation[props.property] = value;
156
+ }
157
+ else {
158
+ innerTranslations.value.push({
159
+ languageCode,
160
+ [props.property]: value
161
+ });
162
+ }
163
+ };
164
+
165
+ const onSubmitTranslations = (): void => {
166
+ if (props.editable) {
167
+ emit("update:translations", innerTranslations.value);
168
+ }
169
+ emit('update:translationsExpanded', false);
170
+ };
171
+
172
+ const onCancelTranslations = (): void => {
173
+ emit('update:translationsExpanded', false);
174
+ };
175
+
176
+ return {
177
+ languages,
178
+ onCancelTranslations,
179
+ onSubmitTranslations,
180
+ getTranslation,
181
+ setTranslation
182
+ };
183
+ }
184
+ });
185
+ </script>
@@ -5,30 +5,34 @@
5
5
  >
6
6
  <FSCol
7
7
  v-if="L"
8
- class="fs-map"
9
8
  width="fill"
9
+ :class="['fs-map', { 'fs-map-fullscreen': fullScreen }]"
10
10
  >
11
- <FSRow
12
- v-if="selectableLayers.length > 1"
13
- class="fs-map-overlay-layer-choice"
11
+ <FSCol
12
+ v-if="$slots.leftoverlay"
13
+ class="fs-map-overlay-left"
14
+ height="calc(100% - 32px)"
15
+ width="hug"
14
16
  gap="2px"
15
17
  >
16
- <FSChip
17
- v-for="mapLayer in mapLayers.filter((layer) => selectableLayers.includes(layer.name))"
18
- variant="full"
19
- :color="innerSelectedLayer === mapLayer.name ? 'dark' : 'light'"
20
- :label="mapLayer.label"
21
- :key="mapLayer.name"
22
- :editable="true"
23
- @click="setMapBaseLayer(mapLayer.name as 'osm' | 'imagery')"
24
- />
25
- </FSRow>
18
+ <FSCard
19
+ height="100%"
20
+ >
21
+ <FSFadeOut
22
+ height="fill"
23
+ >
24
+ <slot
25
+ name="leftoverlay"
26
+ />
27
+ </FSFadeOut>
28
+ </FSCard>
29
+ </FSCol>
26
30
  <FSRow
27
31
  v-if="$props.editable && !editingLocation && $props.selectedLocationId !== null"
28
32
  class="fs-map-overlay-edit-button"
29
33
  >
30
34
  <FSButton
31
- prepend-icon="mdi-pencil"
35
+ prependIcon="mdi-pencil"
32
36
  :label="$tr('ui.map.modify', 'Modify')"
33
37
  @click="editingLocation = true"
34
38
  />
@@ -41,45 +45,73 @@
41
45
  :id="mapId"
42
46
  />
43
47
  </FSCol>
44
-
45
48
  <FSCol
46
- class="fs-map-overlay-container"
49
+ class="fs-map-overlay-right-top"
47
50
  align="center-center"
48
51
  >
49
- <FSCol
50
- class="fs-map-zoom-overlay"
51
- align="bottom-center"
52
- width="hug"
52
+ <slot
53
+ name="toprightoverlay"
53
54
  >
54
- <FSButton
55
- v-if="$props.showMyLocation"
56
- prependIcon="mdi-crosshairs-gps"
57
- color="primary"
58
- variant="full"
59
- :elevation="true"
60
- :border="false"
61
- @click="locate"
62
- />
63
- <FSCol
64
- v-if="$props.showZoomButtons"
65
- gap="0"
55
+ <FSRow
56
+ gap="2px"
66
57
  >
58
+ <FSMapLayerButton
59
+ v-if="$props.selectableLayers?.length && $props.selectableLayers.length > 1"
60
+ :layers="mapLayers.filter((layer) => $props.selectableLayers?.includes(layer.name) ?? true)"
61
+ v-model="innerSelectedLayer"
62
+ />
67
63
  <FSButton
68
- class="fs-map-zoom-plus"
69
- prependIcon="mdi-plus"
64
+ v-if="$props.showFullScreen"
65
+ prependIcon="mdi-fullscreen"
70
66
  :elevation="true"
71
- :border="false"
72
- @click="zoomIn"
67
+ @click="fullScreen = !fullScreen"
73
68
  />
69
+ </FSRow>
70
+ </slot>
71
+ </FSCol>
72
+ <FSCol
73
+ class="fs-map-overlay-right-bottom"
74
+ align="center-center"
75
+ >
76
+ <slot
77
+ name="bottomrightoverlay"
78
+ >
79
+ <FSCol
80
+ class="fs-map-zoom-overlay"
81
+ align="bottom-center"
82
+ width="hug"
83
+ >
74
84
  <FSButton
75
- class="fs-map-zoom-minus"
76
- prependIcon="mdi-minus"
85
+ v-if="$props.showMyLocation"
86
+ prependIcon="mdi-crosshairs-gps"
87
+ color="primary"
88
+ variant="full"
77
89
  :elevation="true"
78
90
  :border="false"
79
- @click="zoomOut"
91
+ @click="locate"
80
92
  />
93
+ <FSCol
94
+ v-if="$props.showZoomButtons"
95
+ gap="0"
96
+ >
97
+
98
+ <FSButton
99
+ class="fs-map-zoom-plus"
100
+ prependIcon="mdi-plus"
101
+ :elevation="true"
102
+ :border="false"
103
+ @click="zoomIn"
104
+ />
105
+ <FSButton
106
+ class="fs-map-zoom-minus"
107
+ prependIcon="mdi-minus"
108
+ :elevation="true"
109
+ :border="false"
110
+ @click="zoomOut"
111
+ />
112
+ </FSCol>
81
113
  </FSCol>
82
- </FSCol>
114
+ </slot>
83
115
  <FSMapEditPointAddressOverlay
84
116
  v-if="editingLocation"
85
117
  :label="$tr('ui.map.address', 'Address')"
@@ -108,9 +140,10 @@ import { ColorEnum, type FSLocation, type MapLayer } from "../../models";
108
140
  import { useColors, useAddress } from "../../composables";
109
141
 
110
142
  import FSMapEditPointAddressOverlay from "./FSMapEditPointAddressOverlay.vue";
143
+ import FSMapLayerButton from "./FSMapLayerButton.vue";
144
+ import FSFadeOut from "../FSFadeOut.vue";
111
145
  import FSButton from "../FSButton.vue";
112
146
  import FSCard from "../FSCard.vue";
113
- import FSChip from "../FSChip.vue";
114
147
  import FSCol from "../FSCol.vue";
115
148
  import FSRow from "../FSRow.vue";
116
149
 
@@ -118,9 +151,10 @@ export default defineComponent({
118
151
  name: "FSMap",
119
152
  components: {
120
153
  FSMapEditPointAddressOverlay,
154
+ FSMapLayerButton,
155
+ FSFadeOut,
121
156
  FSButton,
122
157
  FSCard,
123
- FSChip,
124
158
  FSCol,
125
159
  FSRow
126
160
  },
@@ -185,6 +219,11 @@ export default defineComponent({
185
219
  required: false,
186
220
  default: true
187
221
  },
222
+ showFullScreen: {
223
+ type: Boolean,
224
+ required: false,
225
+ default: false
226
+ },
188
227
  grayscale: {
189
228
  type: Boolean,
190
229
  required: false,
@@ -202,6 +241,7 @@ export default defineComponent({
202
241
  const innerSelectedLayer = ref(props.selectedLayer);
203
242
  const innerModelValue = ref(props.modelValue);
204
243
  const editingLocation = ref(false);
244
+ const fullScreen = ref(false);
205
245
 
206
246
  const mapId = `map-${Math.random().toString(36).substring(7)}`;
207
247
  const defaultZoom = 15;
@@ -238,6 +278,7 @@ export default defineComponent({
238
278
  {
239
279
  name: "osm",
240
280
  label: $tr("ui.map.layer.osm", "Map"),
281
+ image: new URL("../../assets/images/map/osm.png", import.meta.url).href,
241
282
  layer: LL.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
242
283
  maxZoom: 20,
243
284
  attribution: '© OpenStreetMap'
@@ -246,6 +287,7 @@ export default defineComponent({
246
287
  {
247
288
  name: "imagery",
248
289
  label: $tr("ui.map.layer.imagery", "Imagery"),
290
+ image: new URL("../../assets/images/map/imagery.png", import.meta.url).href,
249
291
  layer: LL.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
250
292
  attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
251
293
  maxZoom: 19
@@ -323,7 +365,7 @@ export default defineComponent({
323
365
  baseLayerGroup.addTo(map);
324
366
  siteLayerGroup.addTo(map);
325
367
  myLocationLayerGroup.addTo(map);
326
- setMapBaseLayer(props.selectedLayer);
368
+ setMapBaseLayer(innerSelectedLayer.value);
327
369
  displaySites();
328
370
  displayLocations();
329
371
  markerLayerGroup.addTo(map);
@@ -340,18 +382,16 @@ export default defineComponent({
340
382
  };
341
383
 
342
384
  const setMapBaseLayer = (layerName: 'osm' | 'imagery') => {
343
- innerSelectedLayer.value = layerName;
344
385
  const layer = mapLayers.find((mapLayer) => mapLayer.name === layerName) ?? mapLayers[0];
345
386
  baseLayerGroup.clearLayers();
346
387
  layer.layer.addTo(baseLayerGroup);
347
388
  };
348
389
 
349
390
  const onNewAddressEntered = (address: Address) => {
350
- if (!props.selectedLocationId) {
351
- return;
352
- }
391
+ if (!map) {return;}
392
+ if (!props.selectedLocationId) {return;}
353
393
  modifyLocationAddress(props.selectedLocationId, address);
354
- map?.panTo([address.latitude, address.longitude]);
394
+ map.panTo([address.latitude, address.longitude]);
355
395
  };
356
396
 
357
397
  const onNewCoordEntered = async (lat: number, lng: number) => {
@@ -365,17 +405,26 @@ export default defineComponent({
365
405
  };
366
406
 
367
407
  const zoomIn = () => {
368
- map?.zoomIn();
408
+ if (!map) {
409
+ return;
410
+ }
411
+ map.zoomIn();
369
412
  };
370
413
 
371
414
  const zoomOut = () => {
372
- map?.zoomOut();
415
+ if (!map) {
416
+ return;
417
+ }
418
+ map.zoomOut();
373
419
  };
374
420
 
375
421
  const locate = () => {
376
- map?.locate();
377
- map?.on('locationfound', (e: L.LocationEvent) => {
378
- map?.panTo(e.latlng);
422
+ if (!map) {
423
+ return;
424
+ }
425
+ map.locate();
426
+ map.on('locationfound', (e: L.LocationEvent) => {
427
+ map.panTo(e.latlng);
379
428
  const iconHtml = `<div class="fs-map-mylocation-pin"></div>`;
380
429
  const icon = LL.divIcon({
381
430
  html: iconHtml,
@@ -391,12 +440,15 @@ export default defineComponent({
391
440
  const onCancel = () => {
392
441
  editingLocation.value = false;
393
442
  innerModelValue.value = props.modelValue;
443
+ if (!map) {
444
+ return;
445
+ }
394
446
  displayLocations();
395
447
  if (innerModelValue.value.length > 0) {
396
- map?.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
448
+ map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
397
449
  }
398
450
  else {
399
- map?.panTo([props.center[0], props.center[1]]);
451
+ map.panTo([props.center[0], props.center[1]]);
400
452
  }
401
453
  if (props.modelValue.length > 1) {
402
454
  emit('update:selectedLocationId', null);
@@ -405,12 +457,15 @@ export default defineComponent({
405
457
 
406
458
  const onSubmit = () => {
407
459
  emit('update:modelValue', innerModelValue.value);
460
+ if (!map) {
461
+ return;
462
+ }
408
463
  editingLocation.value = false;
409
464
  if (innerModelValue.value.length > 0) {
410
- map?.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
465
+ map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
411
466
  }
412
467
  else {
413
- map?.flyTo([props.center[0], props.center[1]], map?.getZoom() ?? defaultZoom, { animate: false });
468
+ map.flyTo([props.center[0], props.center[1]], map.getZoom() ?? defaultZoom, { animate: false });
414
469
  }
415
470
  if (props.modelValue.length > 1) {
416
471
  emit('update:selectedLocationId', null);
@@ -419,6 +474,9 @@ export default defineComponent({
419
474
 
420
475
  onMounted(() => {
421
476
  initMap();
477
+ if (props.selectedLocationId && props.modelValue.length === 1) {
478
+ editingLocation.value = true;
479
+ }
422
480
  });
423
481
 
424
482
  watch(() => innerModelValue.value, () => {
@@ -426,36 +484,42 @@ export default defineComponent({
426
484
  });
427
485
 
428
486
  watch(() => props.selectedLocationId, () => {
487
+ if (!props.selectedLocationId || !map) {
488
+ return;
489
+ }
490
+
429
491
  Object.values(markers).forEach((marker) => {
430
492
  marker.getElement()?.classList.remove('fs-map-location-selected');
431
493
  });
432
494
 
433
- if (!props.selectedLocationId) {
434
- return;
435
- }
436
495
  const marker = markers[props.selectedLocationId];
437
496
  marker.getElement()?.classList.add('fs-map-location-selected');
438
- map?.flyTo(marker.getLatLng(), 17, { animate: false });
497
+ map.flyTo(marker.getLatLng(), 17, { animate: false });
439
498
  })
440
499
 
441
500
  watch(() => props.selectedSiteId, () => {
442
- if (!props.selectedSiteId) {
501
+ if (!props.selectedSiteId || !map) {
443
502
  return;
444
503
  }
445
504
  const site = sites[props.selectedSiteId];
446
505
  if (site) {
447
- map?.fitBounds(site.getBounds(), { maxZoom: 17 });
506
+ map.fitBounds(site.getBounds(), { maxZoom: 17 });
448
507
  }
449
508
  });
450
509
 
510
+ watch(innerSelectedLayer, () => {
511
+ setMapBaseLayer(innerSelectedLayer.value);
512
+ });
513
+
451
514
  return {
452
- L,
453
515
  innerSelectedLayer,
454
516
  editingLocation,
455
517
  innerModelValue,
518
+ fullScreen,
456
519
  mapLayers,
457
520
  mapId,
458
521
  style,
522
+ L,
459
523
  onNewAddressEntered,
460
524
  onNewCoordEntered,
461
525
  setMapBaseLayer,