@dative-gpi/foundation-shared-components 1.0.65 → 1.0.67-map-edit

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 (294) hide show
  1. package/assets/images/map/snow.png +0 -0
  2. package/components/FSAccordionPanel.vue +40 -21
  3. package/components/FSBreadcrumbs.vue +21 -12
  4. package/components/FSButton.vue +11 -9
  5. package/components/FSCalendar.vue +50 -12
  6. package/components/FSCalendarTwin.vue +95 -43
  7. package/components/FSCard.vue +61 -9
  8. package/components/FSCardPlaceholder.vue +8 -5
  9. package/components/FSCheckbox.vue +5 -4
  10. package/components/FSChip.vue +73 -45
  11. package/components/FSChipGroup.vue +69 -0
  12. package/components/FSClickable.vue +11 -9
  13. package/components/FSClock.vue +30 -12
  14. package/components/FSCol.vue +11 -5
  15. package/components/FSDialog.vue +2 -1
  16. package/components/FSDialogContent.vue +12 -11
  17. package/components/FSDialogForm.vue +22 -2
  18. package/components/FSDialogFormBody.vue +50 -31
  19. package/components/FSDialogMenu.vue +17 -8
  20. package/components/FSDialogMultiFormBody.vue +23 -13
  21. package/components/FSDialogRemove.vue +7 -8
  22. package/components/FSDialogSubmit.vue +20 -11
  23. package/components/FSEditImageUI.vue +13 -5
  24. package/components/FSFadeOut.vue +53 -21
  25. package/components/FSForm.vue +10 -8
  26. package/components/FSGrid.vue +0 -1
  27. package/components/FSIcon.vue +2 -2
  28. package/components/FSIconCard.vue +68 -12
  29. package/components/FSInformationsMenu.vue +142 -0
  30. package/components/FSInstantPicker.vue +268 -0
  31. package/components/FSLink.vue +25 -9
  32. package/components/FSLoader.vue +28 -10
  33. package/components/FSMenu.vue +47 -0
  34. package/components/FSOptionGroup.vue +8 -8
  35. package/components/FSOptionItem.vue +4 -4
  36. package/components/FSOptionsMenu.vue +165 -0
  37. package/components/FSPagination.vue +1 -1
  38. package/components/FSPlayButtons.vue +72 -0
  39. package/components/FSProgressBar.vue +94 -0
  40. package/components/FSRadio.vue +5 -4
  41. package/components/FSRadioGroup.vue +3 -3
  42. package/components/FSRangePicker.vue +275 -0
  43. package/components/FSRangeSlider.vue +84 -0
  44. package/components/FSRouterLink.vue +42 -0
  45. package/components/FSSlideGroup.vue +19 -5
  46. package/components/FSSlider.vue +44 -79
  47. package/components/FSSpan.vue +12 -7
  48. package/components/FSSwitch.vue +44 -48
  49. package/components/FSTab.vue +15 -13
  50. package/components/FSTabs.vue +32 -7
  51. package/components/FSTag.vue +3 -3
  52. package/components/FSTagGroup.vue +6 -4
  53. package/components/FSText.vue +11 -7
  54. package/components/FSToggleSet.vue +7 -7
  55. package/components/FSWindow.vue +122 -5
  56. package/components/FSWrapGroup.vue +13 -1
  57. package/components/agenda/FSAgenda.vue +26 -7
  58. package/components/agenda/FSAgendaDialogCalendar.vue +2 -2
  59. package/components/agenda/FSAgendaHeader.vue +35 -10
  60. package/components/agenda/FSAgendaHorizontalEvent.vue +18 -6
  61. package/components/agenda/FSAgendaHoursRow.vue +48 -8
  62. package/components/agenda/FSAgendaVerticalEvent.vue +1 -1
  63. package/components/agenda/FSDayAgenda.vue +0 -1
  64. package/components/agenda/FSMonthAgenda.vue +1 -6
  65. package/components/agenda/FSSelectAgendaMode.vue +2 -2
  66. package/components/agenda/FSWeekAgenda.vue +5 -10
  67. package/components/autocompletes/FSAutoCompleteAddress.vue +25 -20
  68. package/components/autocompletes/FSAutocompleteLanguage.vue +7 -1
  69. package/components/autocompletes/FSAutocompleteTimeZone.vue +7 -1
  70. package/components/buttons/FSButtonAdd.vue +1 -1
  71. package/components/buttons/FSButtonAddLabel.vue +1 -1
  72. package/components/buttons/FSButtonCancel.vue +1 -1
  73. package/components/buttons/FSButtonCancelLabel.vue +1 -1
  74. package/components/buttons/FSButtonCheckbox.vue +4 -4
  75. package/components/buttons/FSButtonCopy.vue +1 -1
  76. package/components/buttons/FSButtonCopyLabel.vue +1 -1
  77. package/components/buttons/FSButtonDuplicate.vue +1 -1
  78. package/components/buttons/FSButtonDuplicateLabel.vue +1 -1
  79. package/components/buttons/FSButtonEdit.vue +1 -1
  80. package/components/buttons/FSButtonEditLabel.vue +1 -1
  81. package/components/buttons/FSButtonFile.vue +1 -1
  82. package/components/buttons/FSButtonFileLabel.vue +1 -1
  83. package/components/buttons/FSButtonNext.vue +1 -1
  84. package/components/buttons/FSButtonNextLabel.vue +1 -1
  85. package/components/buttons/FSButtonPrevious.vue +1 -1
  86. package/components/buttons/FSButtonPreviousLabel.vue +1 -1
  87. package/components/buttons/FSButtonRedo.vue +1 -1
  88. package/components/buttons/FSButtonRedoLabel.vue +1 -1
  89. package/components/buttons/FSButtonRemove.vue +1 -1
  90. package/components/buttons/FSButtonRemoveLabel.vue +1 -1
  91. package/components/buttons/FSButtonSave.vue +1 -1
  92. package/components/buttons/FSButtonSaveLabel.vue +1 -1
  93. package/components/buttons/FSButtonSearch.vue +1 -1
  94. package/components/buttons/FSButtonSearchLabel.vue +1 -1
  95. package/components/buttons/FSButtonUndo.vue +1 -1
  96. package/components/buttons/FSButtonUndoLabel.vue +1 -1
  97. package/components/buttons/FSButtonUpdate.vue +1 -1
  98. package/components/buttons/FSButtonUpdateLabel.vue +1 -1
  99. package/components/buttons/FSButtonValidate.vue +1 -1
  100. package/components/buttons/FSButtonValidateLabel.vue +1 -1
  101. package/components/calendar/FSSimpleCalendar.vue +145 -0
  102. package/components/calendar/FSSimpleCalendarHeader.vue +60 -0
  103. package/components/calendar/FSSimpleMonthSelector.vue +138 -0
  104. package/components/deviceOrganisations/FSConnectivity.vue +5 -3
  105. package/components/deviceOrganisations/FSStatus.vue +5 -3
  106. package/components/deviceOrganisations/FSStatusCard.vue +7 -9
  107. package/components/deviceOrganisations/FSStatusRichCard.vue +171 -0
  108. package/components/deviceOrganisations/FSWorstAlert.vue +24 -36
  109. package/components/deviceOrganisations/FSWorstAlertCard.vue +8 -47
  110. package/components/fields/FSAutocompleteField.vue +85 -82
  111. package/components/fields/FSAutocompleteTag.vue +1 -1
  112. package/components/fields/FSBaseField.vue +42 -25
  113. package/components/fields/FSClosableSearchField.vue +83 -0
  114. package/components/fields/FSColorField.vue +12 -10
  115. package/components/fields/FSCommentField.vue +28 -16
  116. package/components/fields/FSDateField.vue +13 -10
  117. package/components/fields/FSDateRangeField.vue +6 -5
  118. package/components/fields/FSDateTimeField.vue +14 -11
  119. package/components/fields/FSDateTimeRangeDialog.vue +160 -0
  120. package/components/fields/FSDateTimeRangeField.vue +23 -115
  121. package/components/fields/FSEntityFieldUI.vue +19 -16
  122. package/components/fields/FSGradientField.vue +5 -5
  123. package/components/fields/FSIconField.vue +16 -9
  124. package/components/fields/FSMagicConfigField.vue +15 -7
  125. package/components/fields/FSMagicField.vue +7 -2
  126. package/components/fields/FSNumberField.vue +8 -4
  127. package/components/fields/FSPasswordField.vue +7 -7
  128. package/components/fields/FSRichTextField.vue +78 -58
  129. package/components/fields/FSSearchField.vue +9 -115
  130. package/components/fields/FSSelectField.vue +69 -71
  131. package/components/fields/FSTagField.vue +9 -9
  132. package/components/fields/FSTermField.vue +69 -46
  133. package/components/fields/FSTextArea.vue +17 -11
  134. package/components/fields/FSTextField.vue +15 -10
  135. package/components/fields/FSTimeField.vue +14 -10
  136. package/components/fields/FSTimeRangeField.vue +310 -0
  137. package/components/fields/FSTimeStepField.vue +5 -5
  138. package/components/fields/FSTranslateField.vue +10 -10
  139. package/components/fields/FSTranslateRichTextField.vue +41 -20
  140. package/components/fields/FSTranslateTextArea.vue +10 -10
  141. package/components/fields/FSTreeViewField.vue +15 -13
  142. package/components/fields/periodicField/FSPeriodicDailyField.vue +7 -7
  143. package/components/fields/periodicField/FSPeriodicField.vue +15 -15
  144. package/components/fields/periodicField/FSPeriodicMonthlyField.vue +16 -16
  145. package/components/fields/periodicField/FSPeriodicWeeklyField.vue +28 -18
  146. package/components/fields/periodicField/FSPeriodicYearlyField.vue +12 -12
  147. package/components/lists/FSDataIteratorItem.vue +23 -67
  148. package/components/lists/FSDataTableUI.vue +198 -123
  149. package/components/lists/FSDraggable.vue +2 -2
  150. package/components/lists/FSFilterButton.vue +14 -16
  151. package/components/lists/FSHeaderButton.vue +11 -9
  152. package/components/lists/FSHiddenButton.vue +9 -9
  153. package/components/lists/FSLoadDataTable.vue +10 -7
  154. package/components/lists/FSSimpleList.vue +95 -88
  155. package/components/lists/FSSimpleListItem.vue +131 -0
  156. package/components/map/FSMap.vue +144 -158
  157. package/components/map/FSMapEditLocationAddress.vue +189 -0
  158. package/components/map/FSMapFeatureGroup.vue +7 -1
  159. package/components/map/FSMapLayerButton.vue +4 -3
  160. package/components/map/FSMapMarker.vue +103 -42
  161. package/components/map/FSMapMarkerClusterGroup.vue +32 -7
  162. package/components/map/FSMapOverlay.vue +44 -24
  163. package/components/map/FSMapPolygon.vue +16 -4
  164. package/components/map/FSMapTileLayer.vue +26 -9
  165. package/components/map/layers.ts +0 -0
  166. package/components/selects/FSSelectAutoRefresh.vue +8 -8
  167. package/components/selects/FSSelectDashboardVariableType.vue +4 -3
  168. package/components/selects/FSSelectDateSetting.vue +2 -2
  169. package/components/selects/FSSelectDays.vue +8 -8
  170. package/components/selects/FSSelectListMode.vue +2 -2
  171. package/components/selects/FSSelectMapLayer.vue +68 -0
  172. package/components/selects/FSSelectMonths.vue +13 -13
  173. package/components/selects/chartSelectors/FSAggregationSelector.vue +52 -0
  174. package/components/selects/chartSelectors/FSAxisTypeSelector.vue +49 -0
  175. package/components/selects/chartSelectors/FSDisplayAsSelector.vue +53 -0
  176. package/components/selects/chartSelectors/FSFilterTypeSelector.vue +54 -0
  177. package/components/selects/chartSelectors/FSHeatmapRuleSelector.vue +54 -0
  178. package/components/selects/chartSelectors/FSOperationOnSelector.vue +53 -0
  179. package/components/selects/chartSelectors/FSPlanningTypeSelector.vue +53 -0
  180. package/components/selects/chartSelectors/FSPlotPerSelector.vue +52 -0
  181. package/components/selects/chartSelectors/FSSelectEntityType.vue +59 -0
  182. package/components/selects/chartSelectors/FSSerieTypeSelector.vue +53 -0
  183. package/components/tiles/FSAlertTileUI.vue +90 -0
  184. package/components/tiles/FSChartTileUI.vue +61 -52
  185. package/components/tiles/FSCommentTileUI.vue +38 -13
  186. package/components/tiles/FSDashboardOrganisationTypeTileUI.vue +14 -2
  187. package/components/tiles/FSDashboardShallowTileUI.vue +14 -2
  188. package/components/tiles/FSDeviceOrganisationTileUI.vue +0 -6
  189. package/components/tiles/FSEntityCountBadge.vue +72 -0
  190. package/components/tiles/FSFolderTileUI.vue +38 -4
  191. package/components/tiles/FSGroupTileUI.vue +32 -136
  192. package/components/tiles/FSLoadTile.vue +16 -10
  193. package/components/tiles/FSLocationTileUI.vue +45 -63
  194. package/components/tiles/FSServiceAccountOrganisationTileUI.vue +0 -6
  195. package/components/tiles/FSSimpleTileUI.vue +30 -24
  196. package/components/tiles/FSTile.vue +46 -39
  197. package/components/tiles/FSUserOrganisationTileUI.vue +1 -7
  198. package/components/tiles/FSUserTileUI.vue +119 -0
  199. package/components/toggleSets/FSToggleSetPosition.vue +1 -1
  200. package/components/views/FSBaseView.vue +64 -0
  201. package/components/views/FSEntityView.vue +12 -146
  202. package/components/views/FSSimpleView.vue +29 -0
  203. package/components/views/desktop/FSBaseDefaultDesktopView.vue +135 -0
  204. package/components/views/desktop/FSBaseDesktopView.vue +53 -0
  205. package/components/views/desktop/FSBaseEntityDesktopView.vue +211 -0
  206. package/components/views/mobile/FSBaseDefaultMobileView.vue +133 -0
  207. package/components/views/mobile/FSBaseEntityMobileView.vue +206 -0
  208. package/components/views/mobile/FSBaseMobileView.vue +53 -0
  209. package/composables/index.ts +1 -0
  210. package/composables/useAddress.ts +40 -8
  211. package/composables/useBreakpoints.ts +40 -4
  212. package/composables/useColors.ts +16 -7
  213. package/composables/useMagicFieldProvider.ts +1 -0
  214. package/composables/useMapLayers.ts +69 -0
  215. package/composables/useSlots.ts +2 -1
  216. package/models/colors.ts +2 -1
  217. package/models/deviceAlerts.ts +1 -0
  218. package/models/magicFields.ts +1 -0
  219. package/models/map.ts +12 -10
  220. package/models/rules.ts +5 -2
  221. package/models/tables.ts +2 -1
  222. package/models/variableNode.ts +8 -5
  223. package/package.json +5 -5
  224. package/styles/components/fs_agenda.scss +4 -0
  225. package/styles/components/fs_agenda_event.scss +1 -1
  226. package/styles/components/fs_agenda_hours_row.scss +0 -8
  227. package/styles/components/fs_autocomplete_field.scss +0 -13
  228. package/styles/components/fs_breadcrumbs.scss +18 -36
  229. package/styles/components/fs_button.scss +7 -5
  230. package/styles/components/fs_calendar.scss +1 -0
  231. package/styles/components/fs_card.scss +9 -0
  232. package/styles/components/fs_chip.scss +13 -7
  233. package/styles/components/fs_clickable.scss +16 -23
  234. package/styles/components/fs_clock.scss +0 -10
  235. package/styles/components/fs_col.scss +1 -1
  236. package/styles/components/fs_color_field.scss +0 -4
  237. package/styles/components/fs_data_table.scss +6 -9
  238. package/styles/components/fs_dialog.scss +4 -10
  239. package/styles/components/fs_dialog_menu.scss +4 -2
  240. package/styles/components/fs_draggable.scss +0 -5
  241. package/styles/components/fs_fade_out.scss +10 -2
  242. package/styles/components/fs_filter_button.scss +1 -1
  243. package/styles/components/fs_hidden_button.scss +2 -7
  244. package/styles/components/fs_image_card.scss +6 -4
  245. package/styles/components/fs_magic_config_field.scss +1 -2
  246. package/styles/components/fs_map.scss +57 -17
  247. package/styles/components/fs_meta_field.scss +3 -5
  248. package/styles/components/fs_password_field.scss +4 -2
  249. package/styles/components/fs_progress_bar.scss +14 -0
  250. package/styles/components/fs_radio.scss +0 -11
  251. package/styles/components/fs_rich_text_field.scss +1 -10
  252. package/styles/components/fs_select_field.scss +4 -13
  253. package/styles/components/fs_slide_group.scss +7 -0
  254. package/styles/components/fs_slider.scss +0 -40
  255. package/styles/components/fs_span.scss +13 -5
  256. package/styles/components/fs_status_rich_card.scss +6 -0
  257. package/styles/components/fs_tabs.scss +18 -37
  258. package/styles/components/fs_tag.scss +8 -22
  259. package/styles/components/fs_text_area.scss +2 -21
  260. package/styles/components/fs_tile.scss +0 -19
  261. package/styles/components/fs_window.scss +3 -1
  262. package/styles/components/fs_wrap_group.scss +7 -0
  263. package/styles/components/index.scss +2 -6
  264. package/styles/globals/index.scss +1 -5
  265. package/styles/globals/overrides.scss +26 -54
  266. package/styles/globals/scrollbars.scss +8 -0
  267. package/styles/globals/text_fonts.scss +18 -66
  268. package/styles/globals/touchscreen.scss +2 -2
  269. package/tools/alertsTools.ts +94 -18
  270. package/tools/chartsTools.ts +155 -16
  271. package/tools/index.ts +3 -1
  272. package/tools/reportsTools.ts +38 -0
  273. package/tools/timeRangeTools.ts +125 -0
  274. package/utils/badge.ts +9 -5
  275. package/utils/filter.ts +4 -1
  276. package/utils/index.ts +2 -0
  277. package/utils/leafletMarkers.ts +4 -4
  278. package/utils/operations.ts +108 -0
  279. package/utils/picker.ts +40 -0
  280. package/utils/sort.ts +2 -2
  281. package/utils/time.ts +13 -13
  282. package/components/fields/FSTimeSlotField.vue +0 -250
  283. package/components/views/FSEntityHeader.vue +0 -343
  284. package/components/views/FSListHeader.vue +0 -83
  285. package/components/views/FSListView.vue +0 -83
  286. package/components/views/FSSkeletonView.vue +0 -100
  287. package/styles/components/fs_data_iterator_item.scss +0 -33
  288. package/styles/components/fs_icon_field.scss +0 -12
  289. package/styles/components/fs_search_field.scss +0 -3
  290. package/styles/components/fs_tag_field.scss +0 -8
  291. package/styles/components/fs_time_field.scss +0 -12
  292. package/styles/components/fs_timeslot_field.scss +0 -12
  293. package/styles/globals/breakpoints.scss +0 -20
  294. package/styles/globals/fixes.scss +0 -5
