@rural-environmental-registry/map_component 1.2.0 → 2.0.1

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.
@@ -8,21 +8,26 @@
8
8
  <FontAwesomeIcon :iconName="iconButton" />
9
9
  </ElButton>
10
10
  <ElMenu
11
+ :key="menuOpenedsKey"
11
12
  :class="customClasses.customMenu"
13
+ :default-openeds="defaultOpeneds"
12
14
  mode="vertical"
15
+ @close="onGroupMenuClose"
16
+ @open="onGroupMenuOpen"
13
17
  >
14
18
  <div id="external-id-top-menu"></div>
15
- <template
16
- v-for="group in props.layersConfig"
17
- :key="group.key"
18
- >
19
- <ParentMenu
20
- :groupData="group"
21
- :persist="!!props.options?.persist"
22
- @onChildLayerToggle="onChildLayerChange"
23
- @onGroupLayerToggle="onGroupLayerToggle"
24
- />
25
- </template>
19
+ <SectionMenu
20
+ v-if="normalizedSections.length"
21
+ :editingLayerKey="activeEditingLayerKey"
22
+ :persist="!!props.options?.persist"
23
+ :sections="normalizedSections"
24
+ :selectedSectionKey="resolvedSelectedSectionKey"
25
+ :visibilityMode="visibilityMode"
26
+ @onChildLayerToggle="onChildLayerChange"
27
+ @onGroupLayerToggle="onGroupLayerToggle"
28
+ @onLayerAction="onLayerAction"
29
+ @onSelectedSectionChange="onSelectedSectionChange"
30
+ />
26
31
  <div id="external-id-bottom-menu"></div>
27
32
  </ElMenu>
28
33
  </div>
@@ -33,9 +38,26 @@
33
38
  import L from 'leaflet'
34
39
  import { computed, nextTick, ref, watch } from 'vue'
35
40
  import FontAwesomeIcon from '../fa-icon/FontAwesomeIcon.vue'
36
- import ParentMenu from './ParentMenu.vue'
37
- import { GroupLayerData, LayerData, LayersConfig, LayersMenuConfig } from '../../types'
38
- import { resolveLayerActiveState, shouldInitLayerOnMap } from '../../utils/menuHistory.ts'
41
+ import SectionMenu from './SectionMenu.vue'
42
+ import {
43
+ GroupLayerData,
44
+ LayerActionPayload,
45
+ LayerData,
46
+ LayersConfig,
47
+ LayersMenuConfig,
48
+ SectionEditStatePayload,
49
+ SectionSelectPayload
50
+ } from '../../types'
51
+ import {
52
+ normalizeLayersConfig,
53
+ shouldApplyMapOverlay
54
+ } from '../../utils/layersConfigNormalizer'
55
+ import {
56
+ resolveGroupCollapsedState,
57
+ resolveLayerActiveState,
58
+ setGroupCollapsedHistory,
59
+ shouldInitLayerOnMap
60
+ } from '../../utils/menuHistory.ts'
39
61
 
40
62
  type MenuProps = {
41
63
  layersConfig: LayersConfig
@@ -57,13 +79,112 @@
57
79
  (e: 'stopLoading'): void
58
80
  (e: 'onChildLayerToggle', data: LayerData): void
59
81
  (e: 'onGroupLayerToggle', data: GroupLayerData): void
82
+ (e: 'onLayerAction', payload: LayerActionPayload): void
83
+ (e: 'onSectionEditStateChange', payload: SectionEditStatePayload): void
84
+ (e: 'onSelectedSectionChange', payload: SectionSelectPayload): void
60
85
  }>()
61
86
 
62
87
  const props = defineProps<MenuProps>()
63
88
 
64
- const isMenuOpen = ref<boolean>(false)
89
+ const isMenuOpen = ref<boolean>(!!props.options?.defaultOpen)
65
90
  const convertedLayers = ref<ConvertedLayers>({})
66
91
  const convertedGeoJsonLayers = ref<ConvertedGeoJsonLayers>({})
