@rural-environmental-registry/map_component 2.0.1 → 2.0.3

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.
@@ -75,7 +75,7 @@ export type LayerData = {
75
75
  name: string;
76
76
  active: boolean;
77
77
  activeDefault: boolean;
78
- role?: 'layer' | 'separator';
78
+ role?: 'layer' | 'separator' | 'subgroup';
79
79
  tooltip?: string;
80
80
  toggle?: {
81
81
  active: string;
@@ -104,6 +104,8 @@ export type LayerData = {
104
104
  infoIcons?: LayerInfoIconConfig[];
105
105
  required?: boolean;
106
106
  editActions?: LayerActionConfig[];
107
+ children?: LayerData[];
108
+ collapsed?: boolean;
107
109
  meta?: Record<string, unknown>;
108
110
  };
109
111
  export type GroupLayerData = {
@@ -5,6 +5,8 @@ export declare const normalizeLayersConfig: (config?: LayersConfig | null) => Se
5
5
  export declare const resolveLayerActions: (layer: LayerData, defaults?: LayerActionDefaults) => LayerActionConfig[];
6
6
  export declare const resolveEditControlAction: (defaults: LayerActionDefaults | undefined, kind: "cancel" | "conclude") => LayerActionConfig;
7
7
  export declare const isMenuSeparator: (layer: LayerData | null | undefined) => boolean;
8
+ export declare const isMenuSubgroup: (layer: LayerData | null | undefined) => boolean;
9
+ export declare const walkMenuLayers: (layers: LayerData[] | undefined, visitor: (layer: LayerData) => void) => void;
8
10
  export declare const shouldApplyMapOverlay: (layer: LayerData) => boolean;
9
11
  export declare const collectLayerMetricsMap: (config?: LayersConfig | null) => LayerMetricsMap;
10
12
  export declare const resolveLayerMetrics: (layer: LayerData | null | undefined) => LayerMetricsConfig[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rural-environmental-registry/map_component",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Map component for Vue.js applications",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",
@@ -49,8 +49,10 @@
49
49
  SectionSelectPayload
50
50
  } from '../../types'
51
51
  import {
52
+ isMenuSubgroup,
52
53
  normalizeLayersConfig,
53
- shouldApplyMapOverlay
54
+ shouldApplyMapOverlay,
55
+ walkMenuLayers
54
56
  } from '../../utils/layersConfigNormalizer'
55
57
  import {
56
58
  resolveGroupCollapsedState,
@@ -152,6 +154,15 @@
152
154
  ? group.collapsed
153
155
  : resolveGroupCollapsedState(group.key, undefined, persist)
154
156
  if (!collapsed) openeds.push(group.key)
157
+
158
+ ;(group.layers || []).forEach((layer) => {
159
+ if (!isMenuSubgroup(layer)) return
160
+ const subCollapsed =
161
+ layer.collapsed !== undefined
162
+ ? layer.collapsed
163
+ : resolveGroupCollapsedState(layer.key, true, persist)
164
+ if (!subCollapsed) openeds.push(layer.key)
165
+ })
155
166
  })
156
167
  })
157
168
 