@@ -14,7 +14,7 @@
14
14
  v-if="map"
15
15
  >
16
16
  <FSMapTileLayer
17
- :layer="actualLayer"
17
+ :layers="actualLayer"
18
18
  />
19
19
  <FSMapMarker
20
20
  v-if="gpsPosition"
@@ -22,43 +22,14 @@
22
22
  :color="ColorEnum.Primary"
23
23
  :latlng="gpsPosition"
24
24
  />
25
-
26
- <FSMapFeatureGroup
27
- v-if="$props.areas"
28
- :expected-layers="$props.areas.length"
29
- @update:bounds="(bounds) => areaGroupBounds = bounds"
30
- >
31
- <FSMapPolygon
32
- v-for="area in areas"
33
- :key="area.id"
34
- :color="area.color"
35
- :latlngs="area.coordinates.map((coord) => ({lat: coord.latitude, lng: coord.longitude}))"
36
- @click="$emit('update:selectedAreaId', area.id)"
37
- />
38
- </FSMapFeatureGroup>
39
-
40
- <FSMapMarkerClusterGroup
41
- v-if="$props.locations"
42
- :expected-layers="$props.locations.length"
43
- :disableClusteringAtZoom="defaultZoom"
44
- @update:bounds="(bounds) => locationGroupBounds = bounds"
45
- >
46
- <FSMapMarker
47
- v-for="location in $props.locations"
48
- :selected="location.id === $props.selectedLocationId"
49
- :key="location.id"
50
- :color="location.color ?? ColorEnum.Primary"
51
- :icon="location.icon ?? 'mdi-map-marker'"
52
- :latlng="{lat: location.address.latitude, lng: location.address.longitude}"
53
- @click="$emit('update:selectedLocationId', location.id)"
54
- />
55
- </FSMapMarkerClusterGroup>
25
+ <slot />
56
26
  </template>
