@rural-environmental-registry/map_component 1.2.0 → 2.0.0

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.
@@ -1,7 +1,7 @@
1
1
  import { MapOptions, Layer, PathOptions, ControlOptions, ControlPosition } from 'leaflet';
2
2
  import { GeoJsonObject } from 'geojson';
3
3
  import { PM } from '@geoman-io/leaflet-geoman-free';
4
- export type FaIconName = 'check' | 'xmark' | 'chevron-right' | 'chevron-left' | 'terminal' | 'file-lines' | 'upload' | 'pencil' | 'trash' | 'chevron-down';
4
+ export type FaIconName = 'check' | 'xmark' | 'chevron-right' | 'chevron-left' | 'terminal' | 'file-lines' | 'upload' | 'pencil' | 'trash' | 'chevron-down' | 'eye' | 'eye-slash' | 'circle-info' | 'circle-exclamation' | 'triangle-exclamation' | 'circle-check' | 'circle-xmark' | 'ban' | 'lock' | 'unlock' | 'thumbtack' | 'thumb-tack' | 'map-pin';
5
5
  export type DrawnArea = {
6
6
  m2: number;
7
7
  km2: number;
@@ -10,37 +10,135 @@ export type DrawnArea = {
10
10
  export type IncrementedLayer = Layer & {
11
11
  drawnArea: DrawnArea;
12
12
  };
13
- export type LayerData = {
14
- baseUrl: string;
15
- geojson?: GeoJsonObject | GeoJsonObject[];
16
- layers: string;
17
- format: string;
18
- transparent: boolean;
13
+ export type LayerActionStyle = {
14
+ backgroundColor: string;
15
+ color: string;
16
+ borderColor?: string;
17
+ };
18
+ export type LayerActionDefaultItem = {
19
+ key?: string;
19
20
  name: string;
20
- activeDefault: boolean;
21
- cqlFilter?: string;
21
+ icon?: FaIconName;
22
+ style?: LayerActionStyle;
23
+ disabled?: boolean;
24
+ visible?: boolean;
25
+ };
26
+ export type LayerActionDefaults = {
27
+ edit?: LayerActionDefaultItem;
28
+ import?: LayerActionDefaultItem;
29
+ delete?: LayerActionDefaultItem;
30
+ cancel?: LayerActionDefaultItem;
31
+ conclude?: LayerActionDefaultItem;
32
+ };
33
+ export type LayerActionConfig = {
34
+ key: string;
35
+ name: string;
36
+ icon?: FaIconName;
37
+ type?: 'edit' | 'import' | 'delete' | 'visibility' | 'custom' | 'cancel' | 'conclude';
38
+ disabled?: boolean;
39
+ visible?: boolean;
40
+ style?: LayerActionStyle;
41
+ };
42
+ export type LayerMetricsConfig = {
43
+ title: string;
44
+ type: string;
45
+ value: string | number;
46
+ style?: LayerActionStyle;
47
+ };
48
+ /** @deprecated Use LayerMetricsConfig */
49
+ export type LayerMetrics = LayerMetricsConfig;
50
+ export type LayerMetricsMap = Record<string, LayerMetricsConfig[]>;
51
+ export type LayerInfoIconConfig = {
52
+ key?: string;
53
+ icon: FaIconName;
22
54
  active: boolean;
55
+ tooltip?: string;
56
+ style?: Partial<Pick<LayerActionStyle, 'color' | 'backgroundColor'>>;
57
+ };
58
+ export type LayerInfoIconsMap = Record<string, LayerInfoIconConfig[]>;
59
+ export type GroupActionConfig = LayerActionConfig & {
60
+ active: boolean;
61
+ };
62
+ export type GroupActionsMap = Record<string, GroupActionConfig[]>;
63
+ export type SectionConfig = {
64
+ name?: string;
23
65
  key: string;
24
- toggle: {
66
+ groups: GroupLayerData[];
67
+ actionDefaults?: LayerActionDefaults;
68
+ };
69
+ /** @deprecated Use SectionConfig */
70
+ export type SectionData = SectionConfig;
71
+ export type LayerData = {
72
+ key: string;
73
+ name: string;
74
+ active: boolean;
75
+ activeDefault: boolean;
76
+ role?: 'layer' | 'separator';
77
+ tooltip?: string;
78
+ toggle?: {
25
79
  active: string;
26
80
  inactive: string;
27
81
  };
28
- style: {
82
+ visibility?: {
83
+ labelShow?: string;
84
+ labelHide?: string;
85
+ show?: boolean;
86
+ };
87
+ style?: {
29
88
  color: string;
30
89
  fillColor: string;
90
+ icon?: string;
31
91
  };
32
- options?: any;
92
+ baseUrl?: string;
93
+ layers?: string;
94
+ format?: string;
95
+ transparent?: boolean;
96
+ geojson?: GeoJsonObject | GeoJsonObject[];
97
+ cqlFilter?: string;
98
+ options?: Record<string, unknown>;
99
+ mapSource?: 'wms' | 'geojson' | 'none' | 'consumer';
100
+ actions?: LayerActionConfig[];
101
+ metrics?: LayerMetricsConfig[];
102
+ infoIcons?: LayerInfoIconConfig[];
103
+ required?: boolean;
104
+ editActions?: LayerActionConfig[];
105
+ meta?: Record<string, unknown>;
33
106
  };
34
107
  export type GroupLayerData = {
35
108
  name: string;
36
109
  key: string;
37
- toggle: {
110
+ toggle?: {
38
111
  active: string;
39
112
  inactive: string;
40
113
  };
114
+ visibility?: {
115
+ show?: boolean;
116
+ labelShow?: string;
117
+ labelHide?: string;
118
+ };
119
+ collapsed?: boolean;
120
+ actions?: GroupActionConfig[];
41
121
  layers: LayerData[];
122
+ meta?: Record<string, unknown>;
123
+ };
124
+ export type LayersConfig = GroupLayerData[] | SectionConfig[];
125
+ export type LayerActionPayload = {
126
+ actionKey: string;
127
+ actionType?: LayerActionConfig['type'];
128
+ sectionKey?: string;
129
+ groupKey?: string;
130
+ layerKey?: string;
131
+ layer?: LayerData;
132
+ source: 'child-menu' | 'edit-panel' | 'group-menu';
133
+ };
134
+ export type SectionEditStatePayload = {
135
+ sectionKey: string | null;
136
+ layerKey: string | null;
137
+ layer: LayerData | null;
138
+ };
139
+ export type SectionSelectPayload = {
140
+ sectionKey: string;
42
141
  };
43
- export type LayersConfig = GroupLayerData[];
44
142
  export type DrawingEvent = {
45
143
  type: 'created' | 'edited' | 'deleted';
46
144
  layer: IncrementedLayer;
@@ -66,7 +164,6 @@ export type MapConfigConfig = MapOptions & {
66
164
  id: string;
67
165
  removeControlLayers?: boolean;
68
166
  zoomControlPosition?: ControlPosition;
69
- /** Reposiciona marcadores após zoom (evita drift de divIcon). Padrão: true. */
70
167
  stabilizeMarkersOnZoom?: boolean;
71
168
  };
72
169
  export type MapConfig = {
@@ -76,6 +173,10 @@ export type LayersMenuConfig = {
76
173
  size: 'small' | 'medium' | 'large';
77
174
  removeMenu?: boolean;
78
175
  persist: boolean;
176
+ editingLayerKey?: string | null;
177
+ selectedSectionKey?: string | null;
178
+ visibilityMode?: 'switch' | 'eye';
179
+ defaultOpen?: boolean;
79
180
  };
80
181
  export type MemorialConfig = {
81
182
  show: boolean;
@@ -113,6 +214,7 @@ export type MapToolsConfig = {
113
214
  show?: boolean;
114
215
  title?: string;
115
216
  };
217
+ showInteractionPanel?: boolean;
116
218
  texts?: {
117
219
  measureResult?: string;
118
220
  measureLength?: string;
@@ -0,0 +1,16 @@
1
+ import { GroupActionConfig, GroupActionsMap, GroupLayerData, LayerActionConfig, LayerActionDefaults, LayerData, LayerInfoIconConfig, LayerInfoIconsMap, LayerMetricsConfig, LayerMetricsMap, LayersConfig, SectionConfig } from '../types';
2
+ export declare const isSectionConfig: (item: unknown) => item is SectionConfig;
3
+ export declare const isGroupConfig: (item: unknown) => item is GroupLayerData;
4
+ export declare const normalizeLayersConfig: (config?: LayersConfig | null) => SectionConfig[];
5
+ export declare const resolveLayerActions: (layer: LayerData, defaults?: LayerActionDefaults) => LayerActionConfig[];
6
+ export declare const resolveEditControlAction: (defaults: LayerActionDefaults | undefined, kind: "cancel" | "conclude") => LayerActionConfig;
7
+ export declare const isMenuSeparator: (layer: LayerData | null | undefined) => boolean;
8
+ export declare const shouldApplyMapOverlay: (layer: LayerData) => boolean;
9
+ export declare const collectLayerMetricsMap: (config?: LayersConfig | null) => LayerMetricsMap;
10
+ export declare const resolveLayerMetrics: (layer: LayerData | null | undefined) => LayerMetricsConfig[];
11
+ export declare const collectLayerInfoIconsMap: (config?: LayersConfig | null) => LayerInfoIconsMap;
12
+ export declare const resolveActiveLayerInfoIcons: (layer: LayerData | null | undefined) => LayerInfoIconConfig[];
13
+ export declare const collectGroupActionsMap: (config?: LayersConfig | null) => GroupActionsMap;
14
+ export declare const resolveActiveGroupActions: (group: GroupLayerData | null | undefined) => GroupActionConfig[];
15
+ export declare const withLayerInfoIconActive: (infoIcons: LayerInfoIconConfig[] | undefined, iconRef: string, active: boolean) => LayerInfoIconConfig[] | null;
16
+ export declare const withGroupActionActive: (actions: GroupActionConfig[] | undefined, actionKey: string, active: boolean) => GroupActionConfig[] | null;
@@ -1,8 +1,8 @@
1
1
  import { LayerData } from '../types';
2
2
  export declare const setHistory: (layer: LayerData) => void;
3
- /** Retorna o estado salvo da camada ou `undefined` se não houver histórico. */
4
3
  export declare const getHistory: (layerKey: string) => boolean | undefined;
5
- /** Resolve `active`/`activeDefault` sem mutar o objeto original (mesma regra do fluxo legado). */
4
+ export declare const setGroupCollapsedHistory: (groupKey: string, collapsed: boolean) => void;
5
+ export declare const getGroupCollapsedHistory: (groupKey: string) => boolean | undefined;
6
6
  export declare const resolveLayerActiveState: (layer: LayerData, persist: boolean) => LayerData;
7
- /** Indica se a camada deve ser aplicada ao mapa na inicialização. */
7
+ export declare const resolveGroupCollapsedState: (groupKey: string, collapsedFromConfig: boolean | undefined, persist: boolean) => boolean;
8
8
  export declare const shouldInitLayerOnMap: (layer: LayerData, persist: boolean) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rural-environmental-registry/map_component",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "Map component for Vue.js applications",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",
@@ -25,6 +25,9 @@
25
25
  :options="options.layersMenu"
26
26
  @onChildLayerToggle="emit('onChildLayerToggle', $event)"
27
27
  @onGroupLayerToggle="emit('onGroupLayerToggle', $event)"
28
+ @onLayerAction="emit('onLayerAction', $event)"
29
+ @onSectionEditStateChange="emit('onSectionEditStateChange', $event)"
30
+ @onSelectedSectionChange="emit('onSelectedSectionChange', $event)"
28
31
  @startLoading="isLoading = true"
29
32
  @stopLoading="isLoading = false"
30
33
  />
@@ -46,13 +49,22 @@
46
49
  import { computed, ref } from 'vue'
47
50
  import {
48
51
  DrawingEvent,
52
+ GroupActionConfig,
53
+ GroupActionsMap,
49
54
  GroupLayerData,
50
55
  IncrementedLayer,
56
+ LayerActionPayload,
51
57
  LayerData,
58
+ LayerInfoIconConfig,
59
+ LayerInfoIconsMap,
60
+ LayerMetricsConfig,
61
+ LayerMetricsMap,
52
62
  MapLayers,
53
63
  MapOptionsConfig,
54
64
  DescriptiveMemorial,
55
- MeasureCompleteEvent
65
+ MeasureCompleteEvent,
66
+ SectionEditStatePayload,
67
+ SectionSelectPayload
56
68
  } from '../types'
57
69
  import Loading from './loading/Loading.vue'
58
70
  import Map from './map/LeafletMap.vue'
@@ -61,6 +73,14 @@
61
73
  import { isMemorialLayer, MEMORIAL_KEY } from '../utils/memorialLayer'
62
74
  import { calculateLayerArea } from '../utils/geometryCalculator'
63
75
  import { resolveDrawingPathOptions } from '../utils/drawingPathOptions'
76
+ import {
77
+ collectGroupActionsMap,
78
+ collectLayerInfoIconsMap,
79
+ collectLayerMetricsMap,
80
+ normalizeLayersConfig,
81
+ withGroupActionActive,
82
+ withLayerInfoIconActive
83
+ } from '../utils/layersConfigNormalizer'
64
84
  import type { MemorialDrawShape } from '../utils/drawingPathOptions'
65
85
  import type { Feature, MultiPolygon, Polygon } from 'geojson'
66
86
 
@@ -88,6 +108,12 @@
88
108
  const emit = defineEmits<{
89
109
  (e: 'onGroupLayerToggle', data: GroupLayerData): void
90
110
  (e: 'onChildLayerToggle', data: LayerData): void
111
+ (e: 'onLayerAction', payload: LayerActionPayload): void
112
+ (e: 'onSectionEditStateChange', payload: SectionEditStatePayload): void
113
+ (e: 'onSelectedSectionChange', payload: SectionSelectPayload): void
114
+ (e: 'onLayerMetricsUpdate', payload: { layerKey: string; metrics: LayerMetricsConfig[] }): void
115
+ (e: 'onLayerInfoIconsUpdate', payload: { layerKey: string; infoIcons: LayerInfoIconConfig[] }): void
116
+ (e: 'onGroupActionsUpdate', payload: { groupKey: string; actions: GroupActionConfig[] }): void
91
117
  (e: 'onDrawing', data: DrawingEvent): void
92
118
  (e: 'onCoordinateSystemChange', system: string): void
93
119
  (e: 'onFullscreenChange', active: boolean): void
@@ -112,6 +138,60 @@
112
138
  const coordinatePanelRef = ref()
113
139
  const isLoading = ref<boolean>(false)
114
140
 
141
+ const metrics = computed((): LayerMetricsMap =>
142
+ collectLayerMetricsMap(props.layers?.customLayers)
143
+ )
144
+
145
+ const infoIcons = computed((): LayerInfoIconsMap =>
146
+ collectLayerInfoIconsMap(props.layers?.customLayers)
147
+ )
148
+
149
+ const groupActions = computed((): GroupActionsMap =>
150
+ collectGroupActionsMap(props.layers?.customLayers)
151
+ )
152
+
153
+ const setLayerMetrics = (layerKey: string, nextMetrics: LayerMetricsConfig[]): void => {
154
+ emit('onLayerMetricsUpdate', { layerKey, metrics: nextMetrics })
155
+ }
156
+
157
+ const findLayerInfoIcons = (layerKey: string): LayerInfoIconConfig[] | undefined => {
158
+ for (const section of normalizeLayersConfig(props.layers?.customLayers)) {
159
+ for (const group of section.groups) {
160
+ const layer = group.layers?.find((item) => item.key === layerKey)
161
+ if (layer) return layer.infoIcons
162
+ }
163
+ }
164
+ return undefined
165
+ }
166
+
167
+ const findGroupActions = (groupKey: string): GroupActionConfig[] | undefined => {
168
+ for (const section of normalizeLayersConfig(props.layers?.customLayers)) {
169
+ const group = section.groups.find((item) => item.key === groupKey)
170
+ if (group) return group.actions
171
+ }
172
+ return undefined
173
+ }
174
+
175
+ const setLayerInfoIconActive = (layerKey: string, iconRef: string, active: boolean): void => {
176
+ const next = withLayerInfoIconActive(findLayerInfoIcons(layerKey), iconRef, active)
177
+ if (!next) return
178
+ emit('onLayerInfoIconsUpdate', { layerKey, infoIcons: next })
179
+ }
180
+
181
+ const setLayerInfoIcons = (layerKey: string, nextInfoIcons: LayerInfoIconConfig[]): void => {
182
+ emit('onLayerInfoIconsUpdate', { layerKey, infoIcons: nextInfoIcons })
183
+ }
184
+
185
+ const setGroupActionActive = (groupKey: string, actionKey: string, active: boolean): void => {
186
+ const next = withGroupActionActive(findGroupActions(groupKey), actionKey, active)
187
+ if (!next) return
188
+ emit('onGroupActionsUpdate', { groupKey, actions: next })
189
+ }
190
+
191
+ const setGroupActions = (groupKey: string, nextActions: GroupActionConfig[]): void => {
192
+ emit('onGroupActionsUpdate', { groupKey, actions: nextActions })
193
+ }
194
+
115
195
  const getMemorialPathOptions = (shape: MemorialDrawShape): L.PathOptions =>
116
196
  mapRef.value?.getDrawingPathOptions(shape) ??
117
197
  resolveDrawingPathOptions(mapRef.value?.map, props.options.drawing, shape)
@@ -276,7 +356,15 @@
276
356
  enterFullscreen,
277
357
  exitFullscreen,
278
358
  toggleFullscreen,
279
- toggleMeasureArea
359
+ toggleMeasureArea,
360
+ metrics,
361
+ setLayerMetrics,
362
+ infoIcons,
363
+ setLayerInfoIcons,
364
+ setLayerInfoIconActive,
365
+ groupActions,
366
+ setGroupActions,
367
+ setGroupActionActive
280
368
  })
281
369
  </script>
282
370
 
@@ -1,8 +1,9 @@
1
1
  <template>
2
- <FontAwesomeIcon :icon="['fas', iconName]" />
2
+ <FontAwesomeIcon :icon="['fas', resolvedIconName]" />
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
+ import { computed } from 'vue'
6
7
  import { library } from '@fortawesome/fontawesome-svg-core'
7
8
  import {
8
9
  faCheck,
@@ -14,16 +15,33 @@
14
15
  faUpload,
15
16
  faPencil,
16
17
  faTrash,
17
- faChevronDown
18
+ faChevronDown,
19
+ faEye,
20
+ faEyeSlash,
21
+ faCircleInfo,
22
+ faCircleExclamation,
23
+ faTriangleExclamation,
24
+ faCircleCheck,
25
+ faCircleXmark,
26
+ faBan,
27
+ faLock,
28
+ faUnlock,
29
+ faThumbtack,
30
+ faMapPin
18
31
  } from '@fortawesome/free-solid-svg-icons'
19
32
  import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
20
33
  import { FaIconName } from '../../types'
21
34
 
22
35
  type IconProps = {
23
- iconName: FaIconName
36
+ iconName: FaIconName | string
24
37
  }
25
38
 
26
- defineProps<IconProps>()
39
+ const props = defineProps<IconProps>()
40
+
41
+ const resolvedIconName = computed(() => {
42
+ const raw = props.iconName?.trim() ?? ''
43
+ return raw.replace(/^(fas|far|fal|fab)\s+/i, '').replace(/^fa-/i, '')
44
+ })
27
45
 
28
46
  library.add(
29
47
  faCheck,
@@ -35,7 +53,19 @@
35
53
  faUpload,
36
54
  faPencil,
37
55
  faTrash,
38
- faChevronDown
56
+ faChevronDown,
57
+ faEye,
58
+ faEyeSlash,
59
+ faCircleInfo,
60
+ faCircleExclamation,
61
+ faTriangleExclamation,
62
+ faCircleCheck,
63
+ faCircleXmark,
64
+ faBan,
65
+ faLock,
66
+ faUnlock,
67
+ faThumbtack,
68
+ faMapPin
39
69
  )
40
70
  </script>
41
71
 
@@ -81,10 +81,12 @@
81
81
 
82
82
  const initMap = (): void => {
83
83
  const { config } = props.mapOptions
84
+ const toolsConfig = resolveMapToolsConfig(props.toolsOptions)
85
+ const useToolsZoom = !!toolsConfig && toolsConfig.zoom?.show !== false
84
86
  const mapConfig: MapConfigConfig = {
85
87
  ...DEFAULT_MAP_OPTIONS,
86
88
  ...config,
87
- ...(resolveMapToolsConfig(props.toolsOptions) ? { zoomControl: false } : {})
89
+ ...(useToolsZoom ? { zoomControl: false } : {})
88
90
  }
89
91
 
90
92
  mapHandlerInstance = new MapHandler(mapConfig)