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.
@@ -58,7 +58,7 @@ __export(react_exports, {
58
58
  module.exports = __toCommonJS(react_exports);
59
59
 
60
60
  // src/react/ZenitMap.tsx
61
- var import_react4 = __toESM(require("react"));
61
+ var import_react5 = __toESM(require("react"));
62
62
  var import_react_leaflet4 = require("react-leaflet");
63
63
  var import_leaflet4 = __toESM(require("leaflet"));
64
64
 
@@ -293,10 +293,31 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
293
293
  }
294
294
 
295
295
  // src/react/map/layer-geojson.tsx
296
+ var import_react = require("react");
296
297
  var import_react_leaflet = require("react-leaflet");
297
298
  var import_leaflet = __toESM(require("leaflet"));
298
299
  var import_jsx_runtime = require("react/jsx-runtime");
299
300
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
301
+ function normalizeBboxFromData(data) {
302
+ const bboxCandidate = data.bbox;
303
+ if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
304
+ const [minLon, minLat, maxLon, maxLat] = bboxCandidate;
305
+ const values = [minLon, minLat, maxLon, maxLat].map((value) => Number(value));
306
+ if (values.every((value) => Number.isFinite(value))) {
307
+ return values;
308
+ }
309
+ return null;
310
+ }
311
+ function buildIdsSample(features) {
312
+ return features.slice(0, 10).map((feature) => {
313
+ const typedFeature = feature;
314
+ if (typedFeature.id !== void 0 && typedFeature.id !== null) {
315
+ return String(typedFeature.id);
316
+ }
317
+ const featureId = typedFeature.properties?.featureId;
318
+ return featureId !== void 0 && featureId !== null ? String(featureId) : "";
319
+ }).join(",");
320
+ }
300
321
  function getGeometryType(feature) {
301
322
  const t = feature?.geometry?.type;
302
323
  return typeof t === "string" ? t : null;
@@ -332,8 +353,58 @@ var LayerGeoJson = ({
332
353
  const features = data.features ?? [];
333
354
  const fillFeatures = features.filter(isNonPointGeometry);
334
355
  const pointFeatures = features.filter(isPointGeometry);
356
+ const dataVersionRef = (0, import_react.useRef)(0);
357
+ const prevSignatureRef = (0, import_react.useRef)("");
358
+ const firstId = features.length > 0 ? String(features[0]?.id ?? "") : "";
359
+ const lastId = features.length > 0 ? String(features[features.length - 1]?.id ?? "") : "";
360
+ const bbox = normalizeBboxFromData(data);
361
+ const idsSample = buildIdsSample(features);
362
+ const signature = bbox ? `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}|${bbox[0]}|${bbox[1]}|${bbox[2]}|${bbox[3]}` : `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}`;
363
+ const signatureToken = signature.replace(/[^a-zA-Z0-9_-]/g, "_");
364
+ if (prevSignatureRef.current !== signature) {
365
+ dataVersionRef.current += 1;
366
+ prevSignatureRef.current = signature;
367
+ }
335
368
  const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
336
369
  const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
370
+ const clusterLayerRef = (0, import_react.useRef)(null);
371
+ const canCluster = typeof import_leaflet.default.markerClusterGroup === "function";
372
+ (0, import_react.useEffect)(() => {
373
+ if (!mapInstance || !panesReady || !pointsData || !canCluster) return;
374
+ const markerClusterGroup = import_leaflet.default.markerClusterGroup;
375
+ const clusterLayer = clusterLayerRef.current ?? markerClusterGroup();
376
+ clusterLayerRef.current = clusterLayer;
377
+ if (!mapInstance.hasLayer(clusterLayer)) {
378
+ mapInstance.addLayer(clusterLayer);
379
+ }
380
+ clusterLayer.clearLayers();
381
+ const geoJsonLayer = import_leaflet.default.geoJSON(pointsData, {
382
+ pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
383
+ radius: isMobile ? 8 : 6,
384
+ pane: mapInstance.getPane(pointsPaneName) ? pointsPaneName : void 0,
385
+ ...styleFn(feature, layerType, baseOpacity)
386
+ }),
387
+ onEachFeature
388
+ });
389
+ clusterLayer.addLayer(geoJsonLayer);
390
+ return () => {
391
+ clusterLayer.clearLayers();
392
+ if (mapInstance.hasLayer(clusterLayer)) {
393
+ mapInstance.removeLayer(clusterLayer);
394
+ }
395
+ };
396
+ }, [
397
+ baseOpacity,
398
+ canCluster,
399
+ isMobile,
400
+ layerType,
401
+ mapInstance,
402
+ onEachFeature,
403
+ panesReady,
404
+ pointsData,
405
+ pointsPaneName,
406
+ styleFn
407
+ ]);
337
408
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
338
409
  fillData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
339
410
  import_react_leaflet.GeoJSON,
@@ -346,9 +417,9 @@ var LayerGeoJson = ({
346
417
  onPolygonLabel?.(feature, layer);
347
418
  }
348
419
  },
349
- `fill-${layerId}`
420
+ `fill-${layerId}-${signatureToken}-v${dataVersionRef.current}`
350
421
  ),
351
- pointsData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
422
+ pointsData && !canCluster && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
352
423
  import_react_leaflet.GeoJSON,
353
424
  {
354
425
  data: pointsData,
@@ -359,32 +430,32 @@ var LayerGeoJson = ({
359
430
  }),
360
431
  onEachFeature
361
432
  },
362
- `points-${layerId}`
433
+ `points-${layerId}-${signatureToken}-v${dataVersionRef.current}`
363
434
  )
364
435
  ] });
365
436
  };
366
437
 
367
438
  // src/react/map/location-control.tsx
368
- var import_react2 = require("react");
439
+ var import_react3 = require("react");
369
440
  var import_react_dom = require("react-dom");
370
441
  var import_react_leaflet2 = require("react-leaflet");
371
442
  var import_leaflet3 = __toESM(require("leaflet"));
372
443
 
373
444
  // src/react/hooks/use-geolocation.ts