57
27
  </div>
58
28
 
59
29
  <FSMapLayerButton
60
30
  v-if="$props.allowedLayers?.length && $props.allowedLayers.length > 1"
61
- :layers="mapLayers.filter((layer) => $props.allowedLayers?.includes(layer.name) ?? true)"
31
+ :disabled="$props.disabled"
32
+ :layers="layers.filter((layer) => $props.allowedLayers?.includes(layer.name) ?? true)"
62
33
  :modelValue="$props.currentLayer"
63
34
  @update:model-value="$emit('update:currentLayer', $event)"
64
35
  />
@@ -69,6 +40,7 @@
69
40
  >
70
41
  <FSButton
71
42
  v-if="$props.showMyLocation"
43
+ :disabled="$props.disabled"
72
44
  icon="mdi-crosshairs-gps"
73
45
  color="primary"
74
46
  variant="full"
@@ -83,12 +55,14 @@
83
55
  gap="0"
84
56
  >
85
57
  <FSButton
58
+ :disabled="$props.disabled"
86
59
  class="fs-map-zoom-plus-button"
87
60
  icon="mdi-plus"
88
61
  @click="() => map!.zoomIn()"
89
62
  :border="false"
90
63
  />
91
64
  <FSButton
65
+ :disabled="$props.disabled"
92
66
  class="fs-map-zoom-minus-button"
