zenit-sdk 0.1.0 → 0.1.2
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.
- package/dist/{chunk-PCTRVN4O.mjs → chunk-URDEEWUZ.mjs} +446 -94
- package/dist/chunk-URDEEWUZ.mjs.map +1 -0
- package/dist/{index-DvcYGhqj.d.mts → index-BSljZaYk.d.mts} +13 -0
- package/dist/{index-DvcYGhqj.d.ts → index-BSljZaYk.d.ts} +13 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +527 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -5
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +518 -166
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-PCTRVN4O.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/react/ZenitMap.tsx
|
|
2
|
-
import
|
|
2
|
+
import React4, { useCallback as useCallback2, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo2, useRef as useRef5, useState as useState2, forwardRef } from "react";
|
|
3
3
|
import { MapContainer, Marker as Marker2, TileLayer, ZoomControl } from "react-leaflet";
|
|
4
4
|
import L4 from "leaflet";
|
|
5
5
|
|
|
@@ -246,10 +246,31 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
// src/react/map/layer-geojson.tsx
|
|
249
|
+
import { useEffect, useRef } from "react";
|
|
249
250
|
import { GeoJSON } from "react-leaflet";
|
|
250
251
|
import L from "leaflet";
|
|
251
252
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
252
253
|
var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
|
|
254
|
+
function normalizeBboxFromData(data) {
|
|
255
|
+
const bboxCandidate = data.bbox;
|
|
256
|
+
if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
|
|
257
|
+
const [minLon, minLat, maxLon, maxLat] = bboxCandidate;
|
|
258
|
+
const values = [minLon, minLat, maxLon, maxLat].map((value) => Number(value));
|
|
259
|
+
if (values.every((value) => Number.isFinite(value))) {
|
|
260
|
+
return values;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
function buildIdsSample(features) {
|
|
265
|
+
return features.slice(0, 10).map((feature) => {
|
|
266
|
+
const typedFeature = feature;
|
|
267
|
+
if (typedFeature.id !== void 0 && typedFeature.id !== null) {
|
|
268
|
+
return String(typedFeature.id);
|
|
269
|
+
}
|
|
270
|
+
const featureId = typedFeature.properties?.featureId;
|
|
271
|
+
return featureId !== void 0 && featureId !== null ? String(featureId) : "";
|
|
272
|
+
}).join(",");
|
|
273
|
+
}
|
|
253
274
|
function getGeometryType(feature) {
|
|
254
275
|
const t = feature?.geometry?.type;
|
|
255
276
|
return typeof t === "string" ? t : null;
|
|
@@ -285,8 +306,58 @@ var LayerGeoJson = ({
|
|
|
285
306
|
const features = data.features ?? [];
|
|
286
307
|
const fillFeatures = features.filter(isNonPointGeometry);
|
|
287
308
|
const pointFeatures = features.filter(isPointGeometry);
|
|
309
|
+
const dataVersionRef = useRef(0);
|
|
310
|
+
const prevSignatureRef = useRef("");
|
|
311
|
+
const firstId = features.length > 0 ? String(features[0]?.id ?? "") : "";
|
|
312
|
+
const lastId = features.length > 0 ? String(features[features.length - 1]?.id ?? "") : "";
|
|
313
|
+
const bbox = normalizeBboxFromData(data);
|
|
314
|
+
const idsSample = buildIdsSample(features);
|
|
315
|
+
const signature = bbox ? `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}|${bbox[0]}|${bbox[1]}|${bbox[2]}|${bbox[3]}` : `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}`;
|
|
316
|
+
const signatureToken = signature.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
317
|
+
if (prevSignatureRef.current !== signature) {
|
|
318
|
+
dataVersionRef.current += 1;
|
|
319
|
+
prevSignatureRef.current = signature;
|
|
320
|
+
}
|
|
288
321
|
const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
|
|
289
322
|
const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
|
|
323
|
+
const clusterLayerRef = useRef(null);
|
|
324
|
+
const canCluster = typeof L.markerClusterGroup === "function";
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (!mapInstance || !panesReady || !pointsData || !canCluster) return;
|
|
327
|
+
const markerClusterGroup = L.markerClusterGroup;
|
|
328
|
+
const clusterLayer = clusterLayerRef.current ?? markerClusterGroup();
|
|
329
|
+
clusterLayerRef.current = clusterLayer;
|
|
330
|
+
if (!mapInstance.hasLayer(clusterLayer)) {
|
|
331
|
+
mapInstance.addLayer(clusterLayer);
|
|
332
|
+
}
|
|
333
|
+
clusterLayer.clearLayers();
|
|
334
|
+
const geoJsonLayer = L.geoJSON(pointsData, {
|
|
335
|
+
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
|
336
|
+
radius: isMobile ? 8 : 6,
|
|
337
|
+
pane: mapInstance.getPane(pointsPaneName) ? pointsPaneName : void 0,
|
|
338
|
+
...styleFn(feature, layerType, baseOpacity)
|
|
339
|
+
}),
|
|
340
|
+
onEachFeature
|
|
341
|
+
});
|
|
342
|
+
clusterLayer.addLayer(geoJsonLayer);
|
|
343
|
+
return () => {
|
|
344
|
+
clusterLayer.clearLayers();
|
|
345
|
+
if (mapInstance.hasLayer(clusterLayer)) {
|
|
346
|
+
mapInstance.removeLayer(clusterLayer);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}, [
|
|
350
|
+
baseOpacity,
|
|
351
|
+
canCluster,
|
|
352
|
+
isMobile,
|
|
353
|
+
layerType,
|
|
354
|
+
mapInstance,
|
|
355
|
+
onEachFeature,
|
|
356
|
+
panesReady,
|
|
357
|
+
pointsData,
|
|
358
|
+
pointsPaneName,
|
|
359
|
+
styleFn
|
|
360
|
+
]);
|
|
290
361
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
291
362
|
fillData && /* @__PURE__ */ jsx(
|
|
292
363
|
GeoJSON,
|
|
@@ -299,9 +370,9 @@ var LayerGeoJson = ({
|
|
|
299
370
|
onPolygonLabel?.(feature, layer);
|
|
300
371
|
}
|
|
301
372
|
},
|
|
302
|
-
`fill-${layerId}`
|
|
373
|
+
`fill-${layerId}-${signatureToken}-v${dataVersionRef.current}`
|
|
303
374
|
),
|
|
304
|
-
pointsData && /* @__PURE__ */ jsx(
|
|
375
|
+
pointsData && !canCluster && /* @__PURE__ */ jsx(
|
|
305
376
|
GeoJSON,
|
|
306
377
|
{
|
|
307
378
|
data: pointsData,
|
|
@@ -312,24 +383,24 @@ var LayerGeoJson = ({
|
|
|
312
383
|
}),
|
|
313
384
|
onEachFeature
|
|
314
385
|
},
|
|
315
|
-
`points-${layerId}`
|
|
386
|
+
`points-${layerId}-${signatureToken}-v${dataVersionRef.current}`
|
|
316
387
|
)
|
|
317
388
|
] });
|
|
318
389
|
};
|
|
319
390
|
|
|
320
391
|
// src/react/map/location-control.tsx
|
|
321
|
-
import { useEffect as
|
|
392
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
|
|
322
393
|
import { createPortal } from "react-dom";
|
|
323
394
|
import { Circle, Marker, useMap } from "react-leaflet";
|
|
324
395
|
import L3 from "leaflet";
|
|
325
396
|
|
|
326
397
|
// src/react/hooks/use-geolocation.ts
|
|
327
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
398
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
328
399
|
function useGeolocation(options) {
|
|
329
400
|
const [isTracking, setIsTracking] = useState(false);
|
|
330
401
|
const [location, setLocation] = useState(null);
|
|
331
402
|
const [error, setError] = useState(null);
|
|
332
|
-
const watchIdRef =
|
|
403
|
+
const watchIdRef = useRef2(null);
|
|
333
404
|
const stopTracking = useCallback(() => {
|
|
334
405
|
if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
|
|
335
406
|
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
@@ -371,7 +442,7 @@ function useGeolocation(options) {
|
|
|
371
442
|
}
|
|
372
443
|
}, [isTracking, startTracking, stopTracking]);
|
|
373
444
|
const clearError = useCallback(() => setError(null), []);
|
|
374
|
-
|
|
445
|
+
useEffect2(() => {
|
|
375
446
|
return () => {
|
|
376
447
|
stopTracking();
|
|
377
448
|
};
|
|
@@ -798,10 +869,10 @@ var LocateIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18",
|
|
|
798
869
|
] });
|
|
799
870
|
var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
|
|
800
871
|
const map = useMap();
|
|
801
|
-
const controlRef =
|
|
802
|
-
const hasCenteredRef =
|
|
872
|
+
const controlRef = useRef3(null);
|
|
873
|
+
const hasCenteredRef = useRef3(false);
|
|
803
874
|
const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
|
|
804
|
-
|
|
875
|
+
useEffect3(() => {
|
|
805
876
|
if (typeof document === "undefined") return;
|
|
806
877
|
if (document.getElementById(LOCATION_STYLE_ID)) return;
|
|
807
878
|
const styleTag = document.createElement("style");
|
|
@@ -809,7 +880,7 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
|
|
|
809
880
|
styleTag.textContent = LOCATION_STYLES;
|
|
810
881
|
document.head.appendChild(styleTag);
|
|
811
882
|
}, []);
|
|
812
|
-
|
|
883
|
+
useEffect3(() => {
|
|
813
884
|
const control = L3.control({ position });
|
|
814
885
|
control.onAdd = () => {
|
|
815
886
|
const container = L3.DomUtil.create("div", "zenit-location-control");
|
|
@@ -823,7 +894,7 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
|
|
|
823
894
|
controlRef.current = null;
|
|
824
895
|
};
|
|
825
896
|
}, [map, position]);
|
|
826
|
-
|
|
897
|
+
useEffect3(() => {
|
|
827
898
|
if (!location || !isTracking) return;
|
|
828
899
|
if (hasCenteredRef.current) return;
|
|
829
900
|
hasCenteredRef.current = true;
|
|
@@ -870,8 +941,20 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
|
|
|
870
941
|
};
|
|
871
942
|
|
|
872
943
|
// src/react/map/map-handlers.tsx
|
|
873
|
-
import { useEffect as
|
|
944
|
+
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
874
945
|
import { useMap as useMap2 } from "react-leaflet";
|
|
946
|
+
var MapInvalidator = ({ trigger }) => {
|
|
947
|
+
const map = useMap2();
|
|
948
|
+
const lastTrigger = useRef4(void 0);
|
|
949
|
+
useEffect4(() => {
|
|
950
|
+
if (lastTrigger.current === trigger) return;
|
|
951
|
+
lastTrigger.current = trigger;
|
|
952
|
+
requestAnimationFrame(() => {
|
|
953
|
+
map.invalidateSize();
|
|
954
|
+
});
|
|
955
|
+
}, [map, trigger]);
|
|
956
|
+
return null;
|
|
957
|
+
};
|
|
875
958
|
function computeBBoxFromGeojson(geojson) {
|
|
876
959
|
if (!geojson || !Array.isArray(geojson.features)) return null;
|
|
877
960
|
const coords = [];
|
|
@@ -918,9 +1001,9 @@ var BBoxZoomHandler = ({
|
|
|
918
1001
|
enabled = true
|
|
919
1002
|
}) => {
|
|
920
1003
|
const map = useMap2();
|
|
921
|
-
const lastAppliedBBox =
|
|
922
|
-
const lastUserInteracted =
|
|
923
|
-
|
|
1004
|
+
const lastAppliedBBox = useRef4(null);
|
|
1005
|
+
const lastUserInteracted = useRef4(false);
|
|
1006
|
+
useEffect4(() => {
|
|
924
1007
|
const handleInteraction = () => {
|
|
925
1008
|
lastUserInteracted.current = true;
|
|
926
1009
|
};
|
|
@@ -931,7 +1014,7 @@ var BBoxZoomHandler = ({
|
|
|
931
1014
|
map.off("zoomstart", handleInteraction);
|
|
932
1015
|
};
|
|
933
1016
|
}, [map]);
|
|
934
|
-
|
|
1017
|
+
useEffect4(() => {
|
|
935
1018
|
if (!enabled) return;
|
|
936
1019
|
let resolvedBBox = bbox ?? null;
|
|
937
1020
|
if (!resolvedBBox && geojson) {
|
|
@@ -959,7 +1042,7 @@ var BBoxZoomHandler = ({
|
|
|
959
1042
|
};
|
|
960
1043
|
var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
|
|
961
1044
|
const map = useMap2();
|
|
962
|
-
|
|
1045
|
+
useEffect4(() => {
|
|
963
1046
|
const handleZoom = () => {
|
|
964
1047
|
onZoomChange(map.getZoom());
|
|
965
1048
|
};
|
|
@@ -973,7 +1056,7 @@ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
|
|
|
973
1056
|
};
|
|
974
1057
|
var MapInstanceBridge = ({ onReady }) => {
|
|
975
1058
|
const map = useMap2();
|
|
976
|
-
|
|
1059
|
+
useEffect4(() => {
|
|
977
1060
|
onReady(map);
|
|
978
1061
|
}, [map, onReady]);
|
|
979
1062
|
return null;
|
|
@@ -1100,6 +1183,8 @@ var ZenitMap = forwardRef(({
|
|
|
1100
1183
|
const [loadingMap, setLoadingMap] = useState2(false);
|
|
1101
1184
|
const [mapError, setMapError] = useState2(null);
|
|
1102
1185
|
const [mapInstance, setMapInstance] = useState2(null);
|
|
1186
|
+
const [layerGeojsonOverrides, setLayerGeojsonOverrides] = useState2({});
|
|
1187
|
+
const originalGeojsonByLayerIdRef = useRef5({});
|
|
1103
1188
|
const [panesReady, setPanesReady] = useState2(false);
|
|
1104
1189
|
const [currentZoom, setCurrentZoom] = useState2(initialZoom ?? DEFAULT_ZOOM);
|
|
1105
1190
|
const [isPopupOpen, setIsPopupOpen] = useState2(false);
|
|
@@ -1108,7 +1193,7 @@ var ZenitMap = forwardRef(({
|
|
|
1108
1193
|
return window.matchMedia("(max-width: 768px)").matches;
|
|
1109
1194
|
});
|
|
1110
1195
|
const normalizedLayers = useMemo2(() => normalizeMapLayers(map), [map]);
|
|
1111
|
-
|
|
1196
|
+
useEffect5(() => {
|
|
1112
1197
|
if (typeof window === "undefined") return;
|
|
1113
1198
|
const mql = window.matchMedia("(max-width: 768px)");
|
|
1114
1199
|
const onChange = (e) => {
|
|
@@ -1126,17 +1211,17 @@ var ZenitMap = forwardRef(({
|
|
|
1126
1211
|
}
|
|
1127
1212
|
return;
|
|
1128
1213
|
}, []);
|
|
1129
|
-
|
|
1214
|
+
useEffect5(() => {
|
|
1130
1215
|
if (featureInfoMode === "popup") {
|
|
1131
1216
|
ensurePopupStyles();
|
|
1132
1217
|
}
|
|
1133
1218
|
}, [featureInfoMode]);
|
|
1134
|
-
|
|
1219
|
+
useEffect5(() => {
|
|
1135
1220
|
if (featureInfoMode !== "popup") {
|
|
1136
1221
|
setIsPopupOpen(false);
|
|
1137
1222
|
}
|
|
1138
1223
|
}, [featureInfoMode]);
|
|
1139
|
-
|
|
1224
|
+
useEffect5(() => {
|
|
1140
1225
|
if (!mapInstance) return;
|
|
1141
1226
|
const popupPane = mapInstance.getPane("popupPane");
|
|
1142
1227
|
if (popupPane) {
|
|
@@ -1145,7 +1230,7 @@ var ZenitMap = forwardRef(({
|
|
|
1145
1230
|
const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
|
|
1146
1231
|
labelsPane.style.zIndex = "600";
|
|
1147
1232
|
}, [mapInstance]);
|
|
1148
|
-
|
|
1233
|
+
useEffect5(() => {
|
|
1149
1234
|
if (!mapInstance) return;
|
|
1150
1235
|
const handlePopupOpen = () => setIsPopupOpen(true);
|
|
1151
1236
|
const handlePopupClose = () => setIsPopupOpen(false);
|
|
@@ -1214,7 +1299,7 @@ var ZenitMap = forwardRef(({
|
|
|
1214
1299
|
const [mapOverrides, setMapOverrides] = useState2([]);
|
|
1215
1300
|
const [controlOverrides, setControlOverrides] = useState2([]);
|
|
1216
1301
|
const [uiOverrides, setUiOverrides] = useState2([]);
|
|
1217
|
-
|
|
1302
|
+
useEffect5(() => {
|
|
1218
1303
|
let isMounted = true;
|
|
1219
1304
|
setLoadingMap(true);
|
|
1220
1305
|
setMapError(null);
|
|
@@ -1237,7 +1322,7 @@ var ZenitMap = forwardRef(({
|
|
|
1237
1322
|
isMounted = false;
|
|
1238
1323
|
};
|
|
1239
1324
|
}, [client.maps, mapId, onError, onLoadingChange]);
|
|
1240
|
-
|
|
1325
|
+
useEffect5(() => {
|
|
1241
1326
|
if (normalizedLayers.length === 0) {
|
|
1242
1327
|
setLayers([]);
|
|
1243
1328
|
setBaseStates([]);
|
|
@@ -1268,7 +1353,7 @@ var ZenitMap = forwardRef(({
|
|
|
1268
1353
|
setMapOverrides(initialOverrides);
|
|
1269
1354
|
setUiOverrides([]);
|
|
1270
1355
|
}, [normalizedLayers]);
|
|
1271
|
-
|
|
1356
|
+
useEffect5(() => {
|
|
1272
1357
|
if (!layerControls) {
|
|
1273
1358
|
setControlOverrides([]);
|
|
1274
1359
|
return;
|
|
@@ -1280,7 +1365,7 @@ var ZenitMap = forwardRef(({
|
|
|
1280
1365
|
}));
|
|
1281
1366
|
setControlOverrides(overrides);
|
|
1282
1367
|
}, [layerControls]);
|
|
1283
|
-
|
|
1368
|
+
useEffect5(() => {
|
|
1284
1369
|
if (layerStates) {
|
|
1285
1370
|
return;
|
|
1286
1371
|
}
|
|
@@ -1290,12 +1375,12 @@ var ZenitMap = forwardRef(({
|
|
|
1290
1375
|
onLayerStateChange?.(reset);
|
|
1291
1376
|
}
|
|
1292
1377
|
}, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
|
|
1293
|
-
|
|
1378
|
+
useEffect5(() => {
|
|
1294
1379
|
if (layerStates) {
|
|
1295
1380
|
setEffectiveStates(layerStates);
|
|
1296
1381
|
}
|
|
1297
1382
|
}, [layerStates]);
|
|
1298
|
-
|
|
1383
|
+
useEffect5(() => {
|
|
1299
1384
|
if (layerStates) {
|
|
1300
1385
|
return;
|
|
1301
1386
|
}
|
|
@@ -1308,11 +1393,11 @@ var ZenitMap = forwardRef(({
|
|
|
1308
1393
|
setEffectiveStates(next);
|
|
1309
1394
|
onLayerStateChange?.(next);
|
|
1310
1395
|
}, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
|
|
1311
|
-
|
|
1396
|
+
useEffect5(() => {
|
|
1312
1397
|
if (!Array.isArray(layerControls) || layerControls.length > 0) return;
|
|
1313
1398
|
setUiOverrides([]);
|
|
1314
1399
|
}, [layerControls]);
|
|
1315
|
-
|
|
1400
|
+
useEffect5(() => {
|
|
1316
1401
|
if (layerStates) {
|
|
1317
1402
|
return;
|
|
1318
1403
|
}
|
|
@@ -1382,16 +1467,30 @@ var ZenitMap = forwardRef(({
|
|
|
1382
1467
|
return DEFAULT_CENTER;
|
|
1383
1468
|
}, [initialCenter, map?.settings?.center]);
|
|
1384
1469
|
const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
|
|
1385
|
-
|
|
1470
|
+
useEffect5(() => {
|
|
1386
1471
|
setCurrentZoom(zoom);
|
|
1387
1472
|
}, [zoom]);
|
|
1473
|
+
useEffect5(() => {
|
|
1474
|
+
if (!layerGeojson) return;
|
|
1475
|
+
layers.forEach((layer) => {
|
|
1476
|
+
const layerKey = String(layer.mapLayer.layerId);
|
|
1477
|
+
const incoming = layerGeojson[layer.mapLayer.layerId] ?? layerGeojson[layerKey] ?? null;
|
|
1478
|
+
if (incoming && !originalGeojsonByLayerIdRef.current[layerKey]) {
|
|
1479
|
+
originalGeojsonByLayerIdRef.current[layerKey] = incoming;
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}, [layerGeojson, layers]);
|
|
1388
1483
|
const decoratedLayers = useMemo2(() => {
|
|
1389
|
-
return layers.map((layer) =>
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1484
|
+
return layers.map((layer) => {
|
|
1485
|
+
const layerKey = String(layer.mapLayer.layerId);
|
|
1486
|
+
const override = layerGeojsonOverrides[layerKey];
|
|
1487
|
+
return {
|
|
1488
|
+
...layer,
|
|
1489
|
+
effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
|
|
1490
|
+
data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
|
|
1491
|
+
};
|
|
1492
|
+
});
|
|
1493
|
+
}, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
|
|
1395
1494
|
const orderedLayers = useMemo2(() => {
|
|
1396
1495
|
return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
|
|
1397
1496
|
}, [decoratedLayers]);
|
|
@@ -1466,7 +1565,7 @@ var ZenitMap = forwardRef(({
|
|
|
1466
1565
|
},
|
|
1467
1566
|
[onMapReady]
|
|
1468
1567
|
);
|
|
1469
|
-
|
|
1568
|
+
useEffect5(() => {
|
|
1470
1569
|
if (!mapInstance) {
|
|
1471
1570
|
setPanesReady(false);
|
|
1472
1571
|
return;
|
|
@@ -1620,6 +1719,24 @@ var ZenitMap = forwardRef(({
|
|
|
1620
1719
|
};
|
|
1621
1720
|
mapInstance.fitBounds(bounds, fitOptions);
|
|
1622
1721
|
},
|
|
1722
|
+
fitToBbox: (bbox, padding) => {
|
|
1723
|
+
if (!mapInstance) return;
|
|
1724
|
+
if (typeof bbox.minLat !== "number" || typeof bbox.minLon !== "number" || typeof bbox.maxLat !== "number" || typeof bbox.maxLon !== "number" || !Number.isFinite(bbox.minLat) || !Number.isFinite(bbox.minLon) || !Number.isFinite(bbox.maxLat) || !Number.isFinite(bbox.maxLon)) {
|
|
1725
|
+
console.warn("[ZenitMap.fitToBbox] Invalid bbox", bbox);
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
const resolvedPadding = padding ?? (isMobile ? 40 : 24);
|
|
1729
|
+
const bounds = L4.latLngBounds([bbox.minLat, bbox.minLon], [bbox.maxLat, bbox.maxLon]);
|
|
1730
|
+
mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
|
|
1731
|
+
},
|
|
1732
|
+
fitToGeoJson: (fc, padding) => {
|
|
1733
|
+
if (!mapInstance) return;
|
|
1734
|
+
if (!fc?.features?.length) return;
|
|
1735
|
+
const bounds = L4.geoJSON(fc).getBounds();
|
|
1736
|
+
if (!bounds.isValid()) return;
|
|
1737
|
+
const resolvedPadding = padding ?? (isMobile ? 40 : 24);
|
|
1738
|
+
mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
|
|
1739
|
+
},
|
|
1623
1740
|
setView: (coordinates, zoom2) => {
|
|
1624
1741
|
if (!mapInstance) return;
|
|
1625
1742
|
mapInstance.setView([coordinates.lat, coordinates.lon], zoom2 ?? mapInstance.getZoom(), {
|
|
@@ -1644,8 +1761,25 @@ var ZenitMap = forwardRef(({
|
|
|
1644
1761
|
highlightFeature: (layerId, featureId) => {
|
|
1645
1762
|
upsertUiOverride(layerId, { overrideVisible: true, overrideOpacity: 1 });
|
|
1646
1763
|
},
|
|
1764
|
+
updateLayerGeoJson: (layerId, featureCollection) => {
|
|
1765
|
+
const layerKey = String(layerId);
|
|
1766
|
+
setLayerGeojsonOverrides((prev) => ({ ...prev, [layerKey]: featureCollection }));
|
|
1767
|
+
},
|
|
1768
|
+
restoreLayerGeoJson: (layerId) => {
|
|
1769
|
+
const layerKey = String(layerId);
|
|
1770
|
+
const original = originalGeojsonByLayerIdRef.current[layerKey];
|
|
1771
|
+
setLayerGeojsonOverrides((prev) => {
|
|
1772
|
+
const next = { ...prev };
|
|
1773
|
+
if (original) {
|
|
1774
|
+
next[layerKey] = original;
|
|
1775
|
+
} else {
|
|
1776
|
+
delete next[layerKey];
|
|
1777
|
+
}
|
|
1778
|
+
return next;
|
|
1779
|
+
});
|
|
1780
|
+
},
|
|
1647
1781
|
getMapInstance: () => mapInstance
|
|
1648
|
-
}), [effectiveStates, mapInstance]);
|
|
1782
|
+
}), [effectiveStates, isMobile, mapInstance]);
|
|
1649
1783
|
if (loadingMap) {
|
|
1650
1784
|
return /* @__PURE__ */ jsx3("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
|
|
1651
1785
|
}
|
|
@@ -1698,6 +1832,7 @@ var ZenitMap = forwardRef(({
|
|
|
1698
1832
|
),
|
|
1699
1833
|
/* @__PURE__ */ jsx3(ZoomControl, { position: "topright" }),
|
|
1700
1834
|
/* @__PURE__ */ jsx3(MapInstanceBridge, { onReady: handleMapReady }),
|
|
1835
|
+
/* @__PURE__ */ jsx3(MapInvalidator, { trigger: mapId }),
|
|
1701
1836
|
/* @__PURE__ */ jsx3(
|
|
1702
1837
|
BBoxZoomHandler,
|
|
1703
1838
|
{
|
|
@@ -1714,7 +1849,7 @@ var ZenitMap = forwardRef(({
|
|
|
1714
1849
|
const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
|
|
1715
1850
|
const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
|
|
1716
1851
|
const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
|
|
1717
|
-
return /* @__PURE__ */ jsxs3(
|
|
1852
|
+
return /* @__PURE__ */ jsxs3(React4.Fragment, { children: [
|
|
1718
1853
|
layerState.data && /* @__PURE__ */ jsx3(
|
|
1719
1854
|
LayerGeoJson,
|
|
1720
1855
|
{
|
|
@@ -1871,13 +2006,13 @@ var ZenitMap = forwardRef(({
|
|
|
1871
2006
|
ZenitMap.displayName = "ZenitMap";
|
|
1872
2007
|
|
|
1873
2008
|
// src/react/ZenitLayerManager.tsx
|
|
1874
|
-
import
|
|
2009
|
+
import React5, { useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState3 } from "react";
|
|
1875
2010
|
|
|
1876
2011
|
// src/react/icons.tsx
|
|
1877
2012
|
import { Eye, EyeOff, ChevronLeft, ChevronRight, Layers, Upload, X, ZoomIn } from "lucide-react";
|
|
1878
2013
|
|
|
1879
2014
|
// src/react/ZenitLayerManager.tsx
|
|
1880
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2015
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1881
2016
|
var FLOAT_TOLERANCE = 1e-3;
|
|
1882
2017
|
function areEffectiveStatesEqual(a, b) {
|
|
1883
2018
|
if (a.length !== b.length) return false;
|
|
@@ -1915,7 +2050,9 @@ var ZenitLayerManager = ({
|
|
|
1915
2050
|
showUploadTab = true,
|
|
1916
2051
|
showLayerVisibilityIcon = true,
|
|
1917
2052
|
layerFeatureCounts,
|
|
1918
|
-
mapLayers
|
|
2053
|
+
mapLayers,
|
|
2054
|
+
onApplyLayerFilter,
|
|
2055
|
+
onClearLayerFilter
|
|
1919
2056
|
}) => {
|
|
1920
2057
|
const [map, setMap] = useState3(null);
|
|
1921
2058
|
const [loadingMap, setLoadingMap] = useState3(false);
|
|
@@ -1923,7 +2060,16 @@ var ZenitLayerManager = ({
|
|
|
1923
2060
|
const [layers, setLayers] = useState3([]);
|
|
1924
2061
|
const [activeTab, setActiveTab] = useState3("layers");
|
|
1925
2062
|
const [panelVisible, setPanelVisible] = useState3(true);
|
|
1926
|
-
const
|
|
2063
|
+
const [selectedFilterLayerId, setSelectedFilterLayerId] = useState3("");
|
|
2064
|
+
const [selectedFilterField, setSelectedFilterField] = useState3("");
|
|
2065
|
+
const [selectedFilterValue, setSelectedFilterValue] = useState3("");
|
|
2066
|
+
const [catalogByLayerField, setCatalogByLayerField] = useState3({});
|
|
2067
|
+
const [loadingCatalog, setLoadingCatalog] = useState3(false);
|
|
2068
|
+
const [applyingFilter, setApplyingFilter] = useState3(false);
|
|
2069
|
+
const [filterError, setFilterError] = useState3(null);
|
|
2070
|
+
const [appliedFilter, setAppliedFilter] = useState3(null);
|
|
2071
|
+
const catalogAbortRef = useRef6(null);
|
|
2072
|
+
const lastEmittedStatesRef = useRef6(null);
|
|
1927
2073
|
const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
|
|
1928
2074
|
const baseStates = useMemo3(
|
|
1929
2075
|
() => initLayerStates(
|
|
@@ -1964,7 +2110,7 @@ var ZenitLayerManager = ({
|
|
|
1964
2110
|
});
|
|
1965
2111
|
return index;
|
|
1966
2112
|
}, [map, mapLayers]);
|
|
1967
|
-
const resolveUserOpacity =
|
|
2113
|
+
const resolveUserOpacity = React5.useCallback((state) => {
|
|
1968
2114
|
if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
|
|
1969
2115
|
if (typeof state.overrideOpacity === "string") {
|
|
1970
2116
|
const parsed = Number.parseFloat(state.overrideOpacity);
|
|
@@ -1972,7 +2118,7 @@ var ZenitLayerManager = ({
|
|
|
1972
2118
|
}
|
|
1973
2119
|
return state.opacity ?? 1;
|
|
1974
2120
|
}, []);
|
|
1975
|
-
const resolveEffectiveOpacity =
|
|
2121
|
+
const resolveEffectiveOpacity = React5.useCallback(
|
|
1976
2122
|
(layerId, userOpacity) => {
|
|
1977
2123
|
if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
|
|
1978
2124
|
return userOpacity;
|
|
@@ -2002,7 +2148,7 @@ var ZenitLayerManager = ({
|
|
|
2002
2148
|
};
|
|
2003
2149
|
});
|
|
2004
2150
|
}, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
|
|
2005
|
-
|
|
2151
|
+
useEffect6(() => {
|
|
2006
2152
|
let cancelled = false;
|
|
2007
2153
|
setLoadingMap(true);
|
|
2008
2154
|
setMapError(null);
|
|
@@ -2034,12 +2180,12 @@ var ZenitLayerManager = ({
|
|
|
2034
2180
|
cancelled = true;
|
|
2035
2181
|
};
|
|
2036
2182
|
}, [client.maps, mapId]);
|
|
2037
|
-
|
|
2183
|
+
useEffect6(() => {
|
|
2038
2184
|
if (!showUploadTab && activeTab === "upload") {
|
|
2039
2185
|
setActiveTab("layers");
|
|
2040
2186
|
}
|
|
2041
2187
|
}, [activeTab, showUploadTab]);
|
|
2042
|
-
|
|
2188
|
+
useEffect6(() => {
|
|
2043
2189
|
if (isControlled) return;
|
|
2044
2190
|
if (!onLayerStatesChange) return;
|
|
2045
2191
|
const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
|
|
@@ -2057,7 +2203,7 @@ var ZenitLayerManager = ({
|
|
|
2057
2203
|
mapZoom,
|
|
2058
2204
|
onLayerStatesChange
|
|
2059
2205
|
]);
|
|
2060
|
-
const updateLayerVisible =
|
|
2206
|
+
const updateLayerVisible = React5.useCallback(
|
|
2061
2207
|
(layerId, visible) => {
|
|
2062
2208
|
if (!onLayerStatesChange) return;
|
|
2063
2209
|
const next = effectiveStates.map(
|
|
@@ -2067,7 +2213,7 @@ var ZenitLayerManager = ({
|
|
|
2067
2213
|
},
|
|
2068
2214
|
[effectiveStates, onLayerStatesChange]
|
|
2069
2215
|
);
|
|
2070
|
-
const updateLayerOpacity =
|
|
2216
|
+
const updateLayerOpacity = React5.useCallback(
|
|
2071
2217
|
(layerId, opacity) => {
|
|
2072
2218
|
if (!onLayerStatesChange) return;
|
|
2073
2219
|
const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
|
|
@@ -2078,7 +2224,7 @@ var ZenitLayerManager = ({
|
|
|
2078
2224
|
},
|
|
2079
2225
|
[effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
|
|
2080
2226
|
);
|
|
2081
|
-
const resolveFeatureCount =
|
|
2227
|
+
const resolveFeatureCount = React5.useCallback(
|
|
2082
2228
|
(layerId, layer) => {
|
|
2083
2229
|
const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
|
|
2084
2230
|
if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
|
|
@@ -2116,7 +2262,140 @@ var ZenitLayerManager = ({
|
|
|
2116
2262
|
return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
|
|
2117
2263
|
});
|
|
2118
2264
|
}, [effectiveStates, layers, resolveFeatureCount]);
|
|
2119
|
-
const
|
|
2265
|
+
const filterableLayers = useMemo3(() => {
|
|
2266
|
+
return decoratedLayers.filter((entry) => {
|
|
2267
|
+
const prefilters = entry.mapLayer.layerConfig?.prefilters;
|
|
2268
|
+
return !!prefilters && Object.keys(prefilters).length > 0;
|
|
2269
|
+
});
|
|
2270
|
+
}, [decoratedLayers]);
|
|
2271
|
+
const selectedFilterLayer = useMemo3(
|
|
2272
|
+
() => filterableLayers.find((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId) ?? null,
|
|
2273
|
+
[filterableLayers, selectedFilterLayerId]
|
|
2274
|
+
);
|
|
2275
|
+
const filterFields = useMemo3(() => {
|
|
2276
|
+
const prefilters = selectedFilterLayer?.mapLayer.layerConfig?.prefilters;
|
|
2277
|
+
return prefilters ? Object.keys(prefilters) : [];
|
|
2278
|
+
}, [selectedFilterLayer]);
|
|
2279
|
+
const activeCatalogKey = selectedFilterLayer ? `${selectedFilterLayer.mapLayer.layerId}:${selectedFilterField}` : null;
|
|
2280
|
+
const activeCatalogValues = activeCatalogKey ? catalogByLayerField[activeCatalogKey] ?? [] : [];
|
|
2281
|
+
const extractCatalogValues = React5.useCallback((catalogData, field) => {
|
|
2282
|
+
const values = /* @__PURE__ */ new Set();
|
|
2283
|
+
const pushValue = (value) => {
|
|
2284
|
+
if (value === null || value === void 0) return;
|
|
2285
|
+
const normalized = String(value).trim();
|
|
2286
|
+
if (normalized) values.add(normalized);
|
|
2287
|
+
};
|
|
2288
|
+
if (catalogData && typeof catalogData === "object") {
|
|
2289
|
+
const maybeRecord = catalogData;
|
|
2290
|
+
const directField = maybeRecord[field];
|
|
2291
|
+
if (Array.isArray(directField)) {
|
|
2292
|
+
directField.forEach(pushValue);
|
|
2293
|
+
}
|
|
2294
|
+
const items = maybeRecord.items;
|
|
2295
|
+
if (Array.isArray(items)) {
|
|
2296
|
+
items.forEach((item) => {
|
|
2297
|
+
if (!item || typeof item !== "object") return;
|
|
2298
|
+
const row = item;
|
|
2299
|
+
const rowField = row.field;
|
|
2300
|
+
if (String(rowField ?? "").toUpperCase() === field.toUpperCase() && Array.isArray(row.values)) {
|
|
2301
|
+
row.values.forEach(pushValue);
|
|
2302
|
+
}
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
const features = maybeRecord.features;
|
|
2306
|
+
if (Array.isArray(features)) {
|
|
2307
|
+
features.forEach((feature) => {
|
|
2308
|
+
if (!feature || typeof feature !== "object") return;
|
|
2309
|
+
const properties = feature.properties;
|
|
2310
|
+
if (properties && field in properties) {
|
|
2311
|
+
pushValue(properties[field]);
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
return [...values].sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
2317
|
+
}, []);
|
|
2318
|
+
useEffect6(() => {
|
|
2319
|
+
if (!filterableLayers.length) {
|
|
2320
|
+
setSelectedFilterLayerId("");
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
if (!selectedFilterLayerId || !filterableLayers.some((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId)) {
|
|
2324
|
+
setSelectedFilterLayerId(String(filterableLayers[0].mapLayer.layerId));
|
|
2325
|
+
}
|
|
2326
|
+
}, [filterableLayers, selectedFilterLayerId]);
|
|
2327
|
+
useEffect6(() => {
|
|
2328
|
+
if (!filterFields.length) {
|
|
2329
|
+
setSelectedFilterField("");
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
if (!selectedFilterField || !filterFields.includes(selectedFilterField)) {
|
|
2333
|
+
setSelectedFilterField(filterFields[0]);
|
|
2334
|
+
setSelectedFilterValue("");
|
|
2335
|
+
}
|
|
2336
|
+
}, [filterFields, selectedFilterField]);
|
|
2337
|
+
useEffect6(() => {
|
|
2338
|
+
if (activeTab !== "filters") return;
|
|
2339
|
+
if (!selectedFilterLayer || !selectedFilterField || !activeCatalogKey) return;
|
|
2340
|
+
if (catalogByLayerField[activeCatalogKey]) return;
|
|
2341
|
+
catalogAbortRef.current?.abort();
|
|
2342
|
+
const controller = new AbortController();
|
|
2343
|
+
catalogAbortRef.current = controller;
|
|
2344
|
+
setLoadingCatalog(true);
|
|
2345
|
+
setFilterError(null);
|
|
2346
|
+
client.layers.getLayerFeaturesCatalog(selectedFilterLayer.mapLayer.layerId).then((response) => {
|
|
2347
|
+
if (controller.signal.aborted) return;
|
|
2348
|
+
const values = extractCatalogValues(response.data, selectedFilterField);
|
|
2349
|
+
setCatalogByLayerField((prev) => ({ ...prev, [activeCatalogKey]: values }));
|
|
2350
|
+
}).catch((error) => {
|
|
2351
|
+
if (controller.signal.aborted) return;
|
|
2352
|
+
const message = error instanceof Error ? error.message : "No se pudo cargar el cat\xE1logo";
|
|
2353
|
+
setFilterError(message);
|
|
2354
|
+
}).finally(() => {
|
|
2355
|
+
if (!controller.signal.aborted) setLoadingCatalog(false);
|
|
2356
|
+
});
|
|
2357
|
+
return () => {
|
|
2358
|
+
controller.abort();
|
|
2359
|
+
};
|
|
2360
|
+
}, [activeCatalogKey, activeTab, catalogByLayerField, client.layers, extractCatalogValues, selectedFilterField, selectedFilterLayer]);
|
|
2361
|
+
const handleApplyFilter = React5.useCallback(async () => {
|
|
2362
|
+
if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
|
|
2363
|
+
setApplyingFilter(true);
|
|
2364
|
+
setFilterError(null);
|
|
2365
|
+
try {
|
|
2366
|
+
await onApplyLayerFilter({
|
|
2367
|
+
layerId: selectedFilterLayer.mapLayer.layerId,
|
|
2368
|
+
field: selectedFilterField,
|
|
2369
|
+
value: selectedFilterValue
|
|
2370
|
+
});
|
|
2371
|
+
setAppliedFilter({
|
|
2372
|
+
layerId: selectedFilterLayer.mapLayer.layerId,
|
|
2373
|
+
field: selectedFilterField,
|
|
2374
|
+
value: selectedFilterValue
|
|
2375
|
+
});
|
|
2376
|
+
} catch (error) {
|
|
2377
|
+
const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
|
|
2378
|
+
setFilterError(message);
|
|
2379
|
+
} finally {
|
|
2380
|
+
setApplyingFilter(false);
|
|
2381
|
+
}
|
|
2382
|
+
}, [onApplyLayerFilter, selectedFilterField, selectedFilterLayer, selectedFilterValue]);
|
|
2383
|
+
const handleClearFilter = React5.useCallback(async () => {
|
|
2384
|
+
if (!selectedFilterLayer) return;
|
|
2385
|
+
setApplyingFilter(true);
|
|
2386
|
+
setFilterError(null);
|
|
2387
|
+
try {
|
|
2388
|
+
await onClearLayerFilter?.({ layerId: selectedFilterLayer.mapLayer.layerId, field: selectedFilterField || void 0 });
|
|
2389
|
+
setSelectedFilterValue("");
|
|
2390
|
+
setAppliedFilter(null);
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
const message = error instanceof Error ? error.message : "No se pudo limpiar el filtro";
|
|
2393
|
+
setFilterError(message);
|
|
2394
|
+
} finally {
|
|
2395
|
+
setApplyingFilter(false);
|
|
2396
|
+
}
|
|
2397
|
+
}, [onClearLayerFilter, selectedFilterField, selectedFilterLayer]);
|
|
2398
|
+
const resolveLayerStyle = React5.useCallback(
|
|
2120
2399
|
(layerId) => {
|
|
2121
2400
|
const layerKey = String(layerId);
|
|
2122
2401
|
const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
|
|
@@ -2454,6 +2733,18 @@ var ZenitLayerManager = ({
|
|
|
2454
2733
|
"Subir"
|
|
2455
2734
|
]
|
|
2456
2735
|
}
|
|
2736
|
+
),
|
|
2737
|
+
/* @__PURE__ */ jsxs4(
|
|
2738
|
+
"button",
|
|
2739
|
+
{
|
|
2740
|
+
type: "button",
|
|
2741
|
+
className: `zlm-tab${activeTab === "filters" ? " is-active" : ""}`,
|
|
2742
|
+
onClick: () => setActiveTab("filters"),
|
|
2743
|
+
children: [
|
|
2744
|
+
/* @__PURE__ */ jsx4(Layers, { size: 16 }),
|
|
2745
|
+
"Filtros"
|
|
2746
|
+
]
|
|
2747
|
+
}
|
|
2457
2748
|
)
|
|
2458
2749
|
]
|
|
2459
2750
|
}
|
|
@@ -2461,6 +2752,68 @@ var ZenitLayerManager = ({
|
|
|
2461
2752
|
] }),
|
|
2462
2753
|
panelVisible && /* @__PURE__ */ jsxs4("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
|
|
2463
2754
|
activeTab === "layers" && renderLayerCards(),
|
|
2755
|
+
activeTab === "filters" && /* @__PURE__ */ jsx4("div", { className: "zlm-filter-panel", style: { display: "flex", flexDirection: "column", gap: 10, background: "#fff", border: "1px solid #e2e8f0", borderRadius: 12, padding: 12 }, children: !filterableLayers.length ? /* @__PURE__ */ jsx4("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
2756
|
+
filterableLayers.length > 1 && /* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
|
|
2757
|
+
"Capa",
|
|
2758
|
+
/* @__PURE__ */ jsx4("select", { className: "zlm-filter-select", value: selectedFilterLayerId, onChange: (e) => setSelectedFilterLayerId(e.target.value), children: filterableLayers.map((layer) => /* @__PURE__ */ jsx4("option", { value: String(layer.mapLayer.layerId), children: layer.layerName ?? `Capa ${layer.mapLayer.layerId}` }, String(layer.mapLayer.layerId))) })
|
|
2759
|
+
] }),
|
|
2760
|
+
/* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
|
|
2761
|
+
"Campo",
|
|
2762
|
+
/* @__PURE__ */ jsx4("select", { className: "zlm-filter-select", value: selectedFilterField, onChange: (e) => {
|
|
2763
|
+
setSelectedFilterField(e.target.value);
|
|
2764
|
+
setSelectedFilterValue("");
|
|
2765
|
+
}, children: filterFields.map((field) => /* @__PURE__ */ jsx4("option", { value: field, children: field }, field)) })
|
|
2766
|
+
] }),
|
|
2767
|
+
/* @__PURE__ */ jsxs4("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
|
|
2768
|
+
"Valor",
|
|
2769
|
+
/* @__PURE__ */ jsxs4(
|
|
2770
|
+
"select",
|
|
2771
|
+
{
|
|
2772
|
+
className: "zlm-filter-select",
|
|
2773
|
+
value: selectedFilterValue,
|
|
2774
|
+
onChange: (e) => {
|
|
2775
|
+
const value = e.target.value;
|
|
2776
|
+
setSelectedFilterValue(value);
|
|
2777
|
+
},
|
|
2778
|
+
children: [
|
|
2779
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: "Seleccionar\u2026" }),
|
|
2780
|
+
activeCatalogValues.map((value) => /* @__PURE__ */ jsx4("option", { value, children: value }, value))
|
|
2781
|
+
]
|
|
2782
|
+
}
|
|
2783
|
+
)
|
|
2784
|
+
] }),
|
|
2785
|
+
loadingCatalog && /* @__PURE__ */ jsx4("div", { style: { color: "#64748b", fontSize: 12 }, children: "Cargando cat\xE1logo\u2026" }),
|
|
2786
|
+
!loadingCatalog && selectedFilterField && activeCatalogValues.length === 0 && /* @__PURE__ */ jsx4("div", { style: { color: "#64748b", fontSize: 12 }, children: "No hay cat\xE1logo disponible para este filtro." }),
|
|
2787
|
+
filterError && /* @__PURE__ */ jsx4("div", { style: { color: "#b91c1c", fontSize: 12 }, children: filterError }),
|
|
2788
|
+
appliedFilter && /* @__PURE__ */ jsxs4("div", { className: "zlm-badge", style: { alignSelf: "flex-start" }, children: [
|
|
2789
|
+
"Activo: ",
|
|
2790
|
+
appliedFilter.field,
|
|
2791
|
+
" = ",
|
|
2792
|
+
appliedFilter.value
|
|
2793
|
+
] }),
|
|
2794
|
+
/* @__PURE__ */ jsxs4("div", { className: "zlm-filter-actions", style: { display: "flex", gap: 8 }, children: [
|
|
2795
|
+
/* @__PURE__ */ jsx4(
|
|
2796
|
+
"button",
|
|
2797
|
+
{
|
|
2798
|
+
type: "button",
|
|
2799
|
+
className: "zlm-panel-toggle",
|
|
2800
|
+
disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
|
|
2801
|
+
onClick: handleApplyFilter,
|
|
2802
|
+
children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
|
|
2803
|
+
}
|
|
2804
|
+
),
|
|
2805
|
+
/* @__PURE__ */ jsx4(
|
|
2806
|
+
"button",
|
|
2807
|
+
{
|
|
2808
|
+
type: "button",
|
|
2809
|
+
className: "zlm-panel-toggle",
|
|
2810
|
+
disabled: applyingFilter,
|
|
2811
|
+
onClick: handleClearFilter,
|
|
2812
|
+
children: "Limpiar"
|
|
2813
|
+
}
|
|
2814
|
+
)
|
|
2815
|
+
] })
|
|
2816
|
+
] }) }),
|
|
2464
2817
|
showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx4("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
|
|
2465
2818
|
] })
|
|
2466
2819
|
] });
|
|
@@ -2499,11 +2852,11 @@ var ZenitFeatureFilterPanel = ({
|
|
|
2499
2852
|
};
|
|
2500
2853
|
|
|
2501
2854
|
// src/react/ai/FloatingChatBox.tsx
|
|
2502
|
-
import { useCallback as useCallback4, useEffect as
|
|
2855
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef8, useState as useState5 } from "react";
|
|
2503
2856
|
import { createPortal as createPortal2 } from "react-dom";
|
|
2504
2857
|
|
|
2505
2858
|
// src/react/hooks/use-chat.ts
|
|
2506
|
-
import { useCallback as useCallback3, useRef as
|
|
2859
|
+
import { useCallback as useCallback3, useRef as useRef7, useState as useState4 } from "react";
|
|
2507
2860
|
|
|
2508
2861
|
// src/ai/chat.service.ts
|
|
2509
2862
|
var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
|
|
@@ -2663,7 +3016,7 @@ var useSendMessageStream = (config) => {
|
|
|
2663
3016
|
const [streamingText, setStreamingText] = useState4("");
|
|
2664
3017
|
const [completeResponse, setCompleteResponse] = useState4(null);
|
|
2665
3018
|
const [error, setError] = useState4(null);
|
|
2666
|
-
const requestIdRef =
|
|
3019
|
+
const requestIdRef = useRef7(0);
|
|
2667
3020
|
const reset = useCallback3(() => {
|
|
2668
3021
|
setIsStreaming(false);
|
|
2669
3022
|
setStreamingText("");
|
|
@@ -2875,7 +3228,7 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2875
3228
|
};
|
|
2876
3229
|
|
|
2877
3230
|
// src/react/ai/FloatingChatBox.tsx
|
|
2878
|
-
import { Fragment as
|
|
3231
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2879
3232
|
var ChatIcon = () => /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
|
|
2880
3233
|
var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2881
3234
|
/* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
@@ -2961,6 +3314,17 @@ var styles = {
|
|
|
2961
3314
|
width: 480,
|
|
2962
3315
|
height: 640
|
|
2963
3316
|
},
|
|
3317
|
+
panelMobileFullscreen: {
|
|
3318
|
+
position: "fixed",
|
|
3319
|
+
top: 0,
|
|
3320
|
+
left: 0,
|
|
3321
|
+
right: 0,
|
|
3322
|
+
bottom: 0,
|
|
3323
|
+
width: "100%",
|
|
3324
|
+
height: "100%",
|
|
3325
|
+
borderRadius: 0,
|
|
3326
|
+
margin: 0
|
|
3327
|
+
},
|
|
2964
3328
|
// Header with green gradient
|
|
2965
3329
|
header: {
|
|
2966
3330
|
padding: "14px 16px",
|
|
@@ -3210,16 +3574,16 @@ var FloatingChatBox = ({
|
|
|
3210
3574
|
const [errorMessage, setErrorMessage] = useState5(null);
|
|
3211
3575
|
const [isFocused, setIsFocused] = useState5(false);
|
|
3212
3576
|
const [isMobile, setIsMobile] = useState5(false);
|
|
3213
|
-
const messagesEndRef =
|
|
3214
|
-
const messagesContainerRef =
|
|
3215
|
-
const chatBoxRef =
|
|
3577
|
+
const messagesEndRef = useRef8(null);
|
|
3578
|
+
const messagesContainerRef = useRef8(null);
|
|
3579
|
+
const chatBoxRef = useRef8(null);
|
|
3216
3580
|
const chatConfig = useMemo4(() => {
|
|
3217
3581
|
if (!baseUrl) return void 0;
|
|
3218
3582
|
return { baseUrl, accessToken, getAccessToken };
|
|
3219
3583
|
}, [accessToken, baseUrl, getAccessToken]);
|
|
3220
3584
|
const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
|
|
3221
3585
|
const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
|
|
3222
|
-
|
|
3586
|
+
useEffect7(() => {
|
|
3223
3587
|
if (open && isMobile) {
|
|
3224
3588
|
setExpanded(true);
|
|
3225
3589
|
}
|
|
@@ -3229,7 +3593,7 @@ var FloatingChatBox = ({
|
|
|
3229
3593
|
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
|
|
3230
3594
|
}
|
|
3231
3595
|
}, []);
|
|
3232
|
-
|
|
3596
|
+
useEffect7(() => {
|
|
3233
3597
|
if (open && messages.length === 0) {
|
|
3234
3598
|
setMessages([
|
|
3235
3599
|
{
|
|
@@ -3240,10 +3604,10 @@ var FloatingChatBox = ({
|
|
|
3240
3604
|
]);
|
|
3241
3605
|
}
|
|
3242
3606
|
}, [open, messages.length]);
|
|
3243
|
-
|
|
3607
|
+
useEffect7(() => {
|
|
3244
3608
|
scrollToBottom();
|
|
3245
3609
|
}, [messages, streamingText, scrollToBottom]);
|
|
3246
|
-
|
|
3610
|
+
useEffect7(() => {
|
|
3247
3611
|
if (!open) return;
|
|
3248
3612
|
if (isMobile && expanded) return;
|
|
3249
3613
|
const handleClickOutside = (event) => {
|
|
@@ -3256,7 +3620,7 @@ var FloatingChatBox = ({
|
|
|
3256
3620
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
3257
3621
|
};
|
|
3258
3622
|
}, [open, isMobile, expanded]);
|
|
3259
|
-
|
|
3623
|
+
useEffect7(() => {
|
|
3260
3624
|
if (typeof window === "undefined") return;
|
|
3261
3625
|
const mediaQuery = window.matchMedia("(max-width: 768px)");
|
|
3262
3626
|
const updateMobile = () => setIsMobile(mediaQuery.matches);
|
|
@@ -3274,7 +3638,7 @@ var FloatingChatBox = ({
|
|
|
3274
3638
|
}
|
|
3275
3639
|
};
|
|
3276
3640
|
}, []);
|
|
3277
|
-
|
|
3641
|
+
useEffect7(() => {
|
|
3278
3642
|
if (typeof document === "undefined") return;
|
|
3279
3643
|
if (!open || !isMobile) return;
|
|
3280
3644
|
document.body.style.overflow = "hidden";
|
|
@@ -3486,22 +3850,6 @@ var FloatingChatBox = ({
|
|
|
3486
3850
|
box-sizing: border-box;
|
|
3487
3851
|
}
|
|
3488
3852
|
@media (max-width: 768px) {
|
|
3489
|
-
.zenit-chat-panel.zenit-chat-panel--fullscreen {
|
|
3490
|
-
position: fixed !important;
|
|
3491
|
-
left: 0 !important;
|
|
3492
|
-
right: 0 !important;
|
|
3493
|
-
top: 4rem !important;
|
|
3494
|
-
bottom: 0 !important;
|
|
3495
|
-
width: 100% !important;
|
|
3496
|
-
max-width: 100% !important;
|
|
3497
|
-
height: auto !important;
|
|
3498
|
-
border-radius: 0 !important;
|
|
3499
|
-
display: flex !important;
|
|
3500
|
-
flex-direction: column !important;
|
|
3501
|
-
overflow: hidden !important;
|
|
3502
|
-
z-index: 100000 !important;
|
|
3503
|
-
padding-top: env(safe-area-inset-top);
|
|
3504
|
-
}
|
|
3505
3853
|
.zenit-chat-panel.zenit-chat-panel--fullscreen .zenit-ai-body {
|
|
3506
3854
|
flex: 1;
|
|
3507
3855
|
min-height: 0;
|
|
@@ -3521,16 +3869,20 @@ var FloatingChatBox = ({
|
|
|
3521
3869
|
"div",
|
|
3522
3870
|
{
|
|
3523
3871
|
ref: chatBoxRef,
|
|
3524
|
-
className: `zenit-chat-panel${expanded ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
|
|
3525
|
-
style: {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3872
|
+
className: `zenit-chat-panel${expanded && !isMobile ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
|
|
3873
|
+
style: (() => {
|
|
3874
|
+
const desktopStyle = expanded ? styles.panelExpanded : styles.panelNormal;
|
|
3875
|
+
const mobileStyle = styles.panelMobileFullscreen;
|
|
3876
|
+
return {
|
|
3877
|
+
...styles.panel,
|
|
3878
|
+
...isMobile ? mobileStyle : desktopStyle
|
|
3879
|
+
};
|
|
3880
|
+
})(),
|
|
3529
3881
|
children: [
|
|
3530
3882
|
/* @__PURE__ */ jsxs6("header", { style: styles.header, children: [
|
|
3531
3883
|
/* @__PURE__ */ jsx7("h3", { style: styles.title, children: "Asistente Zenit AI" }),
|
|
3532
3884
|
/* @__PURE__ */ jsxs6("div", { style: styles.headerButtons, children: [
|
|
3533
|
-
/* @__PURE__ */ jsx7(
|
|
3885
|
+
!isMobile && /* @__PURE__ */ jsx7(
|
|
3534
3886
|
"button",
|
|
3535
3887
|
{
|
|
3536
3888
|
type: "button",
|
|
@@ -3604,7 +3956,7 @@ var FloatingChatBox = ({
|
|
|
3604
3956
|
...styles.messageBubble,
|
|
3605
3957
|
...styles.assistantMessage
|
|
3606
3958
|
},
|
|
3607
|
-
children: streamingText ? /* @__PURE__ */ jsxs6(
|
|
3959
|
+
children: streamingText ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
3608
3960
|
/* @__PURE__ */ jsx7(MarkdownRenderer, { content: streamingText }),
|
|
3609
3961
|
/* @__PURE__ */ jsx7("span", { style: styles.cursor })
|
|
3610
3962
|
] }) : /* @__PURE__ */ jsxs6("div", { style: styles.thinkingText, children: [
|
|
@@ -3671,7 +4023,7 @@ var FloatingChatBox = ({
|
|
|
3671
4023
|
},
|
|
3672
4024
|
onClick: () => setOpen((prev) => !prev),
|
|
3673
4025
|
"aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
|
|
3674
|
-
children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(
|
|
4026
|
+
children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
3675
4027
|
/* @__PURE__ */ jsx7(ChatIcon, {}),
|
|
3676
4028
|
!isMobile && /* @__PURE__ */ jsx7("span", { children: "Asistente IA" })
|
|
3677
4029
|
] })
|
|
@@ -3722,4 +4074,4 @@ export {
|
|
|
3722
4074
|
useSendMessageStream,
|
|
3723
4075
|
FloatingChatBox
|
|
3724
4076
|
};
|
|
3725
|
-
//# sourceMappingURL=chunk-
|
|
4077
|
+
//# sourceMappingURL=chunk-URDEEWUZ.mjs.map
|