374
- var import_react = require("react");
445
+ var import_react2 = require("react");
375
446
  function useGeolocation(options) {
376
- const [isTracking, setIsTracking] = (0, import_react.useState)(false);
377
- const [location, setLocation] = (0, import_react.useState)(null);
378
- const [error, setError] = (0, import_react.useState)(null);
379
- const watchIdRef = (0, import_react.useRef)(null);
380
- const stopTracking = (0, import_react.useCallback)(() => {
447
+ const [isTracking, setIsTracking] = (0, import_react2.useState)(false);
448
+ const [location, setLocation] = (0, import_react2.useState)(null);
449
+ const [error, setError] = (0, import_react2.useState)(null);
450
+ const watchIdRef = (0, import_react2.useRef)(null);
451
+ const stopTracking = (0, import_react2.useCallback)(() => {
381
452
  if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
382
453
  navigator.geolocation.clearWatch(watchIdRef.current);
383
454
  }
384
455
  watchIdRef.current = null;
385
456
  setIsTracking(false);
386
457
  }, []);
387
- const startTracking = (0, import_react.useCallback)(() => {
458
+ const startTracking = (0, import_react2.useCallback)(() => {
388
459
  if (typeof navigator === "undefined" || !navigator.geolocation) {
389
460
  setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
390
461
  return;
@@ -410,15 +481,15 @@ function useGeolocation(options) {
410
481
  }
411
482
  );
412
483
  }, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
413
- const toggleTracking = (0, import_react.useCallback)(() => {
484
+ const toggleTracking = (0, import_react2.useCallback)(() => {
414
485
  if (isTracking) {
415
486
  stopTracking();
416
487
  } else {
417
488
  startTracking();
418
489
  }
419
490
  }, [isTracking, startTracking, stopTracking]);
420
- const clearError = (0, import_react.useCallback)(() => setError(null), []);
421
- (0, import_react.useEffect)(() => {
491
+ const clearError = (0, import_react2.useCallback)(() => setError(null), []);
492
+ (0, import_react2.useEffect)(() => {
422
493
  return () => {
423
494
  stopTracking();
424
495
  };
@@ -845,10 +916,10 @@ var LocateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { wi
845
916
  ] });
846
917
  var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
847
918
  const map = (0, import_react_leaflet2.useMap)();
848
- const controlRef = (0, import_react2.useRef)(null);
849
- const hasCenteredRef = (0, import_react2.useRef)(false);
919
+ const controlRef = (0, import_react3.useRef)(null);
920
+ const hasCenteredRef = (0, import_react3.useRef)(false);
850
921
  const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
851
- (0, import_react2.useEffect)(() => {
922
+ (0, import_react3.useEffect)(() => {
852
923
  if (typeof document === "undefined") return;
853
924
  if (document.getElementById(LOCATION_STYLE_ID)) return;
854
925
  const styleTag = document.createElement("style");
@@ -856,7 +927,7 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
856
927
  styleTag.textContent = LOCATION_STYLES;
857
928
  document.head.appendChild(styleTag);
858
929
  }, []);
859
- (0, import_react2.useEffect)(() => {
930
+ (0, import_react3.useEffect)(() => {
860
931
  const control = import_leaflet3.default.control({ position });
861
932
  control.onAdd = () => {
862
933
  const container = import_leaflet3.default.DomUtil.create("div", "zenit-location-control");
@@ -870,13 +941,13 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
870
941
  controlRef.current = null;
871
942
  };
872
943
  }, [map, position]);
873
- (0, import_react2.useEffect)(() => {
944
+ (0, import_react3.useEffect)(() => {
874
945
  if (!location || !isTracking) return;
875
946
  if (hasCenteredRef.current) return;
876
947
  hasCenteredRef.current = true;
877
948
  map.flyTo([location.lat, location.lon], zoom, { animate: true });
878
949
  }, [isTracking, location, map, zoom]);
879
- const markerIcon = (0, import_react2.useMemo)(() => createLocationIcon(), []);
950
+ const markerIcon = (0, import_react3.useMemo)(() => createLocationIcon(), []);
880
951
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
881
952
  controlRef.current && (0, import_react_dom.createPortal)(
882
953
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
@@ -917,8 +988,20 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
917
988
  };
918
989
 
919
990
  // src/react/map/map-handlers.tsx
920
- var import_react3 = require("react");
991
+ var import_react4 = require("react");
921
992
  var import_react_leaflet3 = require("react-leaflet");
993
+ var MapInvalidator = ({ trigger }) => {
994
+ const map = (0, import_react_leaflet3.useMap)();
995
+ const lastTrigger = (0, import_react4.useRef)(void 0);
996
+ (0, import_react4.useEffect)(() => {
997
+ if (lastTrigger.current === trigger) return;
998
+ lastTrigger.current = trigger;
999
+ requestAnimationFrame(() => {
1000
+ map.invalidateSize();
1001
+ });
1002
+ }, [map, trigger]);
1003
+ return null;
1004
+ };
922
1005
  function computeBBoxFromGeojson(geojson) {
923
1006
  if (!geojson || !Array.isArray(geojson.features)) return null;
924
1007
  const coords = [];
@@ -965,9 +1048,9 @@ var BBoxZoomHandler = ({
965
1048
  enabled = true
966
1049
  }) => {
967
1050
  const map = (0, import_react_leaflet3.useMap)();
968
- const lastAppliedBBox = (0, import_react3.useRef)(null);
969
- const lastUserInteracted = (0, import_react3.useRef)(false);
970
- (0, import_react3.useEffect)(() => {
1051
+ const lastAppliedBBox = (0, import_react4.useRef)(null);
1052
+ const lastUserInteracted = (0, import_react4.useRef)(false);
1053
+ (0, import_react4.useEffect)(() => {
971
1054
  const handleInteraction = () => {
972
1055
  lastUserInteracted.current = true;
973
1056
  };
@@ -978,7 +1061,7 @@ var BBoxZoomHandler = ({
978
1061
  map.off("zoomstart", handleInteraction);
979
1062
  };
980
1063
  }, [map]);
981
- (0, import_react3.useEffect)(() => {
1064
+ (0, import_react4.useEffect)(() => {
982
1065
  if (!enabled) return;
983
1066
  let resolvedBBox = bbox ?? null;
984
1067
  if (!resolvedBBox && geojson) {
@@ -1006,7 +1089,7 @@ var BBoxZoomHandler = ({
1006
1089
  };
1007
1090
  var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
1008
1091
  const map = (0, import_react_leaflet3.useMap)();
1009
- (0, import_react3.useEffect)(() => {
1092
+ (0, import_react4.useEffect)(() => {
1010
1093
  const handleZoom = () => {
1011
1094
  onZoomChange(map.getZoom());
1012
1095
  };
@@ -1020,7 +1103,7 @@ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
1020
1103
  };
1021
1104
  var MapInstanceBridge = ({ onReady }) => {
1022
1105
  const map = (0, import_react_leaflet3.useMap)();
1023
- (0, import_react3.useEffect)(() => {
1106
+ (0, import_react4.useEffect)(() => {
1024
1107
  onReady(map);
1025
1108
  }, [map, onReady]);
1026
1109
  return null;
@@ -1116,7 +1199,7 @@ function normalizeCenterTuple(center) {
1116
1199
  }
1117
1200
  return null;
1118
1201
  }
1119
- var ZenitMap = (0, import_react4.forwardRef)(({
1202
+ var ZenitMap = (0, import_react5.forwardRef)(({
1120
1203
  client,
1121
1204
  mapId,
1122
1205
  height = "500px",
@@ -1141,21 +1224,23 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1141
1224
  onZoomChange,
1142
1225
  onMapReady
1143
1226
  }, ref) => {
1144
- const [map, setMap] = (0, import_react4.useState)(null);
1145
- const [layers, setLayers] = (0, import_react4.useState)([]);
1146
- const [effectiveStates, setEffectiveStates] = (0, import_react4.useState)([]);
1147
- const [loadingMap, setLoadingMap] = (0, import_react4.useState)(false);
1148
- const [mapError, setMapError] = (0, import_react4.useState)(null);
1149
- const [mapInstance, setMapInstance] = (0, import_react4.useState)(null);
1150
- const [panesReady, setPanesReady] = (0, import_react4.useState)(false);
1151
- const [currentZoom, setCurrentZoom] = (0, import_react4.useState)(initialZoom ?? DEFAULT_ZOOM);
1152
- const [isPopupOpen, setIsPopupOpen] = (0, import_react4.useState)(false);
1153
- const [isMobile, setIsMobile] = (0, import_react4.useState)(() => {
1227
+ const [map, setMap] = (0, import_react5.useState)(null);
1228
+ const [layers, setLayers] = (0, import_react5.useState)([]);
1229
+ const [effectiveStates, setEffectiveStates] = (0, import_react5.useState)([]);
1230
+ const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
1231
+ const [mapError, setMapError] = (0, import_react5.useState)(null);
1232
+ const [mapInstance, setMapInstance] = (0, import_react5.useState)(null);
1233
+ const [layerGeojsonOverrides, setLayerGeojsonOverrides] = (0, import_react5.useState)({});
1234
+ const originalGeojsonByLayerIdRef = (0, import_react5.useRef)({});
1235
+ const [panesReady, setPanesReady] = (0, import_react5.useState)(false);
1236
+ const [currentZoom, setCurrentZoom] = (0, import_react5.useState)(initialZoom ?? DEFAULT_ZOOM);
1237
+ const [isPopupOpen, setIsPopupOpen] = (0, import_react5.useState)(false);
1238
+ const [isMobile, setIsMobile] = (0, import_react5.useState)(() => {
1154
1239
  if (typeof window === "undefined") return false;
1155
1240
  return window.matchMedia("(max-width: 768px)").matches;
1156
1241
  });
1157
- const normalizedLayers = (0, import_react4.useMemo)(() => normalizeMapLayers(map), [map]);
1158
- (0, import_react4.useEffect)(() => {
1242
+ const normalizedLayers = (0, import_react5.useMemo)(() => normalizeMapLayers(map), [map]);
1243
+ (0, import_react5.useEffect)(() => {
1159
1244
  if (typeof window === "undefined") return;
1160
1245
  const mql = window.matchMedia("(max-width: 768px)");
1161
1246
  const onChange = (e) => {
@@ -1173,17 +1258,17 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1173
1258
  }
1174
1259
  return;
1175
1260
  }, []);
1176
- (0, import_react4.useEffect)(() => {
1261
+ (0, import_react5.useEffect)(() => {
1177
1262
  if (featureInfoMode === "popup") {
1178
1263
  ensurePopupStyles();
1179
1264
  }
1180
1265
  }, [featureInfoMode]);
1181
- (0, import_react4.useEffect)(() => {
1266
+ (0, import_react5.useEffect)(() => {
1182
1267
  if (featureInfoMode !== "popup") {
1183
1268
  setIsPopupOpen(false);
1184
1269
  }
1185
1270
  }, [featureInfoMode]);
1186
- (0, import_react4.useEffect)(() => {
1271
+ (0, import_react5.useEffect)(() => {
1187
1272
  if (!mapInstance) return;
1188
1273
  const popupPane = mapInstance.getPane("popupPane");
1189
1274
  if (popupPane) {
@@ -1192,7 +1277,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1192
1277
  const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
1193
1278
  labelsPane.style.zIndex = "600";
1194
1279
  }, [mapInstance]);
1195
- (0, import_react4.useEffect)(() => {
1280
+ (0, import_react5.useEffect)(() => {
1196
1281
  if (!mapInstance) return;
1197
1282
  const handlePopupOpen = () => setIsPopupOpen(true);
1198
1283
  const handlePopupClose = () => setIsPopupOpen(false);
@@ -1203,7 +1288,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1203
1288
  mapInstance.off("popupclose", handlePopupClose);
1204
1289
  };
1205
1290
  }, [mapInstance]);
1206
- const layerStyleIndex = (0, import_react4.useMemo)(() => {
1291
+ const layerStyleIndex = (0, import_react5.useMemo)(() => {
1207
1292
  const index = /* @__PURE__ */ new Map();
1208
1293
  (map?.mapLayers ?? []).forEach((entry) => {
1209
1294
  const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
@@ -1214,7 +1299,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1214
1299
  });
1215
1300
  return index;
1216
1301
  }, [map]);
1217
- const labelKeyIndex = (0, import_react4.useMemo)(() => {
1302
+ const labelKeyIndex = (0, import_react5.useMemo)(() => {
1218
1303
  const index = /* @__PURE__ */ new Map();
1219
1304
  normalizedLayers.forEach((entry) => {
1220
1305
  const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
@@ -1224,7 +1309,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1224
1309
  });
1225
1310
  return index;
1226
1311
  }, [normalizedLayers]);
1227
- const layerMetaIndex = (0, import_react4.useMemo)(() => {
1312
+ const layerMetaIndex = (0, import_react5.useMemo)(() => {
1228
1313
  const index = /* @__PURE__ */ new Map();
1229
1314
  normalizedLayers.forEach((entry) => {
1230
1315
  index.set(String(entry.layerId), {
@@ -1234,7 +1319,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1234
1319
  });
1235
1320
  return index;
1236
1321
  }, [normalizedLayers]);
1237
- const overlayStyleFunction = (0, import_react4.useMemo)(() => {
1322
+ const overlayStyleFunction = (0, import_react5.useMemo)(() => {
1238
1323
  return (feature) => {
1239
1324
  const featureLayerId = getFeatureLayerId(feature);
1240
1325
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -1253,15 +1338,15 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1253
1338
  return defaultOptions;
1254
1339
  };
1255
1340
  }, [layerStyleIndex, mapLayers, overlayStyle]);
1256
- const overlayStyleFn = (0, import_react4.useCallback)(
1341
+ const overlayStyleFn = (0, import_react5.useCallback)(
1257
1342
  (feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
1258
1343
  [overlayStyleFunction]
1259
1344
  );
1260
- const [baseStates, setBaseStates] = (0, import_react4.useState)([]);
1261
- const [mapOverrides, setMapOverrides] = (0, import_react4.useState)([]);
1262
- const [controlOverrides, setControlOverrides] = (0, import_react4.useState)([]);
1263
- const [uiOverrides, setUiOverrides] = (0, import_react4.useState)([]);
1264
- (0, import_react4.useEffect)(() => {
1345
+ const [baseStates, setBaseStates] = (0, import_react5.useState)([]);
1346
+ const [mapOverrides, setMapOverrides] = (0, import_react5.useState)([]);
1347
+ const [controlOverrides, setControlOverrides] = (0, import_react5.useState)([]);
1348
+ const [uiOverrides, setUiOverrides] = (0, import_react5.useState)([]);
1349
+ (0, import_react5.useEffect)(() => {
1265
1350
  let isMounted = true;
1266
1351
  setLoadingMap(true);
1267
1352
  setMapError(null);
@@ -1284,7 +1369,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1284
1369
  isMounted = false;
1285
1370
  };
1286
1371
  }, [client.maps, mapId, onError, onLoadingChange]);
1287
- (0, import_react4.useEffect)(() => {
1372
+ (0, import_react5.useEffect)(() => {
1288
1373
  if (normalizedLayers.length === 0) {
1289
1374
  setLayers([]);
1290
1375
  setBaseStates([]);
@@ -1315,7 +1400,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1315
1400
  setMapOverrides(initialOverrides);
1316
1401
  setUiOverrides([]);
1317
1402
  }, [normalizedLayers]);
1318
- (0, import_react4.useEffect)(() => {
1403
+ (0, import_react5.useEffect)(() => {
1319
1404
  if (!layerControls) {
1320
1405
  setControlOverrides([]);
1321
1406
  return;
@@ -1327,7 +1412,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1327
1412
  }));
1328
1413
  setControlOverrides(overrides);
1329
1414
  }, [layerControls]);
1330
- (0, import_react4.useEffect)(() => {
1415
+ (0, import_react5.useEffect)(() => {
1331
1416
  if (layerStates) {
1332
1417
  return;
1333
1418
  }
@@ -1337,12 +1422,12 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1337
1422
  onLayerStateChange?.(reset);
1338
1423
  }
1339
1424
  }, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
1340
- (0, import_react4.useEffect)(() => {
1425
+ (0, import_react5.useEffect)(() => {
1341
1426
  if (layerStates) {
1342
1427
  setEffectiveStates(layerStates);
1343
1428
  }
1344
1429
  }, [layerStates]);
1345
- (0, import_react4.useEffect)(() => {
1430
+ (0, import_react5.useEffect)(() => {
1346
1431
  if (layerStates) {
1347
1432
  return;
1348
1433
  }
@@ -1355,11 +1440,11 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1355
1440
  setEffectiveStates(next);
1356
1441
  onLayerStateChange?.(next);
1357
1442
  }, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
1358
- (0, import_react4.useEffect)(() => {
1443
+ (0, import_react5.useEffect)(() => {
1359
1444
  if (!Array.isArray(layerControls) || layerControls.length > 0) return;
1360
1445
  setUiOverrides([]);
1361
1446
  }, [layerControls]);
1362
- (0, import_react4.useEffect)(() => {
1447
+ (0, import_react5.useEffect)(() => {
1363
1448
  if (layerStates) {
1364
1449
  return;
1365
1450
  }
@@ -1379,7 +1464,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1379
1464
  return [...filtered, nextEntry];
1380
1465
  });
1381
1466
  };
1382
- const updateOpacityFromUi = (0, import_react4.useCallback)(
1467
+ const updateOpacityFromUi = (0, import_react5.useCallback)(
1383
1468
  (layerId, uiOpacity) => {
1384
1469
  const meta = layerMetaIndex.get(String(layerId));
1385
1470
  const baseOpacity = clampOpacity3(uiOpacity);
@@ -1418,7 +1503,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1418
1503
  },
1419
1504
  [currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
1420
1505
  );
1421
- const center = (0, import_react4.useMemo)(() => {
1506
+ const center = (0, import_react5.useMemo)(() => {
1422
1507
  if (initialCenter) {
1423
1508
  return initialCenter;
1424
1509
  }
@@ -1429,30 +1514,44 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1429
1514
  return DEFAULT_CENTER;
1430
1515
  }, [initialCenter, map?.settings?.center]);
1431
1516
  const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
1432
- (0, import_react4.useEffect)(() => {
1517
+ (0, import_react5.useEffect)(() => {
1433
1518
  setCurrentZoom(zoom);
1434
1519
  }, [zoom]);
1435
- const decoratedLayers = (0, import_react4.useMemo)(() => {
1436
- return layers.map((layer) => ({
1437
- ...layer,
1438
- effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1439
- data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1440
- }));
1441
- }, [effectiveStates, layerGeojson, layers]);
1442
- const orderedLayers = (0, import_react4.useMemo)(() => {
1520
+ (0, import_react5.useEffect)(() => {
1521
+ if (!layerGeojson) return;
1522
+ layers.forEach((layer) => {
1523
+ const layerKey = String(layer.mapLayer.layerId);
1524
+ const incoming = layerGeojson[layer.mapLayer.layerId] ?? layerGeojson[layerKey] ?? null;
1525
+ if (incoming && !originalGeojsonByLayerIdRef.current[layerKey]) {
1526
+ originalGeojsonByLayerIdRef.current[layerKey] = incoming;
1527
+ }
1528
+ });
1529
+ }, [layerGeojson, layers]);
1530
+ const decoratedLayers = (0, import_react5.useMemo)(() => {
1531
+ return layers.map((layer) => {
1532
+ const layerKey = String(layer.mapLayer.layerId);
1533
+ const override = layerGeojsonOverrides[layerKey];
1534
+ return {
1535
+ ...layer,
1536
+ effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1537
+ data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
1538
+ };
1539
+ });
1540
+ }, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
1541
+ const orderedLayers = (0, import_react5.useMemo)(() => {
1443
1542
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1444
1543
  }, [decoratedLayers]);
1445
- const autoZoomGeojson = (0, import_react4.useMemo)(
1544
+ const autoZoomGeojson = (0, import_react5.useMemo)(
1446
1545
  () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
1447
1546
  [orderedLayers]
1448
1547
  );
1449
- const resolveLayerStyle = (0, import_react4.useCallback)(
1548
+ const resolveLayerStyle = (0, import_react5.useCallback)(
1450
1549
  (layerId) => {
1451
1550
  return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
1452
1551
  },
1453
1552
  [layerStyleIndex, mapLayers]
1454
1553
  );
1455
- const labelMarkers = (0, import_react4.useMemo)(() => {
1554
+ const labelMarkers = (0, import_react5.useMemo)(() => {
1456
1555
  const markers = [];
1457
1556
  decoratedLayers.forEach((layerState) => {
1458
1557
  if (!layerState.effective?.visible) return;
@@ -1489,7 +1588,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1489
1588
  });
1490
1589
  return markers;
1491
1590
  }, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
1492
- const ensureLayerPanes = (0, import_react4.useCallback)(
1591
+ const ensureLayerPanes = (0, import_react5.useCallback)(
1493
1592
  (targetMap, targetLayers) => {
1494
1593
  const baseZIndex = 400;
1495
1594
  targetLayers.forEach((layer) => {
@@ -1505,7 +1604,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1505
1604
  },
1506
1605
  []
1507
1606
  );
1508
- const handleMapReady = (0, import_react4.useCallback)(
1607
+ const handleMapReady = (0, import_react5.useCallback)(
1509
1608
  (instance) => {
1510
1609
  setPanesReady(false);
1511
1610
  setMapInstance(instance);
@@ -1513,7 +1612,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1513
1612
  },
1514
1613
  [onMapReady]
1515
1614
  );
1516
- (0, import_react4.useEffect)(() => {
1615
+ (0, import_react5.useEffect)(() => {
1517
1616
  if (!mapInstance) {
1518
1617
  setPanesReady(false);
1519
1618
  return;
@@ -1533,7 +1632,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1533
1632
  setPanesReady(true);
1534
1633
  }
1535
1634
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
1536
- const overlayOnEachFeature = (0, import_react4.useMemo)(() => {
1635
+ const overlayOnEachFeature = (0, import_react5.useMemo)(() => {
1537
1636
  return (feature, layer) => {
1538
1637
  const layerId = getFeatureLayerId(feature) ?? void 0;
1539
1638
  const geometryType = feature?.geometry?.type;
@@ -1644,7 +1743,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1644
1743
  return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
1645
1744
  };
1646
1745
  };
1647
- (0, import_react4.useImperativeHandle)(ref, () => ({
1746
+ (0, import_react5.useImperativeHandle)(ref, () => ({
1648
1747
  setLayerOpacity: (layerId, opacity) => {
1649
1748
  upsertUiOverride(layerId, { overrideOpacity: opacity });
1650
1749
  },
@@ -1667,6 +1766,24 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1667
1766
  };
1668
1767
  mapInstance.fitBounds(bounds, fitOptions);
1669
1768
  },
1769
+ fitToBbox: (bbox, padding) => {
1770
+ if (!mapInstance) return;
1771
+ 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)) {
1772
+ console.warn("[ZenitMap.fitToBbox] Invalid bbox", bbox);
1773
+ return;
1774
+ }
1775
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1776
+ const bounds = import_leaflet4.default.latLngBounds([bbox.minLat, bbox.minLon], [bbox.maxLat, bbox.maxLon]);
1777
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1778
+ },
1779
+ fitToGeoJson: (fc, padding) => {
1780
+ if (!mapInstance) return;
1781
+ if (!fc?.features?.length) return;
1782
+ const bounds = import_leaflet4.default.geoJSON(fc).getBounds();
1783
+ if (!bounds.isValid()) return;
1784
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
1785
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
1786
+ },
1670
1787
  setView: (coordinates, zoom2) => {
1671
1788
  if (!mapInstance) return;
1672
1789
  mapInstance.setView([coordinates.lat, coordinates.lon], zoom2 ?? mapInstance.getZoom(), {
@@ -1691,8 +1808,25 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1691
1808
  highlightFeature: (layerId, featureId) => {
1692
1809
  upsertUiOverride(layerId, { overrideVisible: true, overrideOpacity: 1 });
1693
1810
  },
1811
+ updateLayerGeoJson: (layerId, featureCollection) => {
1812
+ const layerKey = String(layerId);
1813
+ setLayerGeojsonOverrides((prev) => ({ ...prev, [layerKey]: featureCollection }));
1814
+ },
1815
+ restoreLayerGeoJson: (layerId) => {
1816
+ const layerKey = String(layerId);
1817
+ const original = originalGeojsonByLayerIdRef.current[layerKey];
1818
+ setLayerGeojsonOverrides((prev) => {
1819
+ const next = { ...prev };
1820
+ if (original) {
1821
+ next[layerKey] = original;
1822
+ } else {
1823
+ delete next[layerKey];
1824
+ }
1825
+ return next;
1826
+ });
1827
+ },
1694
1828
  getMapInstance: () => mapInstance
1695
- }), [effectiveStates, mapInstance]);
1829
+ }), [effectiveStates, isMobile, mapInstance]);
1696
1830
  if (loadingMap) {
1697
1831
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1698
1832
  }
@@ -1745,6 +1879,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1745
1879
  ),
1746
1880
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_leaflet4.ZoomControl, { position: "topright" }),
1747
1881
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInstanceBridge, { onReady: handleMapReady }),
1882
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInvalidator, { trigger: mapId }),
1748
1883
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1749
1884
  BBoxZoomHandler,
1750
1885
  {
@@ -1761,7 +1896,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1761
1896
  const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
1762
1897
  const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
1763
1898
  const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
1764
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.default.Fragment, { children: [
1899
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
1765
1900
  layerState.data && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1766
1901
  LayerGeoJson,
1767
1902
  {
@@ -1918,7 +2053,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
1918
2053
  ZenitMap.displayName = "ZenitMap";
1919
2054
 
1920
2055
  // src/react/ZenitLayerManager.tsx
1921
- var import_react5 = __toESM(require("react"));
2056
+ var import_react6 = __toESM(require("react"));
1922
2057
 
1923
2058
  // src/react/icons.tsx
1924
2059
  var import_lucide_react = require("lucide-react");
@@ -1962,17 +2097,28 @@ var ZenitLayerManager = ({
1962
2097
  showUploadTab = true,
1963
2098
  showLayerVisibilityIcon = true,
1964
2099
  layerFeatureCounts,
1965
- mapLayers
2100
+ mapLayers,
2101
+ onApplyLayerFilter,
2102
+ onClearLayerFilter
1966
2103
  }) => {
1967
- const [map, setMap] = (0, import_react5.useState)(null);
1968
- const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
1969
- const [mapError, setMapError] = (0, import_react5.useState)(null);
1970
- const [layers, setLayers] = (0, import_react5.useState)([]);
1971
- const [activeTab, setActiveTab] = (0, import_react5.useState)("layers");
1972
- const [panelVisible, setPanelVisible] = (0, import_react5.useState)(true);
1973
- const lastEmittedStatesRef = (0, import_react5.useRef)(null);
2104
+ const [map, setMap] = (0, import_react6.useState)(null);
2105
+ const [loadingMap, setLoadingMap] = (0, import_react6.useState)(false);
2106
+ const [mapError, setMapError] = (0, import_react6.useState)(null);
2107
+ const [layers, setLayers] = (0, import_react6.useState)([]);
2108
+ const [activeTab, setActiveTab] = (0, import_react6.useState)("layers");
2109
+ const [panelVisible, setPanelVisible] = (0, import_react6.useState)(true);
2110
+ const [selectedFilterLayerId, setSelectedFilterLayerId] = (0, import_react6.useState)("");
2111
+ const [selectedFilterField, setSelectedFilterField] = (0, import_react6.useState)("");
2112
+ const [selectedFilterValue, setSelectedFilterValue] = (0, import_react6.useState)("");
2113
+ const [catalogByLayerField, setCatalogByLayerField] = (0, import_react6.useState)({});
2114
+ const [loadingCatalog, setLoadingCatalog] = (0, import_react6.useState)(false);
2115
+ const [applyingFilter, setApplyingFilter] = (0, import_react6.useState)(false);
2116
+ const [filterError, setFilterError] = (0, import_react6.useState)(null);
2117
+ const [appliedFilter, setAppliedFilter] = (0, import_react6.useState)(null);
2118
+ const catalogAbortRef = (0, import_react6.useRef)(null);
2119
+ const lastEmittedStatesRef = (0, import_react6.useRef)(null);
1974
2120
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
1975
- const baseStates = (0, import_react5.useMemo)(
2121
+ const baseStates = (0, import_react6.useMemo)(
1976
2122
  () => initLayerStates(
1977
2123
  layers.map((entry) => ({
1978
2124
  ...entry.mapLayer,
@@ -1983,7 +2129,7 @@ var ZenitLayerManager = ({
1983
2129
  ),
1984
2130
  [layers]
1985
2131
  );
1986
- const overrideStates = (0, import_react5.useMemo)(
2132
+ const overrideStates = (0, import_react6.useMemo)(
1987
2133
  () => layers.map(
1988
2134
  (entry) => ({
1989
2135
  layerId: entry.mapLayer.layerId,
@@ -1993,11 +2139,11 @@ var ZenitLayerManager = ({
1993
2139
  ),
1994
2140
  [layers]
1995
2141
  );
1996
- const effectiveStates = (0, import_react5.useMemo)(
2142
+ const effectiveStates = (0, import_react6.useMemo)(
1997
2143
  () => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
1998
2144
  [baseStates, layerStates, overrideStates]
1999
2145
  );
2000
- const layerMetaIndex = (0, import_react5.useMemo)(() => {
2146
+ const layerMetaIndex = (0, import_react6.useMemo)(() => {
2001
2147
  const index = /* @__PURE__ */ new Map();
2002
2148
  mapLayers?.forEach((entry) => {
2003
2149
  const key = String(entry.layerId);
@@ -2011,7 +2157,7 @@ var ZenitLayerManager = ({
2011
2157
  });
2012
2158
  return index;
2013
2159
  }, [map, mapLayers]);
2014
- const resolveUserOpacity = import_react5.default.useCallback((state) => {
2160
+ const resolveUserOpacity = import_react6.default.useCallback((state) => {
2015
2161
  if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
2016
2162
  if (typeof state.overrideOpacity === "string") {
2017
2163
  const parsed = Number.parseFloat(state.overrideOpacity);
@@ -2019,7 +2165,7 @@ var ZenitLayerManager = ({
2019
2165
  }
2020
2166
  return state.opacity ?? 1;
2021
2167
  }, []);
2022
- const resolveEffectiveOpacity = import_react5.default.useCallback(
2168
+ const resolveEffectiveOpacity = import_react6.default.useCallback(
2023
2169
  (layerId, userOpacity) => {
2024
2170
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
2025
2171
  return userOpacity;
@@ -2035,7 +2181,7 @@ var ZenitLayerManager = ({
2035
2181
  },
2036
2182
  [autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
2037
2183
  );
2038
- const effectiveStatesWithZoom = (0, import_react5.useMemo)(() => {
2184
+ const effectiveStatesWithZoom = (0, import_react6.useMemo)(() => {
2039
2185
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
2040
2186
  return effectiveStates;
2041
2187
  }
@@ -2049,7 +2195,7 @@ var ZenitLayerManager = ({
2049
2195
  };
2050
2196
  });
2051
2197
  }, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
2052
- (0, import_react5.useEffect)(() => {
2198
+ (0, import_react6.useEffect)(() => {
2053
2199
  let cancelled = false;
2054
2200
  setLoadingMap(true);
2055
2201
  setMapError(null);
@@ -2081,12 +2227,12 @@ var ZenitLayerManager = ({
2081
2227
  cancelled = true;
2082
2228
  };
2083
2229
  }, [client.maps, mapId]);
2084
- (0, import_react5.useEffect)(() => {
2230
+ (0, import_react6.useEffect)(() => {
2085
2231
  if (!showUploadTab && activeTab === "upload") {
2086
2232
  setActiveTab("layers");
2087
2233
  }
2088
2234
  }, [activeTab, showUploadTab]);
2089
- (0, import_react5.useEffect)(() => {
2235
+ (0, import_react6.useEffect)(() => {
2090
2236
  if (isControlled) return;
2091
2237
  if (!onLayerStatesChange) return;
2092
2238
  const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
@@ -2104,7 +2250,7 @@ var ZenitLayerManager = ({
2104
2250
  mapZoom,
2105
2251
  onLayerStatesChange
2106
2252
  ]);
2107
- const updateLayerVisible = import_react5.default.useCallback(
2253
+ const updateLayerVisible = import_react6.default.useCallback(
2108
2254
  (layerId, visible) => {
2109
2255
  if (!onLayerStatesChange) return;
2110
2256
  const next = effectiveStates.map(
@@ -2114,7 +2260,7 @@ var ZenitLayerManager = ({
2114
2260
  },
2115
2261
  [effectiveStates, onLayerStatesChange]
2116
2262
  );
2117
- const updateLayerOpacity = import_react5.default.useCallback(
2263
+ const updateLayerOpacity = import_react6.default.useCallback(
2118
2264
  (layerId, opacity) => {
2119
2265
  if (!onLayerStatesChange) return;
2120
2266
  const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
@@ -2125,7 +2271,7 @@ var ZenitLayerManager = ({
2125
2271
  },
2126
2272
  [effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
2127
2273
  );
2128
- const resolveFeatureCount = import_react5.default.useCallback(
2274
+ const resolveFeatureCount = import_react6.default.useCallback(
2129
2275
  (layerId, layer) => {
2130
2276
  const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
2131
2277
  if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
@@ -2134,7 +2280,7 @@ var ZenitLayerManager = ({
2134
2280
  },
2135
2281
  [layerFeatureCounts]
2136
2282
  );
2137
- const decoratedLayers = (0, import_react5.useMemo)(() => {
2283
+ const decoratedLayers = (0, import_react6.useMemo)(() => {
2138
2284
  return layers.map((entry) => ({
2139
2285
  ...entry,
2140
2286
  effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
@@ -2163,7 +2309,140 @@ var ZenitLayerManager = ({
2163
2309
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
2164
2310
  });
2165
2311
  }, [effectiveStates, layers, resolveFeatureCount]);
2166
- const resolveLayerStyle = import_react5.default.useCallback(
2312
+ const filterableLayers = (0, import_react6.useMemo)(() => {
2313
+ return decoratedLayers.filter((entry) => {
2314
+ const prefilters = entry.mapLayer.layerConfig?.prefilters;
2315
+ return !!prefilters && Object.keys(prefilters).length > 0;
2316
+ });
2317
+ }, [decoratedLayers]);
2318
+ const selectedFilterLayer = (0, import_react6.useMemo)(
2319
+ () => filterableLayers.find((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId) ?? null,
2320
+ [filterableLayers, selectedFilterLayerId]
2321
+ );
2322
+ const filterFields = (0, import_react6.useMemo)(() => {
2323
+ const prefilters = selectedFilterLayer?.mapLayer.layerConfig?.prefilters;
2324
+ return prefilters ? Object.keys(prefilters) : [];
2325
+ }, [selectedFilterLayer]);
2326
+ const activeCatalogKey = selectedFilterLayer ? `${selectedFilterLayer.mapLayer.layerId}:${selectedFilterField}` : null;
2327
+ const activeCatalogValues = activeCatalogKey ? catalogByLayerField[activeCatalogKey] ?? [] : [];
2328
+ const extractCatalogValues = import_react6.default.useCallback((catalogData, field) => {
2329
+ const values = /* @__PURE__ */ new Set();
2330
+ const pushValue = (value) => {
2331
+ if (value === null || value === void 0) return;
2332
+ const normalized = String(value).trim();
2333
+ if (normalized) values.add(normalized);
2334
+ };
2335
+ if (catalogData && typeof catalogData === "object") {
2336
+ const maybeRecord = catalogData;
2337
+ const directField = maybeRecord[field];
2338
+ if (Array.isArray(directField)) {
2339
+ directField.forEach(pushValue);
2340
+ }
2341
+ const items = maybeRecord.items;
2342
+ if (Array.isArray(items)) {
2343
+ items.forEach((item) => {
2344
+ if (!item || typeof item !== "object") return;
2345
+ const row = item;
2346
+ const rowField = row.field;
2347
+ if (String(rowField ?? "").toUpperCase() === field.toUpperCase() && Array.isArray(row.values)) {
2348
+ row.values.forEach(pushValue);
2349
+ }
2350
+ });
2351
+ }
2352
+ const features = maybeRecord.features;
2353
+ if (Array.isArray(features)) {
2354
+ features.forEach((feature) => {
2355
+ if (!feature || typeof feature !== "object") return;
2356
+ const properties = feature.properties;
2357
+ if (properties && field in properties) {
2358
+ pushValue(properties[field]);
2359
+ }
2360
+ });
2361
+ }
2362
+ }
2363
+ return [...values].sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
2364
+ }, []);
2365
+ (0, import_react6.useEffect)(() => {
2366
+ if (!filterableLayers.length) {
2367
+ setSelectedFilterLayerId("");
2368
+ return;
2369
+ }
2370
+ if (!selectedFilterLayerId || !filterableLayers.some((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId)) {
2371
+ setSelectedFilterLayerId(String(filterableLayers[0].mapLayer.layerId));
2372
+ }
2373
+ }, [filterableLayers, selectedFilterLayerId]);
2374
+ (0, import_react6.useEffect)(() => {
2375
+ if (!filterFields.length) {
2376
+ setSelectedFilterField("");
2377
+ return;
2378
+ }
2379
+ if (!selectedFilterField || !filterFields.includes(selectedFilterField)) {
2380
+ setSelectedFilterField(filterFields[0]);
2381
+ setSelectedFilterValue("");
2382
+ }
2383
+ }, [filterFields, selectedFilterField]);
2384
+ (0, import_react6.useEffect)(() => {
2385
+ if (activeTab !== "filters") return;
2386
+ if (!selectedFilterLayer || !selectedFilterField || !activeCatalogKey) return;
2387
+ if (catalogByLayerField[activeCatalogKey]) return;
2388
+ catalogAbortRef.current?.abort();
2389
+ const controller = new AbortController();
2390
+ catalogAbortRef.current = controller;
2391
+ setLoadingCatalog(true);
2392
+ setFilterError(null);
2393
+ client.layers.getLayerFeaturesCatalog(selectedFilterLayer.mapLayer.layerId).then((response) => {
2394
+ if (controller.signal.aborted) return;
2395
+ const values = extractCatalogValues(response.data, selectedFilterField);
2396
+ setCatalogByLayerField((prev) => ({ ...prev, [activeCatalogKey]: values }));
2397
+ }).catch((error) => {
2398
+ if (controller.signal.aborted) return;
2399
+ const message = error instanceof Error ? error.message : "No se pudo cargar el cat\xE1logo";
2400
+ setFilterError(message);
2401
+ }).finally(() => {
2402
+ if (!controller.signal.aborted) setLoadingCatalog(false);
2403
+ });
2404
+ return () => {
2405
+ controller.abort();
2406
+ };
2407
+ }, [activeCatalogKey, activeTab, catalogByLayerField, client.layers, extractCatalogValues, selectedFilterField, selectedFilterLayer]);
2408
+ const handleApplyFilter = import_react6.default.useCallback(async () => {
2409
+ if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
2410
+ setApplyingFilter(true);
2411
+ setFilterError(null);
2412
+ try {
2413
+ await onApplyLayerFilter({
2414
+ layerId: selectedFilterLayer.mapLayer.layerId,
2415
+ field: selectedFilterField,
2416
+ value: selectedFilterValue
2417
+ });
2418
+ setAppliedFilter({
2419
+ layerId: selectedFilterLayer.mapLayer.layerId,
2420
+ field: selectedFilterField,
2421
+ value: selectedFilterValue
2422
+ });
2423
+ } catch (error) {
2424
+ const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
2425
+ setFilterError(message);
2426
+ } finally {
2427
+ setApplyingFilter(false);
2428
+ }
2429
+ }, [onApplyLayerFilter, selectedFilterField, selectedFilterLayer, selectedFilterValue]);
2430
+ const handleClearFilter = import_react6.default.useCallback(async () => {
2431
+ if (!selectedFilterLayer) return;
2432
+ setApplyingFilter(true);
2433
+ setFilterError(null);
2434
+ try {
2435
+ await onClearLayerFilter?.({ layerId: selectedFilterLayer.mapLayer.layerId, field: selectedFilterField || void 0 });
2436
+ setSelectedFilterValue("");
2437
+ setAppliedFilter(null);
2438
+ } catch (error) {
2439
+ const message = error instanceof Error ? error.message : "No se pudo limpiar el filtro";
2440
+ setFilterError(message);
2441
+ } finally {
2442
+ setApplyingFilter(false);
2443
+ }
2444
+ }, [onClearLayerFilter, selectedFilterField, selectedFilterLayer]);
2445
+ const resolveLayerStyle = import_react6.default.useCallback(
2167
2446
  (layerId) => {
2168
2447
  const layerKey = String(layerId);
2169
2448
  const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
@@ -2501,6 +2780,18 @@ var ZenitLayerManager = ({
2501
2780
  "Subir"
2502
2781
  ]
2503
2782
  }
2783
+ ),
2784
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2785
+ "button",
2786
+ {
2787
+ type: "button",
2788
+ className: `zlm-tab${activeTab === "filters" ? " is-active" : ""}`,
2789
+ onClick: () => setActiveTab("filters"),
2790
+ children: [
2791
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Layers, { size: 16 }),
2792
+ "Filtros"
2793
+ ]
2794
+ }
2504
2795
  )
2505
2796
  ]
2506
2797
  }
@@ -2508,6 +2799,68 @@ var ZenitLayerManager = ({
2508
2799
  ] }),
2509
2800
  panelVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2510
2801
  activeTab === "layers" && renderLayerCards(),
2802
+ activeTab === "filters" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2803
+ filterableLayers.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2804
+ "Capa",
2805
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { className: "zlm-filter-select", value: selectedFilterLayerId, onChange: (e) => setSelectedFilterLayerId(e.target.value), children: filterableLayers.map((layer) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: String(layer.mapLayer.layerId), children: layer.layerName ?? `Capa ${layer.mapLayer.layerId}` }, String(layer.mapLayer.layerId))) })
2806
+ ] }),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2808
+ "Campo",
2809
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { className: "zlm-filter-select", value: selectedFilterField, onChange: (e) => {
2810
+ setSelectedFilterField(e.target.value);
2811
+ setSelectedFilterValue("");
2812
+ }, children: filterFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: field, children: field }, field)) })
2813
+ ] }),
2814
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
2815
+ "Valor",
2816
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2817
+ "select",
2818
+ {
2819
+ className: "zlm-filter-select",
2820
+ value: selectedFilterValue,
2821
+ onChange: (e) => {
2822
+ const value = e.target.value;
2823
+ setSelectedFilterValue(value);
2824
+ },
2825
+ children: [
2826
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "Seleccionar\u2026" }),
2827
+ activeCatalogValues.map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value, children: value }, value))
2828
+ ]
2829
+ }
2830
+ )
2831
+ ] }),
2832
+ loadingCatalog && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 12 }, children: "Cargando cat\xE1logo\u2026" }),
2833
+ !loadingCatalog && selectedFilterField && activeCatalogValues.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 12 }, children: "No hay cat\xE1logo disponible para este filtro." }),
2834
+ filterError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#b91c1c", fontSize: 12 }, children: filterError }),
2835
+ appliedFilter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-badge", style: { alignSelf: "flex-start" }, children: [
2836
+ "Activo: ",
2837
+ appliedFilter.field,
2838
+ " = ",
2839
+ appliedFilter.value
2840
+ ] }),
2841
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-filter-actions", style: { display: "flex", gap: 8 }, children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2843
+ "button",
2844
+ {
2845
+ type: "button",
2846
+ className: "zlm-panel-toggle",
2847
+ disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
2848
+ onClick: handleApplyFilter,
2849
+ children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
2850
+ }
2851
+ ),
2852
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2853
+ "button",
2854
+ {
2855
+ type: "button",
2856
+ className: "zlm-panel-toggle",
2857
+ disabled: applyingFilter,
2858
+ onClick: handleClearFilter,
2859
+ children: "Limpiar"
2860
+ }
2861
+ )
2862
+ ] })
2863
+ ] }) }),
2511
2864
  showUploadTab && activeTab === "upload" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2512
2865
  ] })
2513
2866
  ] });
@@ -2546,11 +2899,11 @@ var ZenitFeatureFilterPanel = ({
2546
2899
  };
2547
2900
 
2548
2901
  // src/react/ai/FloatingChatBox.tsx
2549
- var import_react7 = require("react");
2902
+ var import_react8 = require("react");
2550
2903
  var import_react_dom2 = require("react-dom");
2551
2904
 
2552
2905
  // src/react/hooks/use-chat.ts
2553
- var import_react6 = require("react");
2906
+ var import_react7 = require("react");
2554
2907
 
2555
2908
  // src/ai/chat.service.ts
2556
2909
  var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
@@ -2682,9 +3035,9 @@ var sendMessageStream = async (mapId, request, callbacks = {}, options, config)
2682
3035
 
2683
3036
  // src/react/hooks/use-chat.ts
2684
3037
  var useSendMessage = (config) => {
2685
- const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
2686
- const [error, setError] = (0, import_react6.useState)(null);
2687
- const send = (0, import_react6.useCallback)(
3038
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
3039
+ const [error, setError] = (0, import_react7.useState)(null);
3040
+ const send = (0, import_react7.useCallback)(
2688
3041
  async (mapId, request, options) => {
2689
3042
  setIsLoading(true);
2690
3043
  setError(null);
@@ -2702,18 +3055,18 @@ var useSendMessage = (config) => {
2702
3055
  return { sendMessage: send, isLoading, error };
2703
3056
  };
2704
3057
  var useSendMessageStream = (config) => {
2705
- const [isStreaming, setIsStreaming] = (0, import_react6.useState)(false);
2706
- const [streamingText, setStreamingText] = (0, import_react6.useState)("");
2707
- const [completeResponse, setCompleteResponse] = (0, import_react6.useState)(null);
2708
- const [error, setError] = (0, import_react6.useState)(null);
2709
- const requestIdRef = (0, import_react6.useRef)(0);
2710
- const reset = (0, import_react6.useCallback)(() => {
3058
+ const [isStreaming, setIsStreaming] = (0, import_react7.useState)(false);
3059
+ const [streamingText, setStreamingText] = (0, import_react7.useState)("");
3060
+ const [completeResponse, setCompleteResponse] = (0, import_react7.useState)(null);
3061
+ const [error, setError] = (0, import_react7.useState)(null);
3062
+ const requestIdRef = (0, import_react7.useRef)(0);
3063
+ const reset = (0, import_react7.useCallback)(() => {
2711
3064
  setIsStreaming(false);
2712
3065
  setStreamingText("");
2713
3066
  setCompleteResponse(null);
2714
3067
  setError(null);
2715
3068
  }, []);
2716
- const send = (0, import_react6.useCallback)(
3069
+ const send = (0, import_react7.useCallback)(
2717
3070
  async (mapId, request, options) => {
2718
3071
  const requestId = requestIdRef.current + 1;
2719
3072
  requestIdRef.current = requestId;
@@ -3004,6 +3357,17 @@ var styles = {
3004
3357
  width: 480,
3005
3358
  height: 640
3006
3359
  },
3360
+ panelMobileFullscreen: {
3361
+ position: "fixed",
3362
+ top: 0,
3363
+ left: 0,
3364
+ right: 0,
3365
+ bottom: 0,
3366
+ width: "100%",
3367
+ height: "100%",
3368
+ borderRadius: 0,
3369
+ margin: 0
3370
+ },
3007
3371
  // Header with green gradient
3008
3372
  header: {
3009
3373
  padding: "14px 16px",
@@ -3237,42 +3601,42 @@ var FloatingChatBox = ({
3237
3601
  open: openProp
3238
3602
  }) => {
3239
3603
  const isControlled = openProp !== void 0;
3240
- const [internalOpen, setInternalOpen] = (0, import_react7.useState)(false);
3604
+ const [internalOpen, setInternalOpen] = (0, import_react8.useState)(false);
3241
3605
  const open = isControlled ? openProp : internalOpen;
3242
- const setOpen = (0, import_react7.useCallback)((value) => {
3606
+ const setOpen = (0, import_react8.useCallback)((value) => {
3243
3607
  const newValue = typeof value === "function" ? value(open) : value;
3244
3608
  if (!isControlled) {
3245
3609
  setInternalOpen(newValue);
3246
3610
  }
3247
3611
  onOpenChange?.(newValue);
3248
3612
  }, [isControlled, open, onOpenChange]);
3249
- const [expanded, setExpanded] = (0, import_react7.useState)(false);
3250
- const [messages, setMessages] = (0, import_react7.useState)([]);
3251
- const [inputValue, setInputValue] = (0, import_react7.useState)("");
3252
- const [conversationId, setConversationId] = (0, import_react7.useState)();
3253
- const [errorMessage, setErrorMessage] = (0, import_react7.useState)(null);
3254
- const [isFocused, setIsFocused] = (0, import_react7.useState)(false);
3255
- const [isMobile, setIsMobile] = (0, import_react7.useState)(false);
3256
- const messagesEndRef = (0, import_react7.useRef)(null);
3257
- const messagesContainerRef = (0, import_react7.useRef)(null);
3258
- const chatBoxRef = (0, import_react7.useRef)(null);
3259
- const chatConfig = (0, import_react7.useMemo)(() => {
3613
+ const [expanded, setExpanded] = (0, import_react8.useState)(false);
3614
+ const [messages, setMessages] = (0, import_react8.useState)([]);
3615
+ const [inputValue, setInputValue] = (0, import_react8.useState)("");
3616
+ const [conversationId, setConversationId] = (0, import_react8.useState)();
3617
+ const [errorMessage, setErrorMessage] = (0, import_react8.useState)(null);
3618
+ const [isFocused, setIsFocused] = (0, import_react8.useState)(false);
3619
+ const [isMobile, setIsMobile] = (0, import_react8.useState)(false);
3620
+ const messagesEndRef = (0, import_react8.useRef)(null);
3621
+ const messagesContainerRef = (0, import_react8.useRef)(null);
3622
+ const chatBoxRef = (0, import_react8.useRef)(null);
3623
+ const chatConfig = (0, import_react8.useMemo)(() => {
3260
3624
  if (!baseUrl) return void 0;
3261
3625
  return { baseUrl, accessToken, getAccessToken };
3262
3626
  }, [accessToken, baseUrl, getAccessToken]);
3263
3627
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
3264
3628
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
3265
- (0, import_react7.useEffect)(() => {
3629
+ (0, import_react8.useEffect)(() => {
3266
3630
  if (open && isMobile) {
3267
3631
  setExpanded(true);
3268
3632
  }
3269
3633
  }, [open, isMobile]);
3270
- const scrollToBottom = (0, import_react7.useCallback)(() => {
3634
+ const scrollToBottom = (0, import_react8.useCallback)(() => {
3271
3635
  if (messagesEndRef.current) {
3272
3636
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
3273
3637
  }
3274
3638
  }, []);
3275
- (0, import_react7.useEffect)(() => {
3639
+ (0, import_react8.useEffect)(() => {
3276
3640
  if (open && messages.length === 0) {
3277
3641
  setMessages([
3278
3642
  {
@@ -3283,10 +3647,10 @@ var FloatingChatBox = ({
3283
3647
  ]);
3284
3648
  }
3285
3649
  }, [open, messages.length]);
3286
- (0, import_react7.useEffect)(() => {
3650
+ (0, import_react8.useEffect)(() => {
3287
3651
  scrollToBottom();
3288
3652
  }, [messages, streamingText, scrollToBottom]);
3289
- (0, import_react7.useEffect)(() => {
3653
+ (0, import_react8.useEffect)(() => {
3290
3654
  if (!open) return;
3291
3655
  if (isMobile && expanded) return;
3292
3656
  const handleClickOutside = (event) => {
@@ -3299,7 +3663,7 @@ var FloatingChatBox = ({
3299
3663
  document.removeEventListener("mousedown", handleClickOutside);
3300
3664
  };
3301
3665
  }, [open, isMobile, expanded]);
3302
- (0, import_react7.useEffect)(() => {
3666
+ (0, import_react8.useEffect)(() => {
3303
3667
  if (typeof window === "undefined") return;
3304
3668
  const mediaQuery = window.matchMedia("(max-width: 768px)");
3305
3669
  const updateMobile = () => setIsMobile(mediaQuery.matches);
@@ -3317,7 +3681,7 @@ var FloatingChatBox = ({
3317
3681
  }
3318
3682
  };
3319
3683
  }, []);
3320
- (0, import_react7.useEffect)(() => {
3684
+ (0, import_react8.useEffect)(() => {
3321
3685
  if (typeof document === "undefined") return;
3322
3686
  if (!open || !isMobile) return;
3323
3687
  document.body.style.overflow = "hidden";
@@ -3325,10 +3689,10 @@ var FloatingChatBox = ({
3325
3689
  document.body.style.overflow = "";
3326
3690
  };
3327
3691
  }, [open, isMobile]);
3328
- const addMessage = (0, import_react7.useCallback)((message) => {
3692
+ const addMessage = (0, import_react8.useCallback)((message) => {
3329
3693
  setMessages((prev) => [...prev, message]);
3330
3694
  }, []);
3331
- const handleSend = (0, import_react7.useCallback)(async () => {
3695
+ const handleSend = (0, import_react8.useCallback)(async () => {
3332
3696
  if (!mapId) {
3333
3697
  setErrorMessage("Selecciona un mapa para usar el asistente.");
3334
3698
  return;
@@ -3381,7 +3745,7 @@ var FloatingChatBox = ({
3381
3745
  sendMessage2,
3382
3746
  userId
3383
3747
  ]);
3384
- const handleKeyDown = (0, import_react7.useCallback)(
3748
+ const handleKeyDown = (0, import_react8.useCallback)(
3385
3749
  (event) => {
3386
3750
  if (event.key === "Enter" && !event.shiftKey) {
3387
3751
  event.preventDefault();
@@ -3392,7 +3756,7 @@ var FloatingChatBox = ({
3392
3756
  },
3393
3757
  [canSend, handleSend]
3394
3758
  );
3395
- const handleFollowUpClick = (0, import_react7.useCallback)((question) => {
3759
+ const handleFollowUpClick = (0, import_react8.useCallback)((question) => {
3396
3760
  setInputValue(question);
3397
3761
  }, []);
3398
3762
  const renderMetadata = (response) => {
@@ -3414,7 +3778,7 @@ var FloatingChatBox = ({
3414
3778
  ] }, index)) })
3415
3779
  ] });
3416
3780
  };
3417
- const handleActionClick = (0, import_react7.useCallback)((action) => {
3781
+ const handleActionClick = (0, import_react8.useCallback)((action) => {
3418
3782
  if (isStreaming) return;
3419
3783
  setOpen(false);
3420
3784
  requestAnimationFrame(() => {
@@ -3529,22 +3893,6 @@ var FloatingChatBox = ({
3529
3893
  box-sizing: border-box;
3530
3894
  }
3531
3895
  @media (max-width: 768px) {
3532
- .zenit-chat-panel.zenit-chat-panel--fullscreen {
3533
- position: fixed !important;
3534
- left: 0 !important;
3535
- right: 0 !important;
3536
- top: 4rem !important;
3537
- bottom: 0 !important;
3538
- width: 100% !important;
3539
- max-width: 100% !important;
3540
- height: auto !important;
3541
- border-radius: 0 !important;
3542
- display: flex !important;
3543
- flex-direction: column !important;
3544
- overflow: hidden !important;
3545
- z-index: 100000 !important;
3546
- padding-top: env(safe-area-inset-top);
3547
- }
3548
3896
  .zenit-chat-panel.zenit-chat-panel--fullscreen .zenit-ai-body {
3549
3897
  flex: 1;
3550
3898
  min-height: 0;
@@ -3564,16 +3912,20 @@ var FloatingChatBox = ({
3564
3912
  "div",
3565
3913
  {
3566
3914
  ref: chatBoxRef,
3567
- className: `zenit-chat-panel${expanded ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
3568
- style: {
3569
- ...styles.panel,
3570
- ...expanded ? styles.panelExpanded : styles.panelNormal
3571
- },
3915
+ className: `zenit-chat-panel${expanded && !isMobile ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
3916
+ style: (() => {
3917
+ const desktopStyle = expanded ? styles.panelExpanded : styles.panelNormal;
3918
+ const mobileStyle = styles.panelMobileFullscreen;
3919
+ return {
3920
+ ...styles.panel,
3921
+ ...isMobile ? mobileStyle : desktopStyle
3922
+ };
3923
+ })(),
3572
3924
  children: [
3573
3925
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { style: styles.header, children: [
3574
3926
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3575
3927
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.headerButtons, children: [
3576
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3928
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3577
3929
  "button",
3578
3930
  {
3579
3931
  type: "button",