@dative-gpi/foundation-shared-components 1.0.26 → 1.0.28-remove-deprecated2

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 (265) hide show
  1. package/components/FSBreadcrumbs.vue +1 -0
  2. package/components/FSButton.vue +10 -10
  3. package/components/FSCalendar.vue +52 -14
  4. package/components/FSCalendarTwin.vue +96 -40
  5. package/components/FSCard.vue +28 -7
  6. package/components/FSCardPlaceholder.vue +8 -5
  7. package/components/FSChip.vue +12 -2
  8. package/components/FSClickable.vue +20 -12
  9. package/components/FSClock.vue +16 -7
  10. package/components/FSColorIcon.vue +23 -5
  11. package/components/FSDialog.vue +2 -1
  12. package/components/FSDialogContent.vue +12 -11
  13. package/components/FSDialogForm.vue +22 -2
  14. package/components/FSDialogFormBody.vue +51 -32
  15. package/components/FSDialogMultiFormBody.vue +79 -56
  16. package/components/FSDialogRemove.vue +7 -7
  17. package/components/FSDialogSubmit.vue +20 -11
  18. package/components/FSEditImage.vue +1 -1
  19. package/components/FSEditImageUI.vue +20 -10
  20. package/components/FSFadeOut.vue +44 -18
  21. package/components/FSForm.vue +10 -8
  22. package/components/FSGrid.vue +1 -1
  23. package/components/FSIcon.vue +2 -1
  24. package/components/FSIconCard.vue +21 -4
  25. package/components/FSImage.vue +12 -4
  26. package/components/FSImageUI.vue +8 -15
  27. package/components/FSLink.vue +25 -9
  28. package/components/FSLoader.vue +28 -11
  29. package/components/FSOptionGroup.vue +51 -3
  30. package/components/FSRouterLink.vue +42 -0
  31. package/components/FSSlideGroup.vue +19 -5
  32. package/components/FSSpan.vue +9 -2
  33. package/components/FSSwitch.vue +57 -27
  34. package/components/FSTab.vue +15 -13
  35. package/components/FSTabs.vue +32 -7
  36. package/components/FSTag.vue +14 -3
  37. package/components/FSTagGroup.vue +1 -1
  38. package/components/FSText.vue +4 -2
  39. package/components/FSWindow.vue +128 -4
  40. package/components/FSWrapGroup.vue +13 -1
  41. package/components/agenda/FSAgenda.vue +223 -0
  42. package/components/agenda/FSAgendaDialogCalendar.vue +76 -0
  43. package/components/agenda/FSAgendaHeader.vue +215 -0
  44. package/components/agenda/FSAgendaHorizontalEvent.vue +174 -0
  45. package/components/agenda/FSAgendaHorizontalTimeLineMarker.vue +46 -0
  46. package/components/agenda/FSAgendaHoursCol.vue +103 -0
  47. package/components/agenda/FSAgendaHoursRow.vue +164 -0
  48. package/components/agenda/FSAgendaVerticalEvent.vue +160 -0
  49. package/components/agenda/FSAgendaVerticalTimeLineMarker.vue +46 -0
  50. package/components/agenda/FSDayAgenda.vue +199 -0
  51. package/components/agenda/FSMonthAgenda.vue +252 -0
  52. package/components/agenda/FSSelectAgendaMode.vue +54 -0
  53. package/components/agenda/FSWeekAgenda.vue +323 -0
  54. package/components/autocompletes/FSAutoCompleteAddress.vue +22 -20
  55. package/components/autocompletes/FSAutocompleteLanguage.vue +24 -39
  56. package/components/autocompletes/FSAutocompleteTimeZone.vue +26 -38
  57. package/components/buttons/FSButtonAdd.vue +1 -1
  58. package/components/buttons/FSButtonAddLabel.vue +1 -1
  59. package/components/buttons/FSButtonCancel.vue +1 -1
  60. package/components/buttons/FSButtonCancelLabel.vue +1 -1
  61. package/components/buttons/FSButtonCopy.vue +28 -0
  62. package/components/buttons/FSButtonCopyIcon.vue +28 -0
  63. package/components/buttons/FSButtonCopyLabel.vue +27 -0
  64. package/components/buttons/FSButtonCopyMini.vue +28 -0
  65. package/components/buttons/FSButtonDragIcon.vue +27 -0
  66. package/components/buttons/FSButtonDuplicate.vue +1 -1
  67. package/components/buttons/FSButtonDuplicateLabel.vue +1 -1
  68. package/components/buttons/FSButtonEdit.vue +1 -1
  69. package/components/buttons/FSButtonEditLabel.vue +1 -1
  70. package/components/buttons/FSButtonFile.vue +1 -1
  71. package/components/buttons/FSButtonFileLabel.vue +1 -1
  72. package/components/buttons/FSButtonNext.vue +1 -1
  73. package/components/buttons/FSButtonNextLabel.vue +1 -1
  74. package/components/buttons/FSButtonPrevious.vue +1 -1
  75. package/components/buttons/FSButtonPreviousLabel.vue +1 -1
  76. package/components/buttons/FSButtonRedo.vue +1 -1
  77. package/components/buttons/FSButtonRedoLabel.vue +1 -1
  78. package/components/buttons/FSButtonRemove.vue +1 -1
  79. package/components/buttons/FSButtonRemoveLabel.vue +1 -1
  80. package/components/buttons/FSButtonSave.vue +1 -1
  81. package/components/buttons/FSButtonSaveLabel.vue +1 -1
  82. package/components/buttons/FSButtonSearch.vue +1 -1
  83. package/components/buttons/FSButtonSearchLabel.vue +1 -1
  84. package/components/buttons/FSButtonUndo.vue +1 -1
  85. package/components/buttons/FSButtonUndoLabel.vue +1 -1
  86. package/components/buttons/FSButtonUpdate.vue +1 -1
  87. package/components/buttons/FSButtonUpdateLabel.vue +1 -1
  88. package/components/buttons/FSButtonValidate.vue +1 -1
  89. package/components/buttons/FSButtonValidateLabel.vue +1 -1
  90. package/components/calendar/FSSimpleCalendar.vue +145 -0
  91. package/components/calendar/FSSimpleCalendarHeader.vue +60 -0
  92. package/components/calendar/FSSimpleMonthSelector.vue +138 -0
  93. package/components/deviceOrganisations/FSConnectivity.vue +11 -1
  94. package/components/deviceOrganisations/FSConnectivityCard.vue +19 -47
  95. package/components/deviceOrganisations/FSStatus.vue +11 -1
  96. package/components/deviceOrganisations/FSStatusCard.vue +35 -61
  97. package/components/deviceOrganisations/FSStatusesCarousel.vue +1 -0
  98. package/components/deviceOrganisations/FSStatusesRow.vue +9 -3
  99. package/components/deviceOrganisations/FSWorstAlert.vue +30 -37
  100. package/components/deviceOrganisations/FSWorstAlertCard.vue +36 -85
  101. package/components/fields/FSAutocompleteField.vue +516 -341
  102. package/components/fields/FSAutocompleteTag.vue +100 -0
  103. package/components/fields/FSBaseField.vue +44 -27
  104. package/components/fields/FSColorField.vue +42 -39
  105. package/components/fields/FSCommentField.vue +105 -0
  106. package/components/fields/FSDateField.vue +2 -2
  107. package/components/fields/FSDateRangeField.vue +3 -2
  108. package/components/fields/FSDateTimeField.vue +4 -3
  109. package/components/fields/FSDateTimeRangeField.vue +8 -6
  110. package/components/fields/FSEntityFieldUI.vue +271 -0
  111. package/components/fields/FSGradientField.vue +27 -33
  112. package/components/fields/FSIconField.vue +0 -1
  113. package/components/fields/FSMagicConfigField.vue +34 -19
  114. package/components/fields/FSMagicField.vue +9 -4
  115. package/components/fields/FSNumberField.vue +13 -6
  116. package/components/fields/FSRichTextField.vue +102 -52
  117. package/components/fields/FSSearchField.vue +9 -115
  118. package/components/fields/FSSelectField.vue +489 -252
  119. package/components/fields/FSTagField.vue +1 -1
  120. package/components/fields/FSTermField.vue +33 -11
  121. package/components/fields/FSTextArea.vue +26 -7
  122. package/components/fields/FSTextField.vue +19 -10
  123. package/components/fields/FSTimeRangeField.vue +304 -0
  124. package/components/fields/FSTimeStepField.vue +3 -3
  125. package/components/fields/FSTranslateField.vue +4 -3
  126. package/components/fields/FSTranslateRichTextField.vue +15 -10
  127. package/components/fields/FSTranslateTextArea.vue +233 -0
  128. package/components/fields/FSTreeViewField.vue +3 -3
  129. package/components/fields/periodicField/FSPeriodicDailyField.vue +120 -0
  130. package/components/fields/periodicField/FSPeriodicField.vue +131 -0
  131. package/components/fields/periodicField/FSPeriodicMonthlyField.vue +222 -0
  132. package/components/fields/periodicField/FSPeriodicWeeklyField.vue +120 -0
  133. package/components/fields/periodicField/FSPeriodicYearlyField.vue +144 -0
  134. package/components/lists/FSDataTableUI.vue +148 -89
  135. package/components/lists/FSDraggable.vue +2 -2
  136. package/components/lists/FSFilterButton.vue +23 -28
  137. package/components/lists/FSHeaderButton.vue +4 -4
  138. package/components/lists/FSHiddenButton.vue +11 -15
  139. package/components/lists/FSLoadDataTable.vue +10 -7
  140. package/components/lists/FSSimpleList.vue +234 -0
  141. package/components/lists/FSSimpleListItem.vue +132 -0
  142. package/components/map/FSMap.vue +280 -399
  143. package/components/map/FSMapFeatureGroup.vue +51 -0
  144. package/components/map/FSMapLayerButton.vue +5 -5
  145. package/components/map/FSMapMarker.vue +120 -0
  146. package/components/map/FSMapMarkerClusterGroup.vue +72 -0
  147. package/components/map/FSMapOverlay.vue +70 -82
  148. package/components/map/FSMapPolygon.vue +81 -0
  149. package/components/map/FSMapTileLayer.vue +50 -0
  150. package/components/map/keys.ts +4 -0
  151. package/components/selects/FSSelectAutoRefresh.vue +9 -9
  152. package/components/selects/FSSelectDashboardVariableType.vue +5 -4
  153. package/components/selects/FSSelectDateSetting.vue +3 -2
  154. package/components/selects/FSSelectDays.vue +9 -9
  155. package/components/selects/FSSelectListMode.vue +51 -0
  156. package/components/selects/FSSelectMonths.vue +67 -0
  157. package/components/selects/chartSelectors/FSAggregationSelector.vue +52 -0
  158. package/components/selects/chartSelectors/FSAxisTypeSelector.vue +49 -0
  159. package/components/selects/chartSelectors/FSDisplayAsSelector.vue +53 -0
  160. package/components/selects/chartSelectors/FSFilterTypeSelector.vue +54 -0
  161. package/components/selects/chartSelectors/FSHeatmapRuleSelector.vue +54 -0
  162. package/components/selects/chartSelectors/FSOperationOnSelector.vue +53 -0
  163. package/components/selects/chartSelectors/FSPlanningTypeSelector.vue +53 -0
  164. package/components/selects/chartSelectors/FSPlotPerSelector.vue +52 -0
  165. package/components/selects/chartSelectors/FSSelectEntityType.vue +59 -0
  166. package/components/selects/chartSelectors/FSSerieTypeSelector.vue +53 -0
  167. package/components/tiles/FSAlertTileUI.vue +90 -0
  168. package/components/tiles/FSChartTile.vue +73 -0
  169. package/components/tiles/FSChartTileUI.vue +111 -0
  170. package/components/tiles/FSCommentTileUI.vue +174 -0
  171. package/components/tiles/FSDeviceOrganisationTileUI.vue +6 -0
  172. package/components/tiles/FSGroupTileUI.vue +2 -2
  173. package/components/tiles/FSLoadTile.vue +2 -0
  174. package/components/tiles/FSLocationTileUI.vue +192 -0
  175. package/components/tiles/FSModelTileUI.vue +18 -0
  176. package/components/tiles/FSSimpleTileUI.vue +9 -4
  177. package/components/tiles/FSTile.vue +93 -74
  178. package/components/tiles/FSUserOrganisationTileUI.vue +1 -1
  179. package/components/toggleSets/FSToggleSetPosition.vue +2 -2
  180. package/components/views/FSBaseView.vue +64 -0
  181. package/components/views/FSEntityView.vue +12 -140
  182. package/components/views/FSSimpleView.vue +29 -0
  183. package/components/views/desktop/FSBaseDefaultDesktopView.vue +134 -0
  184. package/components/views/desktop/FSBaseDesktopView.vue +53 -0
  185. package/components/views/desktop/FSBaseEntityDesktopView.vue +208 -0
  186. package/components/views/mobile/FSBaseDefaultMobileView.vue +132 -0
  187. package/components/views/mobile/FSBaseEntityMobileView.vue +198 -0
  188. package/components/views/mobile/FSBaseMobileView.vue +53 -0
  189. package/composables/useAddress.ts +2 -2
  190. package/composables/useBreakpoints.ts +39 -3
  191. package/composables/useColors.ts +3 -2
  192. package/composables/useMagicFieldProvider.ts +1 -0
  193. package/models/agenda.ts +9 -0
  194. package/models/deviceAlerts.ts +1 -1
  195. package/models/deviceConnectivities.ts +1 -1
  196. package/models/index.ts +1 -0
  197. package/models/magicFields.ts +1 -0
  198. package/models/map.ts +2 -2
  199. package/models/rules.ts +5 -5
  200. package/models/tables.ts +5 -2
  201. package/models/variableNode.ts +8 -5
  202. package/package.json +5 -5
  203. package/styles/components/fs_agenda.scss +36 -0
  204. package/styles/components/fs_agenda_event.scss +41 -0
  205. package/styles/components/fs_agenda_hours_col.scss +4 -0
  206. package/styles/components/fs_agenda_hours_row.scss +5 -0
  207. package/styles/components/fs_agenda_time_line_marker.scss +19 -0
  208. package/styles/components/fs_autocomplete_field.scss +0 -13
  209. package/styles/components/fs_breadcrumbs.scss +6 -12
  210. package/styles/components/fs_card.scss +0 -1
  211. package/styles/components/fs_clickable.scss +7 -14
  212. package/styles/components/fs_clock.scss +0 -10
  213. package/styles/components/fs_color_field.scss +1 -4
  214. package/styles/components/fs_data_table.scss +6 -9
  215. package/styles/components/fs_dialog.scss +7 -17
  216. package/styles/components/fs_edit_image.scss +8 -0
  217. package/styles/components/fs_fade_out.scss +11 -2
  218. package/styles/components/fs_filter_button.scss +1 -6
  219. package/styles/components/fs_gradient_field.scss +11 -11
  220. package/styles/components/fs_hidden_button.scss +2 -7
  221. package/styles/components/fs_image_card.scss +1 -1
  222. package/styles/components/fs_magic_config_field.scss +1 -2
  223. package/styles/components/fs_map.scss +36 -30
  224. package/styles/components/fs_meta_field.scss +3 -5
  225. package/styles/components/fs_option_group.scss +15 -5
  226. package/styles/components/fs_rich_text_field.scss +1 -9
  227. package/styles/components/fs_select_date_settings.scss +3 -0
  228. package/styles/components/fs_select_field.scss +0 -13
  229. package/styles/components/fs_slide_group.scss +7 -0
  230. package/styles/components/fs_span.scss +2 -1
  231. package/styles/components/fs_switch.scss +1 -0
  232. package/styles/components/fs_tabs.scss +10 -24
  233. package/styles/components/fs_tag.scss +3 -19
  234. package/styles/components/fs_text_area.scss +13 -17
  235. package/styles/components/fs_tile.scss +21 -15
  236. package/styles/components/fs_window.scss +7 -0
  237. package/styles/components/fs_wrap_group.scss +7 -0
  238. package/styles/components/index.scss +5 -5
  239. package/styles/globals/index.scss +1 -5
  240. package/styles/globals/overrides.scss +17 -57
  241. package/styles/globals/text_fonts.scss +18 -66
  242. package/tools/alertsTools.ts +69 -0
  243. package/tools/chartsTools.ts +427 -0
  244. package/tools/index.ts +3 -0
  245. package/tools/timeRangeTools.ts +125 -0
  246. package/utils/filter.ts +18 -0
  247. package/utils/index.ts +1 -0
  248. package/utils/leafletMarkers.ts +9 -3
  249. package/utils/sort.ts +2 -2
  250. package/utils/statuses.ts +1 -1
  251. package/utils/time.ts +17 -17
  252. package/components/autocompletes/FSAutocompleteTag.vue +0 -138
  253. package/components/fields/FSTimeSlotField.vue +0 -250
  254. package/components/map/FSMapEditPointAddressOverlay.vue +0 -164
  255. package/components/views/FSEntityHeader.vue +0 -350
  256. package/components/views/FSListHeader.vue +0 -83
  257. package/components/views/FSListView.vue +0 -83
  258. package/components/views/FSSkeletonView.vue +0 -100
  259. package/styles/components/fs_icon_field.scss +0 -12
  260. package/styles/components/fs_map_overlay.scss +0 -38
  261. package/styles/components/fs_tag_field.scss +0 -8
  262. package/styles/components/fs_time_field.scss +0 -12
  263. package/styles/components/fs_timeslot_field.scss +0 -12
  264. package/styles/globals/breakpoints.scss +0 -20
  265. package/styles/globals/fixes.scss +0 -5
