datastake-daf 0.6.161 → 0.6.163

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,9 +1,9 @@
1
1
  import {
2
- AimOutlined,
3
- DownOutlined,
4
- MinusOutlined,
5
- PlusOutlined,
6
- UpOutlined,
2
+ AimOutlined,
3
+ DownOutlined,
4
+ MinusOutlined,
5
+ PlusOutlined,
6
+ UpOutlined,
7
7
  } from "@ant-design/icons";
8
8
  import L from "leaflet";
9
9
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -14,300 +14,232 @@ import { renderTooltipJsx as renderTooltipHtml } from "../../../../utils/tooltip
14
14
  import ChainIcon from "./ChainIcon";
15
15
  import { isExtraSmallMarker, isSmallMarker } from "./ChainIcon/utils";
16
16
  import StakeholderIcon from "./StakeholderIcon";
17
-
18
- // Event type color constants
19
- // const EVENT_COLORS = {
20
- // MAJOR: "#cf1322", // Red for major incidents
21
- // MODERATE: "#fa8c16", // Orange for moderate incidents
22
- // MINOR: "#fadb14", // Yellow for minor incidents
23
- // CORRECTIVE: "#12b76a", // Green for corrective actions
24
- // };
25
-
26
17
  export function useMapHelper({
27
- type,
28
- mapRef,
29
- mapCenter,
30
- allData,
31
- renderTooltip,
32
- onClickLink,
33
- link,
34
- tooltipAsText,
35
- renderMarker,
36
- zoom,
37
- selectedMarkersId,
38
- handleSelectMarker,
39
- openPopupIdRef,
18
+ type,
19
+ mapRef,
20
+ mapCenter,
21
+ allData,
22
+ renderTooltip,
23
+ onClickLink,
24
+ link,
25
+ tooltipAsText,
26
+ renderMarker,
27
+ zoom,
28
+ selectedMarkersId,
29
+ handleSelectMarker,
30
+ openPopupIdRef,
40
31
  }) {
41
- const [mapMarkers, setMapMarkers] = useState([]);
42
- const [activeMarker, setActiveMarker] = useState(null);
43
- const getZoom = useCallback(
44
- (mapRef) =>
45
- mapRef.getZoom() > SET_VIEW_DEFAULT_ZOOM
46
- ? mapRef.getZoom()
47
- : SET_VIEW_DEFAULT_ZOOM,
48
- []
49
- );
50
- const roots = useRef([]);
51
- const polylinesRef = useRef([]);
52
- const mappedActiveMarker = useMemo(
53
- () => mapMarkers.find((m) => m.id === activeMarker),
54
- [mapMarkers, activeMarker]
55
- );
56
- const [activeStakeholder, setActiveStakeholder] = useState(null);
57
-
58
- // Recenter Map
59
- const recenterMap = useCallback(() => {
60
- if (mapRef) {
61
- if (allData && allData.length) {
62
- const pos = allData.map((m) =>
63
- Array.isArray(m.area)
64
- ? m.area
65
- : [Number(m.marker?.lat ?? 0), Number(m.marker?.lng ?? 0)]
66
- );
67
- const bounds = new L.LatLngBounds(pos);
68
- mapRef.fitBounds(bounds);
69
- } else {
70
- mapRef.setView(mapCenter, SET_VIEW_DEFAULT_ZOOM);
71
- }
72
- }
73
- }, [mapRef, mapCenter, allData]);
74
-
75
- // Actions to do when active marker is changed
76
- useEffect(() => {
77
- if (mapRef) {
78
- if (activeMarker) {
79
- mapRef.eachLayer((layer) => {
80
- if (layer.options.icon) {
81
- if (layer._leaflet_id === activeMarker) {
82
- layer.openPopup();
83
- layer.off("popupclose");
84
- layer.on("popupclose", () => {
85
- setActiveMarker(null);
86
- });
87
- } else {
88
- layer.off("click");
89
- layer.on("click", () => {
90
- setActiveMarker(layer._leaflet_id);
91
- });
92
- }
93
- }
94
- });
95
- } else {
96
- mapRef.eachLayer((layer) => {
97
- if (layer.options.icon) {
98
- const icon = layer.options.icon;
99
- layer.off("click");
100
- layer.on("click", () => {
101
- setActiveMarker(layer._leaflet_id);
102
- });
103
- layer.setIcon(icon);
104
-
105
- if (layer.getPopup() && layer.getPopup().isOpen()) {
106
- layer.closePopup();
107
- }
108
- }
109
- });
110
- }
111
- }
112
- }, [activeMarker, mapRef]);
113
-
114
- // Draws the marker in the map
115
- const addIconToMap = useCallback(
116
- (coordinates, className, data, name, iconSize, html) => {
117
- if (mapRef) {
118
- const marker =
119
- typeof renderMarker === "function"
120
- ? renderMarker(L, coordinates, data).addTo(mapRef)
121
- : L.marker(coordinates, {
122
- icon: L.divIcon({
123
- html,
124
- className: `map-marker ${className}`,
125
- iconSize,
126
- }),
127
- zIndexOffset: parseInt(iconSize[0]),
128
- }).addTo(mapRef);
129
-
130
- if (name) {
131
- marker.bindPopup(name);
132
- }
133
-
134
- marker.on("click", () => {
135
- setActiveMarker(marker._leaflet_id);
136
- });
137
- return marker;
138
- }
139
- return null;
140
- },
141
- [mapRef, renderMarker]
142
- );
143
-
144
- // Draws the marker in the map and saves it in mapMarkers state
145
- const addIconToMapInitialy = useCallback(
146
- (coordinates, groupName, markerName, data, maxTotal = 0, i = 0) => {
147
- if (mapRef) {
148
- let marker;
149
- let iconClassName = "";
150
- let innerHtml = "";
151
- let iconSize = [25, 25];
152
-
153
- if (type === "event") {
154
- iconClassName = "incident rounded";
155
-
156
- /* const incidents = data?.data || [];
157
- const totals = {
158
- major: incidents.filter((d) => d.type === "major_incident").length,
159
- minor: incidents.filter((d) => d.type === "minor_incident").length,
160
- moderate: incidents.filter((d) => d.type === "moderate_incident").length,
161
- correctiveActions: incidents.filter((d) => d.type === "correctiveActions")
162
- .length,
163
- };
164
-
165
- const total = incidents.length;
166
- let background = "#f1f1f1";
167
-
168
- if (total > 0) {
169
- const categories = [
170
- { key: "major", value: totals.major, color: EVENT_COLORS.MAJOR },
171
- {
172
- key: "moderate",
173
- value: totals.moderate,
174
- color: EVENT_COLORS.MODERATE,
175
- },
176
- { key: "minor", value: totals.minor, color: EVENT_COLORS.MINOR },
177
- {
178
- key: "corrective",
179
- value: totals.correctiveActions,
180
- color: EVENT_COLORS.CORRECTIVE,
181
- },
182
- ]
183
- .filter((c) => c.value > 0)
184
- .map((c) => ({ ...c, perc: (c.value / total) * 100 }));
185
-
186
- if (categories.length === 1) {
187
- background = categories[0].color;
32
+ const [mapMarkers, setMapMarkers] = useState([]);
33
+ const [activeMarker, setActiveMarker] = useState(null);
34
+ const getZoom = useCallback(
35
+ (mapRef) =>
36
+ mapRef.getZoom() > SET_VIEW_DEFAULT_ZOOM ? mapRef.getZoom() : SET_VIEW_DEFAULT_ZOOM,
37
+ [],
38
+ );
39
+ const roots = useRef([]);
40
+ const polylinesRef = useRef([]);
41
+ const mappedActiveMarker = useMemo(
42
+ () => mapMarkers.find((m) => m.id === activeMarker),
43
+ [mapMarkers, activeMarker],
44
+ );
45
+ const [activeStakeholder, setActiveStakeholder] = useState(null);
46
+
47
+ // Recenter Map
48
+ const recenterMap = useCallback(() => {
49
+ if (mapRef) {
50
+ if (allData && allData.length) {
51
+ const pos = allData.map((m) =>
52
+ Array.isArray(m.area)
53
+ ? m.area
54
+ : [Number(m.marker?.lat ?? 0), Number(m.marker?.lng ?? 0)],
55
+ );
56
+ const bounds = new L.LatLngBounds(pos);
57
+ mapRef.fitBounds(bounds);
58
+ } else {
59
+ mapRef.setView(mapCenter, SET_VIEW_DEFAULT_ZOOM);
60
+ }
61
+ }
62
+ }, [mapRef, mapCenter, allData]);
63
+
64
+ // Actions to do when active marker is changed
65
+ useEffect(() => {
66
+ if (mapRef) {
67
+ if (activeMarker) {
68
+ mapRef.eachLayer((layer) => {
69
+ if (layer.options.icon) {
70
+ if (layer._leaflet_id === activeMarker) {
71
+ layer.openPopup();
72
+ layer.off("popupclose");
73
+ layer.on("popupclose", () => {
74
+ setActiveMarker(null);
75
+ });
188
76
  } else {
189
- let currentStop = 0;
190
- const gradientStops = categories.map((cat, index) => {
191
- const stop = categories
192
- .slice(0, index)
193
- .reduce((acc, c) => acc + c.perc, 0);
194
-
195
- return `${cat.color} ${Math.round(stop)}%`;
77
+ layer.off("click");
78
+ layer.on("click", () => {
79
+ setActiveMarker(layer._leaflet_id);
196
80
  });
197
-
198
- background = `radial-gradient(circle, ${gradientStops.join(", ")})`;
199
81
  }
200
82
  }
201
-
202
- let radius = 35;
203
-
204
- if (data.total > 20) {
205
- radius = 65;
206
- } else if (data.total > 10) {
207
- radius = 50;
208
- } else {
209
- radius = 40;
83
+ });
84
+ } else {
85
+ mapRef.eachLayer((layer) => {
86
+ if (layer.options.icon) {
87
+ const icon = layer.options.icon;
88
+ layer.off("click");
89
+ layer.on("click", () => {
90
+ setActiveMarker(layer._leaflet_id);
91
+ });
92
+ layer.setIcon(icon);
93
+
94
+ if (layer.getPopup() && layer.getPopup().isOpen()) {
95
+ layer.closePopup();
96
+ }
210
97
  }
211
- */
212
-
213
- const { background, radius, totals } = getEventMarkerStyle(data);
214
- innerHtml = `
98
+ });
99
+ }
100
+ }
101
+ }, [activeMarker, mapRef]);
102
+
103
+ // Draws the marker in the map
104
+ const addIconToMap = useCallback(
105
+ (coordinates, className, data, name, iconSize, html) => {
106
+ if (mapRef) {
107
+ const marker =
108
+ typeof renderMarker === "function"
109
+ ? renderMarker(L, coordinates, data).addTo(mapRef)
110
+ : L.marker(coordinates, {
111
+ icon: L.divIcon({
112
+ html,
113
+ className: `map-marker ${className}`,
114
+ iconSize,
115
+ }),
116
+ zIndexOffset: parseInt(iconSize[0]),
117
+ }).addTo(mapRef);
118
+
119
+ if (name) {
120
+ marker.bindPopup(name);
121
+ }
122
+
123
+ marker.on("click", () => {
124
+ setActiveMarker(marker._leaflet_id);
125
+ });
126
+ return marker;
127
+ }
128
+ return null;
129
+ },
130
+ [mapRef, renderMarker],
131
+ );
132
+
133
+ // Draws the marker in the map and saves it in mapMarkers state
134
+ const addIconToMapInitialy = useCallback(
135
+ (coordinates, groupName, markerName, data, maxTotal = 0, i = 0) => {
136
+ if (mapRef) {
137
+ let marker;
138
+ let iconClassName = "";
139
+ let innerHtml = "";
140
+ let iconSize = [25, 25];
141
+
142
+ if (type === "event") {
143
+ iconClassName = "incident rounded";
144
+
145
+ const { background, radius, totals } = getEventMarkerStyle(data);
146
+ innerHtml = `
215
147
  <div class="main" style="background: ${background}; border: 2px solid white; width: ${radius}px; height: ${radius}px;">
216
148
  <span>${data.total || 0}</span>
217
149
  </div>`;
218
150
 
219
- const div = document.createElement("div");
220
- const root = createRoot(div);
221
-
222
- root.render(
223
- <>
224
- {renderTooltipHtml({
225
- title: data.name,
226
- subTitle: data.type,
227
- items: renderTooltip({ ...data, totals }),
228
- link,
229
- total: data.sources,
230
- onClickLink: () => onClickLink(data),
231
- })}
232
- </>
233
- );
234
-
235
- roots.current.push(root);
236
-
237
- const base = 30;
238
- const mp = 1.3;
239
- const perc = (data.total / (maxTotal || 1)) * mp;
240
- const size = 50 * perc;
241
- iconSize = size > base ? [size, size] : [base, base];
242
-
243
- marker = addIconToMap(
244
- coordinates,
245
- iconClassName,
246
- data,
247
- tooltipAsText ? data.name : div,
248
- iconSize,
249
- innerHtml
250
- );
251
- } else if (type === "territory") {
252
- marker = L.polygon(data.area, {
253
- color: data.color,
254
- opacity: 0.4,
255
- }).addTo(mapRef);
256
- const div = document.createElement("div");
257
- const root = createRoot(div);
258
-
259
- root.render(
260
- <>
261
- {renderTooltipHtml({
262
- title: data.name,
263
- items: renderTooltip(data),
264
- link,
265
- total: data.sources,
266
- onClickLink: () => onClickLink(data),
267
- })}
268
- </>
269
- );
270
-
271
- roots.current.push(root);
272
-
273
- marker.bindPopup(div);
274
-
275
- marker.on("popupopen", function () {
276
- this.setStyle({ fillOpacity: 0.7 });
277
- });
278
-
279
- marker.on("popupclose", function () {
280
- this.setStyle({ fillOpacity: 0.4 });
281
- });
282
- } else if (type === "stakeholder") {
283
- marker = L.marker(coordinates, {
284
- icon: L.divIcon({
285
- html: `<div id="${i}"></div>`,
286
- className: "marker-stakeholder",
287
- iconSize,
288
- }),
289
- zIndexOffset: parseInt(iconSize[0]),
290
- }).addTo(mapRef);
291
-
292
- const div = document.getElementById(i);
293
- const root = createRoot(div);
294
-
295
- root.render(
296
- <StakeholderIcon
297
- data={data}
298
- title={data.name}
299
- renderTooltip={renderTooltip}
300
- link={link}
301
- onClickLink={onClickLink}
302
- activeStakeholder={activeStakeholder}
303
- setActiveStakeholder={setActiveStakeholder}
304
- />
305
- );
306
-
307
- roots.current.push(root);
308
- } else if (type === "location") {
309
- iconClassName = "";
310
- innerHtml = `
151
+ const div = document.createElement("div");
152
+ const root = createRoot(div);
153
+
154
+ root.render(
155
+ <>
156
+ {renderTooltipHtml({
157
+ title: data.name,
158
+ subTitle: data.type,
159
+ items: renderTooltip({ ...data, totals }),
160
+ link,
161
+ total: data.sources,
162
+ onClickLink: () => onClickLink(data),
163
+ })}
164
+ </>,
165
+ );
166
+
167
+ roots.current.push(root);
168
+
169
+ const base = 30;
170
+ const mp = 1.3;
171
+ const perc = (data.total / (maxTotal || 1)) * mp;
172
+ const size = 50 * perc;
173
+ iconSize = size > base ? [size, size] : [base, base];
174
+
175
+ marker = addIconToMap(
176
+ coordinates,
177
+ iconClassName,
178
+ data,
179
+ tooltipAsText ? data.name : div,
180
+ iconSize,
181
+ innerHtml,
182
+ );
183
+ } else if (type === "territory") {
184
+ marker = L.polygon(data.area, {
185
+ color: data.color,
186
+ opacity: 0.4,
187
+ }).addTo(mapRef);
188
+ const div = document.createElement("div");
189
+ const root = createRoot(div);
190
+
191
+ root.render(
192
+ <>
193
+ {renderTooltipHtml({
194
+ title: data.name,
195
+ items: renderTooltip(data),
196
+ link,
197
+ total: data.sources,
198
+ onClickLink: () => onClickLink(data),
199
+ })}
200
+ </>,
201
+ );
202
+
203
+ roots.current.push(root);
204
+
205
+ marker.bindPopup(div);
206
+
207
+ marker.on("popupopen", function () {
208
+ this.setStyle({ fillOpacity: 0.7 });
209
+ });
210
+
211
+ marker.on("popupclose", function () {
212
+ this.setStyle({ fillOpacity: 0.4 });
213
+ });
214
+ } else if (type === "stakeholder") {
215
+ marker = L.marker(coordinates, {
216
+ icon: L.divIcon({
217
+ html: `<div id="${i}"></div>`,
218
+ className: "marker-stakeholder",
219
+ iconSize,
220
+ }),
221
+ zIndexOffset: parseInt(iconSize[0]),
222
+ }).addTo(mapRef);
223
+
224
+ const div = document.getElementById(i);
225
+ const root = createRoot(div);
226
+
227
+ root.render(
228
+ <StakeholderIcon
229
+ data={data}
230
+ title={data.name}
231
+ renderTooltip={renderTooltip}
232
+ link={link}
233
+ onClickLink={onClickLink}
234
+ activeStakeholder={activeStakeholder}
235
+ setActiveStakeholder={setActiveStakeholder}
236
+ />,
237
+ );
238
+
239
+ roots.current.push(root);
240
+ } else if (type === "location") {
241
+ iconClassName = "";
242
+ innerHtml = `
311
243
 
312
244
  <svg
313
245
  width="28"
@@ -325,208 +257,202 @@ export function useMapHelper({
325
257
 
326
258
  `;
327
259
 
328
- const svg = document.createElement("svg");
329
- const root = createRoot(svg);
330
-
331
- root.render(
332
- <>
333
- {renderTooltipHtml({
334
- title: data.name,
335
- subTitle: data?.subTitle,
336
- total: data.sources,
337
- link,
338
- onClickLink: () => onClickLink(data),
339
- items: renderTooltip(data),
340
- })}
341
- </>
342
- );
343
-
344
- roots.current.push(root);
345
-
346
- marker = addIconToMap(
347
- coordinates,
348
- iconClassName,
349
- data,
350
- tooltipAsText ? data.name : svg,
351
- iconSize,
352
- innerHtml
353
- );
354
- } else if (type === "chain") {
355
- marker = L.marker(coordinates, {
356
- icon: L.divIcon({
357
- html: `<div id="${i}"></div>`,
358
- className: "marker-chain",
359
- iconSize:
360
- isSmallMarker(zoom) || isExtraSmallMarker(zoom)
361
- ? [10.5, 12.6]
362
- : [25, 25],
363
- }),
364
- }).addTo(mapRef);
365
-
366
- const div = document.getElementById(i);
367
- const root = createRoot(div);
368
-
369
- root.render(
370
- <ChainIcon
371
- data={data}
372
- zoom={zoom}
373
- i={i}
374
- allData={allData}
375
- link={link}
376
- renderTooltip={renderTooltip}
377
- onClickLink={onClickLink}
378
- selectedMarkersId={selectedMarkersId}
379
- handleSelectMarker={handleSelectMarker}
380
- mapRef={mapRef}
381
- activeMarker={activeMarker}
382
- setActiveMarker={setActiveMarker}
383
- setMapMarkers={setMapMarkers}
384
- addIconToMap={addIconToMap}
385
- openPopupIdRef={openPopupIdRef}
386
- polylinesRef={polylinesRef}
387
- />
388
- );
389
-
390
- roots.current.push(root);
391
- } else {
392
- iconClassName = "marker";
393
- innerHtml = '<div class="main"></div>';
394
- const div = document.createElement("div");
395
- const root = createRoot(div);
396
-
397
- root.render(
398
- <>
399
- {renderTooltipHtml({
400
- title: data.name,
401
- subTitle: data?.subTitle,
402
- total: data.sources,
403
- link,
404
- onClickLink: () => onClickLink(data),
405
- items: renderTooltip(data),
406
- })}
407
- </>
408
- );
409
-
410
- roots.current.push(root);
411
-
412
- marker = addIconToMap(
413
- coordinates,
414
- iconClassName,
415
- data,
416
- tooltipAsText ? data.name : div,
417
- iconSize,
418
- innerHtml
419
- );
420
- }
421
-
422
- if (marker) {
423
- setMapMarkers((prevMapMarkers) => [
424
- ...prevMapMarkers,
425
- {
426
- id: marker._leaflet_id,
427
- coordinates,
428
- groupName,
429
- markerName,
430
- iconClassName,
431
- data,
432
- },
433
- ]);
434
- }
435
- }
436
- },
437
- [
438
- mapRef,
439
- renderTooltip,
440
- tooltipAsText,
441
- onClickLink,
442
- zoom,
443
- selectedMarkersId,
444
- openPopupIdRef.current,
445
- handleSelectMarker,
446
- activeStakeholder,
447
- setActiveStakeholder,
448
- ]
449
- );
450
-
451
- // Map control handlers
452
- const zoomHandler = useCallback(
453
- () => mapRef.setZoom(mapRef.getZoom() + 1),
454
- [mapRef]
455
- );
456
- const zoomOutHandler = useCallback(
457
- () => mapRef.setZoom(mapRef.getZoom() - 1),
458
- [mapRef]
459
- );
460
-
461
- const moveUpHandler = useCallback(() => {
462
- const center = mapRef.getCenter();
463
- const value = 30 / mapRef.getZoom();
464
- mapRef.panTo(new L.LatLng(center.lat + value, center.lng));
465
- }, [mapRef]);
466
-
467
- const moveDownHandler = useCallback(() => {
468
- const center = mapRef.getCenter();
469
- const value = 30 / mapRef.getZoom();
470
- mapRef.panTo(new L.LatLng(center.lat - value, center.lng));
471
- }, [mapRef]);
472
-
473
- const clearMapMarkers = useCallback(() => {
474
- if (mapRef) {
475
- roots.current.forEach((root) => {
476
- setTimeout(() => {
477
- try {
478
- root.unmount();
479
- } catch (err) {
480
- console.log(err);
481
- }
482
- });
483
- });
484
- roots.current = [];
485
-
486
- mapRef.eachLayer((layer) => {
487
- if (!layer._url) {
488
- mapRef.removeLayer(layer);
489
- }
490
- });
491
- setMapMarkers([]);
492
- }
493
- }, [mapRef]);
494
-
495
- const mapOptionsButtonsConfig = useMemo(
496
- () => [
497
- {
498
- icon: <PlusOutlined style={{ color: "#727272" }} />,
499
- handler: zoomHandler,
500
- },
501
- {
502
- icon: <MinusOutlined style={{ color: "#727272" }} />,
503
- handler: zoomOutHandler,
504
- },
505
- {
506
- icon: <AimOutlined style={{ color: "#727272" }} />,
507
- handler: recenterMap,
508
- },
509
- {
510
- icon: <UpOutlined style={{ color: "#727272" }} />,
511
- handler: moveUpHandler,
512
- },
513
- {
514
- icon: <DownOutlined style={{ color: "#727272" }} />,
515
- handler: moveDownHandler,
516
- },
517
- ],
518
- [zoomHandler, moveUpHandler, moveDownHandler, zoomOutHandler, recenterMap]
519
- );
520
-
521
- return {
522
- addIconToMapInitialy,
523
- activeMarker: mappedActiveMarker,
524
- mapOptionsButtonsConfig,
525
- setActiveMarker,
526
- mapMarkers,
527
- clearMapMarkers,
528
- getZoom,
529
- polylinesRef,
530
- activeStakeholder,
531
- };
260
+ const svg = document.createElement("svg");
261
+ const root = createRoot(svg);
262
+
263
+ root.render(
264
+ <>
265
+ {renderTooltipHtml({
266
+ title: data.name,
267
+ subTitle: data?.subTitle,
268
+ total: data.sources,
269
+ link,
270
+ onClickLink: () => onClickLink(data),
271
+ items: renderTooltip(data),
272
+ })}
273
+ </>,
274
+ );
275
+
276
+ roots.current.push(root);
277
+
278
+ marker = addIconToMap(
279
+ coordinates,
280
+ iconClassName,
281
+ data,
282
+ tooltipAsText ? data.name : svg,
283
+ iconSize,
284
+ innerHtml,
285
+ );
286
+ } else if (type === "chain") {
287
+ marker = L.marker(coordinates, {
288
+ icon: L.divIcon({
289
+ html: `<div id="${i}"></div>`,
290
+ className: "marker-chain",
291
+ iconSize:
292
+ isSmallMarker(zoom) || isExtraSmallMarker(zoom)
293
+ ? [10.5, 12.6]
294
+ : [25, 25],
295
+ }),
296
+ }).addTo(mapRef);
297
+
298
+ const div = document.getElementById(i);
299
+ const root = createRoot(div);
300
+
301
+ root.render(
302
+ <ChainIcon
303
+ data={data}
304
+ zoom={zoom}
305
+ i={i}
306
+ allData={allData}
307
+ link={link}
308
+ renderTooltip={renderTooltip}
309
+ onClickLink={onClickLink}
310
+ selectedMarkersId={selectedMarkersId}
311
+ handleSelectMarker={handleSelectMarker}
312
+ mapRef={mapRef}
313
+ activeMarker={activeMarker}
314
+ setActiveMarker={setActiveMarker}
315
+ setMapMarkers={setMapMarkers}
316
+ addIconToMap={addIconToMap}
317
+ openPopupIdRef={openPopupIdRef}
318
+ polylinesRef={polylinesRef}
319
+ />,
320
+ );
321
+
322
+ roots.current.push(root);
323
+ } else {
324
+ iconClassName = "marker";
325
+ innerHtml = '<div class="main"></div>';
326
+ const div = document.createElement("div");
327
+ const root = createRoot(div);
328
+
329
+ root.render(
330
+ <>
331
+ {renderTooltipHtml({
332
+ title: data.name,
333
+ subTitle: data?.subTitle,
334
+ total: data.sources,
335
+ link,
336
+ onClickLink: () => onClickLink(data),
337
+ items: renderTooltip(data),
338
+ })}
339
+ </>,
340
+ );
341
+
342
+ roots.current.push(root);
343
+
344
+ marker = addIconToMap(
345
+ coordinates,
346
+ iconClassName,
347
+ data,
348
+ tooltipAsText ? data.name : div,
349
+ iconSize,
350
+ innerHtml,
351
+ );
352
+ }
353
+
354
+ if (marker) {
355
+ setMapMarkers((prevMapMarkers) => [
356
+ ...prevMapMarkers,
357
+ {
358
+ id: marker._leaflet_id,
359
+ coordinates,
360
+ groupName,
361
+ markerName,
362
+ iconClassName,
363
+ data,
364
+ },
365
+ ]);
366
+ }
367
+ }
368
+ },
369
+ [
370
+ mapRef,
371
+ renderTooltip,
372
+ tooltipAsText,
373
+ onClickLink,
374
+ zoom,
375
+ selectedMarkersId,
376
+ openPopupIdRef.current,
377
+ handleSelectMarker,
378
+ activeStakeholder,
379
+ setActiveStakeholder,
380
+ ],
381
+ );
382
+
383
+ // Map control handlers
384
+ const zoomHandler = useCallback(() => mapRef.setZoom(mapRef.getZoom() + 1), [mapRef]);
385
+ const zoomOutHandler = useCallback(() => mapRef.setZoom(mapRef.getZoom() - 1), [mapRef]);
386
+
387
+ const moveUpHandler = useCallback(() => {
388
+ const center = mapRef.getCenter();
389
+ const value = 30 / mapRef.getZoom();
390
+ mapRef.panTo(new L.LatLng(center.lat + value, center.lng));
391
+ }, [mapRef]);
392
+
393
+ const moveDownHandler = useCallback(() => {
394
+ const center = mapRef.getCenter();
395
+ const value = 30 / mapRef.getZoom();
396
+ mapRef.panTo(new L.LatLng(center.lat - value, center.lng));
397
+ }, [mapRef]);
398
+
399
+ const clearMapMarkers = useCallback(() => {
400
+ if (mapRef) {
401
+ roots.current.forEach((root) => {
402
+ setTimeout(() => {
403
+ try {
404
+ root.unmount();
405
+ } catch (err) {
406
+ console.log(err);
407
+ }
408
+ });
409
+ });
410
+ roots.current = [];
411
+
412
+ mapRef.eachLayer((layer) => {
413
+ if (!layer._url) {
414
+ mapRef.removeLayer(layer);
415
+ }
416
+ });
417
+ setMapMarkers([]);
418
+ }
419
+ }, [mapRef]);
420
+
421
+ const mapOptionsButtonsConfig = useMemo(
422
+ () => [
423
+ {
424
+ icon: <PlusOutlined style={{ color: "#727272" }} />,
425
+ handler: zoomHandler,
426
+ },
427
+ {
428
+ icon: <MinusOutlined style={{ color: "#727272" }} />,
429
+ handler: zoomOutHandler,
430
+ },
431
+ {
432
+ icon: <AimOutlined style={{ color: "#727272" }} />,
433
+ handler: recenterMap,
434
+ },
435
+ {
436
+ icon: <UpOutlined style={{ color: "#727272" }} />,
437
+ handler: moveUpHandler,
438
+ },
439
+ {
440
+ icon: <DownOutlined style={{ color: "#727272" }} />,
441
+ handler: moveDownHandler,
442
+ },
443
+ ],
444
+ [zoomHandler, moveUpHandler, moveDownHandler, zoomOutHandler, recenterMap],
445
+ );
446
+
447
+ return {
448
+ addIconToMapInitialy,
449
+ activeMarker: mappedActiveMarker,
450
+ mapOptionsButtonsConfig,
451
+ setActiveMarker,
452
+ mapMarkers,
453
+ clearMapMarkers,
454
+ getZoom,
455
+ polylinesRef,
456
+ activeStakeholder,
457
+ };
532
458
  }