92
+ const internalEditingLayerKey = ref<string | null>(null)
93
+ const internalSelectedSectionKey = ref<string | null>(null)
94
+
95
+ const normalizedSections = computed(() => normalizeLayersConfig(props.layersConfig))
96
+
97
+ const visibilityMode = computed(() => props.options?.visibilityMode ?? 'switch')
98
+
99
+ const isEditingControlled = computed(() => props.options?.editingLayerKey !== undefined)
100
+
101
+ const activeEditingLayerKey = computed(() => {
102
+ if (isEditingControlled.value) {
103
+ return props.options?.editingLayerKey ?? null
104
+ }
105
+ return internalEditingLayerKey.value
106
+ })
107
+
108
+ const isSectionSelectionControlled = computed(
109
+ () => props.options?.selectedSectionKey !== undefined
110
+ )
111
+
112
+ const activeSelectedSectionKey = computed(() => {
113
+ if (isSectionSelectionControlled.value) {
114
+ return props.options?.selectedSectionKey ?? null
115
+ }
116
+ return internalSelectedSectionKey.value
117
+ })
118
+
119
+ const resolvedSelectedSectionKey = computed((): string | null => {
120
+ const sections = normalizedSections.value
121
+ if (!sections.length) return null
122
+
123
+ const current = activeSelectedSectionKey.value
124
+ if (current && sections.some((section) => section.key === current)) {
125
+ return current
126
+ }
127
+
128
+ return sections[0].key
129
+ })
130
+
131
+ watch(
132
+ normalizedSections,
133
+ (sections) => {
134
+ if (isSectionSelectionControlled.value) return
135
+
136
+ const current = internalSelectedSectionKey.value
137
+ if (current && sections.some((section) => section.key === current)) return
138
+
139
+ internalSelectedSectionKey.value = sections[0]?.key ?? null
140
+ },
141
+ { immediate: true }
142
+ )
143
+
144
+ const defaultOpeneds = computed(() => {
145
+ const persist = !!props.options?.persist
146
+ const openeds: string[] = []
147
+
148
+ normalizedSections.value.forEach((section) => {
149
+ section.groups.forEach((group) => {
150
+ const collapsed =
151
+ group.collapsed !== undefined
152
+ ? group.collapsed
153
+ : resolveGroupCollapsedState(group.key, undefined, persist)
154
+ if (!collapsed) openeds.push(group.key)
155
+ })
156
+ })
157
+
158
+ return openeds
159
+ })
160
+
161
+ const menuOpenedsKey = computed(() => {
162
+ const stepOpenKeys: string[] = []
163
+ normalizedSections.value.forEach((section) => {
164
+ section.groups.forEach((group) => {
165
+ if (group.collapsed === false && group.meta?.isCurrentStep) {
166
+ stepOpenKeys.push(group.key)
167
+ }
168
+ })
169
+ })
170
+ return stepOpenKeys.slice().sort().join('|') || 'none'
171
+ })
172
+
173
+ watch(
174
+ () => menuOpenedsKey.value,
175
+ () => {
176
+ if (!props.options?.persist) return
177
+
178
+ const openSet = new Set(defaultOpeneds.value)
179
+ normalizedSections.value.forEach((section) => {
180
+ section.groups.forEach((group) => {
181
+ if (group.collapsed === undefined) return
182
+ setGroupCollapsedHistory(group.key, !openSet.has(group.key))
183
+ })
184
+ })
185
+ },
186
+ { flush: 'post' }
187
+ )
67
188
 
68
189
  type CustomClasses = {
69
190
  layerMenu: string
@@ -86,12 +207,69 @@
86
207
  return isMenuOpen.value ? 'chevron-left' : 'chevron-right'
87
208
  })
88
209
 