@@ -1,162 +1,160 @@
1
1
  <template>
2
2
  <FSCard
3
+ class="fs-map"
3
4
  :width="$props.width"
5
+ :height="$props.height"
4
6
  :style="style"
5
7
  v-bind="$attrs"
6
8
  >
7
- <FSCol
8
- v-if="L"
9
- width="fill"
10
- :class="['fs-map', { 'fs-map-fullscreen': fullScreen }]"
9
+ <div
10
+ ref="leafletContainer"
11
+ class="fs-leaflet-container"
11
12
  >
12
- <FSMapOverlay
13
- v-if="$slots['leftoverlay-header'] || $slots['leftoverlay-body']"
14
- :mode="$props.overlayMode"
15
- :height="$props.height"
16
- :mapId="mapId"
17
- @update:mode="$emit('update:overlayMode', $event)"
13
+ <template
14
+ v-if="map"
18
15
  >
19
- <template
20
- v-slot:leftoverlay-header
16
+ <FSMapTileLayer
17
+ :layer="actualLayer"
18
+ />
19
+ <FSMapMarker
20
+ v-if="gpsPosition"
21
+ variant="gps"
22
+ :color="ColorEnum.Primary"
23
+ :latlng="gpsPosition"
24
+ />
25
+
26
+ <FSMapFeatureGroup
27
+ v-if="$props.areas"
28
+ :expected-layers="$props.areas.length"
29
+ @update:bounds="(bounds) => areaGroupBounds = bounds"
21
30
  >
