@dative-gpi/foundation-shared-components 0.0.206 → 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.
- package/assets/images/map/imagery.png +0 -0
- package/assets/images/map/osm.png +0 -0
- package/components/FSClock.vue +2 -6
- package/components/FSImageCard.vue +72 -0
- package/components/fields/FSDateTimeField.vue +2 -6
- package/components/fields/FSDateTimeRangeField.vue +2 -6
- package/components/fields/FSRichTextField.vue +132 -54
- package/components/fields/FSTermField.vue +2 -6
- package/components/fields/FSTimeField.vue +2 -6
- package/components/fields/FSTranslateRichTextField.vue +185 -0
- package/components/map/FSMap.vue +129 -65
- package/components/map/FSMapEditPointAddressOverlay.vue +19 -18
- package/components/map/FSMapLayerButton.vue +71 -0
- package/models/map.ts +1 -0
- package/models/richTextVariable.ts +5 -0
- package/models/variableNode.ts +105 -0
- package/package.json +4 -4
- package/styles/components/fs_image_card.scss +18 -0
- package/styles/components/fs_map.scss +10 -14
- package/styles/components/fs_rich_text_field.scss +16 -4
- package/styles/components/index.scss +1 -0
- package/utils/lexical.ts +2 -0
|
@@ -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>
|
package/components/map/FSMap.vue
CHANGED
|
@@ -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
|
-
<
|
|
12
|
-
v-if="
|
|
13
|
-
class="fs-map-overlay-
|
|
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
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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-
|
|
49
|
+
class="fs-map-overlay-right-top"
|
|
47
50
|
align="center-center"
|
|
48
51
|
>
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
align="bottom-center"
|
|
52
|
-
width="hug"
|
|
52
|
+
<slot
|
|
53
|
+
name="toprightoverlay"
|
|
53
54
|
>
|
|
54
|
-
<
|
|
55
|
-
|
|
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
|
-
|
|
69
|
-
prependIcon="mdi-
|
|
64
|
+
v-if="$props.showFullScreen"
|
|
65
|
+
prependIcon="mdi-fullscreen"
|
|
70
66
|
:elevation="true"
|
|
71
|
-
|
|
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
|
-
|
|
76
|
-
prependIcon="mdi-
|
|
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="
|
|
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
|
-
</
|
|
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 © Esri — 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(
|
|
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 (!
|
|
351
|
-
|
|
352
|
-
}
|
|
391
|
+
if (!map) {return;}
|
|
392
|
+
if (!props.selectedLocationId) {return;}
|
|
353
393
|
modifyLocationAddress(props.selectedLocationId, address);
|
|
354
|
-
map
|
|
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
|
|
408
|
+
if (!map) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
map.zoomIn();
|
|
369
412
|
};
|
|
370
413
|
|
|
371
414
|
const zoomOut = () => {
|
|
372
|
-
map
|
|
415
|
+
if (!map) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
map.zoomOut();
|
|
373
419
|
};
|
|
374
420
|
|
|
375
421
|
const locate = () => {
|
|
376
|
-
map
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
448
|
+
map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
|
|
397
449
|
}
|
|
398
450
|
else {
|
|
399
|
-
map
|
|
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
|
|
465
|
+
map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
|
|
411
466
|
}
|
|
412
467
|
else {
|
|
413
|
-
map
|
|
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
|
|
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
|
|
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,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<FSCard
|
|
3
3
|
padding="16px"
|
|
4
|
-
width="100%"
|
|
5
4
|
height="100%"
|
|
5
|
+
width="100%"
|
|
6
6
|
:elevation="true"
|
|
7
7
|
>
|
|
8
8
|
<FSCol
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
/>
|
|
51
51
|
</FSRow>
|
|
52
52
|
<FSButton
|
|
53
|
-
|
|
53
|
+
prependIcon="mdi-content-save"
|
|
54
|
+
style="display: none;"
|
|
54
55
|
color="primary"
|
|
55
|
-
prepend-icon="mdi-content-save"
|
|
56
56
|
type="submit"
|
|
57
|
-
|
|
57
|
+
:label="$tr('ui.map.save', 'Save')"
|
|
58
58
|
/>
|
|
59
59
|
</FSForm>
|
|
60
60
|
</FSCol>
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
@click="onCancel"
|
|
67
67
|
/>
|
|
68
68
|
<FSButton
|
|
69
|
-
|
|
69
|
+
prependIcon="mdi-content-save"
|
|
70
70
|
color="primary"
|
|
71
|
-
|
|
71
|
+
:label="$tr('ui.map.save', 'Save')"
|
|
72
72
|
@click="onSubmit"
|
|
73
73
|
/>
|
|
74
74
|
</FSRow>
|
|
@@ -104,17 +104,16 @@ export default defineComponent({
|
|
|
104
104
|
},
|
|
105
105
|
props: {
|
|
106
106
|
modelValue: {
|
|
107
|
-
type: Object as PropType<Address>,
|
|
108
|
-
default: null
|
|
109
|
-
required: false,
|
|
107
|
+
type: Object as PropType<Address | null>,
|
|
108
|
+
default: null
|
|
110
109
|
}
|
|
111
110
|
},
|
|
112
111
|
emits: ["update:modelValue", "update:locationCoordinates", "submit", "cancel"],
|
|
113
112
|
setup(props, { emit }) {
|
|
114
113
|
const menuLocationCoordinates = ref(false);
|
|
115
114
|
|
|
116
|
-
const latitude = ref(
|
|
117
|
-
const longitude = ref(
|
|
115
|
+
const latitude = ref(0);
|
|
116
|
+
const longitude = ref(0);
|
|
118
117
|
|
|
119
118
|
const onCoordinatesChange = () => {
|
|
120
119
|
const newModelValue = new Address({
|
|
@@ -130,7 +129,7 @@ export default defineComponent({
|
|
|
130
129
|
};
|
|
131
130
|
|
|
132
131
|
const onAddressFieldSubmit = (address: Address|null) => {
|
|
133
|
-
if(address
|
|
132
|
+
if(!address) {
|
|
134
133
|
return;
|
|
135
134
|
}
|
|
136
135
|
emit('update:modelValue', address);
|
|
@@ -144,10 +143,12 @@ export default defineComponent({
|
|
|
144
143
|
emit('cancel');
|
|
145
144
|
};
|
|
146
145
|
|
|
147
|
-
watch(() => props.modelValue, (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
watch(() => props.modelValue, () => {
|
|
147
|
+
if (props.modelValue) {
|
|
148
|
+
latitude.value = props.modelValue.latitude;
|
|
149
|
+
longitude.value = props.modelValue.longitude;
|
|
150
|
+
}
|
|
151
|
+
}, { immediate: true });
|
|
151
152
|
|
|
152
153
|
return {
|
|
153
154
|
menuLocationCoordinates,
|
|
@@ -155,8 +156,8 @@ export default defineComponent({
|
|
|
155
156
|
latitude,
|
|
156
157
|
onAddressFieldSubmit,
|
|
157
158
|
onCoordinatesChange,
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
onCancel,
|
|
160
|
+
onSubmit
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
});
|