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.mjs CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  sendMessageStream,
36
36
  useSendMessage,
37
37
  useSendMessageStream
38
- } from "./chunk-LX2N2BXV.mjs";
38
+ } from "./chunk-ITF7QCUZ.mjs";
39
39
 
40
40
  // src/http/HttpClient.ts
41
41
  var HttpClient = class {
@@ -345,6 +345,278 @@ function escapeHtml(value) {
345
345
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
346
346
  }
347
347
  var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
348
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
349
+ var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
350
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
351
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
352
+ var ZENIT_LEAFLET_POPUP_STYLES = `
353
+ /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
354
+
355
+ /* Main popup wrapper */
356
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
357
+ border-radius: 12px;
358
+ box-shadow:
359
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
360
+ 0 2px 4px -2px rgba(0, 0, 0, 0.1),
361
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
362
+ padding: 0;
363
+ background: #ffffff;
364
+ overflow: hidden;
365
+ }
366
+
367
+ /* Content area with scroll support */
368
+ .zenit-leaflet-popup .leaflet-popup-content {
369
+ margin: 0;
370
+ padding: 0;
371
+ font-size: 13px;
372
+ line-height: 1.5;
373
+ color: #374151;
374
+ min-width: 100%;
375
+ overflow-y: auto;
376
+ overflow-x: hidden;
377
+ scrollbar-width: thin;
378
+ scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
379
+ }
380
+
381
+ /* Popup tip/arrow shadow */
382
+ .zenit-leaflet-popup .leaflet-popup-tip-container {
383
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
384
+ }
385
+
386
+ .zenit-leaflet-popup .leaflet-popup-tip {
387
+ background: #ffffff;
388
+ box-shadow: none;
389
+ }
390
+
391
+ /* Close button styling */
392
+ .zenit-leaflet-popup .leaflet-popup-close-button {
393
+ color: #9ca3af;
394
+ font-size: 18px;
395
+ font-weight: 400;
396
+ width: 28px;
397
+ height: 28px;
398
+ padding: 0;
399
+ margin: 8px 8px 0 0;
400
+ display: flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ border-radius: 6px;
404
+ transition: all 0.15s ease;
405
+ z-index: 10;
406
+ }
407
+
408
+ .zenit-leaflet-popup .leaflet-popup-close-button:hover {
409
+ color: #374151;
410
+ background-color: #f3f4f6;
411
+ }
412
+
413
+ .zenit-leaflet-popup .leaflet-popup-close-button:active {
414
+ background-color: #e5e7eb;
415
+ }
416
+
417
+ /* Main card container */
418
+ .zenit-popup-card {
419
+ display: flex;
420
+ flex-direction: column;
421
+ gap: 0;
422
+ padding: 16px;
423
+ }
424
+
425
+ /* Individual row styling with subtle separator */
426
+ .zenit-popup-row {
427
+ display: flex;
428
+ flex-direction: column;
429
+ gap: 2px;
430
+ padding: 10px 0;
431
+ border-bottom: 1px solid #f3f4f6;
432
+ }
433
+
434
+ .zenit-popup-row:first-child {
435
+ padding-top: 0;
436
+ }
437
+
438
+ .zenit-popup-row:last-child {
439
+ border-bottom: none;
440
+ padding-bottom: 0;
441
+ }
442
+
443
+ /* Label styling - small, gray, uppercase */
444
+ .zenit-popup-label {
445
+ font-size: 10px;
446
+ font-weight: 500;
447
+ color: #9ca3af;
448
+ text-transform: uppercase;
449
+ letter-spacing: 0.05em;
450
+ line-height: 1.4;
451
+ }
452
+
453
+ /* Value styling - darker, readable */
454
+ .zenit-popup-value {
455
+ font-size: 13px;
456
+ font-weight: 400;
457
+ color: #1f2937;
458
+ overflow-wrap: break-word;
459
+ word-break: break-word;
460
+ line-height: 1.5;
461
+ }
462
+
463
+ /* Special styling for description field */
464
+ .zenit-popup-row.zenit-popup-description {
465
+ background-color: #f9fafb;
466
+ margin: 0 -16px;
467
+ padding: 12px 16px;
468
+ border-bottom: 1px solid #e5e7eb;
469
+ }
470
+
471
+ .zenit-popup-row.zenit-popup-description:first-child {
472
+ margin-top: 0;
473
+ border-radius: 0;
474
+ }
475
+
476
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
477
+ font-size: 13px;
478
+ line-height: 1.6;
479
+ color: #374151;
480
+ max-height: 150px;
481
+ overflow-y: auto;
482
+ padding-right: 4px;
483
+ }
484
+
485
+ /* Preformatted text (JSON objects) */
486
+ .zenit-popup-pre {
487
+ margin: 0;
488
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
489
+ font-size: 11px;
490
+ white-space: pre-wrap;
491
+ word-break: break-word;
492
+ color: #4b5563;
493
+ background-color: #f9fafb;
494
+ padding: 8px;
495
+ border-radius: 6px;
496
+ border: 1px solid #e5e7eb;
497
+ }
498
+
499
+ /* Empty state styling */
500
+ .zenit-popup-empty {
501
+ font-size: 13px;
502
+ color: #9ca3af;
503
+ font-style: italic;
504
+ text-align: center;
505
+ padding: 20px 0;
506
+ }
507
+
508
+ /* Webkit scrollbar styling */
509
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
510
+ width: 6px;
511
+ }
512
+
513
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
514
+ background: transparent;
515
+ }
516
+
517
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
518
+ background-color: rgba(156, 163, 175, 0.4);
519
+ border-radius: 3px;
520
+ }
521
+
522
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
523
+ background-color: rgba(107, 114, 128, 0.6);
524
+ }
525
+
526
+ /* Scrollbar for description field */
527
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
528
+ width: 4px;
529
+ }
530
+
531
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
532
+ background: transparent;
533
+ }
534
+
535
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
536
+ background-color: rgba(156, 163, 175, 0.4);
537
+ border-radius: 2px;
538
+ }
539
+
540
+ /* ===== Responsive: Mobile (<640px) ===== */
541
+ @media (max-width: 640px) {
542
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
543
+ border-radius: 10px;
544
+ }
545
+
546
+ .zenit-leaflet-popup .leaflet-popup-close-button {
547
+ width: 26px;
548
+ height: 26px;
549
+ font-size: 16px;
550
+ margin: 6px 6px 0 0;
551
+ }
552
+
553
+ .zenit-popup-card {
554
+ padding: 12px;
555
+ }
556
+
557
+ .zenit-popup-row {
558
+ padding: 8px 0;
559
+ }
560
+
561
+ .zenit-popup-label {
562
+ font-size: 9px;
563
+ }
564
+
565
+ .zenit-popup-value {
566
+ font-size: 12px;
567
+ }
568
+
569
+ .zenit-popup-row.zenit-popup-description {
570
+ margin: 0 -12px;
571
+ padding: 10px 12px;
572
+ }
573
+
574
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
575
+ font-size: 12px;
576
+ max-height: 120px;
577
+ }
578
+
579
+ .zenit-popup-pre {
580
+ font-size: 10px;
581
+ padding: 6px;
582
+ }
583
+
584
+ .zenit-popup-empty {
585
+ font-size: 12px;
586
+ padding: 16px 0;
587
+ }
588
+ }
589
+
590
+ /* ===== Map tooltip styling ===== */
591
+ .zenit-map-tooltip {
592
+ background-color: rgba(31, 41, 55, 0.95);
593
+ border: none;
594
+ border-radius: 6px;
595
+ color: #ffffff;
596
+ font-size: 12px;
597
+ font-weight: 500;
598
+ padding: 6px 10px;
599
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
600
+ }
601
+
602
+ .zenit-map-tooltip::before {
603
+ border-top-color: rgba(31, 41, 55, 0.95);
604
+ }
605
+ `;
606
+ function ensurePopupStyles() {
607
+ if (typeof document === "undefined") return;
608
+ if (document.getElementById(POPUP_STYLE_ID)) return;
609
+ const styleTag = document.createElement("style");
610
+ styleTag.id = POPUP_STYLE_ID;
611
+ styleTag.textContent = ZENIT_LEAFLET_POPUP_STYLES;
612
+ document.head.appendChild(styleTag);
613
+ }
614
+ function getPopupDimensions() {
615
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
616
+ return DESKTOP_POPUP_DIMENSIONS;
617
+ }
618
+ return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
619
+ }
348
620
  function normalizeDescriptionValue(value) {
349
621
  if (value === void 0 || value === null) return null;
350
622
  if (typeof value === "string") {
@@ -372,92 +644,65 @@ function safeJsonStringify(value) {
372
644
  }
373
645
  return String(value);
374
646
  }
375
- function renderPropertyValue(value) {
647
+ function renderPopupValue(value) {
376
648
  if (value === null || value === void 0) {
377
- return '<span class="prop-empty">\u2014</span>';
649
+ return '<span class="zenit-popup-empty">Sin datos</span>';
378
650
  }
379
651
  if (typeof value === "object") {
380
652
  const json = safeJsonStringify(value);
381
- return `<pre class="prop-json">${escapeHtml(json)}</pre>`;
382
- }
383
- return `<span class="prop-text">${escapeHtml(String(value))}</span>`;
384
- }
385
- var POPUP_TITLE_KEYS = ["name", "title", "nombre", "label", "id"];
386
- var POPUP_CHIP_KEYS = /* @__PURE__ */ new Set(["churn", "color", "sector"]);
387
- function isChipValue(value) {
388
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
389
- }
390
- function getSanitizedChipColor(value) {
391
- if (typeof value !== "string") return null;
392
- const trimmed = value.trim();
393
- if (/^#([0-9a-fA-F]{3}){1,2}$/.test(trimmed)) {
394
- return trimmed;
653
+ return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
395
654
  }
396
- if (/^rgb\((\s*\d+\s*,){2}\s*\d+\s*\)$/.test(trimmed)) {
397
- return trimmed;
398
- }
399
- if (/^rgba\((\s*\d+\s*,){3}\s*(0|1|0?\.\d+)\s*\)$/.test(trimmed)) {
400
- return trimmed;
401
- }
402
- return null;
655
+ return `<span>${escapeHtml(String(value))}</span>`;
403
656
  }
404
- function getPopupTitle(entries) {
405
- for (const candidate of POPUP_TITLE_KEYS) {
406
- const match = entries.find((entry) => entry.normalized === candidate);
407
- if (!match || match.value === null || match.value === void 0) continue;
408
- const value = String(match.value).trim();
409
- if (!value) continue;
410
- return { title: value, key: match.key };
411
- }
412
- return null;
657
+ function shouldIncludePopupEntry(key, value) {
658
+ if (!key) return false;
659
+ const normalized = key.trim().toLowerCase();
660
+ if (!normalized) return false;
661
+ if (normalized.startsWith("_")) return false;
662
+ if (POPUP_EXCLUDED_KEYS.has(normalized)) return false;
663
+ if (value === null || value === void 0) return false;
664
+ if (typeof value === "string" && !value.trim()) return false;
665
+ return true;
413
666
  }
414
- function renderProperties(properties) {
415
- const description = extractDescriptionValue(properties);
416
- const entries = Object.entries(properties).filter(([key]) => !DESCRIPTION_KEYS.has(key.toLowerCase())).map(([key, value]) => ({
417
- key,
418
- value,
419
- normalized: key.trim().toLowerCase()
420
- }));
421
- if (!description && entries.length === 0) return "";
422
- const titleEntry = getPopupTitle(entries);
423
- const titleText = titleEntry?.title ?? "Detalle del elemento";
424
- const chipEntries = entries.filter(
425
- (entry) => POPUP_CHIP_KEYS.has(entry.normalized) && isChipValue(entry.value)
667
+ function isDescriptionKey(key) {
668
+ const normalized = key.trim().toLowerCase();
669
+ return DESCRIPTION_KEYS.has(normalized);
670
+ }
671
+ function formatLabel(key) {
672
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
673
+ }
674
+ function createPopupContent(properties) {
675
+ const entries = Object.entries(properties).filter(
676
+ ([key, value]) => shouldIncludePopupEntry(key, value)
426
677
  );
427
- const listEntries = entries.filter((entry) => {
428
- if (titleEntry && entry.key === titleEntry.key) return false;
429
- if (chipEntries.find((chip) => chip.key === entry.key)) return false;
430
- return true;
431
- });
432
- const descriptionHtml = description ? `<div class="popup-description">${escapeHtml(description)}</div>` : "";
433
- const chipsHtml = chipEntries.length ? `<div class="popup-chip-row">${chipEntries.map((entry) => {
434
- const label = escapeHtml(entry.key.replace(/_/g, " "));
435
- const value = escapeHtml(String(entry.value));
436
- const color = getSanitizedChipColor(entry.value);
437
- const colorStyle = color ? ` style="--chip-color: ${color}"` : "";
438
- return `<span class="popup-chip"${colorStyle}><span class="popup-chip-label">${label}</span><span class="popup-chip-value">${value}</span></span>`;
439
- }).join("")}</div>` : "";
440
- const rowsHtml = listEntries.map((entry) => {
441
- const label = escapeHtml(entry.key.replace(/_/g, " "));
442
- const valueHtml = renderPropertyValue(entry.value);
443
- return `<div class="prop-key">${label}</div><div class="prop-value">${valueHtml}</div>`;
444
- }).join("");
445
- const listHtml = rowsHtml ? `<div class="prop-list">${rowsHtml}</div>` : "";
446
- return `
447
- <div class="feature-popup">
448
- <div class="feature-popup-card">
449
- <div class="feature-popup-header">
450
- <p class="popup-eyebrow">Informaci\xF3n</p>
451
- <h3 class="popup-title">${escapeHtml(titleText)}</h3>
452
- </div>
453
- <div class="feature-popup-body">
454
- ${descriptionHtml}
455
- ${chipsHtml}
456
- ${listHtml}
457
- </div>
678
+ if (entries.length === 0) {
679
+ return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
680
+ }
681
+ const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
682
+ const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
683
+ let rowsHtml = "";
684
+ if (descriptionEntry) {
685
+ const [key, value] = descriptionEntry;
686
+ const label = escapeHtml(formatLabel(key));
687
+ const valueHtml = renderPopupValue(value);
688
+ rowsHtml += `
689
+ <div class="zenit-popup-row zenit-popup-description">
690
+ <div class="zenit-popup-label">${label}</div>
691
+ <div class="zenit-popup-value">${valueHtml}</div>
458
692
  </div>
459
- </div>
460
- `;
693
+ `;
694
+ }
695
+ rowsHtml += otherEntries.map(([key, value]) => {
696
+ const label = escapeHtml(formatLabel(key));
697
+ const valueHtml = renderPopupValue(value);
698
+ return `
699
+ <div class="zenit-popup-row">
700
+ <div class="zenit-popup-label">${label}</div>
701
+ <div class="zenit-popup-value">${valueHtml}</div>
702
+ </div>
703
+ `;
704
+ }).join("");
705
+ return `<div class="zenit-popup-card">${rowsHtml}</div>`;
461
706
  }
462
707
  function withAlpha(color, alpha) {
463
708
  const trimmed = color.trim();
@@ -527,7 +772,7 @@ function getFeatureStyleOverrides(feature) {
527
772
  function buildFeaturePopupHtml(feature) {
528
773
  const properties = feature?.properties;
529
774
  if (!properties) return null;
530
- const rendered = renderProperties(properties);
775
+ const rendered = createPopupContent(properties);
531
776
  return rendered ? rendered : null;
532
777
  }
533
778
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
@@ -705,6 +950,11 @@ var ZenitMap = (0, import_react.forwardRef)(({
705
950
  }
706
951
  return;
707
952
  }, []);
953
+ (0, import_react.useEffect)(() => {
954
+ if (featureInfoMode === "popup") {
955
+ ensurePopupStyles();
956
+ }
957
+ }, [featureInfoMode]);
708
958
  const layerStyleIndex = (0, import_react.useMemo)(() => {
709
959
  const index = /* @__PURE__ */ new Map();
710
960
  (map?.mapLayers ?? []).forEach((entry) => {
@@ -1052,12 +1302,15 @@ var ZenitMap = (0, import_react.forwardRef)(({
1052
1302
  if (featureInfoMode === "popup") {
1053
1303
  const content = buildFeaturePopupHtml(feature);
1054
1304
  if (content) {
1305
+ const { maxWidth, minWidth, maxHeight } = getPopupDimensions();
1055
1306
  layer.bindPopup(content, {
1056
- maxWidth: 420,
1057
- minWidth: 280,
1058
- className: "zenit-feature-popup-shell",
1307
+ maxWidth,
1308
+ minWidth,
1309
+ maxHeight,
1310
+ className: "zenit-leaflet-popup",
1059
1311
  autoPan: true,
1060
1312
  closeButton: true,
1313
+ keepInView: true,
1061
1314
  offset: import_leaflet.default.point(0, -24)
1062
1315
  });
1063
1316
  }