22
- <slot
23
- name="leftoverlay-header"
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)"
24
37
  />
25
- </template>
26
- <template
27
- v-slot:leftoverlay-body
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"
28
45
  >
29
- <slot
30
- name="leftoverlay-body"
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)"
31
55
  />
32
- </template>
33
- </FSMapOverlay>
34
- <FSRow
35
- v-if="$props.editable && !editingLocation && $props.selectedLocationId !== null"
36
- class="fs-map-overlay-edit-button"
37
- >
38
- <FSButton
39
- prependIcon="mdi-pencil-outline"
40
- :label="$tr('ui.map.modify', 'Modify')"
41
- @click="editingLocation = true"
42
- />
43
- </FSRow>
44
- <FSCol>
45
- <div
46
- class="fs-leaflet-container"
47
- :id="mapId"
48
- />
49
- </FSCol>
50
- <FSCol
51
- class="fs-map-overlay-right-top"
52
- align="center-center"
56
+ </FSMapMarkerClusterGroup>
57
+ </template>
58
+ </div>
59
+
60
+ <FSMapLayerButton
61
+ v-if="$props.allowedLayers?.length && $props.allowedLayers.length > 1"
62
+ :layers="mapLayers.filter((layer) => $props.allowedLayers?.includes(layer.name) ?? true)"
63
+ :modelValue="$props.currentLayer"
64
+ @update:model-value="$emit('update:currentLayer', $event)"
65
+ />
66
+
67
+ <FSCol
68
+ v-if="map"
69
+ class="fs-map-control-buttons"
70
+ >
71
+ <FSButton
72
+ v-if="$props.showMyLocation"
73
+ icon="mdi-crosshairs-gps"
74
+ color="primary"
75
+ variant="full"
76
+ :elevation="true"
77
+ @click="() => map!.locate()"
78
+ />
79
+ <FSCard
80
+ v-if="$props.showZoomButtons"
81
+ :elevation="true"
53
82
  >
