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.
@@ -298,6 +298,278 @@ function escapeHtml(value) {
298
298
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
299
299
  }
300
300
  var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
301
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
302
+ var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
303
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
304
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
305
+ var ZENIT_LEAFLET_POPUP_STYLES = `
306
+ /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
307
+
308
+ /* Main popup wrapper */
309
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
310
+ border-radius: 12px;
311
+ box-shadow:
312
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
313
+ 0 2px 4px -2px rgba(0, 0, 0, 0.1),
314
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
315
+ padding: 0;
316
+ background: #ffffff;
317
+ overflow: hidden;
318
+ }
319
+
320
+ /* Content area with scroll support */
321
+ .zenit-leaflet-popup .leaflet-popup-content {
322
+ margin: 0;
323
+ padding: 0;
324
+ font-size: 13px;
325
+ line-height: 1.5;
326
+ color: #374151;
327
+ min-width: 100%;
328
+ overflow-y: auto;
329
+ overflow-x: hidden;
330
+ scrollbar-width: thin;
331
+ scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
332
+ }
333
+
334
+ /* Popup tip/arrow shadow */
335
+ .zenit-leaflet-popup .leaflet-popup-tip-container {
336
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
337
+ }
338
+
339
+ .zenit-leaflet-popup .leaflet-popup-tip {
340
+ background: #ffffff;
341
+ box-shadow: none;
342
+ }
343
+
344
+ /* Close button styling */
345
+ .zenit-leaflet-popup .leaflet-popup-close-button {
346
+ color: #9ca3af;
347
+ font-size: 18px;
348
+ font-weight: 400;
349
+ width: 28px;
350
+ height: 28px;
351
+ padding: 0;
352
+ margin: 8px 8px 0 0;
353
+ display: flex;
354
+ align-items: center;
355
+ justify-content: center;
356
+ border-radius: 6px;
357
+ transition: all 0.15s ease;
358
+ z-index: 10;
359
+ }
360
+
361
+ .zenit-leaflet-popup .leaflet-popup-close-button:hover {
362
+ color: #374151;
363
+ background-color: #f3f4f6;
364
+ }
365
+
366
+ .zenit-leaflet-popup .leaflet-popup-close-button:active {
367
+ background-color: #e5e7eb;
368
+ }
369
+
370
+ /* Main card container */
371
+ .zenit-popup-card {
372
+ display: flex;
373
+ flex-direction: column;
374
+ gap: 0;
375
+ padding: 16px;
376
+ }
377
+
378
+ /* Individual row styling with subtle separator */
379
+ .zenit-popup-row {
380
+ display: flex;
381
+ flex-direction: column;
382
+ gap: 2px;
383
+ padding: 10px 0;
384
+ border-bottom: 1px solid #f3f4f6;
385
+ }
386
+
387
+ .zenit-popup-row:first-child {
388
+ padding-top: 0;
389
+ }
390
+
391
+ .zenit-popup-row:last-child {
392
+ border-bottom: none;
393
+ padding-bottom: 0;
394
+ }
395
+
396
+ /* Label styling - small, gray, uppercase */
397
+ .zenit-popup-label {
398
+ font-size: 10px;
399
+ font-weight: 500;
400
+ color: #9ca3af;
401
+ text-transform: uppercase;
402
+ letter-spacing: 0.05em;
403
+ line-height: 1.4;
404
+ }
405
+
406
+ /* Value styling - darker, readable */
407
+ .zenit-popup-value {
408
+ font-size: 13px;
409
+ font-weight: 400;
410
+ color: #1f2937;
411
+ overflow-wrap: break-word;
412
+ word-break: break-word;
413
+ line-height: 1.5;
414
+ }
415
+
416
+ /* Special styling for description field */
417
+ .zenit-popup-row.zenit-popup-description {
418
+ background-color: #f9fafb;
419
+ margin: 0 -16px;
420
+ padding: 12px 16px;
421
+ border-bottom: 1px solid #e5e7eb;
422
+ }
423
+
424
+ .zenit-popup-row.zenit-popup-description:first-child {
425
+ margin-top: 0;
426
+ border-radius: 0;
427
+ }
428
+
429
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
430
+ font-size: 13px;
431
+ line-height: 1.6;
432
+ color: #374151;
433
+ max-height: 150px;
434
+ overflow-y: auto;
435
+ padding-right: 4px;
436
+ }
437
+
438
+ /* Preformatted text (JSON objects) */
439
+ .zenit-popup-pre {
440
+ margin: 0;
441
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
442
+ font-size: 11px;
443
+ white-space: pre-wrap;
444
+ word-break: break-word;
445
+ color: #4b5563;
446
+ background-color: #f9fafb;
447
+ padding: 8px;
448
+ border-radius: 6px;
449
+ border: 1px solid #e5e7eb;
450
+ }
451
+
452
+ /* Empty state styling */
453
+ .zenit-popup-empty {
454
+ font-size: 13px;
455
+ color: #9ca3af;
456
+ font-style: italic;
457
+ text-align: center;
458
+ padding: 20px 0;
459
+ }
460
+
461
+ /* Webkit scrollbar styling */
462
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
463
+ width: 6px;
464
+ }
465
+
466
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
467
+ background: transparent;
468
+ }
469
+
470
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
471
+ background-color: rgba(156, 163, 175, 0.4);
472
+ border-radius: 3px;
473
+ }
474
+
475
+ .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
476
+ background-color: rgba(107, 114, 128, 0.6);
477
+ }
478
+
479
+ /* Scrollbar for description field */
480
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
481
+ width: 4px;
482
+ }
483
+
484
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
485
+ background: transparent;
486
+ }
487
+
488
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
489
+ background-color: rgba(156, 163, 175, 0.4);
490
+ border-radius: 2px;
491
+ }
492
+
493
+ /* ===== Responsive: Mobile (<640px) ===== */
494
+ @media (max-width: 640px) {
495
+ .zenit-leaflet-popup .leaflet-popup-content-wrapper {
496
+ border-radius: 10px;
497
+ }
498
+
499
+ .zenit-leaflet-popup .leaflet-popup-close-button {
500
+ width: 26px;
501
+ height: 26px;
502
+ font-size: 16px;
503
+ margin: 6px 6px 0 0;
504
+ }
505
+
506
+ .zenit-popup-card {
507
+ padding: 12px;
508
+ }
509
+
510
+ .zenit-popup-row {
511
+ padding: 8px 0;
512
+ }
513
+
514
+ .zenit-popup-label {
515
+ font-size: 9px;
516
+ }
517
+
518
+ .zenit-popup-value {
519
+ font-size: 12px;
520
+ }
521
+
522
+ .zenit-popup-row.zenit-popup-description {
523
+ margin: 0 -12px;
524
+ padding: 10px 12px;
525
+ }
526
+
527
+ .zenit-popup-row.zenit-popup-description .zenit-popup-value {
528
+ font-size: 12px;
529
+ max-height: 120px;
530
+ }
531
+
532
+ .zenit-popup-pre {
533
+ font-size: 10px;
534
+ padding: 6px;
535
+ }
536
+
537
+ .zenit-popup-empty {
538
+ font-size: 12px;
539
+ padding: 16px 0;
540
+ }
541
+ }
542
+
543
+ /* ===== Map tooltip styling ===== */
544
+ .zenit-map-tooltip {
545
+ background-color: rgba(31, 41, 55, 0.95);
546
+ border: none;
547
+ border-radius: 6px;
548
+ color: #ffffff;
549
+ font-size: 12px;
550
+ font-weight: 500;
551
+ padding: 6px 10px;
552
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
553
+ }
554
+
555
+ .zenit-map-tooltip::before {
556
+ border-top-color: rgba(31, 41, 55, 0.95);
557
+ }
558
+ `;
559
+ function ensurePopupStyles() {
560
+ if (typeof document === "undefined") return;
561
+ if (document.getElementById(POPUP_STYLE_ID)) return;
562
+ const styleTag = document.createElement("style");
563
+ styleTag.id = POPUP_STYLE_ID;
564
+ styleTag.textContent = ZENIT_LEAFLET_POPUP_STYLES;
565
+ document.head.appendChild(styleTag);
566
+ }
567
+ function getPopupDimensions() {
568
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
569
+ return DESKTOP_POPUP_DIMENSIONS;
570
+ }
571
+ return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
572
+ }
301
573
  function normalizeDescriptionValue(value) {
302
574
  if (value === void 0 || value === null) return null;
303
575
  if (typeof value === "string") {
@@ -325,92 +597,65 @@ function safeJsonStringify(value) {
325
597
  }
326
598
  return String(value);
327
599
  }
328
- function renderPropertyValue(value) {
600
+ function renderPopupValue(value) {
329
601
  if (value === null || value === void 0) {
330
- return '<span class="prop-empty">\u2014</span>';
602
+ return '<span class="zenit-popup-empty">Sin datos</span>';
331
603
  }
332
604
  if (typeof value === "object") {
333
605
  const json = safeJsonStringify(value);
334
- return `<pre class="prop-json">${escapeHtml(json)}</pre>`;
335
- }
336
- return `<span class="prop-text">${escapeHtml(String(value))}</span>`;
337
- }
338
- var POPUP_TITLE_KEYS = ["name", "title", "nombre", "label", "id"];
339
- var POPUP_CHIP_KEYS = /* @__PURE__ */ new Set(["churn", "color", "sector"]);
340
- function isChipValue(value) {
341
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
342
- }
343
- function getSanitizedChipColor(value) {
344
- if (typeof value !== "string") return null;
345
- const trimmed = value.trim();
346
- if (/^#([0-9a-fA-F]{3}){1,2}$/.test(trimmed)) {
347
- return trimmed;
606
+ return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
348
607
  }
349
- if (/^rgb\((\s*\d+\s*,){2}\s*\d+\s*\)$/.test(trimmed)) {
350
- return trimmed;
351
- }
352
- if (/^rgba\((\s*\d+\s*,){3}\s*(0|1|0?\.\d+)\s*\)$/.test(trimmed)) {
353
- return trimmed;
354
- }
355
- return null;
608
+ return `<span>${escapeHtml(String(value))}</span>`;
356
609
  }
357
- function getPopupTitle(entries) {
358
- for (const candidate of POPUP_TITLE_KEYS) {
359
- const match = entries.find((entry) => entry.normalized === candidate);
360
- if (!match || match.value === null || match.value === void 0) continue;
361
- const value = String(match.value).trim();
362
- if (!value) continue;
363
- return { title: value, key: match.key };
364
- }
365
- return null;
610
+ function shouldIncludePopupEntry(key, value) {
611
+ if (!key) return false;
612
+ const normalized = key.trim().toLowerCase();
613
+ if (!normalized) return false;
614
+ if (normalized.startsWith("_")) return false;
615
+ if (POPUP_EXCLUDED_KEYS.has(normalized)) return false;
616
+ if (value === null || value === void 0) return false;
617
+ if (typeof value === "string" && !value.trim()) return false;
618
+ return true;
366
619
  }
367
- function renderProperties(properties) {
368
- const description = extractDescriptionValue(properties);
369
- const entries = Object.entries(properties).filter(([key]) => !DESCRIPTION_KEYS.has(key.toLowerCase())).map(([key, value]) => ({
370
- key,
371
- value,
372
- normalized: key.trim().toLowerCase()
373
- }));
374
- if (!description && entries.length === 0) return "";
375
- const titleEntry = getPopupTitle(entries);
376
- const titleText = titleEntry?.title ?? "Detalle del elemento";
377
- const chipEntries = entries.filter(
378
- (entry) => POPUP_CHIP_KEYS.has(entry.normalized) && isChipValue(entry.value)
620
+ function isDescriptionKey(key) {
621
+ const normalized = key.trim().toLowerCase();
622
+ return DESCRIPTION_KEYS.has(normalized);
623
+ }
624
+ function formatLabel(key) {
625
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
626
+ }
627
+ function createPopupContent(properties) {
628
+ const entries = Object.entries(properties).filter(
629
+ ([key, value]) => shouldIncludePopupEntry(key, value)
379
630
  );
380
- const listEntries = entries.filter((entry) => {
381
- if (titleEntry && entry.key === titleEntry.key) return false;
382
- if (chipEntries.find((chip) => chip.key === entry.key)) return false;
383
- return true;
384
- });
385
- const descriptionHtml = description ? `<div class="popup-description">${escapeHtml(description)}</div>` : "";
386
- const chipsHtml = chipEntries.length ? `<div class="popup-chip-row">${chipEntries.map((entry) => {
387
- const label = escapeHtml(entry.key.replace(/_/g, " "));
388
- const value = escapeHtml(String(entry.value));
389
- const color = getSanitizedChipColor(entry.value);
390
- const colorStyle = color ? ` style="--chip-color: ${color}"` : "";
391
- return `<span class="popup-chip"${colorStyle}><span class="popup-chip-label">${label}</span><span class="popup-chip-value">${value}</span></span>`;
392
- }).join("")}</div>` : "";
393
- const rowsHtml = listEntries.map((entry) => {
394
- const label = escapeHtml(entry.key.replace(/_/g, " "));
395
- const valueHtml = renderPropertyValue(entry.value);
396
- return `<div class="prop-key">${label}</div><div class="prop-value">${valueHtml}</div>`;
397
- }).join("");
398
- const listHtml = rowsHtml ? `<div class="prop-list">${rowsHtml}</div>` : "";
399
- return `
400
- <div class="feature-popup">
401
- <div class="feature-popup-card">
402
- <div class="feature-popup-header">
403
- <p class="popup-eyebrow">Informaci\xF3n</p>
404
- <h3 class="popup-title">${escapeHtml(titleText)}</h3>
405
- </div>
406
- <div class="feature-popup-body">
407
- ${descriptionHtml}
408
- ${chipsHtml}
409
- ${listHtml}
410
- </div>
631
+ if (entries.length === 0) {
632
+ return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
633
+ }
634
+ const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
635
+ const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
636
+ let rowsHtml = "";
637
+ if (descriptionEntry) {
638
+ const [key, value] = descriptionEntry;
639
+ const label = escapeHtml(formatLabel(key));
640
+ const valueHtml = renderPopupValue(value);
641
+ rowsHtml += `
642
+ <div class="zenit-popup-row zenit-popup-description">
643
+ <div class="zenit-popup-label">${label}</div>
644
+ <div class="zenit-popup-value">${valueHtml}</div>
411
645
  </div>
412
- </div>
413
- `;
646
+ `;
647
+ }
648
+ rowsHtml += otherEntries.map(([key, value]) => {
649
+ const label = escapeHtml(formatLabel(key));
650
+ const valueHtml = renderPopupValue(value);
651
+ return `
652
+ <div class="zenit-popup-row">
653
+ <div class="zenit-popup-label">${label}</div>
654
+ <div class="zenit-popup-value">${valueHtml}</div>
655
+ </div>
656
+ `;
657
+ }).join("");
658
+ return `<div class="zenit-popup-card">${rowsHtml}</div>`;
414
659
  }
415
660
  function withAlpha(color, alpha) {
416
661
  const trimmed = color.trim();
@@ -480,7 +725,7 @@ function getFeatureStyleOverrides(feature) {
480
725
  function buildFeaturePopupHtml(feature) {
481
726
  const properties = feature?.properties;
482
727
  if (!properties) return null;
483
- const rendered = renderProperties(properties);
728
+ const rendered = createPopupContent(properties);
484
729
  return rendered ? rendered : null;
485
730
  }
486
731
  var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
@@ -658,6 +903,11 @@ var ZenitMap = forwardRef(({
658
903
  }
659
904
  return;
660
905
  }, []);
906
+ useEffect(() => {
907
+ if (featureInfoMode === "popup") {
908
+ ensurePopupStyles();
909
+ }
910
+ }, [featureInfoMode]);
661
911
  const layerStyleIndex = useMemo(() => {
662
912
  const index = /* @__PURE__ */ new Map();
663
913
  (map?.mapLayers ?? []).forEach((entry) => {
@@ -1005,12 +1255,15 @@ var ZenitMap = forwardRef(({
1005
1255
  if (featureInfoMode === "popup") {
1006
1256
  const content = buildFeaturePopupHtml(feature);
1007
1257
  if (content) {
1258
+ const { maxWidth, minWidth, maxHeight } = getPopupDimensions();
1008
1259
  layer.bindPopup(content, {
1009
- maxWidth: 420,
1010
- minWidth: 280,
1011
- className: "zenit-feature-popup-shell",
1260
+ maxWidth,
1261
+ minWidth,
1262
+ maxHeight,
1263
+ className: "zenit-leaflet-popup",
1012
1264
  autoPan: true,
1013
1265
  closeButton: true,
1266
+ keepInView: true,
1014
1267
  offset: L.point(0, -24)
1015
1268
  });
1016
1269
  }
@@ -3255,4 +3508,4 @@ export {
3255
3508
  useSendMessageStream,
3256
3509
  FloatingChatBox
3257
3510
  };
3258
- //# sourceMappingURL=chunk-LX2N2BXV.mjs.map
3511
+ //# sourceMappingURL=chunk-ITF7QCUZ.mjs.map