zenit-sdk 0.2.0 → 0.2.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/README.md CHANGED
@@ -309,3 +309,4 @@ npm run example:map
309
309
  ```
310
310
 
311
311
  El `baseUrl` por defecto de los ejemplos es `http://localhost:3200/api/v1` si no se especifica.
312
+ # zenit-sdk
@@ -365,6 +365,28 @@ function sanitizeGeoJson(input, debugKey) {
365
365
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
366
366
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
367
367
  var DEV_MODE = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
368
+ function getPolygonArea(coords) {
369
+ let area = 0;
370
+ for (let i = 0; i < coords.length - 1; i++) {
371
+ area += coords[i][0] * coords[i + 1][1] - coords[i + 1][0] * coords[i][1];
372
+ }
373
+ return Math.abs(area / 2);
374
+ }
375
+ function getFeatureArea(feature) {
376
+ const geom = feature?.geometry;
377
+ if (!geom?.coordinates) return 0;
378
+ try {
379
+ if (geom.type === "Polygon") return getPolygonArea(geom.coordinates[0]);
380
+ if (geom.type === "MultiPolygon") {
381
+ return geom.coordinates.reduce(
382
+ (sum, poly) => sum + getPolygonArea(poly[0]),
383
+ 0
384
+ );
385
+ }
386
+ } catch {
387
+ }
388
+ return 0;
389
+ }
368
390
  function normalizeBboxFromData(data) {
369
391
  const bboxCandidate = data.bbox;
370
392
  if (!Array.isArray(bboxCandidate) || bboxCandidate.length < 4) return null;
@@ -466,7 +488,10 @@ var LayerGeoJson = ({
466
488
  }, [onEachFeature]);
467
489
  const safeData = useMemo(() => sanitizeGeoJson(data, String(layerId)), [data, layerId]);
468
490
  const features = useMemo(() => safeData.features ?? [], [safeData]);
469
- const fillFeatures = useMemo(() => features.filter(isNonPointGeometry), [features]);
491
+ const fillFeatures = useMemo(
492
+ () => [...features.filter(isNonPointGeometry)].sort((a, b) => getFeatureArea(b) - getFeatureArea(a)),
493
+ [features]
494
+ );
470
495
  const pointFeatures = useMemo(() => features.filter(isPointGeometry), [features]);
471
496
  const dataVersionRef = useRef(0);
472
497
  const prevSignatureRef = useRef("");
@@ -511,7 +536,8 @@ var LayerGeoJson = ({
511
536
  return L.marker(latlng, {
512
537
  icon: createPointDivIcon(style, isMobile),
513
538
  pane: clusterPaneName,
514
- interactive: true
539
+ interactive: true,
540
+ bubblingMouseEvents: false
515
541
  });
516
542
  },
517
543
  onEachFeature: (feature, layer) => onEachFeatureRef.current(feature, layer)
@@ -539,7 +565,7 @@ var LayerGeoJson = ({
539
565
  {
540
566
  data: fillData,
541
567
  pane: resolvedFillPane,
542
- style: (feature) => styleFn(feature, layerType, baseOpacity),
568
+ style: (feature) => ({ ...styleFn(feature, layerType, baseOpacity), bubblingMouseEvents: false }),
543
569
  onEachFeature: (feature, layer) => {
544
570
  onEachFeature(feature, layer);
545
571
  onPolygonLabel?.(feature, layer);
@@ -558,7 +584,9 @@ var LayerGeoJson = ({
558
584
  }
559
585
  return L.circleMarker(latlng, {
560
586
  radius: isMobile ? 8 : 6,
561
- ...styleFn(feature, layerType, baseOpacity)
587
+ ...styleFn(feature, layerType, baseOpacity),
588
+ bubblingMouseEvents: false,
589
+ interactive: true
562
590
  });
563
591
  },
564
592
  onEachFeature
@@ -791,6 +819,7 @@ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry", "_private
791
819
  var POPUP_TITLE_KEYS = ["id", "nombre", "name", "title", "titulo", "cluster"];
792
820
  var POPUP_BADGE_KEYS = ["tipo", "type", "category", "categoria", "estado", "status"];
793
821
  var POPUP_DESCRIPTION_KEYS = ["descripcion", "description", "desc"];
822
+ var POPUP_PRIORITY_KEYS = ["insights", "recomendaciones"];
794
823
  var CURRENCY_KEYWORDS = [
795
824
  "capital",
796
825
  "monto",
@@ -1021,6 +1050,14 @@ function shouldIncludePopupEntry(key, value) {
1021
1050
  if (typeof value === "string" && !value.trim()) return false;
1022
1051
  return true;
1023
1052
  }
1053
+ function getPopupPriorityIndex(key) {
1054
+ const normalized = key.trim().toLowerCase();
1055
+ const idx = POPUP_PRIORITY_KEYS.indexOf(normalized);
1056
+ return idx === -1 ? POPUP_PRIORITY_KEYS.length : idx;
1057
+ }
1058
+ function sortEntriesByPriority(entries) {
1059
+ return [...entries].sort((a, b) => getPopupPriorityIndex(a[0]) - getPopupPriorityIndex(b[0]));
1060
+ }
1024
1061
  function createPopupContent(properties) {
1025
1062
  const header = findHeaderProperties(properties);
1026
1063
  const headerText = header.title?.value ?? extractPopupHeader(properties);
@@ -1028,11 +1065,13 @@ function createPopupContent(properties) {
1028
1065
  const badge = header.badge ? getBadgeForProperty(header.badge.key, header.badge.value) : null;
1029
1066
  const colorValue = properties.color;
1030
1067
  const colorBar = typeof colorValue === "string" && isHexColor(colorValue) ? `<div style="height:6px; border-radius:8px; margin:-2px -2px 10px; background:linear-gradient(90deg, ${colorValue}, rgba(255,255,255,0.95));"></div>` : "";
1031
- const entries = Object.entries(properties).filter(([key, value]) => {
1032
- if (!shouldIncludePopupEntry(key, value)) return false;
1033
- if (usedKeys.has(key)) return false;
1034
- return true;
1035
- });
1068
+ const entries = sortEntriesByPriority(
1069
+ Object.entries(properties).filter(([key, value]) => {
1070
+ if (!shouldIncludePopupEntry(key, value)) return false;
1071
+ if (usedKeys.has(key)) return false;
1072
+ return true;
1073
+ })
1074
+ );
1036
1075
  if (entries.length === 0) {
1037
1076
  return '<div style="padding:8px 0; color:#64748b; text-align:center;">Sin datos disponibles</div>';
1038
1077
  }
@@ -1078,7 +1117,10 @@ function buildWhitelistedRows(properties, headerSections) {
1078
1117
  return null;
1079
1118
  }
1080
1119
  const { whitelist, modalType } = selection;
1081
- const entries = applyModalWhitelist(properties, whitelist);
1120
+ const rawEntries = applyModalWhitelist(properties, whitelist);
1121
+ const entries = [...rawEntries].sort(
1122
+ (a, b) => getPopupPriorityIndex(a.matchedKey) - getPopupPriorityIndex(b.matchedKey)
1123
+ );
1082
1124
  if (entries.length === 0) {
1083
1125
  logModalWhitelistDebug({
1084
1126
  modalType,
@@ -1629,6 +1671,39 @@ function isCandidateGeometryType(candidate, allowedTypes) {
1629
1671
  const geometryType = candidate?.geometry?.type;
1630
1672
  return Boolean(geometryType && allowedTypes.has(geometryType));
1631
1673
  }
1674
+ function calculatePolygonArea(coords) {
1675
+ let area = 0;
1676
+ for (let i = 0; i < coords.length - 1; i++) {
1677
+ area += coords[i][0] * coords[i + 1][1] - coords[i + 1][0] * coords[i][1];
1678
+ }
1679
+ return Math.abs(area / 2);
1680
+ }
1681
+ function calculateGeometryArea(geometry) {
1682
+ if (!geometry?.coordinates) return 0;
1683
+ try {
1684
+ if (geometry.type === "Polygon") return calculatePolygonArea(geometry.coordinates[0]);
1685
+ if (geometry.type === "MultiPolygon") {
1686
+ return geometry.coordinates.reduce(
1687
+ (sum, poly) => sum + calculatePolygonArea(poly[0]),
1688
+ 0
1689
+ );
1690
+ }
1691
+ } catch {
1692
+ }
1693
+ return 0;
1694
+ }
1695
+ function calculateLayerAverageArea(features) {
1696
+ if (!features?.length) return 0;
1697
+ try {
1698
+ const total = features.reduce(
1699
+ (sum, f) => sum + (f?.geometry ? calculateGeometryArea(f.geometry) : 0),
1700
+ 0
1701
+ );
1702
+ return total / features.length;
1703
+ } catch {
1704
+ return 0;
1705
+ }
1706
+ }
1632
1707
  function getFeatureStyleOverrides(feature) {
1633
1708
  const candidate = feature?.properties?._style;
1634
1709
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return null;
@@ -1990,7 +2065,7 @@ var ZenitMap = forwardRef(({
1990
2065
  );
1991
2066
  if (layerStates && onLayerStateChange) {
1992
2067
  const next = effectiveStates.map(
1993
- (state) => state.layerId === layerId ? {
2068
+ (state) => isLayerIdMatch(state.layerId, layerId) ? {
1994
2069
  ...state,
1995
2070
  baseOpacity,
1996
2071
  opacity: effectiveOpacity,
@@ -2001,11 +2076,11 @@ var ZenitMap = forwardRef(({
2001
2076
  return;
2002
2077
  }
2003
2078
  setBaseStates(
2004
- (prev) => prev.map((state) => state.layerId === layerId ? { ...state, baseOpacity } : state)
2079
+ (prev) => prev.map((state) => isLayerIdMatch(state.layerId, layerId) ? { ...state, baseOpacity } : state)
2005
2080
  );
2006
2081
  setUiOverrides((prev) => {
2007
- const existing = prev.find((entry) => entry.layerId === layerId);
2008
- const filtered = prev.filter((entry) => entry.layerId !== layerId);
2082
+ const existing = prev.find((entry) => isLayerIdMatch(entry.layerId, layerId));
2083
+ const filtered = prev.filter((entry) => !isLayerIdMatch(entry.layerId, layerId));
2009
2084
  if (existing && existing.overrideVisible !== void 0) {
2010
2085
  return [
2011
2086
  ...filtered,
@@ -2047,13 +2122,18 @@ var ZenitMap = forwardRef(({
2047
2122
  const override = layerGeojsonOverrides[layerKey];
2048
2123
  return {
2049
2124
  ...layer,
2050
- effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
2125
+ effective: effectiveStates.find((state) => isLayerIdMatch(state.layerId, layer.mapLayer.layerId)),
2051
2126
  data: override ?? layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[layerKey] ?? null
2052
2127
  };
2053
2128
  });
2054
2129
  }, [effectiveStates, layerGeojson, layerGeojsonOverrides, layers]);
2055
2130
  const orderedLayers = useMemo3(() => {
2056
- return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
2131
+ return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => {
2132
+ const aArea = calculateLayerAverageArea(a.data?.features ?? []);
2133
+ const bArea = calculateLayerAverageArea(b.data?.features ?? []);
2134
+ if (aArea !== bArea) return bArea - aArea;
2135
+ return a.displayOrder - b.displayOrder;
2136
+ });
2057
2137
  }, [decoratedLayers]);
2058
2138
  const autoZoomGeojson = useMemo3(
2059
2139
  () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
@@ -2106,15 +2186,14 @@ var ZenitMap = forwardRef(({
2106
2186
  (targetMap, targetLayers) => {
2107
2187
  const fillBaseZIndex = 400;
2108
2188
  const pointsBaseZIndex = 750;
2109
- targetLayers.forEach((layer) => {
2110
- const order = Number.isFinite(layer.displayOrder) ? layer.displayOrder : 0;
2111
- const orderOffset = Math.max(0, Math.min(order, 150));
2189
+ const sortedByArea = [...targetLayers].sort((a, b) => (b.averageArea ?? 0) - (a.averageArea ?? 0));
2190
+ sortedByArea.forEach((layer, areaIndex) => {
2112
2191
  const fillPaneName = `zenit-layer-${layer.layerId}-fill`;
2113
2192
  const pointPaneName = `zenit-layer-${layer.layerId}-points`;
2114
2193
  const fillPane = targetMap.getPane(fillPaneName) ?? targetMap.createPane(fillPaneName);
2115
2194
  const pointPane = targetMap.getPane(pointPaneName) ?? targetMap.createPane(pointPaneName);
2116
- fillPane.style.zIndex = String(fillBaseZIndex + orderOffset);
2117
- pointPane.style.zIndex = String(pointsBaseZIndex + orderOffset);
2195
+ fillPane.style.zIndex = String(fillBaseZIndex + areaIndex);
2196
+ pointPane.style.zIndex = String(pointsBaseZIndex + areaIndex);
2118
2197
  });
2119
2198
  },
2120
2199
  []
@@ -2137,10 +2216,11 @@ var ZenitMap = forwardRef(({
2137
2216
  }
2138
2217
  const layerTargets = orderedLayers.map((layer) => ({
2139
2218
  layerId: layer.mapLayer.layerId,
2140
- displayOrder: layer.displayOrder
2219
+ displayOrder: layer.displayOrder,
2220
+ averageArea: calculateLayerAverageArea(layer.data?.features ?? [])
2141
2221
  }));
2142
2222
  if (overlayGeojson) {
2143
- layerTargets.push({ layerId: "overlay-geojson", displayOrder: 999 });
2223
+ layerTargets.push({ layerId: "overlay-geojson", displayOrder: 999, averageArea: 0 });
2144
2224
  }
2145
2225
  ensureLayerPanes(mapInstance, layerTargets);
2146
2226
  const first = layerTargets[0];
@@ -2190,9 +2270,7 @@ var ZenitMap = forwardRef(({
2190
2270
  });
2191
2271
  }
2192
2272
  layer.on("click", (e) => {
2193
- if (clickIntent === "point") {
2194
- L4.DomEvent.stopPropagation(e);
2195
- }
2273
+ L4.DomEvent.stopPropagation(e);
2196
2274
  if (featureInfoMode === "popup" && client && layerId !== void 0 && !extractDescriptionValue(feature?.properties) && feature?.geometry) {
2197
2275
  if (DEV_MODE2) {
2198
2276
  console.debug("[ZenitMap] click/intersect:start", {
@@ -3132,8 +3210,6 @@ var CATALOG_FIELD_BLACKLIST = /* @__PURE__ */ new Set([
3132
3210
  "objectid",
3133
3211
  "OBJECTID",
3134
3212
  "objectId",
3135
- "gforms",
3136
- "GFORMS",
3137
3213
  "nomina",
3138
3214
  "NOMINA",
3139
3215
  "shape_area",
@@ -3195,7 +3271,8 @@ var ZenitLayerManager = ({
3195
3271
  onApplyLayerFilter,
3196
3272
  onClearLayerFilter,
3197
3273
  availableFilterLayers = [],
3198
- filterFieldWhitelist = []
3274
+ filterFieldWhitelist = [],
3275
+ showFiltersTab = true
3199
3276
  }) => {
3200
3277
  const [map, setMap] = useState3(null);
3201
3278
  const [loadingMap, setLoadingMap] = useState3(false);
@@ -3581,20 +3658,21 @@ var ZenitLayerManager = ({
3581
3658
  controller.abort();
3582
3659
  };
3583
3660
  }, [activeCatalogKey, activeTab, catalogByLayerField, catalogFieldsByLayer, client.layers, extractCatalogFieldMap, filterFieldWhitelist, selectedFilterField, selectedFilterLayer]);
3584
- const handleApplyFilter = React7.useCallback(async () => {
3585
- if (!selectedFilterLayer || !selectedFilterField || !selectedFilterValue || !onApplyLayerFilter) return;
3661
+ const handleApplyFilter = React7.useCallback(async (valueOverride) => {
3662
+ const value = valueOverride ?? selectedFilterValue;
3663
+ if (!selectedFilterLayer || !selectedFilterField || !value || !onApplyLayerFilter) return;
3586
3664
  setApplyingFilter(true);
3587
3665
  setFilterError(null);
3588
3666
  try {
3589
3667
  await onApplyLayerFilter({
3590
3668
  layerId: selectedFilterLayer.mapLayer.layerId,
3591
3669
  field: selectedFilterField,
3592
- value: selectedFilterValue
3670
+ value
3593
3671
  });
3594
3672
  setAppliedFilter({
3595
3673
  layerId: selectedFilterLayer.mapLayer.layerId,
3596
3674
  field: selectedFilterField,
3597
- value: selectedFilterValue
3675
+ value
3598
3676
  });
3599
3677
  } catch (error) {
3600
3678
  const message = error instanceof Error ? error.message : "No se pudo aplicar el filtro";
@@ -3973,7 +4051,7 @@ var ZenitLayerManager = ({
3973
4051
  ]
3974
4052
  }
3975
4053
  ),
3976
- !hasPrefilters && /* @__PURE__ */ jsxs6(
4054
+ !hasPrefilters && showFiltersTab && /* @__PURE__ */ jsxs6(
3977
4055
  "button",
3978
4056
  {
3979
4057
  type: "button",
@@ -4008,7 +4086,7 @@ var ZenitLayerManager = ({
4008
4086
  ) }),
4009
4087
  renderLayerCards()
4010
4088
  ] }),
4011
- !hasPrefilters && activeTab === "filters" && /* @__PURE__ */ jsx6("div", { className: "zlm-filter-panel", style: { display: "flex", flexDirection: "column", gap: 12, background: "#fff", border: "1px solid #e2e8f0", borderRadius: 12, padding: 12 }, children: !filterableLayers.length ? /* @__PURE__ */ jsx6("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
4089
+ !hasPrefilters && showFiltersTab && activeTab === "filters" && /* @__PURE__ */ jsx6("div", { className: "zlm-filter-panel", style: { display: "flex", flexDirection: "column", gap: 12, background: "#fff", border: "1px solid #e2e8f0", borderRadius: 12, padding: 12 }, children: !filterableLayers.length ? /* @__PURE__ */ jsx6("div", { style: { color: "#64748b", fontSize: 13 }, children: "No hay filtros disponibles para las capas de este mapa." }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
4012
4090
  filterableLayers.length > 1 && /* @__PURE__ */ jsxs6("label", { style: { display: "flex", flexDirection: "column", gap: 6, fontSize: 12, color: "#475569" }, children: [
4013
4091
  "Capa",
4014
4092
  /* @__PURE__ */ jsx6(
@@ -4047,7 +4125,10 @@ var ZenitLayerManager = ({
4047
4125
  value: selectedFilterValue,
4048
4126
  placeholder: "Seleccionar\u2026",
4049
4127
  searchPlaceholder: "Buscar valor\u2026",
4050
- onChange: (nextValue) => setSelectedFilterValue(nextValue),
4128
+ onChange: (nextValue) => {
4129
+ setSelectedFilterValue(nextValue);
4130
+ if (nextValue) handleApplyFilter(nextValue);
4131
+ },
4051
4132
  options: activeCatalogValues,
4052
4133
  disabled: loadingCatalog || activeCatalogValues.length === 0
4053
4134
  }
@@ -4069,7 +4150,7 @@ var ZenitLayerManager = ({
4069
4150
  type: "button",
4070
4151
  className: "zlm-panel-toggle",
4071
4152
  disabled: !selectedFilterValue || applyingFilter || !onApplyLayerFilter,
4072
- onClick: handleApplyFilter,
4153
+ onClick: () => handleApplyFilter(),
4073
4154
  children: applyingFilter ? "Aplicando\u2026" : "Aplicar"
4074
4155
  }
4075
4156
  ),
@@ -5347,4 +5428,4 @@ export {
5347
5428
  useSendMessageStream,
5348
5429
  FloatingChatBox
5349
5430
  };
5350
- //# sourceMappingURL=chunk-TH7D3ECH.mjs.map
5431
+ //# sourceMappingURL=chunk-EF7HF6U6.mjs.map