93
67
  icon="mdi-minus"
94
68
  @click="() => map!.zoomOut()"
@@ -99,17 +73,19 @@
99
73
  </FSCol>
100
74
 
101
75
  <FSMapOverlay
102
- v-if="$slots['overlay']"
76
+ v-if="overlaySlots && Object.keys(overlaySlots).length"
103
77
  :mode="$props.overlayMode"
104
78
  @update:mode="$emit('update:overlayMode', $event)"
105
79
  @update:height="(height) => overlayHeight = height"
106
80
  @update:width="(width) => overlayWidth = width"
107
81
  >
108
82
  <template
109
- #body
83
+ v-for="(_, name) in overlaySlots"
84
+ v-slot:[name]="slotData"
110
85
  >
111
86
  <slot
112
- name="overlay"
87
+ :name="'overlay-' + name"
88
+ v-bind="slotData"
113
89
  />
114
90
  </template>
115
91
  </FSMapOverlay>
@@ -120,13 +96,10 @@
120
96
  import { computed, defineComponent, onMounted, type Ref, provide, type PropType, ref, type StyleValue, watch, onUnmounted, markRaw } from "vue";
121
97
 
122
98
  import type {} from "leaflet.markercluster";
123
- import { map as createMap, control, tileLayer, latLngBounds, latLng, type LatLng, LatLngBounds, type FitBoundsOptions } from "leaflet";
99
+ import { map as createMap, control, latLngBounds, latLng, type LatLng, type FitBoundsOptions, type ZoomPanOptions, type LatLngBounds } from "leaflet";
124
100
 