@@ -212,8 +223,11 @@
212
223
  ): { sectionKey: string; layer: LayerData } | null => {
213
224
  for (const section of normalizedSections.value) {
214
225
  for (const group of section.groups) {
215
- const layer = group.layers?.find((item) => item.key === layerKey)
216
- if (layer) return { sectionKey: section.key, layer }
226
+ let found: LayerData | undefined
227
+ walkMenuLayers(group.layers, (layer) => {
228
+ if (!found && layer.key === layerKey) found = layer
229
+ })
230
+ if (found) return { sectionKey: section.key, layer: found }
217
231
  }
218
232
  }
219
233
  return null
@@ -279,7 +293,7 @@
279
293
 
280
294
  normalizedSections.value.forEach((section) => {
281
295
  section.groups.forEach((group) => {
282
- group.layers?.forEach((layer) => {
296
+ walkMenuLayers(group.layers, (layer) => {
283
297
  if (initializedLayerKeys.has(layer.key)) return
284
298
  if (!shouldApplyMapOverlay(layer)) return
285
299
  if (!shouldInitLayerOnMap(layer, persist)) return
@@ -313,7 +327,7 @@
313
327
  }
314
328
 
315
329
  const onGroupLayerToggle = (parent: GroupLayerData): void => {
316
- parent.layers.forEach((childLayer: LayerData) => {
330
+ walkMenuLayers(parent.layers, (childLayer) => {
317
331
  applyLayerOverlay(childLayer)
318
332
  })
319
333
 
@@ -363,7 +377,8 @@
363
377
 
364
378
  const handleWmsLayer = (layer: LayerData): void => {
365
379
  if (convertedLayers.value[layer.key]) {
366
- if (!layer.active) return removeWmsLayer(layer)
380
+ if (!layer.active) removeWmsLayer(layer)
381
+ return
367
382
  }
368
383
 
369
384
  if (!layer.active) return
@@ -415,7 +430,9 @@
415
430
 
416
431
  const handleGeoJsonLayer = (layer: LayerData): void => {
417
432
  if (convertedGeoJsonLayers.value[layer.key]) {
418
- if (!layer.active) return removeGeoJsonLayer(layer)
433
+ // Já no mapa: só remove se desativada — evita GeoJSON órfão ao reaplicar active=true.
434
+ if (!layer.active) removeGeoJsonLayer(layer)
435
+ return
419
436
  }
420
437
 
421
438
  if (!layer.active) return
@@ -51,8 +51,29 @@
51
51
  v-for="(child, idx) in childrenLayers"
52
52
  :key="child.key"
53
53
  >
54
+ <ElSubMenu
55
+ v-if="isMenuSubgroup(child)"
56
+ :index="child.key"
57
+ class="parent-menu-subgroup-accordion"
58
+ >
59
+ <template #title>
60
+ <div class="parent-menu-subgroup-title">{{ child.name }}</div>
61
+ </template>
62
+ <ChildMenu
63
+ v-for="(nested, nestedIdx) in child.children || []"
64
+ :key="nested.key"
65
+ :actionDefaults="actionDefaults"
66
+ :data="nested"
67
+ :groupKey="groupData.key"
68
+ :persist="props.persist"
69
+ :sectionKey="sectionKey"
70
+ :visibilityMode="visibilityMode"
71
+ @onChildLayerToggle="onNestedChildChange($event, idx, nestedIdx)"
72
+ @onLayerAction="emit('onLayerAction', $event)"
73
+ />
74
+ </ElSubMenu>
54
75
  <ElMenuItem
55
- v-if="isMenuSeparator(child)"
76
+ v-else-if="isMenuSeparator(child)"
56
77
  :index="child.key"
57
78
  class="parent-menu-subgroup-header"
58
79
  disabled
@@ -114,6 +135,7 @@
114
135
  } from '../../types'
115
136
  import {
116
137
  isMenuSeparator,
138
+ isMenuSubgroup,
117
139
  resolveActiveGroupActions
118
140
  } from '../../utils/layersConfigNormalizer'
119
141
  import { resolveLayerActiveState, setHistory } from '../../utils/menuHistory.ts'
@@ -154,16 +176,26 @@
154
176
  onLayerAction: [LayerActionPayload]
155
177
  }>()
156
178
 
179
+ const resolveMenuEntry = (layer: LayerData): LayerData => {
180
+ if (isMenuSubgroup(layer)) {
181
+ return {
182
+ ...layer,
183
+ children: (layer.children || []).map((child) =>
184
+ resolveLayerActiveState(child, props.persist)
185
+ )
186
+ }
187
+ }
188
+ return resolveLayerActiveState(layer, props.persist)
189
+ }
190
+
157
191
  const childrenLayers = ref<LayerData[]>(
158
- (props.groupData.layers ?? []).map((layer) => resolveLayerActiveState(layer, props.persist))
192
+ (props.groupData.layers ?? []).map((layer) => resolveMenuEntry(layer))
159
193
  )
160
194
 
161
195
  watch(
162
196
  () => props.groupData.layers,
163
197
  (layers) => {
164
- childrenLayers.value = (layers ?? []).map((layer) =>
165
- resolveLayerActiveState(layer, props.persist)
166
- )
198
+ childrenLayers.value = (layers ?? []).map((layer) => resolveMenuEntry(layer))
167
199
  },
168
200
  { deep: true }
169
201
  )
@@ -190,9 +222,20 @@
190
222
  'Ocultar'
191
223
  )
192
224
 
193
- const toggleableLayers = computed(() =>
194
- childrenLayers.value.filter((layer: LayerData) => !isMenuSeparator(layer))
195
- )
225
+ const collectToggleableLeaves = (layers: LayerData[]): LayerData[] => {
226
+ const leaves: LayerData[] = []
227
+ for (const layer of layers) {
228
+ if (isMenuSeparator(layer)) continue
229
+ if (isMenuSubgroup(layer)) {
230
+ leaves.push(...collectToggleableLeaves(layer.children || []))
231
+ continue
232
+ }
233
+ leaves.push(layer)
234
+ }
235
+ return leaves
236
+ }
237
+
238
+ const toggleableLayers = computed(() => collectToggleableLeaves(childrenLayers.value))
196
239
 
197
240
  const toggleVisibleAllLayers = (): void => {
198
241
  if (props.persist) {
@@ -210,6 +253,12 @@
210
253
  set: (active: boolean) => {
211
254
  childrenLayers.value = (props.groupData.layers ?? []).map((layer: LayerData) => {
212
255
  if (isMenuSeparator(layer)) return resolveLayerActiveState(layer, props.persist)
256
+ if (isMenuSubgroup(layer)) {
257
+ return {
258
+ ...layer,
259
+ children: (layer.children || []).map((child) => ({ ...child, active }))
260
+ }
261
+ }
213
262
  return { ...layer, active }
214
263
  })
215
264
 
@@ -222,6 +271,19 @@
222
271
  emit('onChildLayerToggle', layer)
223
272
  }
224
273
 
274
+ const onNestedChildChange = (
275
+ layer: LayerData,
276
+ subgroupIdx: number,
277
+ nestedIdx: number
278
+ ): void => {
279
+ const subgroup = childrenLayers.value[subgroupIdx]
280
+ if (!subgroup) return
281
+ const children = [...(subgroup.children || [])]
282
+ children[nestedIdx] = layer
283
+ childrenLayers.value[subgroupIdx] = { ...subgroup, children }
284
+ emit('onChildLayerToggle', layer)
285
+ }
286
+
225
287
  const onGroupActionClick = (action: GroupActionConfig): void => {
226
288
  if (action.disabled) return
227
289
 
@@ -337,7 +399,6 @@
337
399
  margin: 0 !important;
338
400
  }
339
401
 
340
- /* Título de subgrupo: separador visual, sem accordion */
341
402
  .parent-menu .parent-menu-subgroup-header {
342
403
  height: auto !important;
343
404
  line-height: normal !important;
@@ -355,6 +416,21 @@
355
416
  opacity: 1 !important;
356
417
  }
357
418
 
419
+ .parent-menu .parent-menu-subgroup-accordion {
420
+ padding-left: 0 !important;
421
+ }
422
+
423
+ .parent-menu .parent-menu-subgroup-accordion > .el-sub-menu__title {
424
+ height: auto !important;
425
+ line-height: normal !important;
426
+ white-space: normal !important;
427
+ padding: var(--mapa-size-base-10) var(--mapa-size-base-20) var(--mapa-size-base-5) !important;
428
+ }
429
+
430
+ .parent-menu .parent-menu-subgroup-accordion > .el-sub-menu__title:hover {
431
+ background-color: transparent !important;
432
+ }
433
+
358
434
  .parent-menu .parent-menu-subgroup-title {
359
435
  font-size: var(--mapa-fs-12);
360
436
  font-weight: 700;
@@ -148,7 +148,8 @@
148
148
  import {
149
149
  resolveEditControlAction,
150
150
  resolveLayerActions,
151
- resolveLayerMetrics
151
+ resolveLayerMetrics,
152
+ walkMenuLayers
152
153
  } from '../../utils/layersConfigNormalizer'
153
154
 
154
155
  const DEFAULT_ACTION_STYLE: Required<LayerActionStyle> = {
@@ -226,8 +227,11 @@
226
227
  ): { layer: LayerData; group: GroupLayerData } | null => {
227
228
  if (!layerKey || !section.value) return null
228
229
  for (const group of section.value.groups) {
229
- const layer = group.layers?.find((item) => item.key === layerKey)
230
- if (layer) return { layer, group }
230
+ let found: LayerData | undefined
231
+ walkMenuLayers(group.layers, (layer) => {
232
+ if (!found && layer.key === layerKey) found = layer
233
+ })
234
+ if (found) return { layer: found, group }
231
235
  }
232
236
  return null
233
237
  }
package/src/index.ts CHANGED
@@ -40,6 +40,8 @@ export {
40
40
  resolveEditControlAction,
41
41
  shouldApplyMapOverlay,
42
42
  isMenuSeparator,
43
+ isMenuSubgroup,
44
+ walkMenuLayers,
43
45
  collectLayerMetricsMap,
44
46
  resolveLayerMetrics,
45
47
  collectLayerInfoIconsMap,
@@ -117,7 +117,7 @@ export type LayerData = {
117
117
  name: string
118
118
  active: boolean
119
119
  activeDefault: boolean
120
- role?: 'layer' | 'separator'
120
+ role?: 'layer' | 'separator' | 'subgroup'
121
121
  tooltip?: string
122
122
  toggle?: {
123
123
  active: string
@@ -147,6 +147,8 @@ export type LayerData = {
147
147
  infoIcons?: LayerInfoIconConfig[]
148
148
  required?: boolean
149
149
  editActions?: LayerActionConfig[]
150
+ children?: LayerData[]
151
+ collapsed?: boolean
150
152
  meta?: Record<string, unknown>
151
153
  }
152
154
 
@@ -166,8 +166,26 @@ export const isMenuSeparator = (layer: LayerData | null | undefined): boolean =>
166
166
  return layer?.role === 'separator' || !!layer?.meta?.isSeparator
167
167
  }
168
168
 
169
+ export const isMenuSubgroup = (layer: LayerData | null | undefined): boolean => {
170
+ return layer?.role === 'subgroup' || !!layer?.meta?.isSubgroup
171
+ }
172
+
173
+ export const walkMenuLayers = (
174
+ layers: LayerData[] | undefined,
175
+ visitor: (layer: LayerData) => void
176
+ ): void => {
177
+ for (const layer of layers || []) {
178
+ if (isMenuSubgroup(layer) && layer.children?.length) {
179
+ walkMenuLayers(layer.children, visitor)
180
+ continue
181
+ }
182
+ if (isMenuSeparator(layer)) continue
183
+ visitor(layer)
184
+ }
185
+ }
186
+
169
187
  export const shouldApplyMapOverlay = (layer: LayerData): boolean => {
170
- if (isMenuSeparator(layer)) return false
188
+ if (isMenuSeparator(layer) || isMenuSubgroup(layer)) return false
171
189
  if (layer.mapSource === 'none' || layer.mapSource === 'consumer') return false
172
190
  if (layer.mapSource === 'wms' || layer.mapSource === 'geojson') return true
173
191
  if (layer.geojson) return true