@dative-gpi/foundation-shared-components 0.0.206 → 0.0.208
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 +188 -117
- package/components/map/FSMapEditPointAddressOverlay.vue +19 -18
- package/components/map/FSMapLayerButton.vue +71 -0
- package/components/tiles/FSDeviceOrganisationTileUI.vue +8 -10
- package/components/tiles/FSGroupTileUI.vue +9 -12
- package/components/tiles/FSSimpleTileUI.vue +9 -11
- package/components/tiles/FSTile.vue +0 -7
- package/models/map.ts +2 -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 +28 -50
- package/styles/components/fs_rich_text_field.scss +16 -4
- package/styles/components/index.scss +1 -0
- package/utils/lexical.ts +2 -0
package/components/map/FSMap.vue
CHANGED
|
@@ -5,30 +5,36 @@
|
|
|
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
|
+
width="hug"
|
|
14
15
|
gap="2px"
|
|
15
16
|
>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
<FSCard
|
|
18
|
+
padding="4px"
|
|
19
|
+
:elevation="true"
|
|
20
|
+
:border="false"
|
|
21
|
+
>
|
|
22
|
+
<FSFadeOut
|
|
23
|
+
maskHeight="0"
|
|
24
|
+
:height="`calc(${$props.height} - 40px)`"
|
|
25
|
+
>
|
|
26
|
+
<slot
|
|
27
|
+
name="leftoverlay"
|
|
28
|
+
/>
|
|
29
|
+
</FSFadeOut>
|
|
30
|
+
</FSCard>
|
|
31
|
+
</FSCol>
|
|
26
32
|
<FSRow
|
|
27
33
|
v-if="$props.editable && !editingLocation && $props.selectedLocationId !== null"
|
|
28
34
|
class="fs-map-overlay-edit-button"
|
|
29
35
|
>
|
|
30
36
|
<FSButton
|
|
31
|
-
|
|
37
|
+
prependIcon="mdi-pencil"
|
|
32
38
|
:label="$tr('ui.map.modify', 'Modify')"
|
|
33
39
|
@click="editingLocation = true"
|
|
34
40
|
/>
|
|
@@ -41,45 +47,73 @@
|
|
|
41
47
|
:id="mapId"
|
|
42
48
|
/>
|
|
43
49
|
</FSCol>
|
|
44
|
-
|
|
45
50
|
<FSCol
|
|
46
|
-
class="fs-map-overlay-
|
|
51
|
+
class="fs-map-overlay-right-top"
|
|
47
52
|
align="center-center"
|
|
48
53
|
>
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
align="bottom-center"
|
|
52
|
-
width="hug"
|
|
54
|
+
<slot
|
|
55
|
+
name="toprightoverlay"
|
|
53
56
|
>
|
|
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"
|
|
57
|
+
<FSRow
|
|
58
|
+
gap="2px"
|
|
66
59
|
>
|
|
60
|
+
<FSMapLayerButton
|
|
61
|
+
v-if="$props.selectableLayers?.length && $props.selectableLayers.length > 1"
|
|
62
|
+
:layers="mapLayers.filter((layer) => $props.selectableLayers?.includes(layer.name) ?? true)"
|
|
63
|
+
v-model="innerSelectedLayer"
|
|
64
|
+
/>
|
|
67
65
|
<FSButton
|
|
68
|
-
|
|
69
|
-
prependIcon="mdi-
|
|
66
|
+
v-if="$props.showFullScreen"
|
|
67
|
+
prependIcon="mdi-fullscreen"
|
|
70
68
|
:elevation="true"
|
|
71
|
-
|
|
72
|
-
@click="zoomIn"
|
|
69
|
+
@click="fullScreen = !fullScreen"
|
|
73
70
|
/>
|
|
71
|
+
</FSRow>
|
|
72
|
+
</slot>
|
|
73
|
+
</FSCol>
|
|
74
|
+
<FSCol
|
|
75
|
+
class="fs-map-overlay-right-bottom"
|
|
76
|
+
align="center-center"
|
|
77
|
+
>
|
|
78
|
+
<slot
|
|
79
|
+
name="bottomrightoverlay"
|
|
80
|
+
>
|
|
81
|
+
<FSCol
|
|
82
|
+
class="fs-map-zoom-overlay"
|
|
83
|
+
align="bottom-center"
|
|
84
|
+
width="hug"
|
|
85
|
+
>
|
|
74
86
|
<FSButton
|
|
75
|
-
|
|
76
|
-
prependIcon="mdi-
|
|
87
|
+
v-if="$props.showMyLocation"
|
|
88
|
+
prependIcon="mdi-crosshairs-gps"
|
|
89
|
+
color="primary"
|
|
90
|
+
variant="full"
|
|
77
91
|
:elevation="true"
|
|
78
92
|
:border="false"
|
|
79
|
-
@click="
|
|
93
|
+
@click="locate"
|
|
80
94
|
/>
|
|
95
|
+
<FSCol
|
|
96
|
+
v-if="$props.showZoomButtons"
|
|
97
|
+
gap="0"
|
|
98
|
+
>
|
|
99
|
+
|
|
100
|
+
<FSButton
|
|
101
|
+
class="fs-map-zoom-plus"
|
|
102
|
+
prependIcon="mdi-plus"
|
|
103
|
+
:elevation="true"
|
|
104
|
+
:border="false"
|
|
105
|
+
@click="zoomIn"
|
|
106
|
+
/>
|
|
107
|
+
<FSButton
|
|
108
|
+
class="fs-map-zoom-minus"
|
|
109
|
+
prependIcon="mdi-minus"
|
|
110
|
+
:elevation="true"
|
|
111
|
+
:border="false"
|
|
112
|
+
@click="zoomOut"
|
|
113
|
+
/>
|
|
114
|
+
</FSCol>
|
|
81
115
|
</FSCol>
|
|
82
|
-
</
|
|
116
|
+
</slot>
|
|
83
117
|
<FSMapEditPointAddressOverlay
|
|
84
118
|
v-if="editingLocation"
|
|
85
119
|
:label="$tr('ui.map.address', 'Address')"
|
|
@@ -102,15 +136,16 @@ import "leaflet.markercluster";
|
|
|
102
136
|
|
|
103
137
|
import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
|
|
104
138
|
|
|
105
|
-
import { type Address, type
|
|
139
|
+
import { type Address, type FSArea } from '@dative-gpi/foundation-shared-domain/models';
|
|
106
140
|
|
|
107
141
|
import { ColorEnum, type FSLocation, type MapLayer } from "../../models";
|
|
108
142
|
import { useColors, useAddress } from "../../composables";
|
|
109
143
|
|
|
110
144
|
import FSMapEditPointAddressOverlay from "./FSMapEditPointAddressOverlay.vue";
|
|
145
|
+
import FSMapLayerButton from "./FSMapLayerButton.vue";
|
|
146
|
+
import FSFadeOut from "../FSFadeOut.vue";
|
|
111
147
|
import FSButton from "../FSButton.vue";
|
|
112
148
|
import FSCard from "../FSCard.vue";
|
|
113
|
-
import FSChip from "../FSChip.vue";
|
|
114
149
|
import FSCol from "../FSCol.vue";
|
|
115
150
|
import FSRow from "../FSRow.vue";
|
|
116
151
|
|
|
@@ -118,15 +153,16 @@ export default defineComponent({
|
|
|
118
153
|
name: "FSMap",
|
|
119
154
|
components: {
|
|
120
155
|
FSMapEditPointAddressOverlay,
|
|
156
|
+
FSMapLayerButton,
|
|
157
|
+
FSFadeOut,
|
|
121
158
|
FSButton,
|
|
122
159
|
FSCard,
|
|
123
|
-
FSChip,
|
|
124
160
|
FSCol,
|
|
125
161
|
FSRow
|
|
126
162
|
},
|
|
127
163
|
props: {
|
|
128
164
|
height: {
|
|
129
|
-
type: [
|
|
165
|
+
type: [String, Number] as PropType<string | number | null>,
|
|
130
166
|
required: false,
|
|
131
167
|
default: '400px'
|
|
132
168
|
},
|
|
@@ -135,16 +171,46 @@ export default defineComponent({
|
|
|
135
171
|
required: false,
|
|
136
172
|
default: '100%'
|
|
137
173
|
},
|
|
138
|
-
|
|
139
|
-
type:
|
|
174
|
+
grayscale: {
|
|
175
|
+
type: Boolean,
|
|
140
176
|
required: false,
|
|
141
|
-
default:
|
|
177
|
+
default: false
|
|
178
|
+
},
|
|
179
|
+
editable: {
|
|
180
|
+
type: Boolean,
|
|
181
|
+
required: false,
|
|
182
|
+
default: false
|
|
183
|
+
},
|
|
184
|
+
showMyLocation: {
|
|
185
|
+
type: Boolean,
|
|
186
|
+
required: false,
|
|
187
|
+
default: true
|
|
188
|
+
},
|
|
189
|
+
showZoomButtons: {
|
|
190
|
+
type: Boolean,
|
|
191
|
+
required: false,
|
|
192
|
+
default: true
|
|
193
|
+
},
|
|
194
|
+
showFullScreen: {
|
|
195
|
+
type: Boolean,
|
|
196
|
+
required: false,
|
|
197
|
+
default: false
|
|
142
198
|
},
|
|
143
199
|
center: {
|
|
144
200
|
type: Array as PropType<number[]>,
|
|
145
201
|
required: false,
|
|
146
202
|
default: () => [45.71, 5.07]
|
|
147
203
|
},
|
|
204
|
+
modelValue: {
|
|
205
|
+
type: Array as PropType<FSLocation[]>,
|
|
206
|
+
required: false,
|
|
207
|
+
default: () => [],
|
|
208
|
+
},
|
|
209
|
+
areas: {
|
|
210
|
+
type: Array as PropType<FSArea[]>,
|
|
211
|
+
required: false,
|
|
212
|
+
default: () => [],
|
|
213
|
+
},
|
|
148
214
|
selectedLayer: {
|
|
149
215
|
type: String as PropType<"osm" | "imagery">,
|
|
150
216
|
required: false,
|
|
@@ -160,38 +226,13 @@ export default defineComponent({
|
|
|
160
226
|
required: false,
|
|
161
227
|
default: null
|
|
162
228
|
},
|
|
163
|
-
|
|
229
|
+
selectedAreaId: {
|
|
164
230
|
type: String as PropType<string | null>,
|
|
165
231
|
required: false,
|
|
166
232
|
default: null
|
|
167
|
-
},
|
|
168
|
-
modelValue: {
|
|
169
|
-
type: Array as PropType<FSLocation[]>,
|
|
170
|
-
required: false,
|
|
171
|
-
default: () => [],
|
|
172
|
-
},
|
|
173
|
-
editable: {
|
|
174
|
-
type: Boolean,
|
|
175
|
-
required: false,
|
|
176
|
-
default: false
|
|
177
|
-
},
|
|
178
|
-
showMyLocation: {
|
|
179
|
-
type: Boolean,
|
|
180
|
-
required: false,
|
|
181
|
-
default: true
|
|
182
|
-
},
|
|
183
|
-
showZoomButtons: {
|
|
184
|
-
type: Boolean,
|
|
185
|
-
required: false,
|
|
186
|
-
default: true
|
|
187
|
-
},
|
|
188
|
-
grayscale: {
|
|
189
|
-
type: Boolean,
|
|
190
|
-
required: false,
|
|
191
|
-
default: false
|
|
192
233
|
}
|
|
193
234
|
},
|
|
194
|
-
emits: ["update:modelValue", "update:selectedLocationId", "update:
|
|
235
|
+
emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId"],
|
|
195
236
|
setup(props, { emit }) {
|
|
196
237
|
const { reverseSearch } = useAddress();
|
|
197
238
|
const { getColors } = useColors();
|
|
@@ -202,12 +243,13 @@ export default defineComponent({
|
|
|
202
243
|
const innerSelectedLayer = ref(props.selectedLayer);
|
|
203
244
|
const innerModelValue = ref(props.modelValue);
|
|
204
245
|
const editingLocation = ref(false);
|
|
246
|
+
const fullScreen = ref(false);
|
|
205
247
|
|
|
206
248
|
const mapId = `map-${Math.random().toString(36).substring(7)}`;
|
|
207
249
|
const defaultZoom = 15;
|
|
208
250
|
const markers: { [key: string]: L.Marker } = {};
|
|
209
|
-
const
|
|
210
|
-
const
|
|
251
|
+
const areas: { [key: string]: L.Polygon } = {};
|
|
252
|
+
const areaLayerGroup = new LL.FeatureGroup();
|
|
211
253
|
const baseLayerGroup = new LL.LayerGroup();
|
|
212
254
|
const myLocationLayerGroup = new LL.LayerGroup();
|
|
213
255
|
|
|
@@ -238,6 +280,7 @@ export default defineComponent({
|
|
|
238
280
|
{
|
|
239
281
|
name: "osm",
|
|
240
282
|
label: $tr("ui.map.layer.osm", "Map"),
|
|
283
|
+
image: new URL("../../assets/images/map/osm.png", import.meta.url).href,
|
|
241
284
|
layer: LL.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
242
285
|
maxZoom: 20,
|
|
243
286
|
attribution: '© OpenStreetMap'
|
|
@@ -246,10 +289,11 @@ export default defineComponent({
|
|
|
246
289
|
{
|
|
247
290
|
name: "imagery",
|
|
248
291
|
label: $tr("ui.map.layer.imagery", "Imagery"),
|
|
292
|
+
image: new URL("../../assets/images/map/imagery.png", import.meta.url).href,
|
|
249
293
|
layer: LL.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
|
250
294
|
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
|
|
251
295
|
maxZoom: 19
|
|
252
|
-
})
|
|
296
|
+
})
|
|
253
297
|
}
|
|
254
298
|
];
|
|
255
299
|
|
|
@@ -265,34 +309,38 @@ export default defineComponent({
|
|
|
265
309
|
const displayLocations = () => {
|
|
266
310
|
markerLayerGroup.clearLayers();
|
|
267
311
|
innerModelValue.value.forEach((location) => {
|
|
268
|
-
const iconHtml =
|
|
312
|
+
const iconHtml = `
|
|
313
|
+
<div style="--fs-map-mylocation-pin-color-alpha:${getColors(location.color).base}50;--fs-map-location-pin-color: ${getColors(location.color).base}">
|
|
314
|
+
<div class="fs-map-location-pin">
|
|
315
|
+
<i class="${location.icon} mdi v-icon notranslate v-theme--DefaultTheme fs-icon" aria-hidden="true" style="--fs-icon-font-size: 22px;" ></i>
|
|
316
|
+
</div>
|
|
317
|
+
</div>`;
|
|
269
318
|
const icon = LL.divIcon({
|
|
270
319
|
html: iconHtml,
|
|
271
|
-
className: 'fs-map-location',
|
|
272
320
|
iconSize: [36, 36],
|
|
321
|
+
className: 'fs-map-location',
|
|
273
322
|
iconAnchor: [18, 18],
|
|
274
323
|
});
|
|
275
324
|
const marker = LL.marker([location.address.latitude, location.address.longitude], { icon }).addTo(markerLayerGroup);
|
|
276
325
|
markers[location.id] = marker;
|
|
277
326
|
marker.on('click', () => emit('update:selectedLocationId', location.id));
|
|
278
|
-
|
|
279
327
|
});
|
|
280
328
|
};
|
|
281
329
|
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
props.
|
|
285
|
-
const
|
|
286
|
-
color:
|
|
287
|
-
fillColor:
|
|
330
|
+
const displayAreas = () => {
|
|
331
|
+
areaLayerGroup.clearLayers();
|
|
332
|
+
props.areas.forEach((area) => {
|
|
333
|
+
const areaPolygon = LL.polygon(area.coordinates.map((coord) => [coord.latitude, coord.longitude]), {
|
|
334
|
+
color: area.color,
|
|
335
|
+
fillColor: area.color + "50",
|
|
288
336
|
fillOpacity: 0.5,
|
|
289
|
-
className: 'fs-map-
|
|
290
|
-
}).addTo(
|
|
337
|
+
className: 'fs-map-area',
|
|
338
|
+
}).addTo(areaLayerGroup);
|
|
291
339
|
|
|
292
|
-
|
|
293
|
-
|
|
340
|
+
areas[area.id] = areaPolygon;
|
|
341
|
+
areaPolygon.on('click', () => emit('update:selectedAreaId', area.id));
|
|
294
342
|
});
|
|
295
|
-
}
|
|
343
|
+
};
|
|
296
344
|
|
|
297
345
|
const modifyLocationAddress = (locationId: string, newAddress: Address) => {
|
|
298
346
|
const location = innerModelValue.value.find((loc) => loc.id === locationId);
|
|
@@ -321,10 +369,10 @@ export default defineComponent({
|
|
|
321
369
|
LL.control.attribution({ position: 'bottomleft' }).addTo(map);
|
|
322
370
|
|
|
323
371
|
baseLayerGroup.addTo(map);
|
|
324
|
-
|
|
372
|
+
areaLayerGroup.addTo(map);
|
|
325
373
|
myLocationLayerGroup.addTo(map);
|
|
326
|
-
setMapBaseLayer(
|
|
327
|
-
|
|
374
|
+
setMapBaseLayer(innerSelectedLayer.value);
|
|
375
|
+
displayAreas();
|
|
328
376
|
displayLocations();
|
|
329
377
|
markerLayerGroup.addTo(map);
|
|
330
378
|
|
|
@@ -340,18 +388,17 @@ export default defineComponent({
|
|
|
340
388
|
};
|
|
341
389
|
|
|
342
390
|
const setMapBaseLayer = (layerName: 'osm' | 'imagery') => {
|
|
343
|
-
innerSelectedLayer.value = layerName;
|
|
344
391
|
const layer = mapLayers.find((mapLayer) => mapLayer.name === layerName) ?? mapLayers[0];
|
|
345
392
|
baseLayerGroup.clearLayers();
|
|
346
393
|
layer.layer.addTo(baseLayerGroup);
|
|
347
394
|
};
|
|
348
395
|
|
|
349
396
|
const onNewAddressEntered = (address: Address) => {
|
|
350
|
-
if (!props.selectedLocationId) {
|
|
397
|
+
if (!props.selectedLocationId || !map) {
|
|
351
398
|
return;
|
|
352
399
|
}
|
|
353
400
|
modifyLocationAddress(props.selectedLocationId, address);
|
|
354
|
-
map
|
|
401
|
+
map.panTo([address.latitude, address.longitude]);
|
|
355
402
|
};
|
|
356
403
|
|
|
357
404
|
const onNewCoordEntered = async (lat: number, lng: number) => {
|
|
@@ -365,17 +412,26 @@ export default defineComponent({
|
|
|
365
412
|
};
|
|
366
413
|
|
|
367
414
|
const zoomIn = () => {
|
|
368
|
-
map
|
|
415
|
+
if (!map) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
map.zoomIn();
|
|
369
419
|
};
|
|
370
420
|
|
|
371
421
|
const zoomOut = () => {
|
|
372
|
-
map
|
|
422
|
+
if (!map) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
map.zoomOut();
|
|
373
426
|
};
|
|
374
427
|
|
|
375
428
|
const locate = () => {
|
|
376
|
-
map
|
|
377
|
-
|
|
378
|
-
|
|
429
|
+
if (!map) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
map.locate();
|
|
433
|
+
map.on('locationfound', (e: L.LocationEvent) => {
|
|
434
|
+
map.panTo(e.latlng);
|
|
379
435
|
const iconHtml = `<div class="fs-map-mylocation-pin"></div>`;
|
|
380
436
|
const icon = LL.divIcon({
|
|
381
437
|
html: iconHtml,
|
|
@@ -391,12 +447,15 @@ export default defineComponent({
|
|
|
391
447
|
const onCancel = () => {
|
|
392
448
|
editingLocation.value = false;
|
|
393
449
|
innerModelValue.value = props.modelValue;
|
|
450
|
+
if (!map) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
394
453
|
displayLocations();
|
|
395
454
|
if (innerModelValue.value.length > 0) {
|
|
396
|
-
map
|
|
455
|
+
map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
|
|
397
456
|
}
|
|
398
457
|
else {
|
|
399
|
-
map
|
|
458
|
+
map.panTo([props.center[0], props.center[1]]);
|
|
400
459
|
}
|
|
401
460
|
if (props.modelValue.length > 1) {
|
|
402
461
|
emit('update:selectedLocationId', null);
|
|
@@ -405,12 +464,15 @@ export default defineComponent({
|
|
|
405
464
|
|
|
406
465
|
const onSubmit = () => {
|
|
407
466
|
emit('update:modelValue', innerModelValue.value);
|
|
467
|
+
if (!map) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
408
470
|
editingLocation.value = false;
|
|
409
471
|
if (innerModelValue.value.length > 0) {
|
|
410
|
-
map
|
|
472
|
+
map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
|
|
411
473
|
}
|
|
412
474
|
else {
|
|
413
|
-
map
|
|
475
|
+
map.flyTo([props.center[0], props.center[1]], map.getZoom() ?? defaultZoom, { animate: false });
|
|
414
476
|
}
|
|
415
477
|
if (props.modelValue.length > 1) {
|
|
416
478
|
emit('update:selectedLocationId', null);
|
|
@@ -419,6 +481,9 @@ export default defineComponent({
|
|
|
419
481
|
|
|
420
482
|
onMounted(() => {
|
|
421
483
|
initMap();
|
|
484
|
+
if (props.selectedLocationId && props.modelValue.length === 1) {
|
|
485
|
+
editingLocation.value = true;
|
|
486
|
+
}
|
|
422
487
|
});
|
|
423
488
|
|
|
424
489
|
watch(() => innerModelValue.value, () => {
|
|
@@ -426,36 +491,42 @@ export default defineComponent({
|
|
|
426
491
|
});
|
|
427
492
|
|
|
428
493
|
watch(() => props.selectedLocationId, () => {
|
|
494
|
+
if (!props.selectedLocationId || !map) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
429
498
|
Object.values(markers).forEach((marker) => {
|
|
430
499
|
marker.getElement()?.classList.remove('fs-map-location-selected');
|
|
431
500
|
});
|
|
432
501
|
|
|
433
|
-
if (!props.selectedLocationId) {
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
502
|
const marker = markers[props.selectedLocationId];
|
|
503
|
+
map.flyTo(marker.getLatLng(), 17, { animate: false });
|
|
437
504
|
marker.getElement()?.classList.add('fs-map-location-selected');
|
|
438
|
-
map?.flyTo(marker.getLatLng(), 17, { animate: false });
|
|
439
505
|
})
|
|
440
506
|
|
|
441
|
-
watch(() => props.
|
|
442
|
-
if (!props.
|
|
507
|
+
watch(() => props.selectedAreaId, () => {
|
|
508
|
+
if (!props.selectedAreaId || !map) {
|
|
443
509
|
return;
|
|
444
510
|
}
|
|
445
|
-
const
|
|
446
|
-
if (
|
|
447
|
-
map
|
|
511
|
+
const area = areas[props.selectedAreaId];
|
|
512
|
+
if (area) {
|
|
513
|
+
map.fitBounds(area.getBounds(), { maxZoom: 17 });
|
|
448
514
|
}
|
|
449
515
|
});
|
|
450
516
|
|
|
517
|
+
watch(innerSelectedLayer, () => {
|
|
518
|
+
setMapBaseLayer(innerSelectedLayer.value);
|
|
519
|
+
});
|
|
520
|
+
|
|
451
521
|
return {
|
|
452
|
-
L,
|
|
453
522
|
innerSelectedLayer,
|
|
454
523
|
editingLocation,
|
|
455
524
|
innerModelValue,
|
|
525
|
+
fullScreen,
|
|
456
526
|
mapLayers,
|
|
457
527
|
mapId,
|
|
458
528
|
style,
|
|
529
|
+
L,
|
|
459
530
|
onNewAddressEntered,
|
|
460
531
|
onNewCoordEntered,
|
|
461
532
|
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
|
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FSButton
|
|
3
|
+
prependIcon="mdi-layers-outline"
|
|
4
|
+
:elevation="true"
|
|
5
|
+
@click="dialog = true"
|
|
6
|
+
/>
|
|
7
|
+
<FSDialog
|
|
8
|
+
v-model="dialog"
|
|
9
|
+
title="Select Layers"
|
|
10
|
+
width="460px"
|
|
11
|
+
>
|
|
12
|
+
<template
|
|
13
|
+
v-slot:body
|
|
14
|
+
>
|
|
15
|
+
<FSImageCard
|
|
16
|
+
v-for="layer in layers"
|
|
17
|
+
:variant="modelValue === layer.name ? 'full' : 'background'"
|
|
18
|
+
:color="modelValue === layer.name ? 'primary' : 'light'"
|
|
19
|
+
:label="layer.label"
|
|
20
|
+
:src="layer.image"
|
|
21
|
+
:key="layer.name"
|
|
22
|
+
@click="onLayerClick(layer.name)"
|
|
23
|
+
/>
|
|
24
|
+
</template>
|
|
25
|
+
</FSDialog>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script lang="ts">
|
|
29
|
+
import { defineComponent, type PropType, ref } from "vue";
|
|
30
|
+
|
|
31
|
+
import { type MapLayer } from "../../models";
|
|
32
|
+
|
|
33
|
+
import FSImageCard from "../FSImageCard.vue";
|
|
34
|
+
import FSButton from "../FSButton.vue";
|
|
35
|
+
import FSDialog from "../FSDialog.vue";
|
|
36
|
+
|
|
37
|
+
export default defineComponent({
|
|
38
|
+
name: "FSMapLayerButton",
|
|
39
|
+
components: {
|
|
40
|
+
FSImageCard,
|
|
41
|
+
FSButton,
|
|
42
|
+
FSDialog
|
|
43
|
+
},
|
|
44
|
+
props: {
|
|
45
|
+
layers: {
|
|
46
|
+
type: Array as PropType<MapLayer[]>,
|
|
47
|
+
required: false,
|
|
48
|
+
default: () => []
|
|
49
|
+
},
|
|
50
|
+
modelValue: {
|
|
51
|
+
type: String,
|
|
52
|
+
required: false,
|
|
53
|
+
default: ""
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
emits: ["update:modelValue"],
|
|
57
|
+
setup(_, { emit }) {
|
|
58
|
+
const dialog = ref(false);
|
|
59
|
+
|
|
60
|
+
const onLayerClick = (layer: string) => {
|
|
61
|
+
emit("update:modelValue", layer);
|
|
62
|
+
dialog.value = false;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
dialog,
|
|
67
|
+
onLayerClick
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
</script>
|