54
- <slot
55
- name="toprightoverlay"
83
+ <FSCol
84
+ gap="0"
56
85
  >
57
- <FSRow
58
- gap="2px"
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
- />
65
- <FSButton
66
- v-if="$props.showFullScreen"
67
- prependIcon="mdi-fullscreen"
68
- padding="0 7px"
69
- :elevation="true"
70
- @click="fullScreen = !fullScreen"
71
- />
72
- </FSRow>
73
- </slot>
74
- </FSCol>
75
- <FSCol
76
- class="fs-map-overlay-right-bottom"
77
- align="center-center"
86
+ <FSButton
87
+ class="fs-map-zoom-plus-button"
88
+ icon="mdi-plus"
89
+ @click="() => map!.zoomIn()"
90
+ :border="false"
91
+ />
92
+ <FSButton
93
+ class="fs-map-zoom-minus-button"
94
+ icon="mdi-minus"
95
+ @click="() => map!.zoomOut()"
96
+ :border="false"
97
+ />
98
+ </FSCol>
99
+ </FSCard>
100
+ </FSCol>
101
+
102
+ <FSMapOverlay
103
+ v-if="$slots['overlay']"
104
+ :mode="$props.overlayMode"
105
+ @update:mode="$emit('update:overlayMode', $event)"
106
+ @update:height="(height) => overlayHeight = height"
107
+ @update:width="(width) => overlayWidth = width"
108
+ >
109
+ <template
110
+ #body
78
111
  >
79
112
  <slot
80
- name="bottomrightoverlay"
81
- >
82
- <FSCol
83
- class="fs-map-zoom-overlay"
84
- align="bottom-center"
85
- width="hug"
86
- >
87
- <FSButton
88
- v-if="$props.showMyLocation"
89
- prependIcon="mdi-crosshairs-gps"
90
- padding="0 7px"
91
- color="primary"
92
- variant="full"
93
- :elevation="true"
94
- @click="locate"
95
- />
96
- <FSCol
97
- v-if="$props.showZoomButtons"
98
- gap="0"
99
- >
100
-
101
- <FSButton
102
- prependIcon="mdi-plus"
103
- padding="0 7px"
104
- :elevation="true"
105
- @click="zoomIn"
106
- />
107
- <FSButton
108
- prependIcon="mdi-minus"
109
- padding="0 7px"
110
- :elevation="true"
111
- @click="zoomOut"
112
- />
113
- </FSCol>
114
- </FSCol>
115
- </slot>
116
- <FSMapEditPointAddressOverlay
117
- v-if="editingLocation"
118
- :label="$tr('ui.map.address', 'Address')"
119
- :modelValue="(innerModelValue.find((loc) => loc.id === $props.selectedLocationId))?.address"
120
- @update:locationCoordinates="($event: Address) => onNewCoordEntered($event.latitude, $event.longitude)"
121
- @update:modelValue="($event: Address) => onNewAddressEntered($event)"
122
- @cancel="onCancel"
123
- @submit="onSubmit"
113
+ name="overlay"
124
114
  />
125
- </FSCol>
126
- </FSCol>
115
+ </template>
116
+ </FSMapOverlay>
127
117
  </FSCard>
128
118
  </template>
129
119
 
130
120
  <script lang="ts">
131
- import { computed, defineComponent, onMounted, onUnmounted, type PropType, ref, type StyleValue, watch } from "vue";
132
- import "leaflet.markercluster";
133
- import * as L from "leaflet";
121
+ import { computed, defineComponent, onMounted, type Ref, provide, type PropType, ref, type StyleValue, watch, onUnmounted, markRaw } from "vue";
122
+
123
+ import type {} from "leaflet.markercluster";
124
+ import { map as createMap, control, tileLayer, latLngBounds, latLng, type LatLng, type FitBoundsOptions, type ZoomPanOptions, type LatLngBounds } from "leaflet";
134
125
 
