zenit-sdk 0.0.5 → 0.0.7

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
@@ -1519,6 +1519,278 @@ function escapeHtml(value) {
1519
1519
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1520
1520
  }
1521
1521
  var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
1522
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
1523
+ var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
1524
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
1525
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
1526
+ var ZENIT_LEAFLET_POPUP_STYLES = `
1527
+ /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
1528
+
1529
+ /* Main popup wrapper */
1530
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
1531
+ border-radius: 12px;
1532
+ box-shadow:
1533
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
1534
+ 0 2px 4px -2px rgba(0, 0, 0, 0.1),
1535
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
1536
+ padding: 0;
1537
+ background: #ffffff;
1538
+ overflow: hidden;
1539
+ }
1540
+
1541
+ /* Content area with scroll support */
1542
+ .zenit-leaflet-popup .leaflet-popup-content {
1543
+ margin: 0;
1544
+ padding: 0;
1545
+ font-size: 13px;
1546
+ line-height: 1.5;
1547
+ color: #374151;
1548
+ min-width: 100%;
1549
+ overflow-y: auto;
1550
+ overflow-x: hidden;
1551
+ scrollbar-width: thin;
1552
+ scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
1553
+ }
1554
+
1555
+ /* Popup tip/arrow shadow */
1556
+ .zenit-leaflet-popup .leaflet-popup-tip-container {
1557
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
1558
+ }
1559
+
1560
+ .zenit-leaflet-popup .leaflet-popup-tip {
1561
+ background: #ffffff;
1562
+ box-shadow: none;
1563
+ }
1564
+
1565
+ /* Close button styling */
1566
+ .zenit-leaflet-popup .leaflet-popup-close-button {
1567
+ color: #9ca3af;
1568
+ font-size: 18px;
1569
+ font-weight: 400;
1570
+ width: 28px;
1571
+ height: 28px;
1572
+ padding: 0;
1573
+ margin: 8px 8px 0 0;
1574
+ display: flex;
1575
+ align-items: center;
1576
+ justify-content: center;
1577
+ border-radius: 6px;
1578
+ transition: all 0.15s ease;
1579
+ z-index: 10;
1580
+ }
1581
+
1582
+ .zenit-leaflet-popup .leaflet-popup-close-button:hover {
1583
+ color: #374151;
1584
+ background-color: #f3f4f6;
1585
+ }
1586
+
1587
+ .zenit-leaflet-popup .leaflet-popup-close-button:active {
1588
+ background-color: #e5e7eb;
1589
+ }
1590
+
1591
+ /* Main card container */
1592
+ .zenit-popup-card {
1593
+ display: flex;
1594
+ flex-direction: column;
1595
+ gap: 0;
1596
+ padding: 16px;
1597
+ }
1598
+
1599
+ /* Individual row styling with subtle separator */
1600
+ .zenit-popup-row {
1601
+ display: flex;
1602
+ flex-direction: column;
1603
+ gap: 2px;
1604
+ padding: 10px 0;
1605
+ border-bottom: 1px solid #f3f4f6;
1606
+ }
1607
+
1608
+ .zenit-popup-row:first-child {
1609
+ padding-top: 0;
1610
+ }
1611
+
1612
+ .zenit-popup-row:last-child {
1613
+ border-bottom: none;
1614
+ padding-bottom: 0;
1615
+ }
1616
+
1617
+ /* Label styling - small, gray, uppercase */
1618
+ .zenit-popup-label {
1619
+ font-size: 10px;
1620
+ font-weight: 500;
1621
+ color: #9ca3af;
1622
+ text-transform: uppercase;
1623
+ letter-spacing: 0.05em;
1624
+ line-height: 1.4;
1625
+ }
1626
+
1627
+ /* Value styling - darker, readable */
1628
+ .zenit-popup-value {
1629
+ font-size: 13px;
1630
+ font-weight: 400;
1631
+ color: #1f2937;
1632
+ overflow-wrap: break-word;
1633
+ word-break: break-word;
1634
+ line-height: 1.5;
1635
+ }
1636
+
1637
+ /* Special styling for description field */
1638
+ .zenit-popup-row.zenit-popup-description {
1639
+ background-color: #f9fafb;
1640
+ margin: 0 -16px;
1641
+ padding: 12px 16px;
1642
+ border-bottom: 1px solid #e5e7eb;
1643
+ }
1644
+
1645
+ .zenit-popup-row.zenit-popup-description:first-child {
1646
+ margin-top: 0;
1647
+ border-radius: 0;
1648
+ }
1649
+
1650
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
1651
+ font-size: 13px;
1652
+ line-height: 1.6;
1653
+ color: #374151;
1654
+ max-height: 150px;
1655
+ overflow-y: auto;
1656
+ padding-right: 4px;
1657
+ }
1658
+
1659
+ /* Preformatted text (JSON objects) */
1660
+ .zenit-popup-pre {
1661
+ margin: 0;
1662
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
1663
+ font-size: 11px;
1664
+ white-space: pre-wrap;
1665
+ word-break: break-word;
1666
+ color: #4b5563;
1667
+ background-color: #f9fafb;
1668
+ padding: 8px;
1669
+ border-radius: 6px;
1670
+ border: 1px solid #e5e7eb;
1671
+ }
1672
+
1673
+ /* Empty state styling */
1674
+ .zenit-popup-empty {
1675
+ font-size: 13px;
1676
+ color: #9ca3af;
1677
+ font-style: italic;
1678
+ text-align: center;
1679
+ padding: 20px 0;
1680
+ }
1681
+
1682
+ /* Webkit scrollbar styling */
1683
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
1684
+ width: 6px;
1685
+ }
1686
+
1687
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
1688
+ background: transparent;
1689
+ }
1690
+
1691
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
1692
+ background-color: rgba(156, 163, 175, 0.4);
1693
+ border-radius: 3px;
1694
+ }
1695
+
1696
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
1697
+ background-color: rgba(107, 114, 128, 0.6);
1698
+ }
1699
+
1700
+ /* Scrollbar for description field */
1701
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
1702
+ width: 4px;
1703
+ }
1704
+
1705
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
1706
+ background: transparent;
1707
+ }
1708
+
1709
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
1710
+ background-color: rgba(156, 163, 175, 0.4);
1711
+ border-radius: 2px;
1712
+ }
1713
+
1714
+ /* ===== Responsive: Mobile (<640px) ===== */
1715
+ @media (max-width: 640px) {
1716
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
1717
+ border-radius: 10px;
1718
+ }
1719
+
1720
+ .zenit-leaflet-popup .leaflet-popup-close-button {
1721
+ width: 26px;
1722
+ height: 26px;
1723
+ font-size: 16px;
1724
+ margin: 6px 6px 0 0;
1725
+ }
1726
+
1727
+ .zenit-popup-card {
1728
+ padding: 12px;
1729
+ }
1730
+
1731
+ .zenit-popup-row {
1732
+ padding: 8px 0;
1733
+ }
1734
+
1735
+ .zenit-popup-label {
1736
+ font-size: 9px;
1737
+ }
1738
+
1739
+ .zenit-popup-value {
1740
+ font-size: 12px;
1741
+ }
1742
+
1743
+ .zenit-popup-row.zenit-popup-description {
1744
+ margin: 0 -12px;
1745
+ padding: 10px 12px;
1746
+ }
1747
+
1748
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
1749
+ font-size: 12px;
1750
+ max-height: 120px;
1751
+ }
1752
+
1753
+ .zenit-popup-pre {
1754
+ font-size: 10px;
1755
+ padding: 6px;
1756
+ }
1757
+
1758
+ .zenit-popup-empty {
1759
+ font-size: 12px;
1760
+ padding: 16px 0;
1761
+ }
1762
+ }
1763
+
1764
+ /* ===== Map tooltip styling ===== */
1765
+ .zenit-map-tooltip {
1766
+ background-color: rgba(31, 41, 55, 0.95);
1767
+ border: none;
1768
+ border-radius: 6px;
1769
+ color: #ffffff;
1770
+ font-size: 12px;
1771
+ font-weight: 500;
1772
+ padding: 6px 10px;
1773
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1774
+ }
1775
+
1776
+ .zenit-map-tooltip::before {
1777
+ border-top-color: rgba(31, 41, 55, 0.95);
1778
+ }
1779
+ `;
1780
+ function ensurePopupStyles() {
1781
+ if (typeof document === "undefined") return;
1782
+ if (document.getElementById(POPUP_STYLE_ID)) return;
1783
+ const styleTag = document.createElement("style");
1784
+ styleTag.id = POPUP_STYLE_ID;
1785
+ styleTag.textContent = ZENIT_LEAFLET_POPUP_STYLES;
1786
+ document.head.appendChild(styleTag);
1787
+ }
1788
+ function getPopupDimensions() {
1789
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
1790
+ return DESKTOP_POPUP_DIMENSIONS;
1791
+ }
1792
+ return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
1793
+ }
1522
1794
  function normalizeDescriptionValue(value) {
1523
1795
  if (value === void 0 || value === null) return null;
1524
1796
  if (typeof value === "string") {
@@ -1546,92 +1818,65 @@ function safeJsonStringify(value) {
1546
1818
  }
1547
1819
  return String(value);
1548
1820
  }
1549
- function renderPropertyValue(value) {
1821
+ function renderPopupValue(value) {
1550
1822
  if (value === null || value === void 0) {
1551
- return '<span class="prop-empty">\u2014</span>';
1823
+ return '<span class="zenit-popup-empty">Sin datos</span>';
1552
1824
  }
1553
1825
  if (typeof value === "object") {
1554
1826
  const json = safeJsonStringify(value);
1555
- return `<pre class="prop-json">${escapeHtml(json)}</pre>`;
1827
+ return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
1556
1828
  }
1557
- return `<span class="prop-text">${escapeHtml(String(value))}</span>`;
1829
+ return `<span>${escapeHtml(String(value))}</span>`;
1558
1830
  }
1559
- var POPUP_TITLE_KEYS = ["name", "title", "nombre", "label", "id"];
1560
- var POPUP_CHIP_KEYS = /* @__PURE__ */ new Set(["churn", "color", "sector"]);
1561
- function isChipValue(value) {
1562
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1831
+ function shouldIncludePopupEntry(key, value) {
1832
+ if (!key) return false;
1833
+ const normalized = key.trim().toLowerCase();
1834
+ if (!normalized) return false;
1835
+ if (normalized.startsWith("_")) return false;
1836
+ if (POPUP_EXCLUDED_KEYS.has(normalized)) return false;
1837
+ if (value === null || value === void 0) return false;
1838
+ if (typeof value === "string" && !value.trim()) return false;
1839
+ return true;
1563
1840
  }
1564
- function getSanitizedChipColor(value) {
1565
- if (typeof value !== "string") return null;
1566
- const trimmed = value.trim();
1567
- if (/^#([0-9a-fA-F]{3}){1,2}$/.test(trimmed)) {
1568
- return trimmed;
1569
- }
1570
- if (/^rgb\((\s*\d+\s*,){2}\s*\d+\s*\)$/.test(trimmed)) {
1571
- return trimmed;
1572
- }
1573
- if (/^rgba\((\s*\d+\s*,){3}\s*(0|1|0?\.\d+)\s*\)$/.test(trimmed)) {
1574
- return trimmed;
1575
- }
1576
- return null;
1841
+ function isDescriptionKey(key) {
1842
+ const normalized = key.trim().toLowerCase();
1843
+ return DESCRIPTION_KEYS.has(normalized);
1577
1844
  }
1578
- function getPopupTitle(entries) {
1579
- for (const candidate of POPUP_TITLE_KEYS) {
1580
- const match = entries.find((entry) => entry.normalized === candidate);
1581
- if (!match || match.value === null || match.value === void 0) continue;
1582
- const value = String(match.value).trim();
1583
- if (!value) continue;
1584
- return { title: value, key: match.key };
1585
- }
1586
- return null;
1845
+ function formatLabel(key) {
1846
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
1587
1847
  }
1588
- function renderProperties(properties) {
1589
- const description = extractDescriptionValue(properties);
1590
- const entries = Object.entries(properties).filter(([key]) => !DESCRIPTION_KEYS.has(key.toLowerCase())).map(([key, value]) => ({
1591
- key,
1592
- value,
1593
- normalized: key.trim().toLowerCase()
1594
- }));
1595
- if (!description && entries.length === 0) return "";
1596
- const titleEntry = getPopupTitle(entries);
1597
- const titleText = titleEntry?.title ?? "Detalle del elemento";
1598
- const chipEntries = entries.filter(
1599
- (entry) => POPUP_CHIP_KEYS.has(entry.normalized) && isChipValue(entry.value)
1848
+ function createPopupContent(properties) {
1849
+ const entries = Object.entries(properties).filter(
1850
+ ([key, value]) => shouldIncludePopupEntry(key, value)
1600
1851
  );
1601
- const listEntries = entries.filter((entry) => {
1602
- if (titleEntry && entry.key === titleEntry.key) return false;
1603
- if (chipEntries.find((chip) => chip.key === entry.key)) return false;
1604
- return true;
1605
- });
1606
- const descriptionHtml = description ? `<div class="popup-description">${escapeHtml(description)}</div>` : "";
1607
- const chipsHtml = chipEntries.length ? `<div class="popup-chip-row">${chipEntries.map((entry) => {
1608
- const label = escapeHtml(entry.key.replace(/_/g, " "));
1609
- const value = escapeHtml(String(entry.value));
1610
- const color = getSanitizedChipColor(entry.value);
1611
- const colorStyle = color ? ` style="--chip-color: ${color}"` : "";
1612
- return `<span class="popup-chip"${colorStyle}><span class="popup-chip-label">${label}</span><span class="popup-chip-value">${value}</span></span>`;
1613
- }).join("")}</div>` : "";
1614
- const rowsHtml = listEntries.map((entry) => {
1615
- const label = escapeHtml(entry.key.replace(/_/g, " "));
1616
- const valueHtml = renderPropertyValue(entry.value);
1617
- return `<div class="prop-key">${label}</div><div class="prop-value">${valueHtml}</div>`;
1618
- }).join("");
1619
- const listHtml = rowsHtml ? `<div class="prop-list">${rowsHtml}</div>` : "";
1620
- return `
1621
- <div class="feature-popup">
1622
- <div class="feature-popup-card">
1623
- <div class="feature-popup-header">
1624
- <p class="popup-eyebrow">Informaci\xF3n</p>
1625
- <h3 class="popup-title">${escapeHtml(titleText)}</h3>
1626
- </div>
1627
- <div class="feature-popup-body">
1628
- ${descriptionHtml}
1629
- ${chipsHtml}
1630
- ${listHtml}
1631
- </div>
1852
+ if (entries.length === 0) {
1853
+ return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
1854
+ }
1855
+ const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
1856
+ const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
1857
+ let rowsHtml = "";
1858
+ if (descriptionEntry) {
1859
+ const [key, value] = descriptionEntry;
1860
+ const label = escapeHtml(formatLabel(key));
1861
+ const valueHtml = renderPopupValue(value);
1862
+ rowsHtml += `
1863
+ <div class="zenit-popup-row zenit-popup-description">
1864
+ <div class="zenit-popup-label">${label}</div>
1865
+ <div class="zenit-popup-value">${valueHtml}</div>
1632
1866
  </div>
1633
- </div>
1634
- `;
1867
+ `;
1868
+ }
1869
+ rowsHtml += otherEntries.map(([key, value]) => {
1870
+ const label = escapeHtml(formatLabel(key));
1871
+ const valueHtml = renderPopupValue(value);
1872
+ return `
1873
+ <div class="zenit-popup-row">
1874
+ <div class="zenit-popup-label">${label}</div>
1875
+ <div class="zenit-popup-value">${valueHtml}</div>
1876
+ </div>
1877
+ `;
1878
+ }).join("");
1879
+ return `<div class="zenit-popup-card">${rowsHtml}</div>`;
1635
1880
  }
1636
1881
  function withAlpha(color, alpha) {
1637
1882
  const trimmed = color.trim();
@@ -1701,7 +1946,7 @@ function getFeatureStyleOverrides(feature) {
1701
1946
  function buildFeaturePopupHtml(feature) {
1702
1947
  const properties = feature?.properties;
1703
1948
  if (!properties) return null;
1704
- const rendered = renderProperties(properties);
1949
+ const rendered = createPopupContent(properties);
1705
1950
  return rendered ? rendered : null;
1706
1951
  }
1707
1952
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
@@ -1879,6 +2124,11 @@ var ZenitMap = (0, import_react.forwardRef)(({
1879
2124
  }
1880
2125
  return;
1881
2126
  }, []);
2127
+ (0, import_react.useEffect)(() => {
2128
+ if (featureInfoMode === "popup") {
2129
+ ensurePopupStyles();
2130
+ }
2131
+ }, [featureInfoMode]);
1882
2132
  const layerStyleIndex = (0, import_react.useMemo)(() => {
1883
2133
  const index = /* @__PURE__ */ new Map();
1884
2134
  (map?.mapLayers ?? []).forEach((entry) => {
@@ -2226,12 +2476,15 @@ var ZenitMap = (0, import_react.forwardRef)(({
2226
2476
  if (featureInfoMode === "popup") {
2227
2477
  const content = buildFeaturePopupHtml(feature);
2228
2478
  if (content) {
2479
+ const { maxWidth, minWidth, maxHeight } = getPopupDimensions();
2229
2480
  layer.bindPopup(content, {
2230
- maxWidth: 420,
2231
- minWidth: 280,
2232
- className: "zenit-feature-popup-shell",
2481
+ maxWidth,
2482
+ minWidth,
2483
+ maxHeight,
2484
+ className: "zenit-leaflet-popup",
2233
2485
  autoPan: true,
2234
2486
  closeButton: true,
2487
+ keepInView: true,
2235
2488
  offset: import_leaflet.default.point(0, -24)
2236
2489
  });
2237
2490
  }