89
- const onInitDefaultLayer = (layer: LayerData): void => {
90
- if (layer.geojson) return handleGeoJsonLayer(layer)
210
+ const findLayerContext = (
211
+ layerKey: string
212
+ ): { sectionKey: string; layer: LayerData } | null => {
213
+ for (const section of normalizedSections.value) {
214
+ for (const group of section.groups) {
215
+ const layer = group.layers?.find((item) => item.key === layerKey)
216
+ if (layer) return { sectionKey: section.key, layer }
217
+ }
218
+ }
219
+ return null
220
+ }
221
+
222
+ const setEditingState = (sectionKey: string | null, layerKey: string | null): void => {
223
+ if (!isEditingControlled.value) {
224
+ internalEditingLayerKey.value = layerKey
225
+ }
226
+
227
+ const context = layerKey ? findLayerContext(layerKey) : null
228
+
229
+ emit('onSectionEditStateChange', {
230
+ sectionKey,
231
+ layerKey,
232
+ layer: context?.layer ?? null
233
+ })
234
+ }
235
+
236
+ const setSelectedSection = (sectionKey: string): void => {
237
+ if (!isSectionSelectionControlled.value) {
238
+ internalSelectedSectionKey.value = sectionKey
239
+ }
240
+
241
+ emit('onSelectedSectionChange', { sectionKey })
242
+ }
243
+
244
+ const onSelectedSectionChange = (sectionKey: string): void => {
245
+ setSelectedSection(sectionKey)
246
+ }
247
+
248
+ const onGroupMenuOpen = (index: string): void => {
249
+ if (!props.options?.persist) return
250
+ setGroupCollapsedHistory(index, false)
251
+ }
252
+
253
+ const onGroupMenuClose = (index: string): void => {
254
+ if (!props.options?.persist) return
255
+ setGroupCollapsedHistory(index, true)
256
+ }
257
+
258
+ const applyLayerOverlay = (layer: LayerData): void => {
259
+ if (!shouldApplyMapOverlay(layer)) return
260
+
261
+ if (layer.geojson || layer.mapSource === 'geojson') {
262
+ handleGeoJsonLayer(layer)
263
+ return
264
+ }
91
265
 
92
266
  handleWmsLayer(layer)
93
267
  }
94
268
 
269
+ const onInitDefaultLayer = (layer: LayerData): void => {
270
+ applyLayerOverlay(layer)
271
+ }
272
+
95
273
  const initializedLayerKeys = new Set<string>()
96
274
 
97
275
  const initDefaultLayers = (): void => {
@@ -99,14 +277,17 @@
99
277
 
100
278
  const persist = !!props.options?.persist
101
279
 
102
- props.layersConfig?.forEach((group) => {
103
- group.layers?.forEach((layer) => {
104
- if (initializedLayerKeys.has(layer.key)) return
105
- if (!shouldInitLayerOnMap(layer, persist)) return
106
-
107
- const resolved = resolveLayerActiveState(layer, persist)
108
- onInitDefaultLayer(resolved)
109
- initializedLayerKeys.add(layer.key)
280
+ normalizedSections.value.forEach((section) => {
281
+ section.groups.forEach((group) => {
282
+ group.layers?.forEach((layer) => {
283
+ if (initializedLayerKeys.has(layer.key)) return
284
+ if (!shouldApplyMapOverlay(layer)) return
285
+ if (!shouldInitLayerOnMap(layer, persist)) return
286
+
287
+ const resolved = resolveLayerActiveState(layer, persist)
288
+ onInitDefaultLayer(resolved)
289
+ initializedLayerKeys.add(layer.key)
290
+ })
110
291
  })
111
292
  })
112
293
  }
@@ -127,29 +308,43 @@
127
308
  )
128
309
 
129
310
  const onChildLayerChange = (layer: LayerData): void => {
130
- if (layer.geojson) {
131
- handleGeoJsonLayer(layer)
132
- } else {
133
- handleWmsLayer(layer)
134
- }
135
-
311
+ applyLayerOverlay(layer)
136
312
  emit('onChildLayerToggle', layer)
137
313
  }
138
314
 
139
315
  const onGroupLayerToggle = (parent: GroupLayerData): void => {
140
316
  parent.layers.forEach((childLayer: LayerData) => {
141
- if (childLayer.geojson) {
142
- handleGeoJsonLayer(childLayer)
143
- } else {
144
- handleWmsLayer(childLayer)
145
- }
317
+ applyLayerOverlay(childLayer)
146
318
  })
147
319
 
148
320
  emit('onGroupLayerToggle', parent)
149
321
  }
150
322
 
323
+ const onLayerAction = (payload: LayerActionPayload): void => {
324
+ const isEditAction = payload.actionType === 'edit' || payload.actionKey === 'edit'
325
+ const isEditControlAction =
326
+ payload.source === 'edit-panel' &&
327
+ (payload.actionType === 'cancel' ||
328
+ payload.actionType === 'conclude' ||
329
+ payload.actionKey === 'cancel' ||
330
+ payload.actionKey === 'conclude')
331
+
332
+ if (isEditAction && payload.source === 'child-menu' && payload.layerKey) {
333
+ setEditingState(payload.sectionKey ?? null, payload.layerKey)
334
+ if (payload.sectionKey) {
335
+ setSelectedSection(payload.sectionKey)
336
+ }
337
+ }
338
+
339
+ if (isEditControlAction) {
340
+ setEditingState(null, null)
341
+ }
342
+
343
+ emit('onLayerAction', payload)
344
+ }
345
+
151
346
  const convertToWmsLayer = (layer: LayerData): L.TileLayer => {
152
- const wmsLayer = L.tileLayer.wms(layer.baseUrl, {
347
+ const wmsLayer = L.tileLayer.wms(layer.baseUrl ?? '', {
153
348
  layers: layer.layers,
154
349
  format: layer.format || 'image/png',
155
350
  transparent: layer.transparent
@@ -274,8 +469,7 @@
274
469
  .layer-menu > .map-custom-menu {
275
470
  box-shadow: var(--mapa-base-box-shadow);
276
471
  padding: var(--mapa-size-base-10);
277
- overflow-y: scroll;
278
- scrollbar-width: none;
472
+ overflow-y: auto;
279
473
  height: 100%;
280
474
  }
281
475