135
126
  import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
136
- import { type Address, type FSArea } from '@dative-gpi/foundation-shared-domain/models';
127
+ import { type FSArea } from '@dative-gpi/foundation-shared-domain/models';
137
128
 
138
- import { clusterMarkerHtml, locationMarkerHtml, myLocationMarkerHtml } from "../../utils";
139
- import { useColors, useAddress, useBreakpoints } from "../../composables";
129
+ import { useBreakpoints, useColors } from "../../composables";
140
130
  import { ColorEnum, type FSLocation, type MapLayer } from "../../models";
141
131
 
142
- import FSMapEditPointAddressOverlay from "./FSMapEditPointAddressOverlay.vue";
143
132
  import FSMapLayerButton from "./FSMapLayerButton.vue";
144
133
  import FSMapOverlay from "./FSMapOverlay.vue";
145
134
  import FSButton from "../FSButton.vue";
146
135
  import FSCard from "../FSCard.vue";
147
136
  import FSCol from "../FSCol.vue";
148
- import FSRow from "../FSRow.vue";
137
+
138
+ import FSMapMarker from "./FSMapMarker.vue";
139
+ import FSMapTileLayer from "./FSMapTileLayer.vue";
140
+ import FSMapFeatureGroup from "./FSMapFeatureGroup.vue";
141
+ import FSMapMarkerClusterGroup from "./FSMapMarkerClusterGroup.vue";
142
+ import FSMapPolygon from "./FSMapPolygon.vue";
149
143
 