125
- import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
126
- import { type FSArea } from '@dative-gpi/foundation-shared-domain/models';
127
-
128
- import { useBreakpoints, useColors } from "../../composables";
129
- import { ColorEnum, type FSLocation, type MapLayer } from "../../models";
101
+ import { useBreakpoints, useColors, useMapLayers, useSlots } from "../../composables";
102
+ import { ColorEnum, MapLayers, MapOverlayPositions } from "../../models";
130
103
 
131
104
  import FSMapLayerButton from "./FSMapLayerButton.vue";
132
105
  import FSMapOverlay from "./FSMapOverlay.vue";
@@ -136,19 +109,12 @@ import FSCol from "../FSCol.vue";
136
109
 
137
110
  import FSMapMarker from "./FSMapMarker.vue";
138
111
  import FSMapTileLayer from "./FSMapTileLayer.vue";
139
- import FSMapFeatureGroup from "./FSMapFeatureGroup.vue";
140
- import FSMapMarkerClusterGroup from "./FSMapMarkerClusterGroup.vue";
141
- import FSMapPolygon from "./FSMapPolygon.vue";
142
112
 
143
113
  export default defineComponent({
144
114
  name: "FSMap",
145
115
  components: {
146
116
  FSMapMarker,
147
117
  FSMapTileLayer,
148
- FSMapFeatureGroup,
149
- FSMapMarkerClusterGroup,
150
- FSMapPolygon,
151
-
152
118
  FSMapLayerButton,
153
119
  FSMapOverlay,
154
120
  FSButton,
@@ -166,15 +132,20 @@ export default defineComponent({
166
132
  required: false,
167
133
  default: '100%'
168
134
  },
135
+ disabled: {
136
+ type: Boolean,
137
+ required: false,
138
+ default: false
139
+ },
169
140
  grayscale: {
170
141
  type: Boolean,
171
142
  required: false,
172
143
  default: false
173
144
  },
174
145
  overlayMode: {
175
- type: String as PropType<'collapse' | 'half' | 'expand'>,
146
+ type: String as PropType<MapOverlayPositions>,
176
147
  required: false,
177
- default: 'collapse'
148
+ default: MapOverlayPositions.Collapse
178
149
  },
179
150
  showMyLocation: {
180
151
  type: Boolean,
@@ -192,50 +163,39 @@ export default defineComponent({
192
163
  default: false
193
164
  },
194
165
  center: {
195
- type: Array as PropType<number[]>,
196
- required: false,
197
- default: () => [45.71, 5.07]
198
- },
199
- locations: {
200
- type: Array as PropType<FSLocation[]>,
166
+ type: Array as PropType<number[] | null>,
201
167
  required: false,
202
- default: () => [],
168
+ default: null
203
169
  },
204
- areas: {
205
- type: Array as PropType<FSArea[]>,
170
+ bounds: {
171
+ type: Object as PropType<LatLngBounds | null>,
206
172
  required: false,
207
- default: () => [],
173
+ default: null
208
174
  },
209
175
  currentLayer: {
210
- type: String as PropType<"map" | "imagery">,
176
+ type: String as PropType<MapLayers>,
211
177
  required: false,
212
- default: "map"
178
+ default: MapLayers.Map
213
179
  },
214
180
  allowedLayers: {
215
- type: Array as PropType<string[]>,
181
+ type: Array as PropType<MapLayers[]>,
216
182
  required: false,
217
- default: () => ["map", "imagery"]
183
+ default: () => [MapLayers.Map, MapLayers.Imagery]
218
184
  },
219
- selectedLocationId: {
220
- type: String as PropType<string | null>,
185
+ zoom: {
186
+ type: Number,
221
187
  required: false,
222
- default: null
223
- },
224
- selectedAreaId: {
225
- type: String as PropType<string | null>,
226
- required: false,
227
- default: null
188
+ default: 16
228
189
  }
229
190
  },
230
- emits: ["update:modelValue", "update:selectedLocationId", "update:selectedAreaId", 'update:overlayMode', 'update:currentLayer', "click:latlng"],
191
+ emits: ['update:overlayMode', 'update:currentLayer', "click:latlng", "update:zoom", "update:center"],
231
192
  setup(props, { emit }) {
232
- const { $tr } = useTranslationsProvider();
233
- const { getColors } = useColors();
193
+ const { layers } = useMapLayers();
234
194
  const { isExtraSmall } = useBreakpoints();
195
+ const { getColors } = useColors();
196
+ const { slots } = useSlots();
235
197
 
236
198
  const leafletContainer = ref<HTMLElement>();
237
- const locationGroupBounds = ref<LatLngBounds>();
238
- const areaGroupBounds = ref<LatLngBounds>();
239
199
  const gpsPosition : Ref<LatLng | null> = ref(null);
240
200
  const map: Ref<L.Map | null> = ref(null);
241
201
  const overlayHeight = ref<number>();
@@ -243,7 +203,6 @@ export default defineComponent({
243
203
 
244
204
  provide('map', map);
245
205
 
246
- const defaultZoom = 16;
247
206
  const mapResizeObserver = new ResizeObserver(() => {
248
207
  if(!map.value) {
249
208
  return;
@@ -251,31 +210,8 @@ export default defineComponent({
251
210
  map.value.invalidateSize();
252
211
  });
253
212
 
254
- const mapLayers: MapLayer[] = [
255
- {
256
- name: "map",
257
- label: $tr("ui.map.layer.map", "Map"),
258
- image: new URL("../../assets/images/map/map.png", import.meta.url).href,
259
- layer: tileLayer(`http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? ""}`, {
260
- maxZoom: 22,
261
- subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
262
- attribution: '© Google Map Data'
263
- })
264
- },
265
- {
266
- name: "imagery",
267
- label: $tr("ui.map.layer.imagery", "Imagery"),
268
- image: new URL("../../assets/images/map/imagery.png", import.meta.url).href,
269
- 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 ?? ""}`, {
270
- maxZoom: 22,
271
- subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
272
- attribution: '© Google Map Data'
273
- })
274
- }
275
- ];
276
-
277
213
  const bottomOffset = computed(() => {
278
- if (props.overlayMode !== 'expand' && overlayHeight.value && isExtraSmall.value) {
214
+ if (props.overlayMode !== MapOverlayPositions.Expand && overlayHeight.value && isExtraSmall.value) {
279
215
  return overlayHeight.value;
280
216
  }
281
217
  return 0;
@@ -297,20 +233,14 @@ export default defineComponent({
297
233
  }));
298
234
 
299
235
  const actualLayer = computed(() => {
300
- return mapLayers.find((layer) => layer.name === props.currentLayer)?.layer ?? mapLayers[0].layer;
236
+ return layers.value.find((mapLayer) => mapLayer.name === props.currentLayer)?.layers;
301
237
  });
302
238
 
303
- const bounds = computed<LatLngBounds | null>(() => {
304
- if(!locationGroupBounds.value && !areaGroupBounds.value) {
305
- return null;
306
- }
307
- let bounds = locationGroupBounds.value;
308
- if(bounds && areaGroupBounds.value) {
309
- bounds.extend(areaGroupBounds.value);
310
- } else if(areaGroupBounds.value) {
311
- bounds = areaGroupBounds.value;
312
- }
313
- return bounds as LatLngBounds;
239
+ const overlaySlots = computed(() => {
240
+ return Object.keys(slots).filter((slot) => slot.startsWith("overlay-")).reduce((acc, slot) => {
241
+ acc[slot.replace("overlay-", "")] = slots[slot];
242
+ return acc;
243
+ }, {} as Record<string, unknown>);
314
244
  });
315
245
 
316
246
  const calculateTargetPosition = (target: L.LatLng, zoom?: number) => {
@@ -322,30 +252,55 @@ export default defineComponent({
322
252
  return map.value.unproject(targetPoint, zoom);
323
253
  }
324
254
 
325
- const flyTo = (lat: number, lng: number, zoom: number = defaultZoom) => {
255
+ const flyTo = (lat: number, lng: number, zoom: number, options?: ZoomPanOptions) => {
326
256
  if(!map.value) {
327
257
  return;
328
258
  }
329
- map.value.flyTo(calculateTargetPosition(latLng(lat, lng), zoom), zoom);
259
+
260
+ if(isExtraSmall.value) {
261
+ // We wait for bottom offset to be calculated and stable to focus on the right position
262
+ let tries = 0;
263
+ let oldBottomOffset = bottomOffset.value;
264
+ const interval = setInterval(() => {
265
+ if(oldBottomOffset === bottomOffset.value || tries >= 30) {
266
+ clearInterval(interval);
267
+ map.value!.flyTo(calculateTargetPosition(latLng(lat, lng), zoom), zoom, options);
268
+ }
269
+ oldBottomOffset = bottomOffset.value;
270
+ tries++;
271
+ }, 20);
272
+
273
+ } else {
274
+ map.value.flyTo(calculateTargetPosition(latLng(lat, lng), zoom), zoom, options);
275
+ }
330
276
  }
331
277
 
332
278
  const setView = (lat: number, lng: number, zoom: number) => {
333
279
  if(!map.value) {
334
280
  return;
335
281
  }
336
- map.value.setView(calculateTargetPosition(latLng(lat, lng)), zoom);
282
+ map.value.setView(calculateTargetPosition(latLng(lat, lng), zoom), zoom);
337
283
  }
338
284
 
339
285
  const fitBounds = (bounds: LatLngBounds, options?: FitBoundsOptions) => {
340
- if(!map.value) {
341
- return;
342
- }
343
- const calculatedBounds = new LatLngBounds(
344
- calculateTargetPosition(bounds.getSouthWest()),
345
- calculateTargetPosition(bounds.getNorthEast())
346
- );
347
- map.value.fitBounds(calculatedBounds, options);
348
- }
286
+ if (!map.value) {return;}
287
+ const paddingTopLeft: [number, number] = [
288
+ leftOffset.value + 24,
289
+ 24
290
+ ];
291
+
292
+ const paddingBottomRight: [number, number] = [
293
+ 24,
294
+ bottomOffset.value + 24
295
+ ];
296
+ const paddingOptions = {
297
+ paddingTopLeft,
298
+ paddingBottomRight,
299
+ ...options,
300
+ };
301
+
302
+ map.value.fitBounds(bounds, paddingOptions);
303
+ };
349
304
 
350
305
  onMounted(() => {
351
306
  if(!leafletContainer.value) {
@@ -354,22 +309,39 @@ export default defineComponent({
354
309
 
355
310
  const mapOptions = {
356
311
  zoomControl: false,
357
- scrollWheelZoom: props.enableScrollWheelZoom,
312
+ scrollWheelZoom: props.enableScrollWheelZoom && !props.disabled,
313
+ dragging: !props.disabled,
314
+ doubleClickZoom: false,
358
315
  minZoom: 2,
359
316
  maxZoom: 22,
360
317
  maxBounds: latLngBounds(latLng(-90, -180), latLng(90, 180)),
361
- maxBoundsViscosity: 1.0
362
- };
318
+ maxBoundsViscosity: 1.0,
319
+ zoom: props.zoom,
320
+ center: props.center ? latLng(props.center[0], props.center[1]) : latLng(48.85782, 2.29521)
321
+ } satisfies L.MapOptions;
363
322
 
364
323
  map.value = markRaw(createMap(leafletContainer.value, mapOptions));
365
- setView(props.center[0], props.center[1], defaultZoom);
366
324
 
367
325
  map.value.on('click', (e: L.LeafletMouseEvent) => {
368
326
  emit('click:latlng', e.latlng);
369
327
  });
370
328
 
329
+ map.value.on('zoomend', () => {
330
+ if(!map.value) {
331
+ return;
332
+ }
333
+ emit('update:zoom', map.value.getZoom());
334
+ });
335
+
336
+ map.value.on('moveend', () => {
337
+ if(!map.value) {
338
+ return;
339
+ }
340
+ const center = map.value.getCenter();
341
+ emit('update:center', [center.lat, center.lng]);
342
+ });
343
+
371
344
  map.value.attributionControl.remove();
372
- // to display google attribution in bottom left corner
373
345
  control.attribution({ position: 'bottomleft' }).addTo(map.value);
374
346
 
375
347
  map.value.on('locationfound', (e: L.LocationEvent) => {
@@ -383,7 +355,7 @@ export default defineComponent({
383
355
  return;
384
356
  }
385
357
 
386
- flyTo(e.latlng.lat, e.latlng.lng);
358
+ flyTo(e.latlng.lat, e.latlng.lng, 14);
387
359
  });
388
360
 
389
361
  mapResizeObserver.observe(leafletContainer.value);
@@ -393,56 +365,70 @@ export default defineComponent({
393
365
  mapResizeObserver.disconnect();
394
366
  });
395
367
 
396
- watch (() => props.center, (center) => {
397
- if(!map.value) {
368
+ watch ([() => props.center, () => props.zoom], ([newCenter, newZoom], [oldCenter, oldZoom]) => {
369
+ if(!map.value || !props.center || !newCenter) {
398
370
  return;
399
371
  }
400
- setView(center[0], center[1], defaultZoom);
401
- });
402
372
 
403
- watch (() => props.selectedLocationId, (selectedLocationId) => {
404
- if(!map.value) {
373
+ if(map.value.getZoom() === newZoom && map.value.getCenter().equals(latLng(newCenter[0], newCenter[1]))) {
405
374
  return;
406
375
  }
407
- const selectedLocation = props.locations.find((location) => location.id === selectedLocationId);
408
- if(!selectedLocation) {
409
- return;
376
+
377
+ if((newCenter[0] !== oldCenter?.[0] || newCenter[1] !== oldCenter?.[1]) && newZoom !== oldZoom) {
378
+ setView(newCenter[0], newCenter[1], newZoom);
379
+ }
380
+ else if ((newCenter[0] !== oldCenter?.[0] || newCenter[1] !== oldCenter?.[1])) {
381
+ setView(newCenter[0], newCenter[1], map.value.getZoom());
382
+ }
383
+ else if(newZoom !== oldZoom) {
384
+ map.value.setZoom(newZoom);
410
385
  }
411
- flyTo(selectedLocation?.address.latitude, selectedLocation?.address.longitude);
412
386
  }, { immediate: true });
413
387
 
414
- watch(() => props.selectedAreaId, (selectedAreaId) => {
415
- if(!map.value) {
388
+ watch([() => props.bounds, () => map.value], () => {
389
+ if(!map.value || !props.bounds) {
416
390
  return;
417
391
  }
418
- const selectedArea = props.areas.find((area) => area.id === selectedAreaId);
419
- if(!selectedArea) {
392
+ fitBounds(props.bounds, { maxZoom: 14 });
393
+ });
394
+
395
+ watch(() => props.enableScrollWheelZoom, (newValue) => {
396
+ if(!map.value) {
420
397
  return;
421
398
  }
422
- const bounds = latLngBounds(selectedArea.coordinates.map((coord) => latLng(coord.latitude, coord.longitude)));
423
- fitBounds(bounds);
399
+ if(newValue) {
400
+ map.value.scrollWheelZoom.enable();
401
+ } else {
402
+ map.value.scrollWheelZoom.disable();
403
+ }
424
404
  }, { immediate: true });
425
405
 
426
- watch( () => bounds.value, (bounds) => {
427
- if(!map.value || !bounds) {
406
+ watch(() => props.disabled, (newValue) => {
407
+ if(!map.value) {
428
408
  return;
429
409
  }
430
- fitBounds(bounds, { maxZoom: defaultZoom });
431
- });
410
+ if(newValue) {
411
+ map.value.dragging.disable();
412
+ map.value.scrollWheelZoom.disable();
413
+ } else {
414
+ map.value.dragging.enable();
415
+ if(props.enableScrollWheelZoom) {
416
+ map.value.scrollWheelZoom.enable();
417
+ }
418
+ }
419
+ }, { immediate: true });
432
420
 
433
421
  return {
434
422
  ColorEnum,
435
- defaultZoom,
436
423
  leafletContainer,
437
- locationGroupBounds,
438
424
  overlayHeight,
439
425
  overlayWidth,
440
- areaGroupBounds,
441
426
  map,
442
427
  actualLayer,
443
- mapLayers,
428
+ layers,
444
429
  gpsPosition,
445
- style
430
+ style,
431
+ overlaySlots
446
432
  };
447
433
  }
448
434
  });