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/index.js CHANGED
@@ -371,10 +371,6 @@ function parseQueryParams(options = {}) {
371
371
  return params;
372
372
  }
373
373
  function serializeFilterValue(value) {
374
- if (Array.isArray(value)) {
375
- const serialized2 = value.map(String).filter((item) => item.trim().length > 0).join(",");
376
- return serialized2.length > 0 ? serialized2 : void 0;
377
- }
378
374
  if (value === void 0 || value === null) {
379
375
  return void 0;
380
376
  }
@@ -387,6 +383,15 @@ function buildLayerFilters(filters) {
387
383
  return query;
388
384
  }
389
385
  Object.entries(filters).forEach(([key, value]) => {
386
+ if (Array.isArray(value)) {
387
+ value.forEach((item) => {
388
+ const serialized2 = serializeFilterValue(item);
389
+ if (serialized2 !== void 0) {
390
+ query.append(key, serialized2);
391
+ }
392
+ });
393
+ return;
394
+ }
390
395
  const serialized = serializeFilterValue(value);
391
396
  if (serialized !== void 0) {
392
397
  query.set(key, serialized);
@@ -1521,7 +1526,7 @@ function normalizeBbox(input) {
1521
1526
  }
1522
1527
 
1523
1528
  // src/react/ZenitMap.tsx
1524
- var import_react4 = __toESM(require("react"));
1529
+ var import_react5 = __toESM(require("react"));
1525
1530
  var import_react_leaflet4 = require("react-leaflet");
1526
1531
  var import_leaflet4 = __toESM(require("leaflet"));
1527
1532
 
@@ -1585,10 +1590,31 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
1585
1590
  }
1586
1591
 
1587
1592
  // src/react/map/layer-geojson.tsx
1593
+ var import_react = require("react");
1588
1594
  var import_react_leaflet = require("react-leaflet");
1589
1595
  var import_leaflet = __toESM(require("leaflet"));
1590
1596
  var import_jsx_runtime = require("react/jsx-runtime");
1591
1597
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
1598
+ function normalizeBboxFromData(data) {
1599
+ const bboxCandidate = data.bbox;
1600
+ if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
1601
+ const [minLon, minLat, maxLon, maxLat] = bboxCandidate;
1602
+ const values = [minLon, minLat, maxLon, maxLat].map((value) => Number(value));
1603
+ if (values.every((value) => Number.isFinite(value))) {
1604
+ return values;
1605
+ }
1606
+ return null;
1607
+ }
1608
+ function buildIdsSample(features) {
1609
+ return features.slice(0, 10).map((feature) => {
1610
+ const typedFeature = feature;
1611
+ if (typedFeature.id !== void 0 && typedFeature.id !== null) {
1612
+ return String(typedFeature.id);
1613
+ }
1614
+ const featureId = typedFeature.properties?.featureId;
1615
+ return featureId !== void 0 && featureId !== null ? String(featureId) : "";
1616
+ }).join(",");
1617
+ }
1592
1618
  function getGeometryType(feature) {
1593
1619
  const t = feature?.geometry?.type;
1594
1620
  return typeof t === "string" ? t : null;
@@ -1624,8 +1650,58 @@ var LayerGeoJson = ({
1624
1650
  const features = data.features ?? [];
1625
1651
  const fillFeatures = features.filter(isNonPointGeometry);
1626
1652
  const pointFeatures = features.filter(isPointGeometry);
1653
+ const dataVersionRef = (0, import_react.useRef)(0);
1654
+ const prevSignatureRef = (0, import_react.useRef)("");
1655
+ const firstId = features.length > 0 ? String(features[0]?.id ?? "") : "";
1656
+ const lastId = features.length > 0 ? String(features[features.length - 1]?.id ?? "") : "";
1657
+ const bbox = normalizeBboxFromData(data);
1658
+ const idsSample = buildIdsSample(features);
1659
+ const signature = bbox ? `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}|${bbox[0]}|${bbox[1]}|${bbox[2]}|${bbox[3]}` : `${layerId}|${features.length}|${firstId}|${lastId}|${idsSample}`;
1660
+ const signatureToken = signature.replace(/[^a-zA-Z0-9_-]/g, "_");
1661
+ if (prevSignatureRef.current !== signature) {
1662
+ dataVersionRef.current += 1;
1663
+ prevSignatureRef.current = signature;
1664
+ }
1627
1665
  const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
1628
1666
  const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
1667
+ const clusterLayerRef = (0, import_react.useRef)(null);
1668
+ const canCluster = typeof import_leaflet.default.markerClusterGroup === "function";
1669
+ (0, import_react.useEffect)(() => {
1670
+ if (!mapInstance || !panesReady || !pointsData || !canCluster) return;
1671
+ const markerClusterGroup = import_leaflet.default.markerClusterGroup;
1672
+ const clusterLayer = clusterLayerRef.current ?? markerClusterGroup();
1673
+ clusterLayerRef.current = clusterLayer;
1674
+ if (!mapInstance.hasLayer(clusterLayer)) {
1675
+ mapInstance.addLayer(clusterLayer);
1676
+ }
1677
+ clusterLayer.clearLayers();
1678
+ const geoJsonLayer = import_leaflet.default.geoJSON(pointsData, {
1679
+ pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
1680
+ radius: isMobile ? 8 : 6,
1681
+ pane: mapInstance.getPane(pointsPaneName) ? pointsPaneName : void 0,
1682
+ ...styleFn(feature, layerType, baseOpacity)
1683
+ }),
1684
+ onEachFeature
1685
+ });
1686
+ clusterLayer.addLayer(geoJsonLayer);
1687
+ return () => {
1688
+ clusterLayer.clearLayers();
1689
+ if (mapInstance.hasLayer(clusterLayer)) {
1690
+ mapInstance.removeLayer(clusterLayer);
1691
+ }
1692
+ };
1693
+ }, [
1694
+ baseOpacity,
1695
+ canCluster,
1696
+ isMobile,
1697
+ layerType,
1698
+ mapInstance,
1699
+ onEachFeature,
1700
+ panesReady,
1701
+ pointsData,
1702
+ pointsPaneName,
1703
+ styleFn
1704
+ ]);
1629
1705
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1630
1706
  fillData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1631
1707
  import_react_leaflet.GeoJSON,
@@ -1638,9 +1714,9 @@ var LayerGeoJson = ({
1638
1714
  onPolygonLabel?.(feature, layer);
1639
1715
  }
1640
1716
  },
1641
- `fill-${layerId}`
1717
+ `fill-${layerId}-${signatureToken}-v${dataVersionRef.current}`
1642
1718
  ),
1643
- pointsData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1719
+ pointsData && !canCluster && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1644
1720
  import_react_leaflet.GeoJSON,
1645
1721
  {
1646
1722
  data: pointsData,
@@ -1651,32 +1727,32 @@ var LayerGeoJson = ({
1651
1727
  }),
1652
1728
  onEachFeature
1653
1729
  },
1654
- `points-${layerId}`
1730
+ `points-${layerId}-${signatureToken}-v${dataVersionRef.current}`
1655
1731
  )
1656
1732
  ] });
1657
1733
  };
1658
1734
 
1659
1735
  // src/react/map/location-control.tsx
1660
- var import_react2 = require("react");
1736
+ var import_react3 = require("react");
1661
1737
  var import_react_dom = require("react-dom");
1662
1738
  var import_react_leaflet2 = require("react-leaflet");
1663
1739
  var import_leaflet3 = __toESM(require("leaflet"));
1664
1740
 
1665
1741
  // src/react/hooks/use-geolocation.ts