150
144
  export default defineComponent({
151
145
  name: "FSMap",
152
146
  components: {
153
- FSMapEditPointAddressOverlay,
147
+ FSMapMarker,
148
+ FSMapTileLayer,
149
+ FSMapFeatureGroup,
150
+ FSMapMarkerClusterGroup,
151
+ FSMapPolygon,
152
+
154
153
  FSMapLayerButton,
155
154
  FSMapOverlay,
156
155
  FSButton,
157
156
  FSCard,
158
157
  FSCol,
159
- FSRow
160
158
  },
161
159
  props: {
162
160
  height: {
@@ -174,11 +172,6 @@ export default defineComponent({
174
172
  required: false,
175
173
  default: false
176
174
  },
177
- editable: {
178
- type: Boolean,
179
- required: false,
180
- default: false
181
- },
182
175
  overlayMode: {
183
176
  type: String as PropType<'collapse' | 'half' | 'expand'>,
184
177
  required: false,
@@ -194,11 +187,6 @@ export default defineComponent({
194
187
  required: false,
195
188
  default: true
196
189
  },
197
- showFullScreen: {
198
- type: Boolean,
199
- required: false,
200
- default: false
201
- },
202
190
  enableScrollWheelZoom: {
203
191
  type: Boolean,
204
192
  required: false,
@@ -209,7 +197,7 @@ export default defineComponent({
209
197
  required: false,
210
198
  default: () => [45.71, 5.07]
211
199
  },
212
- modelValue: {
200
+ locations: {
213
201
  type: Array as PropType<FSLocation[]>,
214
202
  required: false,
215
203
  default: () => [],
@@ -219,12 +207,12 @@ export default defineComponent({
219
207
  required: false,
220
208
  default: () => [],
221
209
  },
222
- selectedLayer: {
210
+ currentLayer: {
223
211
  type: String as PropType<"map" | "imagery">,
224
212
  required: false,
225
213
  default: "map"
226
214
  },
227
- selectableLayers: {
215
+ allowedLayers: {
228
216
  type: Array as PropType<string[]>,
229
217
  required: false,
230
218
  default: () => ["map", "imagery"]
@@ -240,40 +228,36 @@ export default defineComponent({
240
228
  default: null
241
229
  }
242
230
  },
243
- emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId", 'update:overlayMode'],
231
+ emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId", 'update:overlayMode', 'update:currentLayer', "click:latlng"],
244
232
  setup(props, { emit }) {
245
233
  const { $tr } = useTranslationsProvider();
246
- const { reverseSearch } = useAddress();
247
234
  const { getColors } = useColors();
248
235
  const { isExtraSmall } = useBreakpoints();
249
236
 
250
- const LL = window.L;
251
-
252
- const innerSelectedLayer = ref(props.selectedLayer);
253
- const innerModelValue = ref(props.modelValue);
254
- const editingLocation = ref(false);
255
- const fullScreen = ref(false);
256
- const leftOverlayHeight = ref<number>();
257
- const leftOverlayWidth = ref<number>();
258
- const resizeObserver = ref<ResizeObserver | null>(null);
237
+ const leafletContainer = ref<HTMLElement>();
238
+ const locationGroupBounds = ref<LatLngBounds>();
239
+ const areaGroupBounds = ref<LatLngBounds>();
240
+ const gpsPosition : Ref<LatLng | null> = ref(null);
241
+ const map: Ref<L.Map | null> = ref(null);
242
+ const overlayHeight = ref<number>();
243
+ const overlayWidth = ref<number>();
259
244
 
260
- const mapId = `map-${Math.random().toString(36).substring(7)}`;
261
- const defaultZoom = 15;
262
- const markers: { [key: string]: L.Marker } = {};
263
- const areas: { [key: string]: L.Polygon } = {};
264
- const areaLayerGroup = new LL.FeatureGroup();
265
- const baseLayerGroup = new LL.LayerGroup();
266
- const myLocationLayerGroup = new LL.LayerGroup();
245
+ provide('map', map);
267
246
 
268
- let map: L.Map;
269
- let markerLayerGroup: L.FeatureGroup | any;
247
+ const defaultZoom = 16;
248
+ const mapResizeObserver = new ResizeObserver(() => {
249
+ if(!map.value) {
250
+ return;
251
+ }
252
+ map.value.invalidateSize();
253
+ });
270
254
 
271
255
  const mapLayers: MapLayer[] = [
272
256
  {
273
257
  name: "map",
274
- label: $tr("ui.map.layer.map", "Map"),
258
+ label: $tr("ui.map-layer.map", "Map"),
275
259
  image: new URL("../../assets/images/map/map.png", import.meta.url).href,
276
- layer: LL.tileLayer(`http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
260
+ layer: tileLayer(`http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
277
261
  maxZoom: 22,
278
262
  subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
279
263
  attribution: '© Google Map Data'
@@ -281,9 +265,9 @@ export default defineComponent({
281
265
  },
282
266
  {
283
267
  name: "imagery",
284
- label: $tr("ui.map.layer.imagery", "Imagery"),
268
+ label: $tr("ui.map-layer.imagery", "Imagery"),
285
269
  image: new URL("../../assets/images/map/imagery.png", import.meta.url).href,
286
- layer: LL.tileLayer(`http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
270
+ layer: tileLayer(`http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
287
271
  maxZoom: 22,
288
272
  subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
289
273
  attribution: '© Google Map Data'
@@ -291,303 +275,200 @@ export default defineComponent({
291
275
  }
292
276
  ];
293
277
 
294
- const bottomMargin = computed(() => {
295
- let margin = 0;
296
- if (props.overlayMode !== 'expand' && leftOverlayHeight.value && isExtraSmall.value) {
297
- margin += leftOverlayHeight.value;
278
+ const bottomOffset = computed(() => {
279
+ if (props.overlayMode !== 'expand' && overlayHeight.value && isExtraSmall.value) {
280
+ return overlayHeight.value;
298
281
  }
299
- return margin;
282
+ return 0;
300
283
  });
301
284
 
302
- const leftMargin = computed(() => {
303
- let margin = 0;
304
- if (leftOverlayWidth.value && !isExtraSmall.value) {
305
- margin += leftOverlayWidth.value;
285
+ const leftOffset = computed(() => {
286
+ if (overlayWidth.value && !isExtraSmall.value) {
287
+ return overlayWidth.value;
306
288
  }
307
- return margin;
289
+ return 0;
308
290
  });
309
291
 
310
292
  const style = computed((): StyleValue => ({
311
293
  "--fs-map-location-pin-color": getColors(ColorEnum.Primary).base,
312
294
  "--fs-map-mylocation-pin-color": getColors(ColorEnum.Primary).base,
313
295
  "--fs-map-mylocation-pin-color-alpha": getColors(ColorEnum.Primary).base + "50",
314
- "--fs-map-leaflet-container-height": props.height as string,
315
- "--fs-map-leaflet-bottom-overlay-margin": `${bottomMargin.value}px`,
316
- "--fs-map-container-grayscale": props.grayscale ? '0.9' : '0'
296
+ "--fs-map-container-grayscale": props.grayscale ? '0.9' : '0',
297
+ "--fs-map-control-buttons-margin-bottom": `${bottomOffset.value}px`,
317
298
  }));
318
299
 
319
- const displayLocations = () => {
320
- markerLayerGroup.clearLayers();
321
- innerModelValue.value.forEach((location) => {
322
- const size = 36;
323
- const icon = L.divIcon({
324
- html: locationMarkerHtml(location.icon, getColors(location.color).base),
325
- iconSize: [size, size],
326
- className: 'fs-map-location',
327
- iconAnchor: [size / 2, size / 2],
328
- });
329
- const marker = LL.marker([location.address.latitude, location.address.longitude], { icon }).addTo(markerLayerGroup);
330
- markers[location.id] = marker;
331
- marker.on('click', () => emit('update:selectedLocationId', location.id));
332
- });
333
- };
300
+ const actualLayer = computed(() => {
301
+ return mapLayers.find((layer) => layer.name === props.currentLayer)?.layer ?? mapLayers[0].layer;
302
+ });
334
303
 
335
- const displayAreas = () => {
336
- areaLayerGroup.clearLayers();
337
- props.areas.forEach((area) => {
338
- const areaPolygon = LL.polygon(area.coordinates.map((coord) => [coord.latitude, coord.longitude]), {
339
- color: area.color,
340
- fillColor: area.color + "50",
341
- fillOpacity: 0.5,
342
- className: 'fs-map-area',
343
- }).addTo(areaLayerGroup);
344
-
345
- areas[area.id] = areaPolygon;
346
- areaPolygon.on('click', () => emit('update:selectedAreaId', area.id));
347
- });
348
- };
304
+ const bounds = computed<LatLngBounds | null>(() => {
305
+ if(!locationGroupBounds.value && !areaGroupBounds.value) {
306
+ return null;
307
+ }
308
+ let bounds = locationGroupBounds.value;
309
+ if(bounds && areaGroupBounds.value) {
310
+ bounds.extend(areaGroupBounds.value);
311
+ } else if(areaGroupBounds.value) {
312
+ bounds = areaGroupBounds.value;
313
+ }
314
+ return bounds as LatLngBounds;
315
+ });
349
316
 
350
- const modifyLocationAddress = (locationId: string, newAddress: Address) => {
351
- const location = innerModelValue.value.find((loc) => loc.id === locationId);
352
- if (!location) {
353
- return;
317
+ const calculateTargetPosition = (target: L.LatLng, zoom?: number) => {
318
+ if(!map.value) {
319
+ return target;
354
320
  }
355
- const newLocation = {
356
- ...location,
357
- address: {
358
- ...newAddress
359
- },
360
- };
361
- innerModelValue.value = innerModelValue.value.map((loc) => loc.id === locationId ? newLocation : loc);
362
- };
321
+ zoom = zoom ?? map.value.getZoom();
322
+ const targetPoint = map.value.project(target, zoom).subtract([leftOffset.value / 2, -bottomOffset.value / 2]);
323
+ return map.value.unproject(targetPoint, zoom);
324
+ }
363
325
 
364
- const initMap = () => {
365
- if (props.editable) {
366
- markerLayerGroup = new LL.FeatureGroup();
326
+ const flyTo = (lat: number, lng: number, zoom: number = defaultZoom, options?: ZoomPanOptions) => {
327
+ if(!map.value) {
328
+ return;
367
329
  }
368
- else {
369
- markerLayerGroup = new LL.MarkerClusterGroup({
370
- spiderfyOnMaxZoom: false,
371
- showCoverageOnHover: false,
372
- disableClusteringAtZoom: 17,
373
- iconCreateFunction: function (cluster: any) {
374
- const size = 36;
375
- return L.divIcon({
376
- html: clusterMarkerHtml(cluster.getChildCount()),
377
- className: 'fs-map-location fs-map-location-full',
378
- iconSize: [size, size],
379
- iconAnchor: [size / 2, size / 2],
380
- });
330
+
331
+ if(isExtraSmall.value) {
332
+ // We wait for bottom offset to be calculated and stable to focus on the right position
333
+ let tries = 0;
334
+ let oldBottomOffset = bottomOffset.value;
335
+ const interval = setInterval(() => {
336
+ if(oldBottomOffset === bottomOffset.value || tries >= 30) {
337
+ clearInterval(interval);
338
+ map.value!.flyTo(calculateTargetPosition(latLng(lat, lng), zoom), zoom, options);
381
339
  }
382
- });
383
- }
384
- const mapOptions = {
385
- zoomControl: false,
386
- scrollWheelZoom: props.enableScrollWheelZoom,
387
- minZoom: 2,
388
- maxBounds: LL.latLngBounds(LL.latLng(-90, -180), LL.latLng(90, 180)),
389
- maxBoundsViscosity: 1.0
390
- };
391
- map = LL.map(mapId, mapOptions).setView([props.center[0], props.center[1]], defaultZoom);
392
- map.attributionControl.remove();
393
- LL.control.attribution({ position: 'bottomleft' }).addTo(map);
394
-
395
- baseLayerGroup.addTo(map);
396
- areaLayerGroup.addTo(map);
397
- myLocationLayerGroup.addTo(map);
398
- setMapBaseLayer(innerSelectedLayer.value);
399
- displayAreas();
400
- displayLocations();
401
- markerLayerGroup.addTo(map);
402
-
403
- if (innerModelValue.value.length > 0) {
404
- map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
340
+ oldBottomOffset = bottomOffset.value;
341
+ tries++;
342
+ }, 20);
343
+
344
+ } else {
345
+ map.value.flyTo(calculateTargetPosition(latLng(lat, lng), zoom), zoom, options);
405
346
  }
347
+
348
+ }
406
349
 
407
- map.on('click', (e: L.LeafletMouseEvent) => {
408
- if (editingLocation.value) {
409
- onNewCoordEntered(+e.latlng.lat.toFixed(6), +e.latlng.lng.toFixed(6));
410
- }
411
- });
412
- };
413
-
414
- const setMapBaseLayer = (layerName: 'map' | 'imagery') => {
415
- const layer = mapLayers.find((mapLayer) => mapLayer.name === layerName) ?? mapLayers[0];
416
- baseLayerGroup.clearLayers();
417
- layer.layer.addTo(baseLayerGroup);
418
- };
419
-
420
- const onNewAddressEntered = (address: Address) => {
421
- if (!props.selectedLocationId || !map) {
350
+ const setView = (lat: number, lng: number, zoom: number) => {
351
+ if(!map.value) {
422
352
  return;
423
353
  }
424
- modifyLocationAddress(props.selectedLocationId, address);
425
- map.panTo(calculateTargetPosition(new L.LatLng(address.latitude, address.longitude)));
426
- };
354
+ map.value.setView(calculateTargetPosition(latLng(lat, lng)), zoom);
355
+ }
427
356
 
428
- const onNewCoordEntered = async (lat: number, lng: number) => {
429
- const address = await reverseSearch(lat, lng);
357
+ const fitBounds = (bounds: LatLngBounds, options?: FitBoundsOptions) => {
358
+ if (!map.value) {return;}
359
+ const paddingTopLeft: [number, number] = [
360
+ leftOffset.value,
361
+ 0
362
+ ];
363
+
364
+ const paddingBottomRight: [number, number] = [
365
+ 0,
366
+ bottomOffset.value
367
+ ];
368
+ const paddingOptions = {
369
+ paddingTopLeft,
370
+ paddingBottomRight,
371
+ ...options,
372
+ };
430
373
 
431
- onNewAddressEntered({
432
- ...address,
433
- latitude: lat,
434
- longitude: lng,
435
- });
374
+ map.value.fitBounds(bounds, paddingOptions);
436
375
  };
437
376
 
438
- const zoomIn = () => {
439
- if (!map) {
377
+ onMounted(() => {
378
+ if(!leafletContainer.value) {
440
379
  return;
441
380
  }
442
- map.zoomIn();
443
- };
444
381
 
445
- const zoomOut = () => {
446
- if (!map) {
447
- return;
448
- }
449
- map.zoomOut();
450
- };
382
+ const mapOptions = {
383
+ zoomControl: false,
384
+ scrollWheelZoom: props.enableScrollWheelZoom,
385
+ minZoom: 2,
386
+ maxZoom: 22,
387
+ maxBounds: latLngBounds(latLng(-90, -180), latLng(90, 180)),
388
+ maxBoundsViscosity: 1.0
389
+ };
451
390
 
452
- const locate = () => {
453
- if (!map) {
454
- return;
455
- }
456
- map.locate();
457
- map.on('locationfound', (e: L.LocationEvent) => {
458
- map.panTo(calculateTargetPosition(e.latlng));
459
- const size= 16;
460
- const icon = L.divIcon({
461
- html: myLocationMarkerHtml(L),
462
- className: 'fs-map-mylocation',
463
- iconSize: [size, size],
464
- iconAnchor: [size / 2, size / 2],
465
- });
466
- myLocationLayerGroup.clearLayers();
467
- LL.marker(e.latlng, { icon }).addTo(myLocationLayerGroup);
391
+ map.value = markRaw(createMap(leafletContainer.value, mapOptions));
392
+ setView(props.center[0], props.center[1], defaultZoom);
393
+
394
+ map.value.on('click', (e: L.LeafletMouseEvent) => {
395
+ emit('click:latlng', e.latlng);
468
396
  });
469
- };
470
397
 
471
- const calculateTargetPosition = (target: L.LatLng, zoom: number = map.getZoom()) => {
472
- const targetPoint = map.project(target, zoom).subtract([leftMargin.value / 2, -bottomMargin.value / 2]);
473
- return map.unproject(targetPoint, zoom);
474
- }
398
+ map.value.attributionControl.remove();
399
+ control.attribution({ position: 'bottomleft' }).addTo(map.value);
475
400
 
476
- const onCancel = () => {
477
- editingLocation.value = false;
478
- innerModelValue.value = props.modelValue;
479
- if (!map) {
480
- return;
481
- }
482
- displayLocations();
483
- if (innerModelValue.value.length > 0) {
484
- map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
485
- }
486
- else {
487
- map.panTo(calculateTargetPosition(new L.LatLng(props.center[0], props.center[1])), { animate: false });
488
- }
489
- if (props.modelValue.length > 1) {
490
- emit('update:selectedLocationId', null);
491
- }
492
- };
401
+ map.value.on('locationfound', (e: L.LocationEvent) => {
402
+ if(!e.latlng) {
403
+ return;
404
+ }
493
405
 
494
- const onSubmit = () => {
495
- emit('update:modelValue', innerModelValue.value);
496
- if (!map) {
497
- return;
498
- }
499
- editingLocation.value = false;
500
- if (innerModelValue.value.length > 0) {
501
- map.fitBounds(markerLayerGroup.getBounds(), { maxZoom: defaultZoom });
502
- }
503
- else {
504
- map.panTo(calculateTargetPosition(new L.LatLng(props.center[0], props.center[1])), { animate: false });
505
- }
506
- if (props.modelValue.length > 1) {
507
- emit('update:selectedLocationId', null);
508
- }
509
- };
406
+ gpsPosition.value = e.latlng;
510
407
 
511
- onMounted(() => {
512
- initMap();
513
- if (props.selectedLocationId && props.modelValue.length === 1) {
514
- editingLocation.value = true;
515
- }
408
+ if(!map.value) {
409
+ return;
410
+ }
516
411
 
517
- resizeObserver.value = new ResizeObserver(entries => {
518
- entries.forEach((entry) => {
519
- if (entry.target.id === `left-overlay-${mapId}`) {
520
- leftOverlayWidth.value = entry.contentRect.width;
521
- }
522
- if (entry.target.id === `left-overlay-mobile-${mapId}`) {
523
- leftOverlayHeight.value = entry.contentRect.height;
524
- }
525
- });
412
+ flyTo(e.latlng.lat, e.latlng.lng);
526
413
  });
527
- if (document.querySelector(`#left-overlay-mobile-${mapId}`)) {
528
- resizeObserver.value.observe(document.querySelector(`#left-overlay-mobile-${mapId}`)!);
529
- }
530
- if (document.querySelector(`#left-overlay-${mapId}`)) {
531
- resizeObserver.value.observe(document.querySelector(`#left-overlay-${mapId}`)!);
532
- }
414
+
415
+ mapResizeObserver.observe(leafletContainer.value);
533
416
  });
534
417
 
535
- onUnmounted((): void => {
536
- if (resizeObserver.value) {
537
- resizeObserver.value.disconnect();
538
- }
418
+ onUnmounted(() => {
419
+ mapResizeObserver.disconnect();
539
420
  });
540
421
 
541
- watch(() => innerModelValue.value, () => {
542
- displayLocations();
422
+ watch (() => props.center, (center) => {
423
+ if(!map.value) {
424
+ return;
425
+ }
426
+ setView(center[0], center[1], defaultZoom);
543
427
  });
544
428
 
545
- watch(() => props.selectedLocationId, () => {
546
- if (!props.selectedLocationId || !map) {
429
+ watch (() => props.selectedLocationId, (selectedLocationId) => {
430
+ if(!map.value) {
547
431
  return;
548
432
  }
433
+ const selectedLocation = props.locations.find((location) => location.id === selectedLocationId);
434
+ if(!selectedLocation) {
435
+ return;
436
+ }
437
+ flyTo(selectedLocation?.address.latitude, selectedLocation?.address.longitude, defaultZoom, { animate: false });
438
+ }, { immediate: true });
549
439
 
550
- Object.values(markers).forEach((marker) => {
551
- marker.getElement()?.classList.remove('fs-map-location-selected');
552
- });
553
-
554
- const marker = markers[props.selectedLocationId];
555
- map.flyTo(calculateTargetPosition(marker.getLatLng(), 17), 17, { animate: false });
556
- marker.getElement()?.classList.add('fs-map-location-selected');
557
- })
558
-
559
- watch(() => props.selectedAreaId, () => {
560
- if (!props.selectedAreaId || !map) {
440
+ watch(() => props.selectedAreaId, (selectedAreaId) => {
441
+ if(!map.value) {
561
442
  return;
562
443
  }
563
- const area = areas[props.selectedAreaId];
564
- if (area) {
565
- map.fitBounds(area.getBounds(), { maxZoom: 17 });
444
+ const selectedArea = props.areas.find((area) => area.id === selectedAreaId);
445
+ if(!selectedArea) {
446
+ return;
566
447
  }
567
- });
448
+ const bounds = latLngBounds(selectedArea.coordinates.map((coord) => latLng(coord.latitude, coord.longitude)));
449
+ fitBounds(bounds);
450
+ }, { immediate: true });
568
451
 
569
- watch(innerSelectedLayer, () => {
570
- setMapBaseLayer(innerSelectedLayer.value);
452
+ watch( () => bounds.value, (bounds) => {
453
+ if(!map.value || !bounds) {
454
+ return;
455
+ }
456
+ fitBounds(bounds, { maxZoom: defaultZoom });
571
457
  });
572
458
 
573
459
  return {
574
- bottomMargin,
575
- editingLocation,
576
- fullScreen,
577
- innerModelValue,
578
- innerSelectedLayer,
460
+ ColorEnum,
461
+ defaultZoom,
462
+ leafletContainer,
463
+ locationGroupBounds,
464
+ overlayHeight,
465
+ overlayWidth,
466
+ areaGroupBounds,
467
+ map,
468
+ actualLayer,
579
469
  mapLayers,
580
- mapId,
581
- style,
582
- L,
583
- onNewAddressEntered,
584
- onNewCoordEntered,
585
- setMapBaseLayer,
586
- onCancel,
587
- onSubmit,
588
- zoomOut,
589
- locate,
590
- zoomIn
470
+ gpsPosition,
471
+ style
591
472
  };
592
473
  }
593
474
  });