1666
- var import_react = require("react");
1742
+ var import_react2 = require("react");
1667
1743
  function useGeolocation(options) {
1668
- const [isTracking, setIsTracking] = (0, import_react.useState)(false);
1669
- const [location, setLocation] = (0, import_react.useState)(null);
1670
- const [error, setError] = (0, import_react.useState)(null);
1671
- const watchIdRef = (0, import_react.useRef)(null);
1672
- const stopTracking = (0, import_react.useCallback)(() => {
1744
+ const [isTracking, setIsTracking] = (0, import_react2.useState)(false);
1745
+ const [location, setLocation] = (0, import_react2.useState)(null);
1746
+ const [error, setError] = (0, import_react2.useState)(null);
1747
+ const watchIdRef = (0, import_react2.useRef)(null);
1748
+ const stopTracking = (0, import_react2.useCallback)(() => {
1673
1749
  if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
1674
1750
  navigator.geolocation.clearWatch(watchIdRef.current);
1675
1751
  }
1676
1752
  watchIdRef.current = null;
1677
1753
  setIsTracking(false);
1678
1754
  }, []);
1679
- const startTracking = (0, import_react.useCallback)(() => {
1755
+ const startTracking = (0, import_react2.useCallback)(() => {
1680
1756
  if (typeof navigator === "undefined" || !navigator.geolocation) {
1681
1757
  setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
1682
1758
  return;
@@ -1702,15 +1778,15 @@ function useGeolocation(options) {
1702
1778
  }
1703
1779
  );
1704
1780
  }, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
1705
- const toggleTracking = (0, import_react.useCallback)(() => {
1781
+ const toggleTracking = (0, import_react2.useCallback)(() => {
1706
1782
  if (isTracking) {
1707
1783
  stopTracking();
1708
1784
  } else {
1709
1785
  startTracking();
1710
1786
  }
1711
1787
  }, [isTracking, startTracking, stopTracking]);
1712
- const clearError = (0, import_react.useCallback)(() => setError(null), []);
1713
- (0, import_react.useEffect)(() => {
1788
+ const clearError = (0, import_react2.useCallback)(() => setError(null), []);
1789
+ (0, import_react2.useEffect)(() => {
1714
1790
  return () => {
1715
1791
  stopTracking();
1716
1792
  };
@@ -2137,10 +2213,10 @@ var LocateIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { wi
2137
2213
  ] });
2138
2214
  var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
2139
2215
  const map = (0, import_react_leaflet2.useMap)();
2140
- const controlRef = (0, import_react2.useRef)(null);
2141
- const hasCenteredRef = (0, import_react2.useRef)(false);
2216
+ const controlRef = (0, import_react3.useRef)(null);
2217
+ const hasCenteredRef = (0, import_react3.useRef)(false);
2142
2218
  const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
2143
- (0, import_react2.useEffect)(() => {
2219
+ (0, import_react3.useEffect)(() => {
2144
2220
  if (typeof document === "undefined") return;
2145
2221
  if (document.getElementById(LOCATION_STYLE_ID)) return;
2146
2222
  const styleTag = document.createElement("style");
@@ -2148,7 +2224,7 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
2148
2224
  styleTag.textContent = LOCATION_STYLES;
2149
2225
  document.head.appendChild(styleTag);
2150
2226
  }, []);
2151
- (0, import_react2.useEffect)(() => {
2227
+ (0, import_react3.useEffect)(() => {
2152
2228
  const control = import_leaflet3.default.control({ position });
2153
2229
  control.onAdd = () => {
2154
2230
  const container = import_leaflet3.default.DomUtil.create("div", "zenit-location-control");
@@ -2162,13 +2238,13 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
2162
2238
  controlRef.current = null;
2163
2239
  };
2164
2240
  }, [map, position]);
2165
- (0, import_react2.useEffect)(() => {
2241
+ (0, import_react3.useEffect)(() => {
2166
2242
  if (!location || !isTracking) return;
2167
2243
  if (hasCenteredRef.current) return;
2168
2244
  hasCenteredRef.current = true;
2169
2245
  map.flyTo([location.lat, location.lon], zoom, { animate: true });
2170
2246
  }, [isTracking, location, map, zoom]);
2171
- const markerIcon = (0, import_react2.useMemo)(() => createLocationIcon(), []);
2247
+ const markerIcon = (0, import_react3.useMemo)(() => createLocationIcon(), []);
2172
2248
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2173
2249
  controlRef.current && (0, import_react_dom.createPortal)(
2174
2250
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
@@ -2209,8 +2285,20 @@ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
2209
2285
  };
2210
2286
 
2211
2287
  // src/react/map/map-handlers.tsx
2212
- var import_react3 = require("react");
2288
+ var import_react4 = require("react");
2213
2289
  var import_react_leaflet3 = require("react-leaflet");
2290
+ var MapInvalidator = ({ trigger }) => {
2291
+ const map = (0, import_react_leaflet3.useMap)();
2292
+ const lastTrigger = (0, import_react4.useRef)(void 0);
2293
+ (0, import_react4.useEffect)(() => {
2294
+ if (lastTrigger.current === trigger) return;
2295
+ lastTrigger.current = trigger;
2296
+ requestAnimationFrame(() => {
2297
+ map.invalidateSize();
2298
+ });
2299
+ }, [map, trigger]);
2300
+ return null;
2301
+ };
2214
2302
  function computeBBoxFromGeojson(geojson) {
2215
2303
  if (!geojson || !Array.isArray(geojson.features)) return null;
2216
2304
  const coords = [];
@@ -2257,9 +2345,9 @@ var BBoxZoomHandler = ({
2257
2345
  enabled = true
2258
2346
  }) => {
2259
2347
  const map = (0, import_react_leaflet3.useMap)();
2260
- const lastAppliedBBox = (0, import_react3.useRef)(null);
2261
- const lastUserInteracted = (0, import_react3.useRef)(false);
2262
- (0, import_react3.useEffect)(() => {
2348
+ const lastAppliedBBox = (0, import_react4.useRef)(null);
2349
+ const lastUserInteracted = (0, import_react4.useRef)(false);
2350
+ (0, import_react4.useEffect)(() => {
2263
2351
  const handleInteraction = () => {
2264
2352
  lastUserInteracted.current = true;
2265
2353
  };
@@ -2270,7 +2358,7 @@ var BBoxZoomHandler = ({
2270
2358
  map.off("zoomstart", handleInteraction);
2271
2359
  };
2272
2360
  }, [map]);
2273
- (0, import_react3.useEffect)(() => {
2361
+ (0, import_react4.useEffect)(() => {
2274
2362
  if (!enabled) return;
2275
2363
  let resolvedBBox = bbox ?? null;
2276
2364
  if (!resolvedBBox && geojson) {
@@ -2298,7 +2386,7 @@ var BBoxZoomHandler = ({
2298
2386
  };
2299
2387
  var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
2300
2388
  const map = (0, import_react_leaflet3.useMap)();
2301
- (0, import_react3.useEffect)(() => {
2389
+ (0, import_react4.useEffect)(() => {
2302
2390
  const handleZoom = () => {
2303
2391
  onZoomChange(map.getZoom());
2304
2392
  };
@@ -2312,7 +2400,7 @@ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
2312
2400
  };
2313
2401
  var MapInstanceBridge = ({ onReady }) => {
2314
2402
  const map = (0, import_react_leaflet3.useMap)();
2315
- (0, import_react3.useEffect)(() => {
2403
+ (0, import_react4.useEffect)(() => {
2316
2404
  onReady(map);
2317
2405
  }, [map, onReady]);
2318
2406
  return null;
@@ -2408,7 +2496,7 @@ function normalizeCenterTuple(center) {
2408
2496
  }
2409
2497
  return null;
2410
2498
  }
2411
- var ZenitMap = (0, import_react4.forwardRef)(({
2499
+ var ZenitMap = (0, import_react5.forwardRef)(({
2412
2500
  client,
2413
2501
  mapId,
2414
2502
  height = "500px",
@@ -2433,21 +2521,23 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2433
2521
  onZoomChange,
2434
2522
  onMapReady
2435
2523
  }, ref) => {
2436
- const [map, setMap] = (0, import_react4.useState)(null);
2437
- const [layers, setLayers] = (0, import_react4.useState)([]);
2438
- const [effectiveStates, setEffectiveStates] = (0, import_react4.useState)([]);
2439
- const [loadingMap, setLoadingMap] = (0, import_react4.useState)(false);
2440
- const [mapError, setMapError] = (0, import_react4.useState)(null);
2441
- const [mapInstance, setMapInstance] = (0, import_react4.useState)(null);
2442
- const [panesReady, setPanesReady] = (0, import_react4.useState)(false);
2443
- const [currentZoom, setCurrentZoom] = (0, import_react4.useState)(initialZoom ?? DEFAULT_ZOOM);
2444
- const [isPopupOpen, setIsPopupOpen] = (0, import_react4.useState)(false);
2445
- const [isMobile, setIsMobile] = (0, import_react4.useState)(() => {
2524
+ const [map, setMap] = (0, import_react5.useState)(null);
2525
+ const [layers, setLayers] = (0, import_react5.useState)([]);
2526
+ const [effectiveStates, setEffectiveStates] = (0, import_react5.useState)([]);
2527
+ const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
2528
+ const [mapError, setMapError] = (0, import_react5.useState)(null);
2529
+ const [mapInstance, setMapInstance] = (0, import_react5.useState)(null);
2530
+ const [layerGeojsonOverrides, setLayerGeojsonOverrides] = (0, import_react5.useState)({});
2531
+ const originalGeojsonByLayerIdRef = (0, import_react5.useRef)({});
2532
+ const [panesReady, setPanesReady] = (0, import_react5.useState)(false);
2533
+ const [currentZoom, setCurrentZoom] = (0, import_react5.useState)(initialZoom ?? DEFAULT_ZOOM);
2534
+ const [isPopupOpen, setIsPopupOpen] = (0, import_react5.useState)(false);
2535
+ const [isMobile, setIsMobile] = (0, import_react5.useState)(() => {
2446
2536
  if (typeof window === "undefined") return false;
2447
2537
  return window.matchMedia("(max-width: 768px)").matches;
2448
2538
  });
2449
- const normalizedLayers = (0, import_react4.useMemo)(() => normalizeMapLayers(map), [map]);
2450
- (0, import_react4.useEffect)(() => {
2539
+ const normalizedLayers = (0, import_react5.useMemo)(() => normalizeMapLayers(map), [map]);
2540
+ (0, import_react5.useEffect)(() => {
2451
2541
  if (typeof window === "undefined") return;
2452
2542
  const mql = window.matchMedia("(max-width: 768px)");
2453
2543
  const onChange = (e) => {
@@ -2465,17 +2555,17 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2465
2555
  }
2466
2556
  return;
2467
2557
  }, []);
2468
- (0, import_react4.useEffect)(() => {
2558
+ (0, import_react5.useEffect)(() => {
2469
2559
  if (featureInfoMode === "popup") {
2470
2560
  ensurePopupStyles();
2471
2561
  }
2472
2562
  }, [featureInfoMode]);
2473
- (0, import_react4.useEffect)(() => {
2563
+ (0, import_react5.useEffect)(() => {
2474
2564
  if (featureInfoMode !== "popup") {
2475
2565
  setIsPopupOpen(false);
2476
2566
  }
2477
2567
  }, [featureInfoMode]);
2478
- (0, import_react4.useEffect)(() => {
2568
+ (0, import_react5.useEffect)(() => {
2479
2569
  if (!mapInstance) return;
2480
2570
  const popupPane = mapInstance.getPane("popupPane");
2481
2571
  if (popupPane) {
@@ -2484,7 +2574,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2484
2574
  const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
2485
2575
  labelsPane.style.zIndex = "600";
2486
2576
  }, [mapInstance]);
2487
- (0, import_react4.useEffect)(() => {
2577
+ (0, import_react5.useEffect)(() => {
2488
2578
  if (!mapInstance) return;
2489
2579
  const handlePopupOpen = () => setIsPopupOpen(true);
2490
2580
  const handlePopupClose = () => setIsPopupOpen(false);
@@ -2495,7 +2585,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2495
2585
  mapInstance.off("popupclose", handlePopupClose);
2496
2586
  };
2497
2587
  }, [mapInstance]);
2498
- const layerStyleIndex = (0, import_react4.useMemo)(() => {
2588
+ const layerStyleIndex = (0, import_react5.useMemo)(() => {
2499
2589
  const index = /* @__PURE__ */ new Map();
2500
2590
  (map?.mapLayers ?? []).forEach((entry) => {
2501
2591
  const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
@@ -2506,7 +2596,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2506
2596
  });
2507
2597
  return index;
2508
2598
  }, [map]);
2509
- const labelKeyIndex = (0, import_react4.useMemo)(() => {
2599
+ const labelKeyIndex = (0, import_react5.useMemo)(() => {
2510
2600
  const index = /* @__PURE__ */ new Map();
2511
2601
  normalizedLayers.forEach((entry) => {
2512
2602
  const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
@@ -2516,7 +2606,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2516
2606
  });
2517
2607
  return index;
2518
2608
  }, [normalizedLayers]);
2519
- const layerMetaIndex = (0, import_react4.useMemo)(() => {
2609
+ const layerMetaIndex = (0, import_react5.useMemo)(() => {
2520
2610
  const index = /* @__PURE__ */ new Map();
2521
2611
  normalizedLayers.forEach((entry) => {
2522
2612
  index.set(String(entry.layerId), {
@@ -2526,7 +2616,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2526
2616
  });
2527
2617
  return index;
2528
2618
  }, [normalizedLayers]);
2529
- const overlayStyleFunction = (0, import_react4.useMemo)(() => {
2619
+ const overlayStyleFunction = (0, import_react5.useMemo)(() => {
2530
2620
  return (feature) => {
2531
2621
  const featureLayerId = getFeatureLayerId(feature);
2532
2622
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -2545,15 +2635,15 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2545
2635
  return defaultOptions;
2546
2636
  };
2547
2637
  }, [layerStyleIndex, mapLayers, overlayStyle]);
2548
- const overlayStyleFn = (0, import_react4.useCallback)(
2638
+ const overlayStyleFn = (0, import_react5.useCallback)(
2549
2639
  (feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
2550
2640
  [overlayStyleFunction]
2551
2641
  );
2552
- const [baseStates, setBaseStates] = (0, import_react4.useState)([]);
2553
- const [mapOverrides, setMapOverrides] = (0, import_react4.useState)([]);
2554
- const [controlOverrides, setControlOverrides] = (0, import_react4.useState)([]);
2555
- const [uiOverrides, setUiOverrides] = (0, import_react4.useState)([]);
2556
- (0, import_react4.useEffect)(() => {
2642
+ const [baseStates, setBaseStates] = (0, import_react5.useState)([]);
2643
+ const [mapOverrides, setMapOverrides] = (0, import_react5.useState)([]);
2644
+ const [controlOverrides, setControlOverrides] = (0, import_react5.useState)([]);
2645
+ const [uiOverrides, setUiOverrides] = (0, import_react5.useState)([]);
2646
+ (0, import_react5.useEffect)(() => {
2557
2647
  let isMounted = true;
2558
2648
  setLoadingMap(true);
2559
2649
  setMapError(null);
@@ -2576,7 +2666,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2576
2666
  isMounted = false;
2577
2667
  };
2578
2668
  }, [client.maps, mapId, onError, onLoadingChange]);
2579
- (0, import_react4.useEffect)(() => {
2669
+ (0, import_react5.useEffect)(() => {
2580
2670
  if (normalizedLayers.length === 0) {
2581
2671
  setLayers([]);
2582
2672
  setBaseStates([]);
@@ -2607,7 +2697,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2607
2697
  setMapOverrides(initialOverrides);
2608
2698
  setUiOverrides([]);
2609
2699
  }, [normalizedLayers]);
2610
- (0, import_react4.useEffect)(() => {
2700
+ (0, import_react5.useEffect)(() => {
2611
2701
  if (!layerControls) {
2612
2702
  setControlOverrides([]);
2613
2703
  return;
@@ -2619,7 +2709,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2619
2709
  }));
2620
2710
  setControlOverrides(overrides);
2621
2711
  }, [layerControls]);
2622
- (0, import_react4.useEffect)(() => {
2712
+ (0, import_react5.useEffect)(() => {
2623
2713
  if (layerStates) {
2624
2714
  return;
2625
2715
  }
@@ -2629,12 +2719,12 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2629
2719
  onLayerStateChange?.(reset);
2630
2720
  }
2631
2721
  }, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
2632
- (0, import_react4.useEffect)(() => {
2722
+ (0, import_react5.useEffect)(() => {
2633
2723
  if (layerStates) {
2634
2724
  setEffectiveStates(layerStates);
2635
2725
  }
2636
2726
  }, [layerStates]);
2637
- (0, import_react4.useEffect)(() => {
2727
+ (0, import_react5.useEffect)(() => {
2638
2728
  if (layerStates) {
2639
2729
  return;
2640
2730
  }
@@ -2647,11 +2737,11 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2647
2737
  setEffectiveStates(next);
2648
2738
  onLayerStateChange?.(next);
2649
2739
  }, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
2650
- (0, import_react4.useEffect)(() => {
2740
+ (0, import_react5.useEffect)(() => {
2651
2741
  if (!Array.isArray(layerControls) || layerControls.length > 0) return;
2652
2742
  setUiOverrides([]);
2653
2743
  }, [layerControls]);
2654
- (0, import_react4.useEffect)(() => {
2744
+ (0, import_react5.useEffect)(() => {
2655
2745
  if (layerStates) {
2656
2746
  return;
2657
2747
  }
@@ -2671,7 +2761,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2671
2761
  return [...filtered, nextEntry];
2672
2762
  });
2673
2763
  };
2674
- const updateOpacityFromUi = (0, import_react4.useCallback)(
2764
+ const updateOpacityFromUi = (0, import_react5.useCallback)(
2675
2765
  (layerId, uiOpacity) => {
2676
2766
  const meta = layerMetaIndex.get(String(layerId));
2677
2767
  const baseOpacity = clampOpacity3(uiOpacity);
@@ -2710,7 +2800,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2710
2800
  },
2711
2801
  [currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
2712
2802
  );
2713
- const center = (0, import_react4.useMemo)(() => {
2803
+ const center = (0, import_react5.useMemo)(() => {
2714
2804
  if (initialCenter) {
2715
2805
  return initialCenter;
2716
2806
  }
@@ -2721,30 +2811,44 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2721
2811
  return DEFAULT_CENTER;
2722
2812
  }, [initialCenter, map?.settings?.center]);
2723
2813
  const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
2724
- (0, import_react4.useEffect)(() => {
2814
+ (0, import_react5.useEffect)(() => {
2725
2815
  setCurrentZoom(zoom);
2726
2816
  }, [zoom]);
2727
- const decoratedLayers = (0, import_react4.useMemo)(() => {
2728
- return layers.map((layer) => ({
2729
- ...layer,
2730
- effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
2731
- data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
2732
- }));
2733
- }, [effectiveStates, layerGeojson, layers]);
2734
- const orderedLayers = (0, import_react4.useMemo)(() => {
2817
+ (0, import_react5.useEffect)(() => {
2818
+ if (!layerGeojson) return;
2819
+ layers.forEach((layer) => {
2820
+ const layerKey = String(layer.mapLayer.layerId);
2821
+ const incoming = layerGeojson[layer.mapLayer.layerId] ?? layerGeojson[layerKey] ?? null;
2822
+ if (incoming && !originalGeojsonByLayerIdRef.current[layerKey]) {
2823
+ originalGeojsonByLayerIdRef.current[layerKey] = incoming;
2824
+ }
2825
+ });
2826
+ }, [layerGeojson, layers]);
2827
+ const decoratedLayers = (0, import_react5.useMemo)(() => {
2828
+ return layers.map((layer) => {
2829
+ const layerKey = String(layer.mapLayer.layerId);
2830
+ const override = layerGeojsonOverrides[layerKey];
2831
+ return {
2832
+ ...layer,
2833
+ effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
2834
+ data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
2835
+ };
2836
+ });
2837
+ }, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
2838
+ const orderedLayers = (0, import_react5.useMemo)(() => {
2735
2839
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
2736
2840
  }, [decoratedLayers]);
2737
- const autoZoomGeojson = (0, import_react4.useMemo)(
2841
+ const autoZoomGeojson = (0, import_react5.useMemo)(
2738
2842
  () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
2739
2843
  [orderedLayers]
2740
2844
  );
2741
- const resolveLayerStyle = (0, import_react4.useCallback)(
2845
+ const resolveLayerStyle = (0, import_react5.useCallback)(
2742
2846
  (layerId) => {
2743
2847
  return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
2744
2848
  },
2745
2849
  [layerStyleIndex, mapLayers]
2746
2850
  );
2747
- const labelMarkers = (0, import_react4.useMemo)(() => {
2851
+ const labelMarkers = (0, import_react5.useMemo)(() => {
2748
2852
  const markers = [];
2749
2853
  decoratedLayers.forEach((layerState) => {
2750
2854
  if (!layerState.effective?.visible) return;
@@ -2781,7 +2885,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2781
2885
  });
2782
2886
  return markers;
2783
2887
  }, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
2784
- const ensureLayerPanes = (0, import_react4.useCallback)(
2888
+ const ensureLayerPanes = (0, import_react5.useCallback)(
2785
2889
  (targetMap, targetLayers) => {
2786
2890
  const baseZIndex = 400;
2787
2891
  targetLayers.forEach((layer) => {
@@ -2797,7 +2901,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2797
2901
  },
2798
2902
  []
2799
2903
  );
2800
- const handleMapReady = (0, import_react4.useCallback)(
2904
+ const handleMapReady = (0, import_react5.useCallback)(
2801
2905
  (instance) => {
2802
2906
  setPanesReady(false);
2803
2907
  setMapInstance(instance);
@@ -2805,7 +2909,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2805
2909
  },
2806
2910
  [onMapReady]
2807
2911
  );
2808
- (0, import_react4.useEffect)(() => {
2912
+ (0, import_react5.useEffect)(() => {
2809
2913
  if (!mapInstance) {
2810
2914
  setPanesReady(false);
2811
2915
  return;
@@ -2825,7 +2929,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2825
2929
  setPanesReady(true);
2826
2930
  }
2827
2931
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
2828
- const overlayOnEachFeature = (0, import_react4.useMemo)(() => {
2932
+ const overlayOnEachFeature = (0, import_react5.useMemo)(() => {
2829
2933
  return (feature, layer) => {
2830
2934
  const layerId = getFeatureLayerId(feature) ?? void 0;
2831
2935
  const geometryType = feature?.geometry?.type;
@@ -2936,7 +3040,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2936
3040
  return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
2937
3041
  };
2938
3042
  };
2939
- (0, import_react4.useImperativeHandle)(ref, () => ({
3043
+ (0, import_react5.useImperativeHandle)(ref, () => ({
2940
3044
  setLayerOpacity: (layerId, opacity) => {
2941
3045
  upsertUiOverride(layerId, { overrideOpacity: opacity });
2942
3046
  },
@@ -2959,6 +3063,24 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2959
3063
  };
2960
3064
  mapInstance.fitBounds(bounds, fitOptions);
2961
3065
  },
3066
+ fitToBbox: (bbox, padding) => {
3067
+ if (!mapInstance) return;
3068
+ 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)) {
3069
+ console.warn("[ZenitMap.fitToBbox] Invalid bbox", bbox);
3070
+ return;
3071
+ }
3072
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
3073
+ const bounds = import_leaflet4.default.latLngBounds([bbox.minLat, bbox.minLon], [bbox.maxLat, bbox.maxLon]);
3074
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
3075
+ },
3076
+ fitToGeoJson: (fc, padding) => {
3077
+ if (!mapInstance) return;
3078
+ if (!fc?.features?.length) return;
3079
+ const bounds = import_leaflet4.default.geoJSON(fc).getBounds();
3080
+ if (!bounds.isValid()) return;
3081
+ const resolvedPadding = padding ?? (isMobile ? 40 : 24);
3082
+ mapInstance.fitBounds(bounds, { padding: [resolvedPadding, resolvedPadding], animate: true });
3083
+ },
2962
3084
  setView: (coordinates, zoom2) => {
2963
3085
  if (!mapInstance) return;
2964
3086
  mapInstance.setView([coordinates.lat, coordinates.lon], zoom2 ?? mapInstance.getZoom(), {
@@ -2983,8 +3105,25 @@ var ZenitMap = (0, import_react4.forwardRef)(({
2983
3105
  highlightFeature: (layerId, featureId) => {
2984
3106
  upsertUiOverride(layerId, { overrideVisible: true, overrideOpacity: 1 });
2985
3107
  },
3108
+ updateLayerGeoJson: (layerId, featureCollection) => {
3109
+ const layerKey = String(layerId);
3110
+ setLayerGeojsonOverrides((prev) => ({ ...prev, [layerKey]: featureCollection }));
3111
+ },
3112
+ restoreLayerGeoJson: (layerId) => {
3113
+ const layerKey = String(layerId);
3114
+ const original = originalGeojsonByLayerIdRef.current[layerKey];
3115
+ setLayerGeojsonOverrides((prev) => {
3116
+ const next = { ...prev };
3117
+ if (original) {
3118
+ next[layerKey] = original;
3119
+ } else {
3120
+ delete next[layerKey];
3121
+ }
3122
+ return next;
3123
+ });
3124
+ },
2986
3125
  getMapInstance: () => mapInstance
2987
- }), [effectiveStates, mapInstance]);
3126
+ }), [effectiveStates, isMobile, mapInstance]);
2988
3127
  if (loadingMap) {
2989
3128
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
2990
3129
  }
@@ -3037,6 +3176,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
3037
3176
  ),
3038
3177
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_leaflet4.ZoomControl, { position: "topright" }),
3039
3178
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInstanceBridge, { onReady: handleMapReady }),
3179
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MapInvalidator, { trigger: mapId }),
3040
3180
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3041
3181
  BBoxZoomHandler,
3042
3182
  {
@@ -3053,7 +3193,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
3053
3193
  const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
3054
3194
  const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
3055
3195
  const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
3056
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.default.Fragment, { children: [
3196
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
3057
3197
  layerState.data && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3058
3198
  LayerGeoJson,
3059
3199
  {
@@ -3210,7 +3350,7 @@ var ZenitMap = (0, import_react4.forwardRef)(({
3210
3350
  ZenitMap.displayName = "ZenitMap";
3211
3351
 
3212
3352
  // src/react/ZenitLayerManager.tsx
3213
- var import_react5 = __toESM(require("react"));
3353
+ var import_react6 = __toESM(require("react"));
3214
3354
 
3215
3355
  // src/react/icons.tsx
3216
3356
  var import_lucide_react = require("lucide-react");
@@ -3254,17 +3394,28 @@ var ZenitLayerManager = ({
3254
3394
  showUploadTab = true,
3255
3395
  showLayerVisibilityIcon = true,
3256
3396
  layerFeatureCounts,
3257
- mapLayers
3397
+ mapLayers,
3398
+ onApplyLayerFilter,
3399
+ onClearLayerFilter
3258
3400
  }) => {
3259
- const [map, setMap] = (0, import_react5.useState)(null);
3260
- const [loadingMap, setLoadingMap] = (0, import_react5.useState)(false);
3261
- const [mapError, setMapError] = (0, import_react5.useState)(null);
3262
- const [layers, setLayers] = (0, import_react5.useState)([]);
3263
- const [activeTab, setActiveTab] = (0, import_react5.useState)("layers");
3264
- const [panelVisible, setPanelVisible] = (0, import_react5.useState)(true);
3265
- const lastEmittedStatesRef = (0, import_react5.useRef)(null);
3401
+ const [map, setMap] = (0, import_react6.useState)(null);
3402
+ const [loadingMap, setLoadingMap] = (0, import_react6.useState)(false);
3403
+ const [mapError, setMapError] = (0, import_react6.useState)(null);
3404
+ const [layers, setLayers] = (0, import_react6.useState)([]);
3405
+ const [activeTab, setActiveTab] = (0, import_react6.useState)("layers");
3406
+ const [panelVisible, setPanelVisible] = (0, import_react6.useState)(true);
3407
+ const [selectedFilterLayerId, setSelectedFilterLayerId] = (0, import_react6.useState)("");
3408
+ const [selectedFilterField, setSelectedFilterField] = (0, import_react6.useState)("");
3409
+ const [selectedFilterValue, setSelectedFilterValue] = (0, import_react6.useState)("");
3410
+ const [catalogByLayerField, setCatalogByLayerField] = (0, import_react6.useState)({});
3411
+ const [loadingCatalog, setLoadingCatalog] = (0, import_react6.useState)(false);
3412
+ const [applyingFilter, setApplyingFilter] = (0, import_react6.useState)(false);
3413
+ const [filterError, setFilterError] = (0, import_react6.useState)(null);
3414
+ const [appliedFilter, setAppliedFilter] = (0, import_react6.useState)(null);
3415
+ const catalogAbortRef = (0, import_react6.useRef)(null);
3416
+ const lastEmittedStatesRef = (0, import_react6.useRef)(null);
3266
3417
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
3267
- const baseStates = (0, import_react5.useMemo)(
3418
+ const baseStates = (0, import_react6.useMemo)(
3268
3419
  () => initLayerStates(
3269
3420
  layers.map((entry) => ({
3270
3421
  ...entry.mapLayer,
@@ -3275,7 +3426,7 @@ var ZenitLayerManager = ({
3275
3426
  ),
3276
3427
  [layers]
3277
3428
  );
3278
- const overrideStates = (0, import_react5.useMemo)(
3429
+ const overrideStates = (0, import_react6.useMemo)(
3279
3430
  () => layers.map(
3280
3431
  (entry) => ({
3281
3432
  layerId: entry.mapLayer.layerId,
@@ -3285,11 +3436,11 @@ var ZenitLayerManager = ({
3285
3436
  ),
3286
3437
  [layers]
3287
3438
  );
3288
- const effectiveStates = (0, import_react5.useMemo)(
3439
+ const effectiveStates = (0, import_react6.useMemo)(
3289
3440
  () => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
3290
3441
  [baseStates, layerStates, overrideStates]
3291
3442
  );
3292
- const layerMetaIndex = (0, import_react5.useMemo)(() => {
3443
+ const layerMetaIndex = (0, import_react6.useMemo)(() => {
3293
3444
  const index = /* @__PURE__ */ new Map();
3294
3445
  mapLayers?.forEach((entry) => {
3295
3446
  const key = String(entry.layerId);
@@ -3303,7 +3454,7 @@ var ZenitLayerManager = ({
3303
3454
  });
3304
3455
  return index;
3305
3456
  }, [map, mapLayers]);
3306
- const resolveUserOpacity = import_react5.default.useCallback((state) => {
3457
+ const resolveUserOpacity = import_react6.default.useCallback((state) => {
3307
3458
  if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
3308
3459
  if (typeof state.overrideOpacity === "string") {
3309
3460
  const parsed = Number.parseFloat(state.overrideOpacity);
@@ -3311,7 +3462,7 @@ var ZenitLayerManager = ({
3311
3462
  }
3312
3463
  return state.opacity ?? 1;
3313
3464
  }, []);
3314
- const resolveEffectiveOpacity = import_react5.default.useCallback(
3465
+ const resolveEffectiveOpacity = import_react6.default.useCallback(
3315
3466
  (layerId, userOpacity) => {
3316
3467
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
3317
3468
  return userOpacity;
@@ -3327,7 +3478,7 @@ var ZenitLayerManager = ({
3327
3478
  },
3328
3479
  [autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
3329
3480
  );
3330
- const effectiveStatesWithZoom = (0, import_react5.useMemo)(() => {
3481
+ const effectiveStatesWithZoom = (0, import_react6.useMemo)(() => {
3331
3482
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
3332
3483
  return effectiveStates;
3333
3484
  }
@@ -3341,7 +3492,7 @@ var ZenitLayerManager = ({
3341
3492
  };
3342
3493
  });
3343
3494
  }, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
3344
- (0, import_react5.useEffect)(() => {
3495
+ (0, import_react6.useEffect)(() => {
3345
3496
  let cancelled = false;
3346
3497
  setLoadingMap(true);
3347
3498
  setMapError(null);
@@ -3373,12 +3524,12 @@ var ZenitLayerManager = ({
3373
3524
  cancelled = true;
3374
3525
  };
3375
3526
  }, [client.maps, mapId]);
3376
- (0, import_react5.useEffect)(() => {
3527
+ (0, import_react6.useEffect)(() => {
3377
3528
  if (!showUploadTab && activeTab === "upload") {
3378
3529
  setActiveTab("layers");
3379
3530
  }
3380
3531
  }, [activeTab, showUploadTab]);
3381
- (0, import_react5.useEffect)(() => {
3532
+ (0, import_react6.useEffect)(() => {
3382
3533
  if (isControlled) return;
3383
3534
  if (!onLayerStatesChange) return;
3384
3535
  const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
@@ -3396,7 +3547,7 @@ var ZenitLayerManager = ({
3396
3547
  mapZoom,
3397
3548
  onLayerStatesChange
3398
3549
  ]);
3399
- const updateLayerVisible = import_react5.default.useCallback(
3550
+ const updateLayerVisible = import_react6.default.useCallback(
3400
3551
  (layerId, visible) => {
3401
3552
  if (!onLayerStatesChange) return;
3402
3553
  const next = effectiveStates.map(
@@ -3406,7 +3557,7 @@ var ZenitLayerManager = ({
3406
3557
  },
3407
3558
  [effectiveStates, onLayerStatesChange]
3408
3559
  );
3409
- const updateLayerOpacity = import_react5.default.useCallback(
3560
+ const updateLayerOpacity = import_react6.default.useCallback(
3410
3561
  (layerId, opacity) => {
3411
3562
  if (!onLayerStatesChange) return;
3412
3563
  const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
@@ -3417,7 +3568,7 @@ var ZenitLayerManager = ({
3417
3568
  },
3418
3569
  [effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
3419
3570
  );
3420
- const resolveFeatureCount = import_react5.default.useCallback(
3571
+ const resolveFeatureCount = import_react6.default.useCallback(
3421
3572
  (layerId, layer) => {
3422
3573
  const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
3423
3574
  if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
@@ -3426,7 +3577,7 @@ var ZenitLayerManager = ({
3426
3577
  },
3427
3578
  [layerFeatureCounts]
3428
3579
  );
3429
- const decoratedLayers = (0, import_react5.useMemo)(() => {
3580
+ const decoratedLayers = (0, import_react6.useMemo)(() => {
3430
3581
  return layers.map((entry) => ({
3431
3582
  ...entry,
3432
3583
  effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
@@ -3455,7 +3606,140 @@ var ZenitLayerManager = ({
3455
3606
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
3456
3607
  });
3457
3608
  }, [effectiveStates, layers, resolveFeatureCount]);
3458
- const resolveLayerStyle = import_react5.default.useCallback(
3609
+ const filterableLayers = (0, import_react6.useMemo)(() => {
3610
+ return decoratedLayers.filter((entry) => {
3611
+ const prefilters = entry.mapLayer.layerConfig?.prefilters;
3612
+ return !!prefilters && Object.keys(prefilters).length > 0;
3613
+ });
3614
+ }, [decoratedLayers]);
3615
+ const selectedFilterLayer = (0, import_react6.useMemo)(
3616
+ () => filterableLayers.find((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId) ?? null,
3617
+ [filterableLayers, selectedFilterLayerId]
3618
+ );
3619
+ const filterFields = (0, import_react6.useMemo)(() => {
3620
+ const prefilters = selectedFilterLayer?.mapLayer.layerConfig?.prefilters;
3621
+ return prefilters ? Object.keys(prefilters) : [];
3622
+ }, [selectedFilterLayer]);
3623
+ const activeCatalogKey = selectedFilterLayer ? `${selectedFilterLayer.mapLayer.layerId}:${selectedFilterField}` : null;
3624
+ const activeCatalogValues = activeCatalogKey ? catalogByLayerField[activeCatalogKey] ?? [] : [];
3625
+ const extractCatalogValues = import_react6.default.useCallback((catalogData, field) => {
3626
+ const values = /* @__PURE__ */ new Set();
3627
+ const pushValue = (value) => {
3628
+ if (value === null || value === void 0) return;
3629
+ const normalized = String(value).trim();
3630
+ if (normalized) values.add(normalized);
3631
+ };
3632
+ if (catalogData && typeof catalogData === "object") {
3633
+ const maybeRecord = catalogData;
3634
+ const directField = maybeRecord[field];
3635
+ if (Array.isArray(directField)) {
3636
+ directField.forEach(pushValue);
3637
+ }
3638
+ const items = maybeRecord.items;
3639
+ if (Array.isArray(items)) {
3640
+ items.forEach((item) => {
3641
+ if (!item || typeof item !== "object") return;
3642
+ const row = item;
3643
+ const rowField = row.field;
3644
+ if (String(rowField ?? "").toUpperCase() === field.toUpperCase() && Array.isArray(row.values)) {
3645
+ row.values.forEach(pushValue);
3646
+ }
3647
+ });
3648
+ }
3649
+ const features = maybeRecord.features;
3650
+ if (Array.isArray(features)) {
3651
+ features.forEach((feature) => {
3652
+ if (!feature || typeof feature !== "object") return;
3653
+ const properties = feature.properties;
3654
+ if (properties && field in properties) {
3655
+ pushValue(properties[field]);
3656
+ }
3657
+ });
3658
+ }
3659
+ }
3660
+ return [...values].sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
3661
+ }, []);
3662
+ (0, import_react6.useEffect)(() => {
3663
+ if (!filterableLayers.length) {
3664
+ setSelectedFilterLayerId("");
3665
+ return;
3666
+ }
3667
+ if (!selectedFilterLayerId || !filterableLayers.some((layer) => String(layer.mapLayer.layerId) === selectedFilterLayerId)) {
3668
+ setSelectedFilterLayerId(String(filterableLayers[0].mapLayer.layerId));
3669
+ }
3670
+ }, [filterableLayers, selectedFilterLayerId]);
3671
+ (0, import_react6.useEffect)(() => {
3672
+ if (!filterFields.length) {
3673
+ setSelectedFilterField("");
3674
+ return;
3675
+ }
3676
+ if (!selectedFilterField || !filterFields.includes(selectedFilterField)) {
3677
+ setSelectedFilterField(filterFields[0]);
3678
+ setSelectedFilterValue("");
3679
+ }
3680
+ }, [filterFields, selectedFilterField]);
3681
+ (0, import_react6.useEffect)(() => {
3682
+ if (activeTab !== "filters") return;
3683
+ if (!selectedFilterLayer || !selectedFilterField || !activeCatalogKey) return;
3684
+ if (catalogByLayerField[activeCatalogKey]) return;
3685
+ catalogAbortRef.current?.abort();
3686
+ const controller = new AbortController();
3687
+ catalogAbortRef.current = controller;
3688
+ setLoadingCatalog(true);
3689
+ setFilterError(null);
3690
+ client.layers.getLayerFeaturesCatalog(selectedFilterLayer.mapLayer.layerId).then((response) => {
3691
+ if (controller.signal.aborted) return;
3692
+ const values = extractCatalogValues(response.data, selectedFilterField);
3693
+ setCatalogByLayerField((prev) => ({ ...prev, [activeCatalogKey]: values }));
3694
+ }).catch((error) => {
3695
+ if (controller.signal.aborted) return;
3696
+ const message = error instanceof Error ? error.message : "No se pudo cargar el cat\xE1logo";
3697
+ setFilterError(message);
3698
+ }).finally(() => {
3699
+ if (!controller.signal.aborted) setLoadingCatalog(false);
3700
+ });
3701
+ return () => {
3702
+ controller.abort();
3703
+ };
3704
+ }, [activeCatalogKey, activeTab, catalogByLayerField, client.layers, extractCatalogValues, selectedFilterField, selectedFilterLayer]);
3705
+ const handleApplyFilter = import_react6.default.useCallback(async () => {
3706
+ if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
3707
+ setApplyingFilter(true);
3708
+ setFilterError(null);
3709
+ try {
3710
+ await onApplyLayerFilter({
3711
+ layerId: selectedFilterLayer.mapLayer.layerId,
3712
+ field: selectedFilterField,
3713
+ value: selectedFilterValue
3714
+ });
3715
+ setAppliedFilter({
3716
+ layerId: selectedFilterLayer.mapLayer.layerId,
3717
+ field: selectedFilterField,
3718
+ value: selectedFilterValue
3719
+ });
3720
+ } catch (error) {
3721
+ const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
3722
+ setFilterError(message);
3723
+ } finally {
3724
+ setApplyingFilter(false);
3725
+ }
3726
+ }, [onApplyLayerFilter, selectedFilterField, selectedFilterLayer, selectedFilterValue]);
3727
+ const handleClearFilter = import_react6.default.useCallback(async () => {
3728
+ if (!selectedFilterLayer) return;
3729
+ setApplyingFilter(true);
3730
+ setFilterError(null);
3731
+ try {
3732
+ await onClearLayerFilter?.({ layerId: selectedFilterLayer.mapLayer.layerId, field: selectedFilterField || void 0 });
3733
+ setSelectedFilterValue("");
3734
+ setAppliedFilter(null);
3735
+ } catch (error) {
3736
+ const message = error instanceof Error ? error.message : "No se pudo limpiar el filtro";
3737
+ setFilterError(message);
3738
+ } finally {
3739
+ setApplyingFilter(false);
3740
+ }
3741
+ }, [onClearLayerFilter, selectedFilterField, selectedFilterLayer]);
3742
+ const resolveLayerStyle = import_react6.default.useCallback(
3459
3743
  (layerId) => {
3460
3744
  const layerKey = String(layerId);
3461
3745
  const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
@@ -3793,6 +4077,18 @@ var ZenitLayerManager = ({
3793
4077
  "Subir"
3794
4078
  ]
3795
4079
  }
4080
+ ),
4081
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4082
+ "button",
4083
+ {
4084
+ type: "button",
4085
+ className: `zlm-tab${activeTab === "filters" ? " is-active" : ""}`,
4086
+ onClick: () => setActiveTab("filters"),
4087
+ children: [
4088
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Layers, { size: 16 }),
4089
+ "Filtros"
4090
+ ]
4091
+ }
3796
4092
  )
3797
4093
  ]
3798
4094
  }
@@ -3800,6 +4096,68 @@ var ZenitLayerManager = ({
3800
4096
  ] }),
3801
4097
  panelVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
3802
4098
  activeTab === "layers" && renderLayerCards(),
4099
+ 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: [
4100
+ filterableLayers.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
4101
+ "Capa",
4102
+ /* @__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))) })
4103
+ ] }),
4104
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
4105
+ "Campo",
4106
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { className: "zlm-filter-select", value: selectedFilterField, onChange: (e) => {
4107
+ setSelectedFilterField(e.target.value);
4108
+ setSelectedFilterValue("");
4109
+ }, children: filterFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: field, children: field }, field)) })
4110
+ ] }),
4111
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
4112
+ "Valor",
4113
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4114
+ "select",
4115
+ {
4116
+ className: "zlm-filter-select",
4117
+ value: selectedFilterValue,
4118
+ onChange: (e) => {
4119
+ const value = e.target.value;
4120
+ setSelectedFilterValue(value);
4121
+ },
4122
+ children: [
4123
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "Seleccionar\u2026" }),
4124
+ activeCatalogValues.map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value, children: value }, value))
4125
+ ]
4126
+ }
4127
+ )
4128
+ ] }),
4129
+ loadingCatalog && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#64748b", fontSize: 12 }, children: "Cargando cat\xE1logo\u2026" }),
4130
+ !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." }),
4131
+ filterError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#b91c1c", fontSize: 12 }, children: filterError }),
4132
+ appliedFilter && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-badge", style: { alignSelf: "flex-start" }, children: [
4133
+ "Activo: ",
4134
+ appliedFilter.field,
4135
+ " = ",
4136
+ appliedFilter.value
4137
+ ] }),
4138
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zlm-filter-actions", style: { display: "flex", gap: 8 }, children: [
4139
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4140
+ "button",
4141
+ {
4142
+ type: "button",
4143
+ className: "zlm-panel-toggle",
4144
+ disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
4145
+ onClick: handleApplyFilter,
4146
+ children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
4147
+ }
4148
+ ),
4149
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4150
+ "button",
4151
+ {
4152
+ type: "button",
4153
+ className: "zlm-panel-toggle",
4154
+ disabled: applyingFilter,
4155
+ onClick: handleClearFilter,
4156
+ children: "Limpiar"
4157
+ }
4158
+ )
4159
+ ] })
4160
+ ] }) }),
3803
4161
  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." })
3804
4162
  ] })
3805
4163
  ] });
@@ -3838,15 +4196,15 @@ var ZenitFeatureFilterPanel = ({
3838
4196
  };
3839
4197
 
3840
4198
  // src/react/ai/FloatingChatBox.tsx
3841
- var import_react7 = require("react");
4199
+ var import_react8 = require("react");
3842
4200
  var import_react_dom2 = require("react-dom");
3843
4201
 
3844
4202
  // src/react/hooks/use-chat.ts
3845
- var import_react6 = require("react");
4203
+ var import_react7 = require("react");
3846
4204
  var useSendMessage = (config) => {
3847
- const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
3848
- const [error, setError] = (0, import_react6.useState)(null);
3849
- const send = (0, import_react6.useCallback)(
4205
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
4206
+ const [error, setError] = (0, import_react7.useState)(null);
4207
+ const send = (0, import_react7.useCallback)(
3850
4208
  async (mapId, request, options) => {
3851
4209
  setIsLoading(true);
3852
4210
  setError(null);
@@ -3864,18 +4222,18 @@ var useSendMessage = (config) => {
3864
4222
  return { sendMessage: send, isLoading, error };
3865
4223
  };
3866
4224
  var useSendMessageStream = (config) => {
3867
- const [isStreaming, setIsStreaming] = (0, import_react6.useState)(false);
3868
- const [streamingText, setStreamingText] = (0, import_react6.useState)("");
3869
- const [completeResponse, setCompleteResponse] = (0, import_react6.useState)(null);
3870
- const [error, setError] = (0, import_react6.useState)(null);
3871
- const requestIdRef = (0, import_react6.useRef)(0);
3872
- const reset = (0, import_react6.useCallback)(() => {
4225
+ const [isStreaming, setIsStreaming] = (0, import_react7.useState)(false);
4226
+ const [streamingText, setStreamingText] = (0, import_react7.useState)("");
4227
+ const [completeResponse, setCompleteResponse] = (0, import_react7.useState)(null);
4228
+ const [error, setError] = (0, import_react7.useState)(null);
4229
+ const requestIdRef = (0, import_react7.useRef)(0);
4230
+ const reset = (0, import_react7.useCallback)(() => {
3873
4231
  setIsStreaming(false);
3874
4232
  setStreamingText("");
3875
4233
  setCompleteResponse(null);
3876
4234
  setError(null);
3877
4235
  }, []);
3878
- const send = (0, import_react6.useCallback)(
4236
+ const send = (0, import_react7.useCallback)(
3879
4237
  async (mapId, request, options) => {
3880
4238
  const requestId = requestIdRef.current + 1;
3881
4239
  requestIdRef.current = requestId;
@@ -4166,6 +4524,17 @@ var styles = {
4166
4524
  width: 480,
4167
4525
  height: 640
4168
4526
  },
4527
+ panelMobileFullscreen: {
4528
+ position: "fixed",
4529
+ top: 0,
4530
+ left: 0,
4531
+ right: 0,
4532
+ bottom: 0,
4533
+ width: "100%",
4534
+ height: "100%",
4535
+ borderRadius: 0,
4536
+ margin: 0
4537
+ },
4169
4538
  // Header with green gradient
4170
4539
  header: {
4171
4540
  padding: "14px 16px",
@@ -4399,42 +4768,42 @@ var FloatingChatBox = ({
4399
4768
  open: openProp
4400
4769
  }) => {
4401
4770
  const isControlled = openProp !== void 0;
4402
- const [internalOpen, setInternalOpen] = (0, import_react7.useState)(false);
4771
+ const [internalOpen, setInternalOpen] = (0, import_react8.useState)(false);
4403
4772
  const open = isControlled ? openProp : internalOpen;
4404
- const setOpen = (0, import_react7.useCallback)((value) => {
4773
+ const setOpen = (0, import_react8.useCallback)((value) => {
4405
4774
  const newValue = typeof value === "function" ? value(open) : value;
4406
4775
  if (!isControlled) {
4407
4776
  setInternalOpen(newValue);
4408
4777
  }
4409
4778
  onOpenChange?.(newValue);
4410
4779
  }, [isControlled, open, onOpenChange]);
4411
- const [expanded, setExpanded] = (0, import_react7.useState)(false);
4412
- const [messages, setMessages] = (0, import_react7.useState)([]);
4413
- const [inputValue, setInputValue] = (0, import_react7.useState)("");
4414
- const [conversationId, setConversationId] = (0, import_react7.useState)();
4415
- const [errorMessage, setErrorMessage] = (0, import_react7.useState)(null);
4416
- const [isFocused, setIsFocused] = (0, import_react7.useState)(false);
4417
- const [isMobile, setIsMobile] = (0, import_react7.useState)(false);
4418
- const messagesEndRef = (0, import_react7.useRef)(null);
4419
- const messagesContainerRef = (0, import_react7.useRef)(null);
4420
- const chatBoxRef = (0, import_react7.useRef)(null);
4421
- const chatConfig = (0, import_react7.useMemo)(() => {
4780
+ const [expanded, setExpanded] = (0, import_react8.useState)(false);
4781
+ const [messages, setMessages] = (0, import_react8.useState)([]);
4782
+ const [inputValue, setInputValue] = (0, import_react8.useState)("");
4783
+ const [conversationId, setConversationId] = (0, import_react8.useState)();
4784
+ const [errorMessage, setErrorMessage] = (0, import_react8.useState)(null);
4785
+ const [isFocused, setIsFocused] = (0, import_react8.useState)(false);
4786
+ const [isMobile, setIsMobile] = (0, import_react8.useState)(false);
4787
+ const messagesEndRef = (0, import_react8.useRef)(null);
4788
+ const messagesContainerRef = (0, import_react8.useRef)(null);
4789
+ const chatBoxRef = (0, import_react8.useRef)(null);
4790
+ const chatConfig = (0, import_react8.useMemo)(() => {
4422
4791
  if (!baseUrl) return void 0;
4423
4792
  return { baseUrl, accessToken, getAccessToken };
4424
4793
  }, [accessToken, baseUrl, getAccessToken]);
4425
4794
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
4426
4795
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
4427
- (0, import_react7.useEffect)(() => {
4796
+ (0, import_react8.useEffect)(() => {
4428
4797
  if (open && isMobile) {
4429
4798
  setExpanded(true);
4430
4799
  }
4431
4800
  }, [open, isMobile]);
4432
- const scrollToBottom = (0, import_react7.useCallback)(() => {
4801
+ const scrollToBottom = (0, import_react8.useCallback)(() => {
4433
4802
  if (messagesEndRef.current) {
4434
4803
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
4435
4804
  }
4436
4805
  }, []);
4437
- (0, import_react7.useEffect)(() => {
4806
+ (0, import_react8.useEffect)(() => {
4438
4807
  if (open && messages.length === 0) {
4439
4808
  setMessages([
4440
4809
  {
@@ -4445,10 +4814,10 @@ var FloatingChatBox = ({
4445
4814
  ]);
4446
4815
  }
4447
4816
  }, [open, messages.length]);
4448
- (0, import_react7.useEffect)(() => {
4817
+ (0, import_react8.useEffect)(() => {
4449
4818
  scrollToBottom();
4450
4819
  }, [messages, streamingText, scrollToBottom]);
4451
- (0, import_react7.useEffect)(() => {
4820
+ (0, import_react8.useEffect)(() => {
4452
4821
  if (!open) return;
4453
4822
  if (isMobile && expanded) return;
4454
4823
  const handleClickOutside = (event) => {
@@ -4461,7 +4830,7 @@ var FloatingChatBox = ({
4461
4830
  document.removeEventListener("mousedown", handleClickOutside);
4462
4831
  };
4463
4832
  }, [open, isMobile, expanded]);
4464
- (0, import_react7.useEffect)(() => {
4833
+ (0, import_react8.useEffect)(() => {
4465
4834
  if (typeof window === "undefined") return;
4466
4835
  const mediaQuery = window.matchMedia("(max-width: 768px)");
4467
4836
  const updateMobile = () => setIsMobile(mediaQuery.matches);
@@ -4479,7 +4848,7 @@ var FloatingChatBox = ({
4479
4848
  }
4480
4849
  };
4481
4850
  }, []);
4482
- (0, import_react7.useEffect)(() => {
4851
+ (0, import_react8.useEffect)(() => {
4483
4852
  if (typeof document === "undefined") return;
4484
4853
  if (!open || !isMobile) return;
4485
4854
  document.body.style.overflow = "hidden";
@@ -4487,10 +4856,10 @@ var FloatingChatBox = ({
4487
4856
  document.body.style.overflow = "";
4488
4857
  };
4489
4858
  }, [open, isMobile]);
4490
- const addMessage = (0, import_react7.useCallback)((message) => {
4859
+ const addMessage = (0, import_react8.useCallback)((message) => {
4491
4860
  setMessages((prev) => [...prev, message]);
4492
4861
  }, []);
4493
- const handleSend = (0, import_react7.useCallback)(async () => {
4862
+ const handleSend = (0, import_react8.useCallback)(async () => {
4494
4863
  if (!mapId) {
4495
4864
  setErrorMessage("Selecciona un mapa para usar el asistente.");
4496
4865
  return;
@@ -4543,7 +4912,7 @@ var FloatingChatBox = ({
4543
4912
  sendMessage2,
4544
4913
  userId
4545
4914
  ]);
4546
- const handleKeyDown = (0, import_react7.useCallback)(
4915
+ const handleKeyDown = (0, import_react8.useCallback)(
4547
4916
  (event) => {
4548
4917
  if (event.key === "Enter" && !event.shiftKey) {
4549
4918
  event.preventDefault();
@@ -4554,7 +4923,7 @@ var FloatingChatBox = ({
4554
4923
  },
4555
4924
  [canSend, handleSend]
4556
4925
  );
4557
- const handleFollowUpClick = (0, import_react7.useCallback)((question) => {
4926
+ const handleFollowUpClick = (0, import_react8.useCallback)((question) => {
4558
4927
  setInputValue(question);
4559
4928
  }, []);
4560
4929
  const renderMetadata = (response) => {
@@ -4576,7 +4945,7 @@ var FloatingChatBox = ({
4576
4945
  ] }, index)) })
4577
4946
  ] });
4578
4947
  };
4579
- const handleActionClick = (0, import_react7.useCallback)((action) => {
4948
+ const handleActionClick = (0, import_react8.useCallback)((action) => {
4580
4949
  if (isStreaming) return;
4581
4950
  setOpen(false);
4582
4951
  requestAnimationFrame(() => {
@@ -4691,22 +5060,6 @@ var FloatingChatBox = ({
4691
5060
  box-sizing: border-box;
4692
5061
  }
4693
5062
  @media (max-width: 768px) {
4694
- .zenit-chat-panel.zenit-chat-panel--fullscreen {
4695
- position: fixed !important;
4696
- left: 0 !important;
4697
- right: 0 !important;
4698
- top: 4rem !important;
4699
- bottom: 0 !important;
4700
- width: 100% !important;
4701
- max-width: 100% !important;
4702
- height: auto !important;
4703
- border-radius: 0 !important;
4704
- display: flex !important;
4705
- flex-direction: column !important;
4706
- overflow: hidden !important;
4707
- z-index: 100000 !important;
4708
- padding-top: env(safe-area-inset-top);
4709
- }
4710
5063
  .zenit-chat-panel.zenit-chat-panel--fullscreen .zenit-ai-body {
4711
5064
  flex: 1;
4712
5065
  min-height: 0;
@@ -4726,16 +5079,20 @@ var FloatingChatBox = ({
4726
5079
  "div",
4727
5080
  {
4728
5081
  ref: chatBoxRef,
4729
- className: `zenit-chat-panel${expanded ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
4730
- style: {
4731
- ...styles.panel,
4732
- ...expanded ? styles.panelExpanded : styles.panelNormal
4733
- },
5082
+ className: `zenit-chat-panel${expanded && !isMobile ? " zenit-chat-panel--expanded" : ""}${isMobile ? " zenit-chat-panel--fullscreen" : ""}`,
5083
+ style: (() => {
5084
+ const desktopStyle = expanded ? styles.panelExpanded : styles.panelNormal;
5085
+ const mobileStyle = styles.panelMobileFullscreen;
5086
+ return {
5087
+ ...styles.panel,
5088
+ ...isMobile ? mobileStyle : desktopStyle
5089
+ };
5090
+ })(),
4734
5091
  children: [
4735
5092
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { style: styles.header, children: [
4736
5093
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { style: styles.title, children: "Asistente Zenit AI" }),
4737
5094
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.headerButtons, children: [
4738
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5095
+ !isMobile && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4739
5096
  "button",
4740
5097
  {
4741
5098
  type: "button",