zenit-sdk 0.0.7 → 0.0.9
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 +32 -0
- package/dist/{chunk-ITF7QCUZ.mjs → chunk-PCTRVN4O.mjs} +1140 -926
- package/dist/chunk-PCTRVN4O.mjs.map +1 -0
- package/dist/{index-kGwfqTc_.d.mts → index-DvcYGhqj.d.mts} +70 -4
- package/dist/{index-kGwfqTc_.d.ts → index-DvcYGhqj.d.ts} +70 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1272 -937
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1154 -940
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1 -1
- package/package.json +10 -7
- package/dist/chunk-ITF7QCUZ.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/react/ZenitMap.tsx
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import React3, { useCallback as useCallback2, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useState as useState2, forwardRef } from "react";
|
|
3
|
+
import { MapContainer, Marker as Marker2, TileLayer, ZoomControl } from "react-leaflet";
|
|
4
|
+
import L4 from "leaflet";
|
|
5
5
|
|
|
6
6
|
// src/maps/helpers.ts
|
|
7
7
|
function toNumber(value) {
|
|
@@ -206,9 +206,9 @@ function getAccentByLayerId(layerId, mapLayers) {
|
|
|
206
206
|
|
|
207
207
|
// src/react/zoomOpacity.ts
|
|
208
208
|
var DEFAULT_OPTIONS = {
|
|
209
|
-
minZoom:
|
|
210
|
-
maxZoom:
|
|
211
|
-
minFactor: 0.
|
|
209
|
+
minZoom: 11,
|
|
210
|
+
maxZoom: 15,
|
|
211
|
+
minFactor: 0.3,
|
|
212
212
|
maxFactor: 1,
|
|
213
213
|
minOpacity: 0.1,
|
|
214
214
|
maxOpacity: 0.92
|
|
@@ -245,302 +245,215 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
|
|
|
245
245
|
return clampNumber(effective, settings.minOpacity, settings.maxOpacity);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
// src/react/
|
|
249
|
-
import {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const collect = (candidate) => {
|
|
257
|
-
if (!Array.isArray(candidate)) return;
|
|
258
|
-
if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number") {
|
|
259
|
-
coords.push([candidate[0], candidate[1]]);
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
candidate.forEach((entry) => collect(entry));
|
|
263
|
-
};
|
|
264
|
-
geojson.features.forEach((feature) => {
|
|
265
|
-
collect(feature.geometry?.coordinates);
|
|
266
|
-
});
|
|
267
|
-
if (coords.length === 0) return null;
|
|
268
|
-
const [firstLon, firstLat] = coords[0];
|
|
269
|
-
const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
|
|
270
|
-
coords.forEach(([lon, lat]) => {
|
|
271
|
-
bbox.minLon = Math.min(bbox.minLon, lon);
|
|
272
|
-
bbox.minLat = Math.min(bbox.minLat, lat);
|
|
273
|
-
bbox.maxLon = Math.max(bbox.maxLon, lon);
|
|
274
|
-
bbox.maxLat = Math.max(bbox.maxLat, lat);
|
|
275
|
-
});
|
|
276
|
-
return bbox;
|
|
248
|
+
// src/react/map/layer-geojson.tsx
|
|
249
|
+
import { GeoJSON } from "react-leaflet";
|
|
250
|
+
import L from "leaflet";
|
|
251
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
252
|
+
var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
|
|
253
|
+
function getGeometryType(feature) {
|
|
254
|
+
const t = feature?.geometry?.type;
|
|
255
|
+
return typeof t === "string" ? t : null;
|
|
277
256
|
}
|
|
278
|
-
function
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
const first = valid[0];
|
|
282
|
-
return valid.slice(1).reduce(
|
|
283
|
-
(acc, bbox) => ({
|
|
284
|
-
minLon: Math.min(acc.minLon, bbox.minLon),
|
|
285
|
-
minLat: Math.min(acc.minLat, bbox.minLat),
|
|
286
|
-
maxLon: Math.max(acc.maxLon, bbox.maxLon),
|
|
287
|
-
maxLat: Math.max(acc.maxLat, bbox.maxLat)
|
|
288
|
-
}),
|
|
289
|
-
{ ...first }
|
|
290
|
-
);
|
|
257
|
+
function isPointGeometry(feature) {
|
|
258
|
+
const geometryType = getGeometryType(feature);
|
|
259
|
+
return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
|
|
291
260
|
}
|
|
292
|
-
function
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
return layerId;
|
|
261
|
+
function isNonPointGeometry(feature) {
|
|
262
|
+
const geometryType = getGeometryType(feature);
|
|
263
|
+
return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
|
|
296
264
|
}
|
|
297
|
-
function
|
|
298
|
-
return
|
|
265
|
+
function buildFeatureCollection(features) {
|
|
266
|
+
return {
|
|
267
|
+
type: "FeatureCollection",
|
|
268
|
+
features
|
|
269
|
+
};
|
|
299
270
|
}
|
|
300
|
-
var
|
|
301
|
-
|
|
271
|
+
var LayerGeoJson = ({
|
|
272
|
+
layerId,
|
|
273
|
+
data,
|
|
274
|
+
baseOpacity,
|
|
275
|
+
isMobile,
|
|
276
|
+
panesReady,
|
|
277
|
+
mapInstance,
|
|
278
|
+
fillPaneName,
|
|
279
|
+
pointsPaneName,
|
|
280
|
+
layerType,
|
|
281
|
+
styleFn,
|
|
282
|
+
onEachFeature,
|
|
283
|
+
onPolygonLabel
|
|
284
|
+
}) => {
|
|
285
|
+
const features = data.features ?? [];
|
|
286
|
+
const fillFeatures = features.filter(isNonPointGeometry);
|
|
287
|
+
const pointFeatures = features.filter(isPointGeometry);
|
|
288
|
+
const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
|
|
289
|
+
const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
|
|
290
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
291
|
+
fillData && /* @__PURE__ */ jsx(
|
|
292
|
+
GeoJSON,
|
|
293
|
+
{
|
|
294
|
+
data: fillData,
|
|
295
|
+
pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
|
|
296
|
+
style: (feature) => styleFn(feature, layerType, baseOpacity),
|
|
297
|
+
onEachFeature: (feature, layer) => {
|
|
298
|
+
onEachFeature(feature, layer);
|
|
299
|
+
onPolygonLabel?.(feature, layer);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
`fill-${layerId}`
|
|
303
|
+
),
|
|
304
|
+
pointsData && /* @__PURE__ */ jsx(
|
|
305
|
+
GeoJSON,
|
|
306
|
+
{
|
|
307
|
+
data: pointsData,
|
|
308
|
+
pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
|
|
309
|
+
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
|
310
|
+
radius: isMobile ? 8 : 6,
|
|
311
|
+
...styleFn(feature, layerType, baseOpacity)
|
|
312
|
+
}),
|
|
313
|
+
onEachFeature
|
|
314
|
+
},
|
|
315
|
+
`points-${layerId}`
|
|
316
|
+
)
|
|
317
|
+
] });
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/react/map/location-control.tsx
|
|
321
|
+
import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
|
|
322
|
+
import { createPortal } from "react-dom";
|
|
323
|
+
import { Circle, Marker, useMap } from "react-leaflet";
|
|
324
|
+
import L3 from "leaflet";
|
|
325
|
+
|
|
326
|
+
// src/react/hooks/use-geolocation.ts
|
|
327
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
328
|
+
function useGeolocation(options) {
|
|
329
|
+
const [isTracking, setIsTracking] = useState(false);
|
|
330
|
+
const [location, setLocation] = useState(null);
|
|
331
|
+
const [error, setError] = useState(null);
|
|
332
|
+
const watchIdRef = useRef(null);
|
|
333
|
+
const stopTracking = useCallback(() => {
|
|
334
|
+
if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
|
|
335
|
+
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
336
|
+
}
|
|
337
|
+
watchIdRef.current = null;
|
|
338
|
+
setIsTracking(false);
|
|
339
|
+
}, []);
|
|
340
|
+
const startTracking = useCallback(() => {
|
|
341
|
+
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
342
|
+
setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
setError(null);
|
|
346
|
+
watchIdRef.current = navigator.geolocation.watchPosition(
|
|
347
|
+
(position) => {
|
|
348
|
+
setLocation({
|
|
349
|
+
lat: position.coords.latitude,
|
|
350
|
+
lon: position.coords.longitude,
|
|
351
|
+
accuracy: position.coords.accuracy
|
|
352
|
+
});
|
|
353
|
+
setIsTracking(true);
|
|
354
|
+
},
|
|
355
|
+
(err) => {
|
|
356
|
+
setError({ code: err.code, message: err.message });
|
|
357
|
+
stopTracking();
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
enableHighAccuracy: options?.enableHighAccuracy ?? true,
|
|
361
|
+
timeout: options?.timeout ?? 12e3,
|
|
362
|
+
maximumAge: options?.maximumAge ?? 0
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
|
|
366
|
+
const toggleTracking = useCallback(() => {
|
|
367
|
+
if (isTracking) {
|
|
368
|
+
stopTracking();
|
|
369
|
+
} else {
|
|
370
|
+
startTracking();
|
|
371
|
+
}
|
|
372
|
+
}, [isTracking, startTracking, stopTracking]);
|
|
373
|
+
const clearError = useCallback(() => setError(null), []);
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
return () => {
|
|
376
|
+
stopTracking();
|
|
377
|
+
};
|
|
378
|
+
}, [stopTracking]);
|
|
379
|
+
return {
|
|
380
|
+
isTracking,
|
|
381
|
+
location,
|
|
382
|
+
error,
|
|
383
|
+
startTracking,
|
|
384
|
+
stopTracking,
|
|
385
|
+
toggleTracking,
|
|
386
|
+
clearError
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/react/map/map-utils.ts
|
|
391
|
+
import L2 from "leaflet";
|
|
302
392
|
var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
|
|
303
|
-
var
|
|
304
|
-
var
|
|
393
|
+
var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry", "_private"]);
|
|
394
|
+
var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
|
|
395
|
+
var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 360, minWidth: 280, maxHeight: 520 };
|
|
396
|
+
var MOBILE_POPUP_DIMENSIONS = { maxWidth: 300, minWidth: 240, maxHeight: 420 };
|
|
305
397
|
var ZENIT_LEAFLET_POPUP_STYLES = `
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
/* Main popup wrapper */
|
|
309
|
-
.zenit-leaflet-popup .leaflet-popup-content-wrapper {
|
|
398
|
+
.custom-leaflet-popup .leaflet-popup-content-wrapper {
|
|
310
399
|
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
400
|
padding: 0;
|
|
316
401
|
background: #ffffff;
|
|
317
|
-
|
|
402
|
+
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.18);
|
|
403
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
318
404
|
}
|
|
319
405
|
|
|
320
|
-
|
|
321
|
-
.zenit-leaflet-popup .leaflet-popup-content {
|
|
406
|
+
.custom-leaflet-popup .leaflet-popup-content {
|
|
322
407
|
margin: 0;
|
|
323
|
-
padding:
|
|
408
|
+
padding: 12px 14px;
|
|
324
409
|
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
410
|
line-height: 1.4;
|
|
411
|
+
color: #0f172a;
|
|
412
|
+
max-height: min(70vh, 520px);
|
|
413
|
+
overflow: auto;
|
|
414
|
+
scrollbar-width: thin;
|
|
415
|
+
scrollbar-color: rgba(148, 163, 184, 0.6) transparent;
|
|
404
416
|
}
|
|
405
417
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
font-size:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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;
|
|
418
|
+
.custom-leaflet-popup .leaflet-popup-close-button {
|
|
419
|
+
color: #64748b;
|
|
420
|
+
font-size: 16px;
|
|
421
|
+
padding: 6px 8px;
|
|
422
|
+
border-radius: 8px;
|
|
423
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
450
424
|
}
|
|
451
425
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
color: #9ca3af;
|
|
456
|
-
font-style: italic;
|
|
457
|
-
text-align: center;
|
|
458
|
-
padding: 20px 0;
|
|
426
|
+
.custom-leaflet-popup .leaflet-popup-close-button:hover {
|
|
427
|
+
color: #0f172a;
|
|
428
|
+
background: rgba(148, 163, 184, 0.2);
|
|
459
429
|
}
|
|
460
430
|
|
|
461
|
-
|
|
462
|
-
.zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
|
|
431
|
+
.custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
|
|
463
432
|
width: 6px;
|
|
464
433
|
}
|
|
465
434
|
|
|
466
|
-
.
|
|
467
|
-
background:
|
|
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);
|
|
435
|
+
.custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
|
|
436
|
+
background: rgba(148, 163, 184, 0.5);
|
|
437
|
+
border-radius: 999px;
|
|
477
438
|
}
|
|
478
439
|
|
|
479
|
-
|
|
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 {
|
|
440
|
+
.custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
|
|
485
441
|
background: transparent;
|
|
486
442
|
}
|
|
487
443
|
|
|
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
444
|
@media (max-width: 640px) {
|
|
495
|
-
.
|
|
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 {
|
|
445
|
+
.custom-leaflet-popup .leaflet-popup-content {
|
|
519
446
|
font-size: 12px;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
.zenit-popup-row.zenit-popup-description {
|
|
523
|
-
margin: 0 -12px;
|
|
524
447
|
padding: 10px 12px;
|
|
448
|
+
max-height: min(65vh, 420px);
|
|
525
449
|
}
|
|
526
450
|
|
|
527
|
-
.
|
|
528
|
-
font-size:
|
|
529
|
-
|
|
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;
|
|
451
|
+
.custom-leaflet-popup .leaflet-popup-close-button {
|
|
452
|
+
font-size: 14px;
|
|
453
|
+
padding: 4px 6px;
|
|
540
454
|
}
|
|
541
455
|
}
|
|
542
456
|
|
|
543
|
-
/* ===== Map tooltip styling ===== */
|
|
544
457
|
.zenit-map-tooltip {
|
|
545
458
|
background-color: rgba(31, 41, 55, 0.95);
|
|
546
459
|
border: none;
|
|
@@ -555,7 +468,23 @@ var ZENIT_LEAFLET_POPUP_STYLES = `
|
|
|
555
468
|
.zenit-map-tooltip::before {
|
|
556
469
|
border-top-color: rgba(31, 41, 55, 0.95);
|
|
557
470
|
}
|
|
471
|
+
|
|
472
|
+
.polygon-label-tooltip {
|
|
473
|
+
background: rgba(15, 23, 42, 0.92);
|
|
474
|
+
border: none;
|
|
475
|
+
border-radius: 6px;
|
|
476
|
+
color: #f8fafc;
|
|
477
|
+
font-weight: 600;
|
|
478
|
+
font-size: 12px;
|
|
479
|
+
padding: 4px 8px;
|
|
480
|
+
}
|
|
558
481
|
`;
|
|
482
|
+
function clampNumber2(value, min, max) {
|
|
483
|
+
return Math.min(max, Math.max(min, value));
|
|
484
|
+
}
|
|
485
|
+
function clampOpacity4(value) {
|
|
486
|
+
return clampNumber2(value, 0, 1);
|
|
487
|
+
}
|
|
559
488
|
function ensurePopupStyles() {
|
|
560
489
|
if (typeof document === "undefined") return;
|
|
561
490
|
if (document.getElementById(POPUP_STYLE_ID)) return;
|
|
@@ -565,47 +494,52 @@ function ensurePopupStyles() {
|
|
|
565
494
|
document.head.appendChild(styleTag);
|
|
566
495
|
}
|
|
567
496
|
function getPopupDimensions() {
|
|
568
|
-
if (typeof window === "undefined"
|
|
497
|
+
if (typeof window === "undefined") {
|
|
569
498
|
return DESKTOP_POPUP_DIMENSIONS;
|
|
570
499
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const trimmed = value.trim();
|
|
577
|
-
return trimmed ? trimmed : null;
|
|
578
|
-
}
|
|
579
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
580
|
-
return String(value);
|
|
581
|
-
}
|
|
582
|
-
return null;
|
|
500
|
+
const isSmallWidth = window.innerWidth < 640;
|
|
501
|
+
const isShortHeight = window.innerHeight < 768;
|
|
502
|
+
const base = isSmallWidth ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
|
|
503
|
+
const maxHeight = isShortHeight ? Math.min(base.maxHeight, 360) : base.maxHeight;
|
|
504
|
+
return { ...base, maxHeight };
|
|
583
505
|
}
|
|
584
|
-
function
|
|
585
|
-
|
|
586
|
-
const matches = Object.entries(properties).find(
|
|
587
|
-
([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
|
|
588
|
-
);
|
|
589
|
-
if (!matches) return null;
|
|
590
|
-
return normalizeDescriptionValue(matches[1]);
|
|
506
|
+
function escapeHtml(value) {
|
|
507
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
591
508
|
}
|
|
592
|
-
function
|
|
593
|
-
|
|
594
|
-
const json = JSON.stringify(value, null, 2);
|
|
595
|
-
if (json !== void 0) return json;
|
|
596
|
-
} catch {
|
|
597
|
-
}
|
|
598
|
-
return String(value);
|
|
509
|
+
function formatLabel(key) {
|
|
510
|
+
return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
|
|
599
511
|
}
|
|
600
512
|
function renderPopupValue(value) {
|
|
601
|
-
if (value === null || value === void 0)
|
|
602
|
-
|
|
513
|
+
if (value === null || value === void 0) return '<span style="color:#94a3b8;">Sin datos</span>';
|
|
514
|
+
if (value instanceof Date) {
|
|
515
|
+
return escapeHtml(value.toLocaleDateString("es-GT"));
|
|
516
|
+
}
|
|
517
|
+
if (typeof value === "number") {
|
|
518
|
+
return escapeHtml(value.toLocaleString("es-GT"));
|
|
519
|
+
}
|
|
520
|
+
if (typeof value === "string") {
|
|
521
|
+
const trimmed = value.trim();
|
|
522
|
+
if (!trimmed) return '<span style="color:#94a3b8;">Sin datos</span>';
|
|
523
|
+
return escapeHtml(trimmed);
|
|
603
524
|
}
|
|
604
525
|
if (typeof value === "object") {
|
|
605
|
-
|
|
606
|
-
|
|
526
|
+
try {
|
|
527
|
+
return `<pre style="margin:0; white-space:pre-wrap; font-size:11px; background:#f8fafc; border:1px solid #e2e8f0; padding:6px 8px; border-radius:6px;">${escapeHtml(
|
|
528
|
+
JSON.stringify(value, null, 2)
|
|
529
|
+
)}</pre>`;
|
|
530
|
+
} catch {
|
|
531
|
+
return escapeHtml(String(value));
|
|
532
|
+
}
|
|
607
533
|
}
|
|
608
|
-
return
|
|
534
|
+
return escapeHtml(String(value));
|
|
535
|
+
}
|
|
536
|
+
function extractPopupHeader(properties) {
|
|
537
|
+
const entry = Object.entries(properties).find(([key, value]) => {
|
|
538
|
+
if (typeof value !== "string") return false;
|
|
539
|
+
const normalized = key.trim().toLowerCase();
|
|
540
|
+
return POPUP_HEADER_KEYS.includes(normalized) && value.trim().length > 0;
|
|
541
|
+
});
|
|
542
|
+
return entry ? entry[1].trim() : null;
|
|
609
543
|
}
|
|
610
544
|
function shouldIncludePopupEntry(key, value) {
|
|
611
545
|
if (!key) return false;
|
|
@@ -617,45 +551,89 @@ function shouldIncludePopupEntry(key, value) {
|
|
|
617
551
|
if (typeof value === "string" && !value.trim()) return false;
|
|
618
552
|
return true;
|
|
619
553
|
}
|
|
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
554
|
function createPopupContent(properties) {
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
555
|
+
const headerText = extractPopupHeader(properties);
|
|
556
|
+
const entries = Object.entries(properties).filter(([key, value]) => {
|
|
557
|
+
if (!shouldIncludePopupEntry(key, value)) return false;
|
|
558
|
+
if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) return false;
|
|
559
|
+
return true;
|
|
560
|
+
});
|
|
631
561
|
if (entries.length === 0) {
|
|
632
|
-
return
|
|
562
|
+
return '<div style="padding:8px 0; color:#64748b; text-align:center;">Sin datos disponibles</div>';
|
|
633
563
|
}
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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>
|
|
645
|
-
</div>
|
|
646
|
-
`;
|
|
647
|
-
}
|
|
648
|
-
rowsHtml += otherEntries.map(([key, value]) => {
|
|
564
|
+
const headerHtml = headerText ? `<div style="font-weight:700; font-size:14px; margin-bottom:8px; color:#0f172a;">${escapeHtml(
|
|
565
|
+
headerText
|
|
566
|
+
)}</div>` : "";
|
|
567
|
+
const rowsHtml = entries.map(([key, value]) => {
|
|
649
568
|
const label = escapeHtml(formatLabel(key));
|
|
650
569
|
const valueHtml = renderPopupValue(value);
|
|
651
570
|
return `
|
|
652
|
-
<div
|
|
653
|
-
<div
|
|
654
|
-
<div
|
|
571
|
+
<div style="display:grid; grid-template-columns:minmax(90px, 35%) 1fr; gap:8px; padding:6px 0; border-bottom:1px solid #e2e8f0;">
|
|
572
|
+
<div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; color:#64748b;">${label}</div>
|
|
573
|
+
<div style="font-size:13px; color:#0f172a; word-break:break-word;">${valueHtml}</div>
|
|
655
574
|
</div>
|
|
656
575
|
`;
|
|
657
576
|
}).join("");
|
|
658
|
-
return `<div
|
|
577
|
+
return `<div>${headerHtml}${rowsHtml}</div>`;
|
|
578
|
+
}
|
|
579
|
+
function isPolygonType(layerType, geometryType) {
|
|
580
|
+
const candidate = (layerType ?? geometryType ?? "").toLowerCase();
|
|
581
|
+
return candidate === "polygon" || candidate === "multipolygon";
|
|
582
|
+
}
|
|
583
|
+
function calculateZoomBasedOpacity(zoom, baseOpacity, layerType, geometryType) {
|
|
584
|
+
if (!isPolygonType(layerType, geometryType)) return clampOpacity4(baseOpacity);
|
|
585
|
+
const minZoom = 11;
|
|
586
|
+
const maxZoom = 15;
|
|
587
|
+
const minFactor = 0.3;
|
|
588
|
+
if (maxZoom <= minZoom) return clampOpacity4(baseOpacity * minFactor);
|
|
589
|
+
const t = clampNumber2((zoom - minZoom) / (maxZoom - minZoom), 0, 1);
|
|
590
|
+
const factor = 1 - (1 - minFactor) * t;
|
|
591
|
+
return clampOpacity4(baseOpacity * factor);
|
|
592
|
+
}
|
|
593
|
+
function layerStyleToLeaflet(options) {
|
|
594
|
+
const { baseOpacity, zoom, layerStyle, geometryType, layerType } = options;
|
|
595
|
+
const sanitizedOpacity = clampOpacity4(baseOpacity);
|
|
596
|
+
const zoomOpacity = calculateZoomBasedOpacity(zoom, sanitizedOpacity, layerType, geometryType);
|
|
597
|
+
const styleFillOpacity = typeof layerStyle?.fillOpacity === "number" ? clampOpacity4(layerStyle.fillOpacity) : 0.8;
|
|
598
|
+
return {
|
|
599
|
+
color: layerStyle?.color ?? layerStyle?.fillColor ?? "#2563eb",
|
|
600
|
+
weight: layerStyle?.weight ?? 2,
|
|
601
|
+
fillColor: layerStyle?.fillColor ?? layerStyle?.color ?? "#2563eb",
|
|
602
|
+
opacity: clampOpacity4(Math.max(0.35, zoomOpacity * 0.9)),
|
|
603
|
+
fillOpacity: clampOpacity4(zoomOpacity * styleFillOpacity)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function getRgbFromColor(color) {
|
|
607
|
+
const trimmed = color.trim();
|
|
608
|
+
if (trimmed.startsWith("#")) {
|
|
609
|
+
const hex = trimmed.replace("#", "");
|
|
610
|
+
const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
|
|
611
|
+
if (expanded.length === 6) {
|
|
612
|
+
const r = parseInt(expanded.slice(0, 2), 16);
|
|
613
|
+
const g = parseInt(expanded.slice(2, 4), 16);
|
|
614
|
+
const b = parseInt(expanded.slice(4, 6), 16);
|
|
615
|
+
return { r, g, b };
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
|
|
619
|
+
if (rgbMatch) {
|
|
620
|
+
const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
|
|
621
|
+
if ([r, g, b].every((value) => Number.isFinite(value))) {
|
|
622
|
+
return { r, g, b };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
function getLabelTextStyles(color) {
|
|
628
|
+
const rgb = getRgbFromColor(color);
|
|
629
|
+
if (!rgb) {
|
|
630
|
+
return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
|
|
631
|
+
}
|
|
632
|
+
const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
|
|
633
|
+
if (luminance > 0.6) {
|
|
634
|
+
return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
|
|
635
|
+
}
|
|
636
|
+
return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
|
|
659
637
|
}
|
|
660
638
|
function withAlpha(color, alpha) {
|
|
661
639
|
const trimmed = color.trim();
|
|
@@ -668,48 +646,385 @@ function withAlpha(color, alpha) {
|
|
|
668
646
|
const b = parseInt(expanded.slice(4, 6), 16);
|
|
669
647
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
670
648
|
}
|
|
671
|
-
}
|
|
672
|
-
if (trimmed.startsWith("rgb(")) {
|
|
673
|
-
const inner = trimmed.slice(4, -1);
|
|
674
|
-
return `rgba(${inner}, ${alpha})`;
|
|
675
|
-
}
|
|
676
|
-
if (trimmed.startsWith("rgba(")) {
|
|
677
|
-
const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
|
|
678
|
-
return `rgba(${inner.join(", ")}, ${alpha})`;
|
|
679
|
-
}
|
|
680
|
-
return color;
|
|
681
|
-
}
|
|
682
|
-
function
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
649
|
+
}
|
|
650
|
+
if (trimmed.startsWith("rgb(")) {
|
|
651
|
+
const inner = trimmed.slice(4, -1);
|
|
652
|
+
return `rgba(${inner}, ${alpha})`;
|
|
653
|
+
}
|
|
654
|
+
if (trimmed.startsWith("rgba(")) {
|
|
655
|
+
const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
|
|
656
|
+
return `rgba(${inner.join(", ")}, ${alpha})`;
|
|
657
|
+
}
|
|
658
|
+
return color;
|
|
659
|
+
}
|
|
660
|
+
function createCustomIcon(label, opacity, color) {
|
|
661
|
+
const size = 60;
|
|
662
|
+
const innerSize = 44;
|
|
663
|
+
const textStyles = getLabelTextStyles(color);
|
|
664
|
+
const safeLabel = escapeHtml(label);
|
|
665
|
+
const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
|
|
666
|
+
const innerBackground = withAlpha(color, 0.9);
|
|
667
|
+
return L2.divIcon({
|
|
668
|
+
className: "zenit-label-marker",
|
|
669
|
+
iconSize: [size, size],
|
|
670
|
+
iconAnchor: [size / 2, size / 2],
|
|
671
|
+
html: `
|
|
672
|
+
<div
|
|
673
|
+
title="${safeLabel}"
|
|
674
|
+
style="
|
|
675
|
+
width:${size}px;
|
|
676
|
+
height:${size}px;
|
|
677
|
+
border-radius:9999px;
|
|
678
|
+
background:rgba(255, 255, 255, 0.95);
|
|
679
|
+
border:3px solid rgba(255, 255, 255, 1);
|
|
680
|
+
display:flex;
|
|
681
|
+
align-items:center;
|
|
682
|
+
justify-content:center;
|
|
683
|
+
opacity:${clampedOpacity};
|
|
684
|
+
box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
|
|
685
|
+
pointer-events:none;
|
|
686
|
+
"
|
|
687
|
+
>
|
|
688
|
+
<div
|
|
689
|
+
style="
|
|
690
|
+
width:${innerSize}px;
|
|
691
|
+
height:${innerSize}px;
|
|
692
|
+
border-radius:9999px;
|
|
693
|
+
background:${innerBackground};
|
|
694
|
+
display:flex;
|
|
695
|
+
align-items:center;
|
|
696
|
+
justify-content:center;
|
|
697
|
+
box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
|
|
698
|
+
"
|
|
699
|
+
>
|
|
700
|
+
<span
|
|
701
|
+
style="
|
|
702
|
+
color:${textStyles.color};
|
|
703
|
+
font-size:20px;
|
|
704
|
+
font-weight:800;
|
|
705
|
+
text-shadow:${textStyles.shadow};
|
|
706
|
+
"
|
|
707
|
+
>
|
|
708
|
+
${safeLabel}
|
|
709
|
+
</span>
|
|
710
|
+
</div>
|
|
711
|
+
</div>
|
|
712
|
+
`
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
function createLocationIcon() {
|
|
716
|
+
return L2.divIcon({
|
|
717
|
+
className: "zenit-location-marker",
|
|
718
|
+
iconSize: [18, 18],
|
|
719
|
+
iconAnchor: [9, 9],
|
|
720
|
+
html: `
|
|
721
|
+
<div style="width:18px;height:18px;border-radius:9999px;background:#2563eb;border:2px solid #fff;box-shadow:0 0 0 4px rgba(37, 99, 235, 0.25);"></div>
|
|
722
|
+
`
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/react/map/location-control.tsx
|
|
727
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
728
|
+
var LOCATION_STYLE_ID = "zenit-location-control-styles";
|
|
729
|
+
var LOCATION_STYLES = `
|
|
730
|
+
.zenit-location-control {
|
|
731
|
+
display: flex;
|
|
732
|
+
flex-direction: column;
|
|
733
|
+
gap: 8px;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.zenit-location-button {
|
|
737
|
+
width: 42px;
|
|
738
|
+
height: 42px;
|
|
739
|
+
border-radius: 12px;
|
|
740
|
+
border: none;
|
|
741
|
+
background: #ffffff;
|
|
742
|
+
color: #0f172a;
|
|
743
|
+
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.2);
|
|
744
|
+
display: inline-flex;
|
|
745
|
+
align-items: center;
|
|
746
|
+
justify-content: center;
|
|
747
|
+
cursor: pointer;
|
|
748
|
+
position: relative;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.zenit-location-button.zenit-location-button--tracking {
|
|
752
|
+
animation: zenitLocationPulse 1.8s infinite;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.zenit-location-button:hover {
|
|
756
|
+
background: #f8fafc;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.zenit-location-badge {
|
|
760
|
+
position: absolute;
|
|
761
|
+
top: -6px;
|
|
762
|
+
right: -6px;
|
|
763
|
+
width: 18px;
|
|
764
|
+
height: 18px;
|
|
765
|
+
border-radius: 999px;
|
|
766
|
+
background: #ef4444;
|
|
767
|
+
color: #fff;
|
|
768
|
+
font-size: 11px;
|
|
769
|
+
font-weight: 700;
|
|
770
|
+
display: inline-flex;
|
|
771
|
+
align-items: center;
|
|
772
|
+
justify-content: center;
|
|
773
|
+
box-shadow: 0 4px 10px rgba(239, 68, 68, 0.35);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.zenit-location-error {
|
|
777
|
+
background: rgba(15, 23, 42, 0.92);
|
|
778
|
+
color: #f8fafc;
|
|
779
|
+
border-radius: 10px;
|
|
780
|
+
padding: 8px 10px;
|
|
781
|
+
font-size: 12px;
|
|
782
|
+
max-width: 200px;
|
|
783
|
+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.3);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
@keyframes zenitLocationPulse {
|
|
787
|
+
0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.35); }
|
|
788
|
+
70% { box-shadow: 0 0 0 12px rgba(16, 185, 129, 0); }
|
|
789
|
+
100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
|
|
790
|
+
}
|
|
791
|
+
`;
|
|
792
|
+
var LocateIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
793
|
+
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "3" }),
|
|
794
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "2", x2: "12", y2: "5" }),
|
|
795
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "22" }),
|
|
796
|
+
/* @__PURE__ */ jsx2("line", { x1: "2", y1: "12", x2: "5", y2: "12" }),
|
|
797
|
+
/* @__PURE__ */ jsx2("line", { x1: "19", y1: "12", x2: "22", y2: "12" })
|
|
798
|
+
] });
|
|
799
|
+
var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
|
|
800
|
+
const map = useMap();
|
|
801
|
+
const controlRef = useRef2(null);
|
|
802
|
+
const hasCenteredRef = useRef2(false);
|
|
803
|
+
const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
|
|
804
|
+
useEffect2(() => {
|
|
805
|
+
if (typeof document === "undefined") return;
|
|
806
|
+
if (document.getElementById(LOCATION_STYLE_ID)) return;
|
|
807
|
+
const styleTag = document.createElement("style");
|
|
808
|
+
styleTag.id = LOCATION_STYLE_ID;
|
|
809
|
+
styleTag.textContent = LOCATION_STYLES;
|
|
810
|
+
document.head.appendChild(styleTag);
|
|
811
|
+
}, []);
|
|
812
|
+
useEffect2(() => {
|
|
813
|
+
const control = L3.control({ position });
|
|
814
|
+
control.onAdd = () => {
|
|
815
|
+
const container = L3.DomUtil.create("div", "zenit-location-control");
|
|
816
|
+
L3.DomEvent.disableClickPropagation(container);
|
|
817
|
+
controlRef.current = container;
|
|
818
|
+
return container;
|
|
819
|
+
};
|
|
820
|
+
control.addTo(map);
|
|
821
|
+
return () => {
|
|
822
|
+
control.remove();
|
|
823
|
+
controlRef.current = null;
|
|
824
|
+
};
|
|
825
|
+
}, [map, position]);
|
|
826
|
+
useEffect2(() => {
|
|
827
|
+
if (!location || !isTracking) return;
|
|
828
|
+
if (hasCenteredRef.current) return;
|
|
829
|
+
hasCenteredRef.current = true;
|
|
830
|
+
map.flyTo([location.lat, location.lon], zoom, { animate: true });
|
|
831
|
+
}, [isTracking, location, map, zoom]);
|
|
832
|
+
const markerIcon = useMemo(() => createLocationIcon(), []);
|
|
833
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
834
|
+
controlRef.current && createPortal(
|
|
835
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
836
|
+
/* @__PURE__ */ jsxs2(
|
|
837
|
+
"button",
|
|
838
|
+
{
|
|
839
|
+
type: "button",
|
|
840
|
+
className: `zenit-location-button${isTracking ? " zenit-location-button--tracking" : ""}`,
|
|
841
|
+
onClick: () => {
|
|
842
|
+
if (error) {
|
|
843
|
+
clearError();
|
|
844
|
+
}
|
|
845
|
+
toggleTracking();
|
|
846
|
+
},
|
|
847
|
+
"aria-label": isTracking ? "Detener ubicaci\xF3n" : "Mostrar mi ubicaci\xF3n",
|
|
848
|
+
children: [
|
|
849
|
+
/* @__PURE__ */ jsx2(LocateIcon, {}),
|
|
850
|
+
error && /* @__PURE__ */ jsx2("span", { className: "zenit-location-badge", children: "!" })
|
|
851
|
+
]
|
|
852
|
+
}
|
|
853
|
+
),
|
|
854
|
+
error && /* @__PURE__ */ jsx2("div", { className: "zenit-location-error", children: error.message || "No se pudo acceder a tu ubicaci\xF3n." })
|
|
855
|
+
] }),
|
|
856
|
+
controlRef.current
|
|
857
|
+
),
|
|
858
|
+
location && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
859
|
+
/* @__PURE__ */ jsx2(Marker, { position: [location.lat, location.lon], icon: markerIcon }),
|
|
860
|
+
/* @__PURE__ */ jsx2(
|
|
861
|
+
Circle,
|
|
862
|
+
{
|
|
863
|
+
center: [location.lat, location.lon],
|
|
864
|
+
radius: location.accuracy,
|
|
865
|
+
pathOptions: { color: "#2563eb", fillColor: "#2563eb", fillOpacity: 0.15 }
|
|
866
|
+
}
|
|
867
|
+
)
|
|
868
|
+
] })
|
|
869
|
+
] });
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
// src/react/map/map-handlers.tsx
|
|
873
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
874
|
+
import { useMap as useMap2 } from "react-leaflet";
|
|
875
|
+
function computeBBoxFromGeojson(geojson) {
|
|
876
|
+
if (!geojson || !Array.isArray(geojson.features)) return null;
|
|
877
|
+
const coords = [];
|
|
878
|
+
const collect = (candidate) => {
|
|
879
|
+
if (!Array.isArray(candidate)) return;
|
|
880
|
+
if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
|
|
881
|
+
coords.push([candidate[0], candidate[1]]);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
candidate.forEach((item) => collect(item));
|
|
885
|
+
};
|
|
886
|
+
geojson.features.forEach((feature) => {
|
|
887
|
+
collect(feature.geometry?.coordinates);
|
|
888
|
+
});
|
|
889
|
+
if (coords.length === 0) return null;
|
|
890
|
+
const [firstLon, firstLat] = coords[0];
|
|
891
|
+
const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
|
|
892
|
+
coords.forEach(([lon, lat]) => {
|
|
893
|
+
bbox.minLon = Math.min(bbox.minLon, lon);
|
|
894
|
+
bbox.minLat = Math.min(bbox.minLat, lat);
|
|
895
|
+
bbox.maxLon = Math.max(bbox.maxLon, lon);
|
|
896
|
+
bbox.maxLat = Math.max(bbox.maxLat, lat);
|
|
897
|
+
});
|
|
898
|
+
return bbox;
|
|
899
|
+
}
|
|
900
|
+
function mergeBBoxes(bboxes) {
|
|
901
|
+
const valid = bboxes.filter((bbox) => !!bbox);
|
|
902
|
+
if (valid.length === 0) return null;
|
|
903
|
+
const first = valid[0];
|
|
904
|
+
return valid.slice(1).reduce(
|
|
905
|
+
(acc, bbox) => ({
|
|
906
|
+
minLon: Math.min(acc.minLon, bbox.minLon),
|
|
907
|
+
minLat: Math.min(acc.minLat, bbox.minLat),
|
|
908
|
+
maxLon: Math.max(acc.maxLon, bbox.maxLon),
|
|
909
|
+
maxLat: Math.max(acc.maxLat, bbox.maxLat)
|
|
910
|
+
}),
|
|
911
|
+
{ ...first }
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
var BBoxZoomHandler = ({
|
|
915
|
+
bbox,
|
|
916
|
+
geojson,
|
|
917
|
+
autoGeojson = [],
|
|
918
|
+
enabled = true
|
|
919
|
+
}) => {
|
|
920
|
+
const map = useMap2();
|
|
921
|
+
const lastAppliedBBox = useRef3(null);
|
|
922
|
+
const lastUserInteracted = useRef3(false);
|
|
923
|
+
useEffect3(() => {
|
|
924
|
+
const handleInteraction = () => {
|
|
925
|
+
lastUserInteracted.current = true;
|
|
926
|
+
};
|
|
927
|
+
map.on("dragstart", handleInteraction);
|
|
928
|
+
map.on("zoomstart", handleInteraction);
|
|
929
|
+
return () => {
|
|
930
|
+
map.off("dragstart", handleInteraction);
|
|
931
|
+
map.off("zoomstart", handleInteraction);
|
|
932
|
+
};
|
|
933
|
+
}, [map]);
|
|
934
|
+
useEffect3(() => {
|
|
935
|
+
if (!enabled) return;
|
|
936
|
+
let resolvedBBox = bbox ?? null;
|
|
937
|
+
if (!resolvedBBox && geojson) {
|
|
938
|
+
resolvedBBox = computeBBoxFromGeojson(geojson);
|
|
939
|
+
}
|
|
940
|
+
if (!resolvedBBox && autoGeojson.length > 0) {
|
|
941
|
+
const bboxes = autoGeojson.map((collection) => computeBBoxFromGeojson(collection));
|
|
942
|
+
resolvedBBox = mergeBBoxes(bboxes);
|
|
692
943
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return
|
|
944
|
+
if (!resolvedBBox) return;
|
|
945
|
+
const serialized = JSON.stringify(resolvedBBox);
|
|
946
|
+
if (lastAppliedBBox.current === serialized) return;
|
|
947
|
+
if (lastUserInteracted.current && !bbox && !geojson) {
|
|
948
|
+
lastUserInteracted.current = false;
|
|
949
|
+
return;
|
|
699
950
|
}
|
|
700
|
-
|
|
951
|
+
const bounds = [
|
|
952
|
+
[resolvedBBox.minLat, resolvedBBox.minLon],
|
|
953
|
+
[resolvedBBox.maxLat, resolvedBBox.maxLon]
|
|
954
|
+
];
|
|
955
|
+
map.fitBounds(bounds, { padding: [12, 12] });
|
|
956
|
+
lastAppliedBBox.current = serialized;
|
|
957
|
+
}, [autoGeojson, bbox, enabled, geojson, map]);
|
|
958
|
+
return null;
|
|
959
|
+
};
|
|
960
|
+
var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
|
|
961
|
+
const map = useMap2();
|
|
962
|
+
useEffect3(() => {
|
|
963
|
+
const handleZoom = () => {
|
|
964
|
+
onZoomChange(map.getZoom());
|
|
965
|
+
};
|
|
966
|
+
map.on("zoomend", handleZoom);
|
|
967
|
+
handleZoom();
|
|
968
|
+
return () => {
|
|
969
|
+
map.off("zoomend", handleZoom);
|
|
970
|
+
};
|
|
971
|
+
}, [map, onZoomChange]);
|
|
972
|
+
return null;
|
|
973
|
+
};
|
|
974
|
+
var MapInstanceBridge = ({ onReady }) => {
|
|
975
|
+
const map = useMap2();
|
|
976
|
+
useEffect3(() => {
|
|
977
|
+
onReady(map);
|
|
978
|
+
}, [map, onReady]);
|
|
701
979
|
return null;
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
// src/react/ZenitMap.tsx
|
|
983
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
984
|
+
var DEFAULT_CENTER = [0, 0];
|
|
985
|
+
var DEFAULT_ZOOM = 3;
|
|
986
|
+
var LABELS_PANE_NAME = "zenit-labels-pane";
|
|
987
|
+
function isRecord(value) {
|
|
988
|
+
return typeof value === "object" && value !== null;
|
|
989
|
+
}
|
|
990
|
+
function isGeoJsonFeatureCollection(value) {
|
|
991
|
+
if (!isRecord(value)) return false;
|
|
992
|
+
const features = value.features;
|
|
993
|
+
if (!Array.isArray(features)) return false;
|
|
994
|
+
const type = value.type;
|
|
995
|
+
return type === void 0 || type === "FeatureCollection";
|
|
996
|
+
}
|
|
997
|
+
function extractGeoJsonFeatureCollection(value) {
|
|
998
|
+
if (isRecord(value) && "data" in value) {
|
|
999
|
+
const data = value.data;
|
|
1000
|
+
return isGeoJsonFeatureCollection(data) ? data : null;
|
|
1001
|
+
}
|
|
1002
|
+
return isGeoJsonFeatureCollection(value) ? value : null;
|
|
702
1003
|
}
|
|
703
|
-
function
|
|
704
|
-
const
|
|
705
|
-
if (
|
|
706
|
-
|
|
1004
|
+
function getFeatureLayerId(feature) {
|
|
1005
|
+
const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
|
|
1006
|
+
if (layerId === void 0 || layerId === null) return null;
|
|
1007
|
+
return layerId;
|
|
1008
|
+
}
|
|
1009
|
+
var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
|
|
1010
|
+
function normalizeDescriptionValue(value) {
|
|
1011
|
+
if (value === void 0 || value === null) return null;
|
|
1012
|
+
if (typeof value === "string") {
|
|
1013
|
+
const trimmed = value.trim();
|
|
1014
|
+
return trimmed ? trimmed : null;
|
|
707
1015
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
|
|
1016
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1017
|
+
return String(value);
|
|
711
1018
|
}
|
|
712
|
-
return
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
function extractDescriptionValue(properties) {
|
|
1022
|
+
if (!properties) return null;
|
|
1023
|
+
const matches = Object.entries(properties).find(
|
|
1024
|
+
([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
|
|
1025
|
+
);
|
|
1026
|
+
if (!matches) return null;
|
|
1027
|
+
return normalizeDescriptionValue(matches[1]);
|
|
713
1028
|
}
|
|
714
1029
|
function getFeatureStyleOverrides(feature) {
|
|
715
1030
|
const candidate = feature?.properties?._style;
|
|
@@ -728,25 +1043,6 @@ function buildFeaturePopupHtml(feature) {
|
|
|
728
1043
|
const rendered = createPopupContent(properties);
|
|
729
1044
|
return rendered ? rendered : null;
|
|
730
1045
|
}
|
|
731
|
-
var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
|
|
732
|
-
function getGeometryType(feature) {
|
|
733
|
-
const t = feature?.geometry?.type;
|
|
734
|
-
return typeof t === "string" ? t : null;
|
|
735
|
-
}
|
|
736
|
-
function isPointGeometry(feature) {
|
|
737
|
-
const geometryType = getGeometryType(feature);
|
|
738
|
-
return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
|
|
739
|
-
}
|
|
740
|
-
function isNonPointGeometry(feature) {
|
|
741
|
-
const geometryType = getGeometryType(feature);
|
|
742
|
-
return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
|
|
743
|
-
}
|
|
744
|
-
function buildFeatureCollection(features) {
|
|
745
|
-
return {
|
|
746
|
-
type: "FeatureCollection",
|
|
747
|
-
features
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
1046
|
function pickIntersectFeature(baseFeature, candidates) {
|
|
751
1047
|
if (!Array.isArray(candidates) || candidates.length === 0) return null;
|
|
752
1048
|
const baseId = baseFeature?.id;
|
|
@@ -773,80 +1069,6 @@ function normalizeCenterTuple(center) {
|
|
|
773
1069
|
}
|
|
774
1070
|
return null;
|
|
775
1071
|
}
|
|
776
|
-
var FitToBounds = ({ bbox }) => {
|
|
777
|
-
const mapInstance = useMap();
|
|
778
|
-
const lastAppliedBBox = useRef(null);
|
|
779
|
-
useEffect(() => {
|
|
780
|
-
const targetBBox = bbox;
|
|
781
|
-
if (!targetBBox) return;
|
|
782
|
-
const serialized = JSON.stringify(targetBBox);
|
|
783
|
-
if (lastAppliedBBox.current === serialized) return;
|
|
784
|
-
const bounds = [
|
|
785
|
-
[targetBBox.minLat, targetBBox.minLon],
|
|
786
|
-
[targetBBox.maxLat, targetBBox.maxLon]
|
|
787
|
-
];
|
|
788
|
-
mapInstance.fitBounds(bounds, { padding: [12, 12] });
|
|
789
|
-
lastAppliedBBox.current = serialized;
|
|
790
|
-
}, [bbox, mapInstance]);
|
|
791
|
-
return null;
|
|
792
|
-
};
|
|
793
|
-
var AutoFitToBounds = ({
|
|
794
|
-
bbox,
|
|
795
|
-
enabled = true
|
|
796
|
-
}) => {
|
|
797
|
-
const mapInstance = useMap();
|
|
798
|
-
const lastAutoBBoxApplied = useRef(null);
|
|
799
|
-
const lastUserInteracted = useRef(false);
|
|
800
|
-
useEffect(() => {
|
|
801
|
-
if (!enabled) return;
|
|
802
|
-
const handleInteraction = () => {
|
|
803
|
-
lastUserInteracted.current = true;
|
|
804
|
-
};
|
|
805
|
-
mapInstance.on("dragstart", handleInteraction);
|
|
806
|
-
mapInstance.on("zoomstart", handleInteraction);
|
|
807
|
-
return () => {
|
|
808
|
-
mapInstance.off("dragstart", handleInteraction);
|
|
809
|
-
mapInstance.off("zoomstart", handleInteraction);
|
|
810
|
-
};
|
|
811
|
-
}, [enabled, mapInstance]);
|
|
812
|
-
useEffect(() => {
|
|
813
|
-
if (!enabled) return;
|
|
814
|
-
if (!bbox) return;
|
|
815
|
-
const serialized = JSON.stringify(bbox);
|
|
816
|
-
if (lastAutoBBoxApplied.current === serialized) return;
|
|
817
|
-
if (lastUserInteracted.current) {
|
|
818
|
-
lastUserInteracted.current = false;
|
|
819
|
-
}
|
|
820
|
-
const bounds = [
|
|
821
|
-
[bbox.minLat, bbox.minLon],
|
|
822
|
-
[bbox.maxLat, bbox.maxLon]
|
|
823
|
-
];
|
|
824
|
-
mapInstance.fitBounds(bounds, { padding: [12, 12] });
|
|
825
|
-
lastAutoBBoxApplied.current = serialized;
|
|
826
|
-
}, [bbox, enabled, mapInstance]);
|
|
827
|
-
return null;
|
|
828
|
-
};
|
|
829
|
-
var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
|
|
830
|
-
const mapInstance = useMap();
|
|
831
|
-
useEffect(() => {
|
|
832
|
-
const handleZoom = () => {
|
|
833
|
-
onZoomChange(mapInstance.getZoom());
|
|
834
|
-
};
|
|
835
|
-
mapInstance.on("zoomend", handleZoom);
|
|
836
|
-
handleZoom();
|
|
837
|
-
return () => {
|
|
838
|
-
mapInstance.off("zoomend", handleZoom);
|
|
839
|
-
};
|
|
840
|
-
}, [mapInstance, onZoomChange]);
|
|
841
|
-
return null;
|
|
842
|
-
};
|
|
843
|
-
var MapInstanceBridge = ({ onReady }) => {
|
|
844
|
-
const mapInstance = useMap();
|
|
845
|
-
useEffect(() => {
|
|
846
|
-
onReady(mapInstance);
|
|
847
|
-
}, [mapInstance, onReady]);
|
|
848
|
-
return null;
|
|
849
|
-
};
|
|
850
1072
|
var ZenitMap = forwardRef(({
|
|
851
1073
|
client,
|
|
852
1074
|
mapId,
|
|
@@ -872,20 +1094,21 @@ var ZenitMap = forwardRef(({
|
|
|
872
1094
|
onZoomChange,
|
|
873
1095
|
onMapReady
|
|
874
1096
|
}, ref) => {
|
|
875
|
-
const [map, setMap] =
|
|
876
|
-
const [layers, setLayers] =
|
|
877
|
-
const [effectiveStates, setEffectiveStates] =
|
|
878
|
-
const [loadingMap, setLoadingMap] =
|
|
879
|
-
const [mapError, setMapError] =
|
|
880
|
-
const [mapInstance, setMapInstance] =
|
|
881
|
-
const [panesReady, setPanesReady] =
|
|
882
|
-
const [currentZoom, setCurrentZoom] =
|
|
883
|
-
const [
|
|
1097
|
+
const [map, setMap] = useState2(null);
|
|
1098
|
+
const [layers, setLayers] = useState2([]);
|
|
1099
|
+
const [effectiveStates, setEffectiveStates] = useState2([]);
|
|
1100
|
+
const [loadingMap, setLoadingMap] = useState2(false);
|
|
1101
|
+
const [mapError, setMapError] = useState2(null);
|
|
1102
|
+
const [mapInstance, setMapInstance] = useState2(null);
|
|
1103
|
+
const [panesReady, setPanesReady] = useState2(false);
|
|
1104
|
+
const [currentZoom, setCurrentZoom] = useState2(initialZoom ?? DEFAULT_ZOOM);
|
|
1105
|
+
const [isPopupOpen, setIsPopupOpen] = useState2(false);
|
|
1106
|
+
const [isMobile, setIsMobile] = useState2(() => {
|
|
884
1107
|
if (typeof window === "undefined") return false;
|
|
885
1108
|
return window.matchMedia("(max-width: 768px)").matches;
|
|
886
1109
|
});
|
|
887
|
-
const normalizedLayers =
|
|
888
|
-
|
|
1110
|
+
const normalizedLayers = useMemo2(() => normalizeMapLayers(map), [map]);
|
|
1111
|
+
useEffect4(() => {
|
|
889
1112
|
if (typeof window === "undefined") return;
|
|
890
1113
|
const mql = window.matchMedia("(max-width: 768px)");
|
|
891
1114
|
const onChange = (e) => {
|
|
@@ -903,12 +1126,37 @@ var ZenitMap = forwardRef(({
|
|
|
903
1126
|
}
|
|
904
1127
|
return;
|
|
905
1128
|
}, []);
|
|
906
|
-
|
|
1129
|
+
useEffect4(() => {
|
|
907
1130
|
if (featureInfoMode === "popup") {
|
|
908
1131
|
ensurePopupStyles();
|
|
909
1132
|
}
|
|
910
1133
|
}, [featureInfoMode]);
|
|
911
|
-
|
|
1134
|
+
useEffect4(() => {
|
|
1135
|
+
if (featureInfoMode !== "popup") {
|
|
1136
|
+
setIsPopupOpen(false);
|
|
1137
|
+
}
|
|
1138
|
+
}, [featureInfoMode]);
|
|
1139
|
+
useEffect4(() => {
|
|
1140
|
+
if (!mapInstance) return;
|
|
1141
|
+
const popupPane = mapInstance.getPane("popupPane");
|
|
1142
|
+
if (popupPane) {
|
|
1143
|
+
popupPane.style.zIndex = "800";
|
|
1144
|
+
}
|
|
1145
|
+
const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
|
|
1146
|
+
labelsPane.style.zIndex = "600";
|
|
1147
|
+
}, [mapInstance]);
|
|
1148
|
+
useEffect4(() => {
|
|
1149
|
+
if (!mapInstance) return;
|
|
1150
|
+
const handlePopupOpen = () => setIsPopupOpen(true);
|
|
1151
|
+
const handlePopupClose = () => setIsPopupOpen(false);
|
|
1152
|
+
mapInstance.on("popupopen", handlePopupOpen);
|
|
1153
|
+
mapInstance.on("popupclose", handlePopupClose);
|
|
1154
|
+
return () => {
|
|
1155
|
+
mapInstance.off("popupopen", handlePopupOpen);
|
|
1156
|
+
mapInstance.off("popupclose", handlePopupClose);
|
|
1157
|
+
};
|
|
1158
|
+
}, [mapInstance]);
|
|
1159
|
+
const layerStyleIndex = useMemo2(() => {
|
|
912
1160
|
const index = /* @__PURE__ */ new Map();
|
|
913
1161
|
(map?.mapLayers ?? []).forEach((entry) => {
|
|
914
1162
|
const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
|
|
@@ -919,7 +1167,7 @@ var ZenitMap = forwardRef(({
|
|
|
919
1167
|
});
|
|
920
1168
|
return index;
|
|
921
1169
|
}, [map]);
|
|
922
|
-
const labelKeyIndex =
|
|
1170
|
+
const labelKeyIndex = useMemo2(() => {
|
|
923
1171
|
const index = /* @__PURE__ */ new Map();
|
|
924
1172
|
normalizedLayers.forEach((entry) => {
|
|
925
1173
|
const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
|
|
@@ -929,7 +1177,7 @@ var ZenitMap = forwardRef(({
|
|
|
929
1177
|
});
|
|
930
1178
|
return index;
|
|
931
1179
|
}, [normalizedLayers]);
|
|
932
|
-
const layerMetaIndex =
|
|
1180
|
+
const layerMetaIndex = useMemo2(() => {
|
|
933
1181
|
const index = /* @__PURE__ */ new Map();
|
|
934
1182
|
normalizedLayers.forEach((entry) => {
|
|
935
1183
|
index.set(String(entry.layerId), {
|
|
@@ -939,7 +1187,7 @@ var ZenitMap = forwardRef(({
|
|
|
939
1187
|
});
|
|
940
1188
|
return index;
|
|
941
1189
|
}, [normalizedLayers]);
|
|
942
|
-
const overlayStyleFunction =
|
|
1190
|
+
const overlayStyleFunction = useMemo2(() => {
|
|
943
1191
|
return (feature) => {
|
|
944
1192
|
const featureLayerId = getFeatureLayerId(feature);
|
|
945
1193
|
const featureStyleOverrides = getFeatureStyleOverrides(feature);
|
|
@@ -958,11 +1206,15 @@ var ZenitMap = forwardRef(({
|
|
|
958
1206
|
return defaultOptions;
|
|
959
1207
|
};
|
|
960
1208
|
}, [layerStyleIndex, mapLayers, overlayStyle]);
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1209
|
+
const overlayStyleFn = useCallback2(
|
|
1210
|
+
(feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
|
|
1211
|
+
[overlayStyleFunction]
|
|
1212
|
+
);
|
|
1213
|
+
const [baseStates, setBaseStates] = useState2([]);
|
|
1214
|
+
const [mapOverrides, setMapOverrides] = useState2([]);
|
|
1215
|
+
const [controlOverrides, setControlOverrides] = useState2([]);
|
|
1216
|
+
const [uiOverrides, setUiOverrides] = useState2([]);
|
|
1217
|
+
useEffect4(() => {
|
|
966
1218
|
let isMounted = true;
|
|
967
1219
|
setLoadingMap(true);
|
|
968
1220
|
setMapError(null);
|
|
@@ -985,7 +1237,7 @@ var ZenitMap = forwardRef(({
|
|
|
985
1237
|
isMounted = false;
|
|
986
1238
|
};
|
|
987
1239
|
}, [client.maps, mapId, onError, onLoadingChange]);
|
|
988
|
-
|
|
1240
|
+
useEffect4(() => {
|
|
989
1241
|
if (normalizedLayers.length === 0) {
|
|
990
1242
|
setLayers([]);
|
|
991
1243
|
setBaseStates([]);
|
|
@@ -1016,7 +1268,7 @@ var ZenitMap = forwardRef(({
|
|
|
1016
1268
|
setMapOverrides(initialOverrides);
|
|
1017
1269
|
setUiOverrides([]);
|
|
1018
1270
|
}, [normalizedLayers]);
|
|
1019
|
-
|
|
1271
|
+
useEffect4(() => {
|
|
1020
1272
|
if (!layerControls) {
|
|
1021
1273
|
setControlOverrides([]);
|
|
1022
1274
|
return;
|
|
@@ -1028,7 +1280,7 @@ var ZenitMap = forwardRef(({
|
|
|
1028
1280
|
}));
|
|
1029
1281
|
setControlOverrides(overrides);
|
|
1030
1282
|
}, [layerControls]);
|
|
1031
|
-
|
|
1283
|
+
useEffect4(() => {
|
|
1032
1284
|
if (layerStates) {
|
|
1033
1285
|
return;
|
|
1034
1286
|
}
|
|
@@ -1038,12 +1290,12 @@ var ZenitMap = forwardRef(({
|
|
|
1038
1290
|
onLayerStateChange?.(reset);
|
|
1039
1291
|
}
|
|
1040
1292
|
}, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
|
|
1041
|
-
|
|
1293
|
+
useEffect4(() => {
|
|
1042
1294
|
if (layerStates) {
|
|
1043
1295
|
setEffectiveStates(layerStates);
|
|
1044
1296
|
}
|
|
1045
1297
|
}, [layerStates]);
|
|
1046
|
-
|
|
1298
|
+
useEffect4(() => {
|
|
1047
1299
|
if (layerStates) {
|
|
1048
1300
|
return;
|
|
1049
1301
|
}
|
|
@@ -1056,11 +1308,11 @@ var ZenitMap = forwardRef(({
|
|
|
1056
1308
|
setEffectiveStates(next);
|
|
1057
1309
|
onLayerStateChange?.(next);
|
|
1058
1310
|
}, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
|
|
1059
|
-
|
|
1311
|
+
useEffect4(() => {
|
|
1060
1312
|
if (!Array.isArray(layerControls) || layerControls.length > 0) return;
|
|
1061
1313
|
setUiOverrides([]);
|
|
1062
1314
|
}, [layerControls]);
|
|
1063
|
-
|
|
1315
|
+
useEffect4(() => {
|
|
1064
1316
|
if (layerStates) {
|
|
1065
1317
|
return;
|
|
1066
1318
|
}
|
|
@@ -1080,18 +1332,13 @@ var ZenitMap = forwardRef(({
|
|
|
1080
1332
|
return [...filtered, nextEntry];
|
|
1081
1333
|
});
|
|
1082
1334
|
};
|
|
1083
|
-
const updateOpacityFromUi =
|
|
1335
|
+
const updateOpacityFromUi = useCallback2(
|
|
1084
1336
|
(layerId, uiOpacity) => {
|
|
1085
1337
|
const meta = layerMetaIndex.get(String(layerId));
|
|
1086
|
-
const
|
|
1338
|
+
const baseOpacity = clampOpacity3(uiOpacity);
|
|
1339
|
+
const effectiveOpacity = calculateZoomBasedOpacity(
|
|
1087
1340
|
currentZoom,
|
|
1088
|
-
meta?.layerType,
|
|
1089
|
-
meta?.geometryType
|
|
1090
|
-
);
|
|
1091
|
-
const baseOpacity = clampOpacity3(uiOpacity / zoomFactor);
|
|
1092
|
-
const effectiveOpacity = getEffectiveLayerOpacity(
|
|
1093
1341
|
baseOpacity,
|
|
1094
|
-
currentZoom,
|
|
1095
1342
|
meta?.layerType,
|
|
1096
1343
|
meta?.geometryType
|
|
1097
1344
|
);
|
|
@@ -1124,7 +1371,7 @@ var ZenitMap = forwardRef(({
|
|
|
1124
1371
|
},
|
|
1125
1372
|
[currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
|
|
1126
1373
|
);
|
|
1127
|
-
const center =
|
|
1374
|
+
const center = useMemo2(() => {
|
|
1128
1375
|
if (initialCenter) {
|
|
1129
1376
|
return initialCenter;
|
|
1130
1377
|
}
|
|
@@ -1135,36 +1382,30 @@ var ZenitMap = forwardRef(({
|
|
|
1135
1382
|
return DEFAULT_CENTER;
|
|
1136
1383
|
}, [initialCenter, map?.settings?.center]);
|
|
1137
1384
|
const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
|
|
1138
|
-
|
|
1385
|
+
useEffect4(() => {
|
|
1139
1386
|
setCurrentZoom(zoom);
|
|
1140
1387
|
}, [zoom]);
|
|
1141
|
-
const decoratedLayers =
|
|
1388
|
+
const decoratedLayers = useMemo2(() => {
|
|
1142
1389
|
return layers.map((layer) => ({
|
|
1143
1390
|
...layer,
|
|
1144
1391
|
effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
|
|
1145
1392
|
data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
|
|
1146
1393
|
}));
|
|
1147
1394
|
}, [effectiveStates, layerGeojson, layers]);
|
|
1148
|
-
const orderedLayers =
|
|
1395
|
+
const orderedLayers = useMemo2(() => {
|
|
1149
1396
|
return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
|
|
1150
1397
|
}, [decoratedLayers]);
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
const autoZoomBBox = useMemo(() => {
|
|
1157
|
-
if (explicitZoomBBox) return null;
|
|
1158
|
-
const visibleBBoxes = orderedLayers.map((layer) => computeBBoxFromGeojson(layer.data));
|
|
1159
|
-
return mergeBBoxes(visibleBBoxes);
|
|
1160
|
-
}, [explicitZoomBBox, orderedLayers]);
|
|
1161
|
-
const resolveLayerStyle = useCallback(
|
|
1398
|
+
const autoZoomGeojson = useMemo2(
|
|
1399
|
+
() => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
|
|
1400
|
+
[orderedLayers]
|
|
1401
|
+
);
|
|
1402
|
+
const resolveLayerStyle = useCallback2(
|
|
1162
1403
|
(layerId) => {
|
|
1163
1404
|
return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
|
|
1164
1405
|
},
|
|
1165
1406
|
[layerStyleIndex, mapLayers]
|
|
1166
1407
|
);
|
|
1167
|
-
const labelMarkers =
|
|
1408
|
+
const labelMarkers = useMemo2(() => {
|
|
1168
1409
|
const markers = [];
|
|
1169
1410
|
decoratedLayers.forEach((layerState) => {
|
|
1170
1411
|
if (!layerState.effective?.visible) return;
|
|
@@ -1174,7 +1415,14 @@ var ZenitMap = forwardRef(({
|
|
|
1174
1415
|
if (!data) return;
|
|
1175
1416
|
const resolvedStyle = resolveLayerStyle(layerState.mapLayer.layerId);
|
|
1176
1417
|
const layerColor = resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "rgba(37, 99, 235, 1)";
|
|
1177
|
-
const
|
|
1418
|
+
const meta = layerMetaIndex.get(String(layerState.mapLayer.layerId));
|
|
1419
|
+
const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
|
|
1420
|
+
const opacity = calculateZoomBasedOpacity(
|
|
1421
|
+
currentZoom,
|
|
1422
|
+
baseOpacity,
|
|
1423
|
+
meta?.layerType,
|
|
1424
|
+
meta?.geometryType
|
|
1425
|
+
);
|
|
1178
1426
|
data.features.forEach((feature, index) => {
|
|
1179
1427
|
const properties = feature.properties;
|
|
1180
1428
|
const value = properties?.[labelKey];
|
|
@@ -1193,26 +1441,24 @@ var ZenitMap = forwardRef(({
|
|
|
1193
1441
|
});
|
|
1194
1442
|
});
|
|
1195
1443
|
return markers;
|
|
1196
|
-
}, [decoratedLayers, labelKeyIndex, resolveLayerStyle]);
|
|
1197
|
-
const ensureLayerPanes =
|
|
1444
|
+
}, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
|
|
1445
|
+
const ensureLayerPanes = useCallback2(
|
|
1198
1446
|
(targetMap, targetLayers) => {
|
|
1199
1447
|
const baseZIndex = 400;
|
|
1200
1448
|
targetLayers.forEach((layer) => {
|
|
1201
1449
|
const order = Number.isFinite(layer.displayOrder) ? layer.displayOrder : 0;
|
|
1450
|
+
const orderOffset = Math.max(0, Math.min(order, 150));
|
|
1202
1451
|
const fillPaneName = `zenit-layer-${layer.layerId}-fill`;
|
|
1203
1452
|
const pointPaneName = `zenit-layer-${layer.layerId}-points`;
|
|
1204
|
-
const labelPaneName = `zenit-layer-${layer.layerId}-labels`;
|
|
1205
1453
|
const fillPane = targetMap.getPane(fillPaneName) ?? targetMap.createPane(fillPaneName);
|
|
1206
1454
|
const pointPane = targetMap.getPane(pointPaneName) ?? targetMap.createPane(pointPaneName);
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
pointPane.style.zIndex = String(baseZIndex + order + 1e3);
|
|
1210
|
-
labelPane.style.zIndex = String(baseZIndex + order + 2e3);
|
|
1455
|
+
fillPane.style.zIndex = String(baseZIndex + orderOffset);
|
|
1456
|
+
pointPane.style.zIndex = String(baseZIndex + orderOffset + 100);
|
|
1211
1457
|
});
|
|
1212
1458
|
},
|
|
1213
1459
|
[]
|
|
1214
1460
|
);
|
|
1215
|
-
const handleMapReady =
|
|
1461
|
+
const handleMapReady = useCallback2(
|
|
1216
1462
|
(instance) => {
|
|
1217
1463
|
setPanesReady(false);
|
|
1218
1464
|
setMapInstance(instance);
|
|
@@ -1220,7 +1466,7 @@ var ZenitMap = forwardRef(({
|
|
|
1220
1466
|
},
|
|
1221
1467
|
[onMapReady]
|
|
1222
1468
|
);
|
|
1223
|
-
|
|
1469
|
+
useEffect4(() => {
|
|
1224
1470
|
if (!mapInstance) {
|
|
1225
1471
|
setPanesReady(false);
|
|
1226
1472
|
return;
|
|
@@ -1234,24 +1480,25 @@ var ZenitMap = forwardRef(({
|
|
|
1234
1480
|
}));
|
|
1235
1481
|
ensureLayerPanes(mapInstance, layerTargets);
|
|
1236
1482
|
const first = layerTargets[0];
|
|
1237
|
-
const testPane = mapInstance.getPane(`zenit-layer-${first.layerId}-
|
|
1238
|
-
|
|
1483
|
+
const testPane = mapInstance.getPane(`zenit-layer-${first.layerId}-fill`);
|
|
1484
|
+
const labelsPane = mapInstance.getPane(LABELS_PANE_NAME);
|
|
1485
|
+
if (testPane && labelsPane) {
|
|
1239
1486
|
setPanesReady(true);
|
|
1240
1487
|
}
|
|
1241
1488
|
}, [mapInstance, orderedLayers, ensureLayerPanes]);
|
|
1242
|
-
const overlayOnEachFeature =
|
|
1489
|
+
const overlayOnEachFeature = useMemo2(() => {
|
|
1243
1490
|
return (feature, layer) => {
|
|
1244
1491
|
const layerId = getFeatureLayerId(feature) ?? void 0;
|
|
1245
1492
|
const geometryType = feature?.geometry?.type;
|
|
1246
|
-
const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof
|
|
1247
|
-
const originalStyle = layer instanceof
|
|
1493
|
+
const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof L4.CircleMarker;
|
|
1494
|
+
const originalStyle = layer instanceof L4.Path ? {
|
|
1248
1495
|
color: layer.options.color,
|
|
1249
1496
|
weight: layer.options.weight,
|
|
1250
1497
|
fillColor: layer.options.fillColor,
|
|
1251
1498
|
opacity: layer.options.opacity,
|
|
1252
1499
|
fillOpacity: layer.options.fillOpacity
|
|
1253
1500
|
} : null;
|
|
1254
|
-
const originalRadius = layer instanceof
|
|
1501
|
+
const originalRadius = layer instanceof L4.CircleMarker ? layer.getRadius() : null;
|
|
1255
1502
|
if (featureInfoMode === "popup") {
|
|
1256
1503
|
const content = buildFeaturePopupHtml(feature);
|
|
1257
1504
|
if (content) {
|
|
@@ -1260,11 +1507,10 @@ var ZenitMap = forwardRef(({
|
|
|
1260
1507
|
maxWidth,
|
|
1261
1508
|
minWidth,
|
|
1262
1509
|
maxHeight,
|
|
1263
|
-
className: "
|
|
1510
|
+
className: "custom-leaflet-popup",
|
|
1264
1511
|
autoPan: true,
|
|
1265
1512
|
closeButton: true,
|
|
1266
|
-
keepInView: true
|
|
1267
|
-
offset: L.point(0, -24)
|
|
1513
|
+
keepInView: true
|
|
1268
1514
|
});
|
|
1269
1515
|
}
|
|
1270
1516
|
}
|
|
@@ -1285,7 +1531,8 @@ var ZenitMap = forwardRef(({
|
|
|
1285
1531
|
id: layerId,
|
|
1286
1532
|
geometry: feature.geometry
|
|
1287
1533
|
}).then((response) => {
|
|
1288
|
-
const
|
|
1534
|
+
const geo = extractGeoJsonFeatureCollection(response);
|
|
1535
|
+
const candidates = geo?.features ?? [];
|
|
1289
1536
|
const resolved = pickIntersectFeature(feature, candidates);
|
|
1290
1537
|
if (!resolved?.properties) return;
|
|
1291
1538
|
const mergedProperties = {
|
|
@@ -1308,7 +1555,7 @@ var ZenitMap = forwardRef(({
|
|
|
1308
1555
|
onFeatureClick?.(feature, layerId);
|
|
1309
1556
|
});
|
|
1310
1557
|
layer.on("mouseover", () => {
|
|
1311
|
-
if (layer instanceof
|
|
1558
|
+
if (layer instanceof L4.Path && originalStyle) {
|
|
1312
1559
|
layer.setStyle({
|
|
1313
1560
|
...originalStyle,
|
|
1314
1561
|
weight: (originalStyle.weight ?? 2) + 1,
|
|
@@ -1316,16 +1563,16 @@ var ZenitMap = forwardRef(({
|
|
|
1316
1563
|
fillOpacity: Math.min(1, (originalStyle.fillOpacity ?? 0.8) + 0.1)
|
|
1317
1564
|
});
|
|
1318
1565
|
}
|
|
1319
|
-
if (layer instanceof
|
|
1566
|
+
if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
|
|
1320
1567
|
layer.setRadius(originalRadius + 1);
|
|
1321
1568
|
}
|
|
1322
1569
|
onFeatureHover?.(feature, layerId);
|
|
1323
1570
|
});
|
|
1324
1571
|
layer.on("mouseout", () => {
|
|
1325
|
-
if (layer instanceof
|
|
1572
|
+
if (layer instanceof L4.Path && originalStyle) {
|
|
1326
1573
|
layer.setStyle(originalStyle);
|
|
1327
1574
|
}
|
|
1328
|
-
if (layer instanceof
|
|
1575
|
+
if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
|
|
1329
1576
|
layer.setRadius(originalRadius);
|
|
1330
1577
|
}
|
|
1331
1578
|
});
|
|
@@ -1337,79 +1584,19 @@ var ZenitMap = forwardRef(({
|
|
|
1337
1584
|
const resolvedStyle = featureStyleOverrides ? { ...style ?? {}, ...featureStyleOverrides } : style;
|
|
1338
1585
|
const geometryType = feature?.geometry?.type;
|
|
1339
1586
|
const resolvedLayerType = layerType ?? geometryType;
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
resolvedLayerType
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
color: resolvedStyle?.color ?? resolvedStyle?.fillColor ?? "#2563eb",
|
|
1352
|
-
weight: resolvedStyle?.weight ?? 2,
|
|
1353
|
-
fillColor: resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "#2563eb",
|
|
1354
|
-
opacity: strokeOpacity,
|
|
1355
|
-
fillOpacity
|
|
1587
|
+
return layerStyleToLeaflet({
|
|
1588
|
+
baseOpacity,
|
|
1589
|
+
zoom: currentZoom,
|
|
1590
|
+
layerStyle: resolvedStyle,
|
|
1591
|
+
geometryType,
|
|
1592
|
+
layerType: resolvedLayerType
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
1595
|
+
const makeStyleFnForLayer = (layerId) => {
|
|
1596
|
+
return (feature, layerType, baseOpacity) => {
|
|
1597
|
+
return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
|
|
1356
1598
|
};
|
|
1357
1599
|
};
|
|
1358
|
-
const buildLabelIcon = useCallback((label, opacity, color) => {
|
|
1359
|
-
const size = 60;
|
|
1360
|
-
const innerSize = 44;
|
|
1361
|
-
const textStyles = getLabelTextStyles(color);
|
|
1362
|
-
const safeLabel = escapeHtml(label);
|
|
1363
|
-
const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
|
|
1364
|
-
const innerBackground = withAlpha(color, 0.9);
|
|
1365
|
-
return L.divIcon({
|
|
1366
|
-
className: "zenit-label-marker",
|
|
1367
|
-
iconSize: [size, size],
|
|
1368
|
-
iconAnchor: [size / 2, size / 2],
|
|
1369
|
-
html: `
|
|
1370
|
-
<div
|
|
1371
|
-
title="${safeLabel}"
|
|
1372
|
-
style="
|
|
1373
|
-
width:${size}px;
|
|
1374
|
-
height:${size}px;
|
|
1375
|
-
border-radius:9999px;
|
|
1376
|
-
background:rgba(255, 255, 255, 0.95);
|
|
1377
|
-
border:3px solid rgba(255, 255, 255, 1);
|
|
1378
|
-
display:flex;
|
|
1379
|
-
align-items:center;
|
|
1380
|
-
justify-content:center;
|
|
1381
|
-
opacity:${clampedOpacity};
|
|
1382
|
-
box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
|
|
1383
|
-
pointer-events:none;
|
|
1384
|
-
"
|
|
1385
|
-
>
|
|
1386
|
-
<div
|
|
1387
|
-
style="
|
|
1388
|
-
width:${innerSize}px;
|
|
1389
|
-
height:${innerSize}px;
|
|
1390
|
-
border-radius:9999px;
|
|
1391
|
-
background:${innerBackground};
|
|
1392
|
-
display:flex;
|
|
1393
|
-
align-items:center;
|
|
1394
|
-
justify-content:center;
|
|
1395
|
-
box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
|
|
1396
|
-
"
|
|
1397
|
-
>
|
|
1398
|
-
<span
|
|
1399
|
-
style="
|
|
1400
|
-
color:${textStyles.color};
|
|
1401
|
-
font-size:20px;
|
|
1402
|
-
font-weight:800;
|
|
1403
|
-
text-shadow:${textStyles.shadow};
|
|
1404
|
-
"
|
|
1405
|
-
>
|
|
1406
|
-
${safeLabel}
|
|
1407
|
-
</span>
|
|
1408
|
-
</div>
|
|
1409
|
-
</div>
|
|
1410
|
-
`
|
|
1411
|
-
});
|
|
1412
|
-
}, []);
|
|
1413
1600
|
useImperativeHandle(ref, () => ({
|
|
1414
1601
|
setLayerOpacity: (layerId, opacity) => {
|
|
1415
1602
|
upsertUiOverride(layerId, { overrideOpacity: opacity });
|
|
@@ -1460,10 +1647,10 @@ var ZenitMap = forwardRef(({
|
|
|
1460
1647
|
getMapInstance: () => mapInstance
|
|
1461
1648
|
}), [effectiveStates, mapInstance]);
|
|
1462
1649
|
if (loadingMap) {
|
|
1463
|
-
return /* @__PURE__ */
|
|
1650
|
+
return /* @__PURE__ */ jsx3("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
|
|
1464
1651
|
}
|
|
1465
1652
|
if (mapError) {
|
|
1466
|
-
return /* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsxs3("div", { style: { padding: 16, height, width, color: "red" }, children: [
|
|
1467
1654
|
"Error al cargar mapa: ",
|
|
1468
1655
|
mapError
|
|
1469
1656
|
] });
|
|
@@ -1475,7 +1662,7 @@ var ZenitMap = forwardRef(({
|
|
|
1475
1662
|
setCurrentZoom(zoomValue);
|
|
1476
1663
|
onZoomChange?.(zoomValue);
|
|
1477
1664
|
};
|
|
1478
|
-
return /* @__PURE__ */
|
|
1665
|
+
return /* @__PURE__ */ jsxs3(
|
|
1479
1666
|
"div",
|
|
1480
1667
|
{
|
|
1481
1668
|
style: {
|
|
@@ -1488,88 +1675,111 @@ var ZenitMap = forwardRef(({
|
|
|
1488
1675
|
boxSizing: "border-box"
|
|
1489
1676
|
},
|
|
1490
1677
|
children: [
|
|
1491
|
-
/* @__PURE__ */
|
|
1492
|
-
|
|
1678
|
+
/* @__PURE__ */ jsx3(
|
|
1679
|
+
"div",
|
|
1493
1680
|
{
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
/* @__PURE__ */ jsx(ZoomControl, { position: "topright" }),
|
|
1508
|
-
/* @__PURE__ */ jsx(MapInstanceBridge, { onReady: handleMapReady }),
|
|
1509
|
-
/* @__PURE__ */ jsx(FitToBounds, { bbox: explicitZoomBBox ?? void 0 }),
|
|
1510
|
-
/* @__PURE__ */ jsx(AutoFitToBounds, { bbox: autoZoomBBox ?? void 0, enabled: !explicitZoomBBox }),
|
|
1511
|
-
/* @__PURE__ */ jsx(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
|
|
1512
|
-
orderedLayers.map((layerState) => {
|
|
1513
|
-
const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
|
|
1514
|
-
const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
|
|
1515
|
-
const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
|
|
1516
|
-
const labelPaneName = `zenit-layer-${layerState.mapLayer.layerId}-labels`;
|
|
1517
|
-
const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
|
|
1518
|
-
const data = layerState.data?.features ?? [];
|
|
1519
|
-
const fillFeatures = data.filter(isNonPointGeometry);
|
|
1520
|
-
const pointFeatures = data.filter(isPointGeometry);
|
|
1521
|
-
const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
|
|
1522
|
-
const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
|
|
1523
|
-
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
1524
|
-
fillData && /* @__PURE__ */ jsx(
|
|
1525
|
-
GeoJSON,
|
|
1681
|
+
className: `zenit-map-shell${isPopupOpen ? " popup-open" : ""}`,
|
|
1682
|
+
style: { flex: 1, position: "relative" },
|
|
1683
|
+
children: /* @__PURE__ */ jsxs3(
|
|
1684
|
+
MapContainer,
|
|
1685
|
+
{
|
|
1686
|
+
center,
|
|
1687
|
+
zoom,
|
|
1688
|
+
style: { height: "100%", width: "100%" },
|
|
1689
|
+
scrollWheelZoom: true,
|
|
1690
|
+
zoomControl: false,
|
|
1691
|
+
children: [
|
|
1692
|
+
/* @__PURE__ */ jsx3(
|
|
1693
|
+
TileLayer,
|
|
1526
1694
|
{
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
|
|
1530
|
-
onEachFeature: overlayOnEachFeature
|
|
1695
|
+
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
1696
|
+
attribution: "\xA9 OpenStreetMap contributors"
|
|
1531
1697
|
}
|
|
1532
1698
|
),
|
|
1533
|
-
|
|
1534
|
-
|
|
1699
|
+
/* @__PURE__ */ jsx3(ZoomControl, { position: "topright" }),
|
|
1700
|
+
/* @__PURE__ */ jsx3(MapInstanceBridge, { onReady: handleMapReady }),
|
|
1701
|
+
/* @__PURE__ */ jsx3(
|
|
1702
|
+
BBoxZoomHandler,
|
|
1535
1703
|
{
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
radius: isMobile ? 8 : 6,
|
|
1540
|
-
...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
|
|
1541
|
-
}),
|
|
1542
|
-
onEachFeature: overlayOnEachFeature
|
|
1704
|
+
bbox: zoomToBbox ?? void 0,
|
|
1705
|
+
geojson: zoomToGeojson ?? void 0,
|
|
1706
|
+
autoGeojson: autoZoomGeojson
|
|
1543
1707
|
}
|
|
1544
1708
|
),
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1709
|
+
/* @__PURE__ */ jsx3(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
|
|
1710
|
+
/* @__PURE__ */ jsx3(LocationControl, {}),
|
|
1711
|
+
orderedLayers.map((layerState) => {
|
|
1712
|
+
const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
|
|
1713
|
+
const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
|
|
1714
|
+
const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
|
|
1715
|
+
const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
|
|
1716
|
+
const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
|
|
1717
|
+
return /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
|
|
1718
|
+
layerState.data && /* @__PURE__ */ jsx3(
|
|
1719
|
+
LayerGeoJson,
|
|
1720
|
+
{
|
|
1721
|
+
layerId: layerState.mapLayer.layerId,
|
|
1722
|
+
data: layerState.data,
|
|
1723
|
+
baseOpacity,
|
|
1724
|
+
isMobile,
|
|
1725
|
+
panesReady,
|
|
1726
|
+
mapInstance,
|
|
1727
|
+
fillPaneName,
|
|
1728
|
+
pointsPaneName,
|
|
1729
|
+
layerType,
|
|
1730
|
+
styleFn: makeStyleFnForLayer(layerState.mapLayer.layerId),
|
|
1731
|
+
onEachFeature: overlayOnEachFeature,
|
|
1732
|
+
onPolygonLabel: labelKey ? (feature, layer) => {
|
|
1733
|
+
const geometryType = feature?.geometry?.type;
|
|
1734
|
+
if (geometryType !== "Polygon" && geometryType !== "MultiPolygon") return;
|
|
1735
|
+
const properties = feature?.properties;
|
|
1736
|
+
const value = properties?.[labelKey];
|
|
1737
|
+
if (!value || !layer.bindTooltip) return;
|
|
1738
|
+
layer.bindTooltip(String(value), {
|
|
1739
|
+
sticky: true,
|
|
1740
|
+
direction: "center",
|
|
1741
|
+
opacity: 0.9,
|
|
1742
|
+
className: "polygon-label-tooltip"
|
|
1743
|
+
});
|
|
1744
|
+
} : void 0
|
|
1745
|
+
}
|
|
1746
|
+
),
|
|
1747
|
+
panesReady && mapInstance?.getPane(LABELS_PANE_NAME) ? labelMarkers.filter(
|
|
1748
|
+
(marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
|
|
1749
|
+
).map((marker) => /* @__PURE__ */ jsx3(
|
|
1750
|
+
Marker2,
|
|
1751
|
+
{
|
|
1752
|
+
position: marker.position,
|
|
1753
|
+
icon: createCustomIcon(marker.label, marker.opacity, marker.color),
|
|
1754
|
+
interactive: false,
|
|
1755
|
+
pane: LABELS_PANE_NAME
|
|
1756
|
+
},
|
|
1757
|
+
marker.key
|
|
1758
|
+
)) : null
|
|
1759
|
+
] }, layerState.mapLayer.layerId.toString());
|
|
1760
|
+
}),
|
|
1761
|
+
overlayGeojson && /* @__PURE__ */ jsx3(
|
|
1762
|
+
LayerGeoJson,
|
|
1549
1763
|
{
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
},
|
|
1570
|
-
String(mapId)
|
|
1571
|
-
) }),
|
|
1572
|
-
showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs(
|
|
1764
|
+
layerId: "overlay-geojson",
|
|
1765
|
+
data: overlayGeojson,
|
|
1766
|
+
baseOpacity: 1,
|
|
1767
|
+
isMobile,
|
|
1768
|
+
panesReady,
|
|
1769
|
+
mapInstance,
|
|
1770
|
+
fillPaneName: "zenit-overlay-fill",
|
|
1771
|
+
pointsPaneName: "zenit-overlay-points",
|
|
1772
|
+
styleFn: overlayStyleFn,
|
|
1773
|
+
onEachFeature: overlayOnEachFeature
|
|
1774
|
+
}
|
|
1775
|
+
)
|
|
1776
|
+
]
|
|
1777
|
+
},
|
|
1778
|
+
String(mapId)
|
|
1779
|
+
)
|
|
1780
|
+
}
|
|
1781
|
+
),
|
|
1782
|
+
showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs3(
|
|
1573
1783
|
"div",
|
|
1574
1784
|
{
|
|
1575
1785
|
style: {
|
|
@@ -1582,7 +1792,7 @@ var ZenitMap = forwardRef(({
|
|
|
1582
1792
|
overflowY: "auto"
|
|
1583
1793
|
},
|
|
1584
1794
|
children: [
|
|
1585
|
-
overlayGeojson && /* @__PURE__ */
|
|
1795
|
+
overlayGeojson && /* @__PURE__ */ jsxs3(
|
|
1586
1796
|
"div",
|
|
1587
1797
|
{
|
|
1588
1798
|
style: {
|
|
@@ -1594,8 +1804,8 @@ var ZenitMap = forwardRef(({
|
|
|
1594
1804
|
marginBottom: 12
|
|
1595
1805
|
},
|
|
1596
1806
|
children: [
|
|
1597
|
-
/* @__PURE__ */
|
|
1598
|
-
/* @__PURE__ */
|
|
1807
|
+
/* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
|
|
1808
|
+
/* @__PURE__ */ jsxs3("div", { style: { fontSize: 13 }, children: [
|
|
1599
1809
|
"GeoJSON externo con ",
|
|
1600
1810
|
(overlayGeojson.features?.length ?? 0).toLocaleString(),
|
|
1601
1811
|
" elementos."
|
|
@@ -1603,14 +1813,14 @@ var ZenitMap = forwardRef(({
|
|
|
1603
1813
|
]
|
|
1604
1814
|
}
|
|
1605
1815
|
),
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
decoratedLayers.map((layerState) => /* @__PURE__ */
|
|
1816
|
+
/* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
|
|
1817
|
+
decoratedLayers.map((layerState) => /* @__PURE__ */ jsxs3(
|
|
1608
1818
|
"div",
|
|
1609
1819
|
{
|
|
1610
1820
|
style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 10, marginBottom: 10 },
|
|
1611
1821
|
children: [
|
|
1612
|
-
/* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
1822
|
+
/* @__PURE__ */ jsxs3("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
1823
|
+
/* @__PURE__ */ jsx3(
|
|
1614
1824
|
"input",
|
|
1615
1825
|
{
|
|
1616
1826
|
type: "checkbox",
|
|
@@ -1621,17 +1831,17 @@ var ZenitMap = forwardRef(({
|
|
|
1621
1831
|
}
|
|
1622
1832
|
}
|
|
1623
1833
|
),
|
|
1624
|
-
/* @__PURE__ */
|
|
1834
|
+
/* @__PURE__ */ jsx3("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
|
|
1625
1835
|
] }),
|
|
1626
|
-
/* @__PURE__ */
|
|
1627
|
-
/* @__PURE__ */
|
|
1628
|
-
/* @__PURE__ */
|
|
1629
|
-
/* @__PURE__ */
|
|
1836
|
+
/* @__PURE__ */ jsxs3("div", { style: { marginTop: 8 }, children: [
|
|
1837
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
|
|
1838
|
+
/* @__PURE__ */ jsx3("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
|
|
1839
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
1630
1840
|
Math.round((layerState.effective?.opacity ?? 1) * 100),
|
|
1631
1841
|
"%"
|
|
1632
1842
|
] })
|
|
1633
1843
|
] }),
|
|
1634
|
-
/* @__PURE__ */
|
|
1844
|
+
/* @__PURE__ */ jsx3(
|
|
1635
1845
|
"input",
|
|
1636
1846
|
{
|
|
1637
1847
|
type: "range",
|
|
@@ -1661,13 +1871,13 @@ var ZenitMap = forwardRef(({
|
|
|
1661
1871
|
ZenitMap.displayName = "ZenitMap";
|
|
1662
1872
|
|
|
1663
1873
|
// src/react/ZenitLayerManager.tsx
|
|
1664
|
-
import
|
|
1874
|
+
import React4, { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
|
|
1665
1875
|
|
|
1666
1876
|
// src/react/icons.tsx
|
|
1667
1877
|
import { Eye, EyeOff, ChevronLeft, ChevronRight, Layers, Upload, X, ZoomIn } from "lucide-react";
|
|
1668
1878
|
|
|
1669
1879
|
// src/react/ZenitLayerManager.tsx
|
|
1670
|
-
import { jsx as
|
|
1880
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1671
1881
|
var FLOAT_TOLERANCE = 1e-3;
|
|
1672
1882
|
function areEffectiveStatesEqual(a, b) {
|
|
1673
1883
|
if (a.length !== b.length) return false;
|
|
@@ -1707,15 +1917,15 @@ var ZenitLayerManager = ({
|
|
|
1707
1917
|
layerFeatureCounts,
|
|
1708
1918
|
mapLayers
|
|
1709
1919
|
}) => {
|
|
1710
|
-
const [map, setMap] =
|
|
1711
|
-
const [loadingMap, setLoadingMap] =
|
|
1712
|
-
const [mapError, setMapError] =
|
|
1713
|
-
const [layers, setLayers] =
|
|
1714
|
-
const [activeTab, setActiveTab] =
|
|
1715
|
-
const [panelVisible, setPanelVisible] =
|
|
1716
|
-
const lastEmittedStatesRef =
|
|
1920
|
+
const [map, setMap] = useState3(null);
|
|
1921
|
+
const [loadingMap, setLoadingMap] = useState3(false);
|
|
1922
|
+
const [mapError, setMapError] = useState3(null);
|
|
1923
|
+
const [layers, setLayers] = useState3([]);
|
|
1924
|
+
const [activeTab, setActiveTab] = useState3("layers");
|
|
1925
|
+
const [panelVisible, setPanelVisible] = useState3(true);
|
|
1926
|
+
const lastEmittedStatesRef = useRef5(null);
|
|
1717
1927
|
const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
|
|
1718
|
-
const baseStates =
|
|
1928
|
+
const baseStates = useMemo3(
|
|
1719
1929
|
() => initLayerStates(
|
|
1720
1930
|
layers.map((entry) => ({
|
|
1721
1931
|
...entry.mapLayer,
|
|
@@ -1726,7 +1936,7 @@ var ZenitLayerManager = ({
|
|
|
1726
1936
|
),
|
|
1727
1937
|
[layers]
|
|
1728
1938
|
);
|
|
1729
|
-
const overrideStates =
|
|
1939
|
+
const overrideStates = useMemo3(
|
|
1730
1940
|
() => layers.map(
|
|
1731
1941
|
(entry) => ({
|
|
1732
1942
|
layerId: entry.mapLayer.layerId,
|
|
@@ -1736,11 +1946,11 @@ var ZenitLayerManager = ({
|
|
|
1736
1946
|
),
|
|
1737
1947
|
[layers]
|
|
1738
1948
|
);
|
|
1739
|
-
const effectiveStates =
|
|
1949
|
+
const effectiveStates = useMemo3(
|
|
1740
1950
|
() => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
|
|
1741
1951
|
[baseStates, layerStates, overrideStates]
|
|
1742
1952
|
);
|
|
1743
|
-
const layerMetaIndex =
|
|
1953
|
+
const layerMetaIndex = useMemo3(() => {
|
|
1744
1954
|
const index = /* @__PURE__ */ new Map();
|
|
1745
1955
|
mapLayers?.forEach((entry) => {
|
|
1746
1956
|
const key = String(entry.layerId);
|
|
@@ -1754,7 +1964,7 @@ var ZenitLayerManager = ({
|
|
|
1754
1964
|
});
|
|
1755
1965
|
return index;
|
|
1756
1966
|
}, [map, mapLayers]);
|
|
1757
|
-
const resolveUserOpacity =
|
|
1967
|
+
const resolveUserOpacity = React4.useCallback((state) => {
|
|
1758
1968
|
if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
|
|
1759
1969
|
if (typeof state.overrideOpacity === "string") {
|
|
1760
1970
|
const parsed = Number.parseFloat(state.overrideOpacity);
|
|
@@ -1762,7 +1972,7 @@ var ZenitLayerManager = ({
|
|
|
1762
1972
|
}
|
|
1763
1973
|
return state.opacity ?? 1;
|
|
1764
1974
|
}, []);
|
|
1765
|
-
const resolveEffectiveOpacity =
|
|
1975
|
+
const resolveEffectiveOpacity = React4.useCallback(
|
|
1766
1976
|
(layerId, userOpacity) => {
|
|
1767
1977
|
if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
|
|
1768
1978
|
return userOpacity;
|
|
@@ -1778,7 +1988,7 @@ var ZenitLayerManager = ({
|
|
|
1778
1988
|
},
|
|
1779
1989
|
[autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
|
|
1780
1990
|
);
|
|
1781
|
-
const effectiveStatesWithZoom =
|
|
1991
|
+
const effectiveStatesWithZoom = useMemo3(() => {
|
|
1782
1992
|
if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
|
|
1783
1993
|
return effectiveStates;
|
|
1784
1994
|
}
|
|
@@ -1792,7 +2002,7 @@ var ZenitLayerManager = ({
|
|
|
1792
2002
|
};
|
|
1793
2003
|
});
|
|
1794
2004
|
}, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
|
|
1795
|
-
|
|
2005
|
+
useEffect5(() => {
|
|
1796
2006
|
let cancelled = false;
|
|
1797
2007
|
setLoadingMap(true);
|
|
1798
2008
|
setMapError(null);
|
|
@@ -1824,12 +2034,12 @@ var ZenitLayerManager = ({
|
|
|
1824
2034
|
cancelled = true;
|
|
1825
2035
|
};
|
|
1826
2036
|
}, [client.maps, mapId]);
|
|
1827
|
-
|
|
2037
|
+
useEffect5(() => {
|
|
1828
2038
|
if (!showUploadTab && activeTab === "upload") {
|
|
1829
2039
|
setActiveTab("layers");
|
|
1830
2040
|
}
|
|
1831
2041
|
}, [activeTab, showUploadTab]);
|
|
1832
|
-
|
|
2042
|
+
useEffect5(() => {
|
|
1833
2043
|
if (isControlled) return;
|
|
1834
2044
|
if (!onLayerStatesChange) return;
|
|
1835
2045
|
const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
|
|
@@ -1847,7 +2057,7 @@ var ZenitLayerManager = ({
|
|
|
1847
2057
|
mapZoom,
|
|
1848
2058
|
onLayerStatesChange
|
|
1849
2059
|
]);
|
|
1850
|
-
const updateLayerVisible =
|
|
2060
|
+
const updateLayerVisible = React4.useCallback(
|
|
1851
2061
|
(layerId, visible) => {
|
|
1852
2062
|
if (!onLayerStatesChange) return;
|
|
1853
2063
|
const next = effectiveStates.map(
|
|
@@ -1857,7 +2067,7 @@ var ZenitLayerManager = ({
|
|
|
1857
2067
|
},
|
|
1858
2068
|
[effectiveStates, onLayerStatesChange]
|
|
1859
2069
|
);
|
|
1860
|
-
const updateLayerOpacity =
|
|
2070
|
+
const updateLayerOpacity = React4.useCallback(
|
|
1861
2071
|
(layerId, opacity) => {
|
|
1862
2072
|
if (!onLayerStatesChange) return;
|
|
1863
2073
|
const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
|
|
@@ -1868,7 +2078,7 @@ var ZenitLayerManager = ({
|
|
|
1868
2078
|
},
|
|
1869
2079
|
[effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
|
|
1870
2080
|
);
|
|
1871
|
-
const resolveFeatureCount =
|
|
2081
|
+
const resolveFeatureCount = React4.useCallback(
|
|
1872
2082
|
(layerId, layer) => {
|
|
1873
2083
|
const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
|
|
1874
2084
|
if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
|
|
@@ -1877,7 +2087,7 @@ var ZenitLayerManager = ({
|
|
|
1877
2087
|
},
|
|
1878
2088
|
[layerFeatureCounts]
|
|
1879
2089
|
);
|
|
1880
|
-
const decoratedLayers =
|
|
2090
|
+
const decoratedLayers = useMemo3(() => {
|
|
1881
2091
|
return layers.map((entry) => ({
|
|
1882
2092
|
...entry,
|
|
1883
2093
|
effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
|
|
@@ -1906,7 +2116,7 @@ var ZenitLayerManager = ({
|
|
|
1906
2116
|
return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
|
|
1907
2117
|
});
|
|
1908
2118
|
}, [effectiveStates, layers, resolveFeatureCount]);
|
|
1909
|
-
const resolveLayerStyle =
|
|
2119
|
+
const resolveLayerStyle = React4.useCallback(
|
|
1910
2120
|
(layerId) => {
|
|
1911
2121
|
const layerKey = String(layerId);
|
|
1912
2122
|
const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
|
|
@@ -1936,10 +2146,10 @@ var ZenitLayerManager = ({
|
|
|
1936
2146
|
...height ? { height } : {}
|
|
1937
2147
|
};
|
|
1938
2148
|
if (loadingMap) {
|
|
1939
|
-
return /* @__PURE__ */
|
|
2149
|
+
return /* @__PURE__ */ jsx4("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
|
|
1940
2150
|
}
|
|
1941
2151
|
if (mapError) {
|
|
1942
|
-
return /* @__PURE__ */
|
|
2152
|
+
return /* @__PURE__ */ jsxs4("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
|
|
1943
2153
|
"Error al cargar mapa: ",
|
|
1944
2154
|
mapError
|
|
1945
2155
|
] });
|
|
@@ -1957,7 +2167,7 @@ var ZenitLayerManager = ({
|
|
|
1957
2167
|
boxShadow: "0 1px 0 rgba(148, 163, 184, 0.25)"
|
|
1958
2168
|
};
|
|
1959
2169
|
const renderLayerCards = () => {
|
|
1960
|
-
return /* @__PURE__ */
|
|
2170
|
+
return /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
|
|
1961
2171
|
const layerId = layerState.mapLayer.layerId;
|
|
1962
2172
|
const layerName = layerState.layerName ?? `Capa ${layerId}`;
|
|
1963
2173
|
const visible = layerState.effective?.visible ?? false;
|
|
@@ -1967,7 +2177,7 @@ var ZenitLayerManager = ({
|
|
|
1967
2177
|
const muted = !visible;
|
|
1968
2178
|
const opacityPercent = Math.round(userOpacity * 100);
|
|
1969
2179
|
const sliderBackground = `linear-gradient(to right, ${layerColor} 0%, ${layerColor} ${opacityPercent}%, #e5e7eb ${opacityPercent}%, #e5e7eb 100%)`;
|
|
1970
|
-
return /* @__PURE__ */
|
|
2180
|
+
return /* @__PURE__ */ jsxs4(
|
|
1971
2181
|
"div",
|
|
1972
2182
|
{
|
|
1973
2183
|
className: `zlm-card${muted ? " is-muted" : ""}`,
|
|
@@ -1982,9 +2192,9 @@ var ZenitLayerManager = ({
|
|
|
1982
2192
|
width: "100%"
|
|
1983
2193
|
},
|
|
1984
2194
|
children: [
|
|
1985
|
-
/* @__PURE__ */
|
|
1986
|
-
/* @__PURE__ */
|
|
1987
|
-
/* @__PURE__ */
|
|
2195
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
|
|
2196
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
|
|
2197
|
+
/* @__PURE__ */ jsx4(
|
|
1988
2198
|
"div",
|
|
1989
2199
|
{
|
|
1990
2200
|
style: {
|
|
@@ -1999,7 +2209,7 @@ var ZenitLayerManager = ({
|
|
|
1999
2209
|
title: "Color de la capa"
|
|
2000
2210
|
}
|
|
2001
2211
|
),
|
|
2002
|
-
showLayerVisibilityIcon && /* @__PURE__ */
|
|
2212
|
+
showLayerVisibilityIcon && /* @__PURE__ */ jsx4(
|
|
2003
2213
|
"button",
|
|
2004
2214
|
{
|
|
2005
2215
|
type: "button",
|
|
@@ -2010,11 +2220,11 @@ var ZenitLayerManager = ({
|
|
|
2010
2220
|
)
|
|
2011
2221
|
),
|
|
2012
2222
|
"aria-label": visible ? "Ocultar capa" : "Mostrar capa",
|
|
2013
|
-
children: visible ? /* @__PURE__ */
|
|
2223
|
+
children: visible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 })
|
|
2014
2224
|
}
|
|
2015
2225
|
),
|
|
2016
|
-
/* @__PURE__ */
|
|
2017
|
-
/* @__PURE__ */
|
|
2226
|
+
/* @__PURE__ */ jsxs4("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
2227
|
+
/* @__PURE__ */ jsx4(
|
|
2018
2228
|
"div",
|
|
2019
2229
|
{
|
|
2020
2230
|
className: "zlm-layer-name",
|
|
@@ -2032,26 +2242,26 @@ var ZenitLayerManager = ({
|
|
|
2032
2242
|
children: layerName
|
|
2033
2243
|
}
|
|
2034
2244
|
),
|
|
2035
|
-
/* @__PURE__ */
|
|
2245
|
+
/* @__PURE__ */ jsxs4("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
|
|
2036
2246
|
"ID ",
|
|
2037
2247
|
layerId
|
|
2038
2248
|
] })
|
|
2039
2249
|
] })
|
|
2040
2250
|
] }),
|
|
2041
|
-
/* @__PURE__ */
|
|
2251
|
+
/* @__PURE__ */ jsx4("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ jsxs4("span", { className: "zlm-badge", children: [
|
|
2042
2252
|
featureCount.toLocaleString(),
|
|
2043
2253
|
" features"
|
|
2044
2254
|
] }) })
|
|
2045
2255
|
] }),
|
|
2046
|
-
/* @__PURE__ */
|
|
2047
|
-
/* @__PURE__ */
|
|
2048
|
-
/* @__PURE__ */
|
|
2049
|
-
/* @__PURE__ */
|
|
2256
|
+
/* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ jsxs4("div", { style: { flex: 1 }, children: [
|
|
2257
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
|
|
2258
|
+
/* @__PURE__ */ jsx4("span", { children: "Opacidad" }),
|
|
2259
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
2050
2260
|
opacityPercent,
|
|
2051
2261
|
"%"
|
|
2052
2262
|
] })
|
|
2053
2263
|
] }),
|
|
2054
|
-
/* @__PURE__ */
|
|
2264
|
+
/* @__PURE__ */ jsx4(
|
|
2055
2265
|
"input",
|
|
2056
2266
|
{
|
|
2057
2267
|
className: "zlm-range",
|
|
@@ -2089,8 +2299,8 @@ var ZenitLayerManager = ({
|
|
|
2089
2299
|
);
|
|
2090
2300
|
}) });
|
|
2091
2301
|
};
|
|
2092
|
-
return /* @__PURE__ */
|
|
2093
|
-
/* @__PURE__ */
|
|
2302
|
+
return /* @__PURE__ */ jsxs4("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
|
|
2303
|
+
/* @__PURE__ */ jsx4("style", { children: `
|
|
2094
2304
|
.zenit-layer-manager .zlm-card {
|
|
2095
2305
|
transition: box-shadow 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
|
|
2096
2306
|
box-shadow: 0 6px 16px rgba(15, 23, 42, 0.08);
|
|
@@ -2185,16 +2395,16 @@ var ZenitLayerManager = ({
|
|
|
2185
2395
|
outline-offset: 2px;
|
|
2186
2396
|
}
|
|
2187
2397
|
` }),
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
/* @__PURE__ */
|
|
2190
|
-
/* @__PURE__ */
|
|
2191
|
-
/* @__PURE__ */
|
|
2192
|
-
/* @__PURE__ */
|
|
2398
|
+
/* @__PURE__ */ jsxs4("div", { style: headerStyle, children: [
|
|
2399
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
2400
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
2401
|
+
/* @__PURE__ */ jsx4("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
|
|
2402
|
+
/* @__PURE__ */ jsxs4("div", { style: { color: "#64748b", fontSize: 12 }, children: [
|
|
2193
2403
|
"Mapa #",
|
|
2194
2404
|
map.id
|
|
2195
2405
|
] })
|
|
2196
2406
|
] }),
|
|
2197
|
-
/* @__PURE__ */
|
|
2407
|
+
/* @__PURE__ */ jsxs4(
|
|
2198
2408
|
"button",
|
|
2199
2409
|
{
|
|
2200
2410
|
type: "button",
|
|
@@ -2202,13 +2412,13 @@ var ZenitLayerManager = ({
|
|
|
2202
2412
|
className: "zlm-panel-toggle",
|
|
2203
2413
|
"aria-label": panelVisible ? "Ocultar panel de capas" : "Mostrar panel de capas",
|
|
2204
2414
|
children: [
|
|
2205
|
-
panelVisible ? /* @__PURE__ */
|
|
2415
|
+
panelVisible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 }),
|
|
2206
2416
|
panelVisible ? "Ocultar" : "Mostrar"
|
|
2207
2417
|
]
|
|
2208
2418
|
}
|
|
2209
2419
|
)
|
|
2210
2420
|
] }),
|
|
2211
|
-
/* @__PURE__ */
|
|
2421
|
+
/* @__PURE__ */ jsxs4(
|
|
2212
2422
|
"div",
|
|
2213
2423
|
{
|
|
2214
2424
|
style: {
|
|
@@ -2221,26 +2431,26 @@ var ZenitLayerManager = ({
|
|
|
2221
2431
|
background: "#f1f5f9"
|
|
2222
2432
|
},
|
|
2223
2433
|
children: [
|
|
2224
|
-
/* @__PURE__ */
|
|
2434
|
+
/* @__PURE__ */ jsxs4(
|
|
2225
2435
|
"button",
|
|
2226
2436
|
{
|
|
2227
2437
|
type: "button",
|
|
2228
2438
|
className: `zlm-tab${activeTab === "layers" ? " is-active" : ""}`,
|
|
2229
2439
|
onClick: () => setActiveTab("layers"),
|
|
2230
2440
|
children: [
|
|
2231
|
-
/* @__PURE__ */
|
|
2441
|
+
/* @__PURE__ */ jsx4(Layers, { size: 16 }),
|
|
2232
2442
|
"Capas"
|
|
2233
2443
|
]
|
|
2234
2444
|
}
|
|
2235
2445
|
),
|
|
2236
|
-
showUploadTab && /* @__PURE__ */
|
|
2446
|
+
showUploadTab && /* @__PURE__ */ jsxs4(
|
|
2237
2447
|
"button",
|
|
2238
2448
|
{
|
|
2239
2449
|
type: "button",
|
|
2240
2450
|
className: `zlm-tab${activeTab === "upload" ? " is-active" : ""}`,
|
|
2241
2451
|
onClick: () => setActiveTab("upload"),
|
|
2242
2452
|
children: [
|
|
2243
|
-
/* @__PURE__ */
|
|
2453
|
+
/* @__PURE__ */ jsx4(Upload, { size: 16 }),
|
|
2244
2454
|
"Subir"
|
|
2245
2455
|
]
|
|
2246
2456
|
}
|
|
@@ -2249,15 +2459,15 @@ var ZenitLayerManager = ({
|
|
|
2249
2459
|
}
|
|
2250
2460
|
)
|
|
2251
2461
|
] }),
|
|
2252
|
-
panelVisible && /* @__PURE__ */
|
|
2462
|
+
panelVisible && /* @__PURE__ */ jsxs4("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
|
|
2253
2463
|
activeTab === "layers" && renderLayerCards(),
|
|
2254
|
-
showUploadTab && activeTab === "upload" && /* @__PURE__ */
|
|
2464
|
+
showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx4("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
|
|
2255
2465
|
] })
|
|
2256
2466
|
] });
|
|
2257
2467
|
};
|
|
2258
2468
|
|
|
2259
2469
|
// src/react/ZenitFeatureFilterPanel.tsx
|
|
2260
|
-
import { jsx as
|
|
2470
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2261
2471
|
var ZenitFeatureFilterPanel = ({
|
|
2262
2472
|
title = "Filtros",
|
|
2263
2473
|
description,
|
|
@@ -2265,7 +2475,7 @@ var ZenitFeatureFilterPanel = ({
|
|
|
2265
2475
|
style,
|
|
2266
2476
|
children
|
|
2267
2477
|
}) => {
|
|
2268
|
-
return /* @__PURE__ */
|
|
2478
|
+
return /* @__PURE__ */ jsxs5(
|
|
2269
2479
|
"section",
|
|
2270
2480
|
{
|
|
2271
2481
|
className,
|
|
@@ -2278,22 +2488,22 @@ var ZenitFeatureFilterPanel = ({
|
|
|
2278
2488
|
...style
|
|
2279
2489
|
},
|
|
2280
2490
|
children: [
|
|
2281
|
-
/* @__PURE__ */
|
|
2282
|
-
/* @__PURE__ */
|
|
2283
|
-
description && /* @__PURE__ */
|
|
2491
|
+
/* @__PURE__ */ jsxs5("header", { style: { marginBottom: 12 }, children: [
|
|
2492
|
+
/* @__PURE__ */ jsx5("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
|
|
2493
|
+
description && /* @__PURE__ */ jsx5("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
|
|
2284
2494
|
] }),
|
|
2285
|
-
/* @__PURE__ */
|
|
2495
|
+
/* @__PURE__ */ jsx5("div", { children })
|
|
2286
2496
|
]
|
|
2287
2497
|
}
|
|
2288
2498
|
);
|
|
2289
2499
|
};
|
|
2290
2500
|
|
|
2291
2501
|
// src/react/ai/FloatingChatBox.tsx
|
|
2292
|
-
import { useCallback as
|
|
2293
|
-
import { createPortal } from "react-dom";
|
|
2502
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef7, useState as useState5 } from "react";
|
|
2503
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
2294
2504
|
|
|
2295
2505
|
// src/react/hooks/use-chat.ts
|
|
2296
|
-
import { useCallback as
|
|
2506
|
+
import { useCallback as useCallback3, useRef as useRef6, useState as useState4 } from "react";
|
|
2297
2507
|
|
|
2298
2508
|
// src/ai/chat.service.ts
|
|
2299
2509
|
var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
|
|
@@ -2429,9 +2639,9 @@ var createChatService = (config) => ({
|
|
|
2429
2639
|
|
|
2430
2640
|
// src/react/hooks/use-chat.ts
|
|
2431
2641
|
var useSendMessage = (config) => {
|
|
2432
|
-
const [isLoading, setIsLoading] =
|
|
2433
|
-
const [error, setError] =
|
|
2434
|
-
const send =
|
|
2642
|
+
const [isLoading, setIsLoading] = useState4(false);
|
|
2643
|
+
const [error, setError] = useState4(null);
|
|
2644
|
+
const send = useCallback3(
|
|
2435
2645
|
async (mapId, request, options) => {
|
|
2436
2646
|
setIsLoading(true);
|
|
2437
2647
|
setError(null);
|
|
@@ -2449,18 +2659,18 @@ var useSendMessage = (config) => {
|
|
|
2449
2659
|
return { sendMessage: send, isLoading, error };
|
|
2450
2660
|
};
|
|
2451
2661
|
var useSendMessageStream = (config) => {
|
|
2452
|
-
const [isStreaming, setIsStreaming] =
|
|
2453
|
-
const [streamingText, setStreamingText] =
|
|
2454
|
-
const [completeResponse, setCompleteResponse] =
|
|
2455
|
-
const [error, setError] =
|
|
2456
|
-
const requestIdRef =
|
|
2457
|
-
const reset =
|
|
2662
|
+
const [isStreaming, setIsStreaming] = useState4(false);
|
|
2663
|
+
const [streamingText, setStreamingText] = useState4("");
|
|
2664
|
+
const [completeResponse, setCompleteResponse] = useState4(null);
|
|
2665
|
+
const [error, setError] = useState4(null);
|
|
2666
|
+
const requestIdRef = useRef6(0);
|
|
2667
|
+
const reset = useCallback3(() => {
|
|
2458
2668
|
setIsStreaming(false);
|
|
2459
2669
|
setStreamingText("");
|
|
2460
2670
|
setCompleteResponse(null);
|
|
2461
2671
|
setError(null);
|
|
2462
2672
|
}, []);
|
|
2463
|
-
const send =
|
|
2673
|
+
const send = useCallback3(
|
|
2464
2674
|
async (mapId, request, options) => {
|
|
2465
2675
|
const requestId = requestIdRef.current + 1;
|
|
2466
2676
|
requestIdRef.current = requestId;
|
|
@@ -2514,7 +2724,7 @@ var useSendMessageStream = (config) => {
|
|
|
2514
2724
|
// src/react/components/MarkdownRenderer.tsx
|
|
2515
2725
|
import ReactMarkdown from "react-markdown";
|
|
2516
2726
|
import remarkGfm from "remark-gfm";
|
|
2517
|
-
import { jsx as
|
|
2727
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2518
2728
|
function normalizeAssistantMarkdown(text) {
|
|
2519
2729
|
if (!text || typeof text !== "string") return "";
|
|
2520
2730
|
let normalized = text;
|
|
@@ -2530,28 +2740,28 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2530
2740
|
if (!normalizedContent) {
|
|
2531
2741
|
return null;
|
|
2532
2742
|
}
|
|
2533
|
-
return /* @__PURE__ */
|
|
2743
|
+
return /* @__PURE__ */ jsx6("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx6(
|
|
2534
2744
|
ReactMarkdown,
|
|
2535
2745
|
{
|
|
2536
2746
|
remarkPlugins: [remarkGfm],
|
|
2537
2747
|
components: {
|
|
2538
2748
|
// Headings with proper spacing
|
|
2539
|
-
h1: ({ children, ...props }) => /* @__PURE__ */
|
|
2540
|
-
h2: ({ children, ...props }) => /* @__PURE__ */
|
|
2541
|
-
h3: ({ children, ...props }) => /* @__PURE__ */
|
|
2542
|
-
h4: ({ children, ...props }) => /* @__PURE__ */
|
|
2543
|
-
h5: ({ children, ...props }) => /* @__PURE__ */
|
|
2544
|
-
h6: ({ children, ...props }) => /* @__PURE__ */
|
|
2749
|
+
h1: ({ children, ...props }) => /* @__PURE__ */ jsx6("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
|
|
2750
|
+
h2: ({ children, ...props }) => /* @__PURE__ */ jsx6("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
|
|
2751
|
+
h3: ({ children, ...props }) => /* @__PURE__ */ jsx6("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
|
|
2752
|
+
h4: ({ children, ...props }) => /* @__PURE__ */ jsx6("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
|
|
2753
|
+
h5: ({ children, ...props }) => /* @__PURE__ */ jsx6("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
|
|
2754
|
+
h6: ({ children, ...props }) => /* @__PURE__ */ jsx6("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
|
|
2545
2755
|
// Paragraphs with comfortable line height
|
|
2546
|
-
p: ({ children, ...props }) => /* @__PURE__ */
|
|
2756
|
+
p: ({ children, ...props }) => /* @__PURE__ */ jsx6("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
|
|
2547
2757
|
// Lists with proper indentation
|
|
2548
|
-
ul: ({ children, ...props }) => /* @__PURE__ */
|
|
2549
|
-
ol: ({ children, ...props }) => /* @__PURE__ */
|
|
2550
|
-
li: ({ children, ...props }) => /* @__PURE__ */
|
|
2758
|
+
ul: ({ children, ...props }) => /* @__PURE__ */ jsx6("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
|
|
2759
|
+
ol: ({ children, ...props }) => /* @__PURE__ */ jsx6("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
|
|
2760
|
+
li: ({ children, ...props }) => /* @__PURE__ */ jsx6("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
|
|
2551
2761
|
// Code blocks
|
|
2552
2762
|
code: ({ inline, children, ...props }) => {
|
|
2553
2763
|
if (inline) {
|
|
2554
|
-
return /* @__PURE__ */
|
|
2764
|
+
return /* @__PURE__ */ jsx6(
|
|
2555
2765
|
"code",
|
|
2556
2766
|
{
|
|
2557
2767
|
style: {
|
|
@@ -2566,7 +2776,7 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2566
2776
|
}
|
|
2567
2777
|
);
|
|
2568
2778
|
}
|
|
2569
|
-
return /* @__PURE__ */
|
|
2779
|
+
return /* @__PURE__ */ jsx6(
|
|
2570
2780
|
"code",
|
|
2571
2781
|
{
|
|
2572
2782
|
style: {
|
|
@@ -2586,9 +2796,9 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2586
2796
|
);
|
|
2587
2797
|
},
|
|
2588
2798
|
// Pre (code block wrapper)
|
|
2589
|
-
pre: ({ children, ...props }) => /* @__PURE__ */
|
|
2799
|
+
pre: ({ children, ...props }) => /* @__PURE__ */ jsx6("pre", { style: { margin: 0 }, ...props, children }),
|
|
2590
2800
|
// Blockquotes
|
|
2591
|
-
blockquote: ({ children, ...props }) => /* @__PURE__ */
|
|
2801
|
+
blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx6(
|
|
2592
2802
|
"blockquote",
|
|
2593
2803
|
{
|
|
2594
2804
|
style: {
|
|
@@ -2604,11 +2814,11 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2604
2814
|
}
|
|
2605
2815
|
),
|
|
2606
2816
|
// Strong/bold
|
|
2607
|
-
strong: ({ children, ...props }) => /* @__PURE__ */
|
|
2817
|
+
strong: ({ children, ...props }) => /* @__PURE__ */ jsx6("strong", { style: { fontWeight: 600 }, ...props, children }),
|
|
2608
2818
|
// Emphasis/italic
|
|
2609
|
-
em: ({ children, ...props }) => /* @__PURE__ */
|
|
2819
|
+
em: ({ children, ...props }) => /* @__PURE__ */ jsx6("em", { style: { fontStyle: "italic" }, ...props, children }),
|
|
2610
2820
|
// Horizontal rule
|
|
2611
|
-
hr: (props) => /* @__PURE__ */
|
|
2821
|
+
hr: (props) => /* @__PURE__ */ jsx6(
|
|
2612
2822
|
"hr",
|
|
2613
2823
|
{
|
|
2614
2824
|
style: {
|
|
@@ -2621,7 +2831,7 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2621
2831
|
}
|
|
2622
2832
|
),
|
|
2623
2833
|
// Tables (GFM)
|
|
2624
|
-
table: ({ children, ...props }) => /* @__PURE__ */
|
|
2834
|
+
table: ({ children, ...props }) => /* @__PURE__ */ jsx6("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ jsx6(
|
|
2625
2835
|
"table",
|
|
2626
2836
|
{
|
|
2627
2837
|
style: {
|
|
@@ -2633,7 +2843,7 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2633
2843
|
children
|
|
2634
2844
|
}
|
|
2635
2845
|
) }),
|
|
2636
|
-
th: ({ children, ...props }) => /* @__PURE__ */
|
|
2846
|
+
th: ({ children, ...props }) => /* @__PURE__ */ jsx6(
|
|
2637
2847
|
"th",
|
|
2638
2848
|
{
|
|
2639
2849
|
style: {
|
|
@@ -2647,7 +2857,7 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2647
2857
|
children
|
|
2648
2858
|
}
|
|
2649
2859
|
),
|
|
2650
|
-
td: ({ children, ...props }) => /* @__PURE__ */
|
|
2860
|
+
td: ({ children, ...props }) => /* @__PURE__ */ jsx6(
|
|
2651
2861
|
"td",
|
|
2652
2862
|
{
|
|
2653
2863
|
style: {
|
|
@@ -2665,32 +2875,32 @@ var MarkdownRenderer = ({ content, className }) => {
|
|
|
2665
2875
|
};
|
|
2666
2876
|
|
|
2667
2877
|
// src/react/ai/FloatingChatBox.tsx
|
|
2668
|
-
import { Fragment, jsx as
|
|
2669
|
-
var ChatIcon = () => /* @__PURE__ */
|
|
2670
|
-
var CloseIcon = () => /* @__PURE__ */
|
|
2671
|
-
/* @__PURE__ */
|
|
2672
|
-
/* @__PURE__ */
|
|
2878
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2879
|
+
var ChatIcon = () => /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
|
|
2880
|
+
var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2881
|
+
/* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2882
|
+
/* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2673
2883
|
] });
|
|
2674
|
-
var ExpandIcon = () => /* @__PURE__ */
|
|
2675
|
-
/* @__PURE__ */
|
|
2676
|
-
/* @__PURE__ */
|
|
2677
|
-
/* @__PURE__ */
|
|
2678
|
-
/* @__PURE__ */
|
|
2884
|
+
var ExpandIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2885
|
+
/* @__PURE__ */ jsx7("polyline", { points: "15 3 21 3 21 9" }),
|
|
2886
|
+
/* @__PURE__ */ jsx7("polyline", { points: "9 21 3 21 3 15" }),
|
|
2887
|
+
/* @__PURE__ */ jsx7("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
|
|
2888
|
+
/* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
|
|
2679
2889
|
] });
|
|
2680
|
-
var CollapseIcon = () => /* @__PURE__ */
|
|
2681
|
-
/* @__PURE__ */
|
|
2682
|
-
/* @__PURE__ */
|
|
2683
|
-
/* @__PURE__ */
|
|
2684
|
-
/* @__PURE__ */
|
|
2890
|
+
var CollapseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2891
|
+
/* @__PURE__ */ jsx7("polyline", { points: "4 14 10 14 10 20" }),
|
|
2892
|
+
/* @__PURE__ */ jsx7("polyline", { points: "20 10 14 10 14 4" }),
|
|
2893
|
+
/* @__PURE__ */ jsx7("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
|
|
2894
|
+
/* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
|
|
2685
2895
|
] });
|
|
2686
|
-
var SendIcon = () => /* @__PURE__ */
|
|
2687
|
-
/* @__PURE__ */
|
|
2688
|
-
/* @__PURE__ */
|
|
2896
|
+
var SendIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2897
|
+
/* @__PURE__ */ jsx7("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
2898
|
+
/* @__PURE__ */ jsx7("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
2689
2899
|
] });
|
|
2690
|
-
var LayersIcon = () => /* @__PURE__ */
|
|
2691
|
-
/* @__PURE__ */
|
|
2692
|
-
/* @__PURE__ */
|
|
2693
|
-
/* @__PURE__ */
|
|
2900
|
+
var LayersIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2901
|
+
/* @__PURE__ */ jsx7("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
|
|
2902
|
+
/* @__PURE__ */ jsx7("polyline", { points: "2 17 12 22 22 17" }),
|
|
2903
|
+
/* @__PURE__ */ jsx7("polyline", { points: "2 12 12 17 22 12" })
|
|
2694
2904
|
] });
|
|
2695
2905
|
var styles = {
|
|
2696
2906
|
root: {
|
|
@@ -2699,8 +2909,8 @@ var styles = {
|
|
|
2699
2909
|
// Floating button (closed state - wide with text, open state - circular with X)
|
|
2700
2910
|
floatingButton: {
|
|
2701
2911
|
position: "fixed",
|
|
2702
|
-
bottom:
|
|
2703
|
-
right:
|
|
2912
|
+
bottom: 16,
|
|
2913
|
+
right: 16,
|
|
2704
2914
|
borderRadius: "999px",
|
|
2705
2915
|
border: "none",
|
|
2706
2916
|
cursor: "pointer",
|
|
@@ -2710,30 +2920,30 @@ var styles = {
|
|
|
2710
2920
|
display: "flex",
|
|
2711
2921
|
alignItems: "center",
|
|
2712
2922
|
justifyContent: "center",
|
|
2713
|
-
fontSize:
|
|
2923
|
+
fontSize: 14,
|
|
2714
2924
|
fontWeight: 600,
|
|
2715
2925
|
zIndex: 99999,
|
|
2716
2926
|
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
|
2717
2927
|
},
|
|
2718
2928
|
floatingButtonClosed: {
|
|
2719
|
-
padding: "
|
|
2929
|
+
padding: "12px 20px",
|
|
2720
2930
|
gap: 8
|
|
2721
2931
|
},
|
|
2722
2932
|
floatingButtonOpen: {
|
|
2723
|
-
width:
|
|
2724
|
-
height:
|
|
2933
|
+
width: 52,
|
|
2934
|
+
height: 52,
|
|
2725
2935
|
padding: 0
|
|
2726
2936
|
},
|
|
2727
2937
|
floatingButtonMobile: {
|
|
2728
|
-
width:
|
|
2729
|
-
height:
|
|
2938
|
+
width: 52,
|
|
2939
|
+
height: 52,
|
|
2730
2940
|
padding: 0
|
|
2731
2941
|
},
|
|
2732
2942
|
// Panel (expandable)
|
|
2733
2943
|
panel: {
|
|
2734
2944
|
position: "fixed",
|
|
2735
|
-
bottom:
|
|
2736
|
-
right:
|
|
2945
|
+
bottom: 80,
|
|
2946
|
+
right: 16,
|
|
2737
2947
|
background: "#fff",
|
|
2738
2948
|
borderRadius: 16,
|
|
2739
2949
|
boxShadow: "0 20px 60px rgba(15, 23, 42, 0.3), 0 0 0 1px rgba(15, 23, 42, 0.05)",
|
|
@@ -2744,16 +2954,16 @@ var styles = {
|
|
|
2744
2954
|
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
|
2745
2955
|
},
|
|
2746
2956
|
panelNormal: {
|
|
2747
|
-
width:
|
|
2748
|
-
height:
|
|
2957
|
+
width: 360,
|
|
2958
|
+
height: 520
|
|
2749
2959
|
},
|
|
2750
2960
|
panelExpanded: {
|
|
2751
|
-
width:
|
|
2752
|
-
height:
|
|
2961
|
+
width: 480,
|
|
2962
|
+
height: 640
|
|
2753
2963
|
},
|
|
2754
2964
|
// Header with green gradient
|
|
2755
2965
|
header: {
|
|
2756
|
-
padding: "16px
|
|
2966
|
+
padding: "14px 16px",
|
|
2757
2967
|
background: "linear-gradient(135deg, #10b981, #059669)",
|
|
2758
2968
|
color: "#fff",
|
|
2759
2969
|
display: "flex",
|
|
@@ -2762,7 +2972,7 @@ var styles = {
|
|
|
2762
2972
|
},
|
|
2763
2973
|
title: {
|
|
2764
2974
|
margin: 0,
|
|
2765
|
-
fontSize:
|
|
2975
|
+
fontSize: 15,
|
|
2766
2976
|
fontWeight: 600,
|
|
2767
2977
|
letterSpacing: "-0.01em"
|
|
2768
2978
|
},
|
|
@@ -2775,8 +2985,8 @@ var styles = {
|
|
|
2775
2985
|
border: "none",
|
|
2776
2986
|
background: "rgba(255, 255, 255, 0.15)",
|
|
2777
2987
|
color: "#fff",
|
|
2778
|
-
width:
|
|
2779
|
-
height:
|
|
2988
|
+
width: 30,
|
|
2989
|
+
height: 30,
|
|
2780
2990
|
borderRadius: 8,
|
|
2781
2991
|
cursor: "pointer",
|
|
2782
2992
|
display: "flex",
|
|
@@ -2787,7 +2997,7 @@ var styles = {
|
|
|
2787
2997
|
// Messages area
|
|
2788
2998
|
messages: {
|
|
2789
2999
|
flex: 1,
|
|
2790
|
-
padding: "
|
|
3000
|
+
padding: "16px",
|
|
2791
3001
|
overflowY: "auto",
|
|
2792
3002
|
background: "#f8fafc",
|
|
2793
3003
|
display: "flex",
|
|
@@ -2802,10 +3012,10 @@ var styles = {
|
|
|
2802
3012
|
},
|
|
2803
3013
|
messageBubble: {
|
|
2804
3014
|
maxWidth: "85%",
|
|
2805
|
-
padding: "12px
|
|
3015
|
+
padding: "10px 12px",
|
|
2806
3016
|
borderRadius: 16,
|
|
2807
3017
|
lineHeight: 1.5,
|
|
2808
|
-
fontSize:
|
|
3018
|
+
fontSize: 13,
|
|
2809
3019
|
whiteSpace: "pre-wrap",
|
|
2810
3020
|
wordBreak: "break-word"
|
|
2811
3021
|
},
|
|
@@ -2919,7 +3129,7 @@ var styles = {
|
|
|
2919
3129
|
// Input area
|
|
2920
3130
|
inputWrapper: {
|
|
2921
3131
|
borderTop: "1px solid #e2e8f0",
|
|
2922
|
-
padding: "14px
|
|
3132
|
+
padding: "10px 14px",
|
|
2923
3133
|
display: "flex",
|
|
2924
3134
|
gap: 10,
|
|
2925
3135
|
alignItems: "flex-end",
|
|
@@ -2930,8 +3140,8 @@ var styles = {
|
|
|
2930
3140
|
resize: "none",
|
|
2931
3141
|
borderRadius: 12,
|
|
2932
3142
|
border: "1.5px solid #cbd5e1",
|
|
2933
|
-
padding: "10px
|
|
2934
|
-
fontSize:
|
|
3143
|
+
padding: "8px 10px",
|
|
3144
|
+
fontSize: 13,
|
|
2935
3145
|
fontFamily: "inherit",
|
|
2936
3146
|
lineHeight: 1.4,
|
|
2937
3147
|
transition: "border-color 0.2s"
|
|
@@ -2943,18 +3153,18 @@ var styles = {
|
|
|
2943
3153
|
sendButton: {
|
|
2944
3154
|
borderRadius: 12,
|
|
2945
3155
|
border: "none",
|
|
2946
|
-
padding: "
|
|
3156
|
+
padding: "8px 12px",
|
|
2947
3157
|
background: "linear-gradient(135deg, #10b981, #059669)",
|
|
2948
3158
|
color: "#fff",
|
|
2949
3159
|
cursor: "pointer",
|
|
2950
|
-
fontSize:
|
|
3160
|
+
fontSize: 13,
|
|
2951
3161
|
fontWeight: 600,
|
|
2952
3162
|
display: "flex",
|
|
2953
3163
|
alignItems: "center",
|
|
2954
3164
|
justifyContent: "center",
|
|
2955
3165
|
transition: "opacity 0.2s, transform 0.2s",
|
|
2956
|
-
minWidth:
|
|
2957
|
-
height:
|
|
3166
|
+
minWidth: 40,
|
|
3167
|
+
height: 40
|
|
2958
3168
|
},
|
|
2959
3169
|
// Status messages
|
|
2960
3170
|
statusNote: {
|
|
@@ -2984,42 +3194,42 @@ var FloatingChatBox = ({
|
|
|
2984
3194
|
open: openProp
|
|
2985
3195
|
}) => {
|
|
2986
3196
|
const isControlled = openProp !== void 0;
|
|
2987
|
-
const [internalOpen, setInternalOpen] =
|
|
3197
|
+
const [internalOpen, setInternalOpen] = useState5(false);
|
|
2988
3198
|
const open = isControlled ? openProp : internalOpen;
|
|
2989
|
-
const setOpen =
|
|
3199
|
+
const setOpen = useCallback4((value) => {
|
|
2990
3200
|
const newValue = typeof value === "function" ? value(open) : value;
|
|
2991
3201
|
if (!isControlled) {
|
|
2992
3202
|
setInternalOpen(newValue);
|
|
2993
3203
|
}
|
|
2994
3204
|
onOpenChange?.(newValue);
|
|
2995
3205
|
}, [isControlled, open, onOpenChange]);
|
|
2996
|
-
const [expanded, setExpanded] =
|
|
2997
|
-
const [messages, setMessages] =
|
|
2998
|
-
const [inputValue, setInputValue] =
|
|
2999
|
-
const [conversationId, setConversationId] =
|
|
3000
|
-
const [errorMessage, setErrorMessage] =
|
|
3001
|
-
const [isFocused, setIsFocused] =
|
|
3002
|
-
const [isMobile, setIsMobile] =
|
|
3003
|
-
const messagesEndRef =
|
|
3004
|
-
const messagesContainerRef =
|
|
3005
|
-
const chatBoxRef =
|
|
3006
|
-
const chatConfig =
|
|
3206
|
+
const [expanded, setExpanded] = useState5(false);
|
|
3207
|
+
const [messages, setMessages] = useState5([]);
|
|
3208
|
+
const [inputValue, setInputValue] = useState5("");
|
|
3209
|
+
const [conversationId, setConversationId] = useState5();
|
|
3210
|
+
const [errorMessage, setErrorMessage] = useState5(null);
|
|
3211
|
+
const [isFocused, setIsFocused] = useState5(false);
|
|
3212
|
+
const [isMobile, setIsMobile] = useState5(false);
|
|
3213
|
+
const messagesEndRef = useRef7(null);
|
|
3214
|
+
const messagesContainerRef = useRef7(null);
|
|
3215
|
+
const chatBoxRef = useRef7(null);
|
|
3216
|
+
const chatConfig = useMemo4(() => {
|
|
3007
3217
|
if (!baseUrl) return void 0;
|
|
3008
3218
|
return { baseUrl, accessToken, getAccessToken };
|
|
3009
3219
|
}, [accessToken, baseUrl, getAccessToken]);
|
|
3010
3220
|
const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
|
|
3011
3221
|
const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
|
|
3012
|
-
|
|
3222
|
+
useEffect6(() => {
|
|
3013
3223
|
if (open && isMobile) {
|
|
3014
3224
|
setExpanded(true);
|
|
3015
3225
|
}
|
|
3016
3226
|
}, [open, isMobile]);
|
|
3017
|
-
const scrollToBottom =
|
|
3227
|
+
const scrollToBottom = useCallback4(() => {
|
|
3018
3228
|
if (messagesEndRef.current) {
|
|
3019
3229
|
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
|
|
3020
3230
|
}
|
|
3021
3231
|
}, []);
|
|
3022
|
-
|
|
3232
|
+
useEffect6(() => {
|
|
3023
3233
|
if (open && messages.length === 0) {
|
|
3024
3234
|
setMessages([
|
|
3025
3235
|
{
|
|
@@ -3030,10 +3240,10 @@ var FloatingChatBox = ({
|
|
|
3030
3240
|
]);
|
|
3031
3241
|
}
|
|
3032
3242
|
}, [open, messages.length]);
|
|
3033
|
-
|
|
3243
|
+
useEffect6(() => {
|
|
3034
3244
|
scrollToBottom();
|
|
3035
3245
|
}, [messages, streamingText, scrollToBottom]);
|
|
3036
|
-
|
|
3246
|
+
useEffect6(() => {
|
|
3037
3247
|
if (!open) return;
|
|
3038
3248
|
if (isMobile && expanded) return;
|
|
3039
3249
|
const handleClickOutside = (event) => {
|
|
@@ -3046,7 +3256,7 @@ var FloatingChatBox = ({
|
|
|
3046
3256
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
3047
3257
|
};
|
|
3048
3258
|
}, [open, isMobile, expanded]);
|
|
3049
|
-
|
|
3259
|
+
useEffect6(() => {
|
|
3050
3260
|
if (typeof window === "undefined") return;
|
|
3051
3261
|
const mediaQuery = window.matchMedia("(max-width: 768px)");
|
|
3052
3262
|
const updateMobile = () => setIsMobile(mediaQuery.matches);
|
|
@@ -3064,7 +3274,7 @@ var FloatingChatBox = ({
|
|
|
3064
3274
|
}
|
|
3065
3275
|
};
|
|
3066
3276
|
}, []);
|
|
3067
|
-
|
|
3277
|
+
useEffect6(() => {
|
|
3068
3278
|
if (typeof document === "undefined") return;
|
|
3069
3279
|
if (!open || !isMobile) return;
|
|
3070
3280
|
document.body.style.overflow = "hidden";
|
|
@@ -3072,10 +3282,10 @@ var FloatingChatBox = ({
|
|
|
3072
3282
|
document.body.style.overflow = "";
|
|
3073
3283
|
};
|
|
3074
3284
|
}, [open, isMobile]);
|
|
3075
|
-
const addMessage =
|
|
3285
|
+
const addMessage = useCallback4((message) => {
|
|
3076
3286
|
setMessages((prev) => [...prev, message]);
|
|
3077
3287
|
}, []);
|
|
3078
|
-
const handleSend =
|
|
3288
|
+
const handleSend = useCallback4(async () => {
|
|
3079
3289
|
if (!mapId) {
|
|
3080
3290
|
setErrorMessage("Selecciona un mapa para usar el asistente.");
|
|
3081
3291
|
return;
|
|
@@ -3110,11 +3320,11 @@ var FloatingChatBox = ({
|
|
|
3110
3320
|
response
|
|
3111
3321
|
});
|
|
3112
3322
|
} catch (error) {
|
|
3113
|
-
setErrorMessage(
|
|
3323
|
+
setErrorMessage("Ocurri\xF3 un error al generar la respuesta.");
|
|
3114
3324
|
addMessage({
|
|
3115
3325
|
id: `error-${Date.now()}`,
|
|
3116
3326
|
role: "assistant",
|
|
3117
|
-
content:
|
|
3327
|
+
content: "\u274C Ocurri\xF3 un error al generar la respuesta."
|
|
3118
3328
|
});
|
|
3119
3329
|
}
|
|
3120
3330
|
}, [
|
|
@@ -3128,7 +3338,7 @@ var FloatingChatBox = ({
|
|
|
3128
3338
|
sendMessage2,
|
|
3129
3339
|
userId
|
|
3130
3340
|
]);
|
|
3131
|
-
const handleKeyDown =
|
|
3341
|
+
const handleKeyDown = useCallback4(
|
|
3132
3342
|
(event) => {
|
|
3133
3343
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
3134
3344
|
event.preventDefault();
|
|
@@ -3139,20 +3349,20 @@ var FloatingChatBox = ({
|
|
|
3139
3349
|
},
|
|
3140
3350
|
[canSend, handleSend]
|
|
3141
3351
|
);
|
|
3142
|
-
const handleFollowUpClick =
|
|
3352
|
+
const handleFollowUpClick = useCallback4((question) => {
|
|
3143
3353
|
setInputValue(question);
|
|
3144
3354
|
}, []);
|
|
3145
3355
|
const renderMetadata = (response) => {
|
|
3146
3356
|
if (!response?.metadata) return null;
|
|
3147
3357
|
const referencedLayers = response.metadata.referencedLayers;
|
|
3148
3358
|
if (!referencedLayers || referencedLayers.length === 0) return null;
|
|
3149
|
-
return /* @__PURE__ */
|
|
3150
|
-
/* @__PURE__ */
|
|
3151
|
-
/* @__PURE__ */
|
|
3359
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.metadataSection, children: [
|
|
3360
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.metadataTitle, children: [
|
|
3361
|
+
/* @__PURE__ */ jsx7(LayersIcon, {}),
|
|
3152
3362
|
"Capas Analizadas"
|
|
3153
3363
|
] }),
|
|
3154
|
-
/* @__PURE__ */
|
|
3155
|
-
/* @__PURE__ */
|
|
3364
|
+
/* @__PURE__ */ jsx7("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ jsxs6("li", { style: styles.metadataItem, children: [
|
|
3365
|
+
/* @__PURE__ */ jsx7("strong", { children: layer.layerName }),
|
|
3156
3366
|
" (",
|
|
3157
3367
|
layer.featureCount,
|
|
3158
3368
|
" ",
|
|
@@ -3161,7 +3371,7 @@ var FloatingChatBox = ({
|
|
|
3161
3371
|
] }, index)) })
|
|
3162
3372
|
] });
|
|
3163
3373
|
};
|
|
3164
|
-
const handleActionClick =
|
|
3374
|
+
const handleActionClick = useCallback4((action) => {
|
|
3165
3375
|
if (isStreaming) return;
|
|
3166
3376
|
setOpen(false);
|
|
3167
3377
|
requestAnimationFrame(() => {
|
|
@@ -3170,9 +3380,9 @@ var FloatingChatBox = ({
|
|
|
3170
3380
|
}, [isStreaming, setOpen, onActionClick]);
|
|
3171
3381
|
const renderActions = (response) => {
|
|
3172
3382
|
if (!response?.suggestedActions?.length) return null;
|
|
3173
|
-
return /* @__PURE__ */
|
|
3174
|
-
/* @__PURE__ */
|
|
3175
|
-
/* @__PURE__ */
|
|
3383
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
|
|
3384
|
+
/* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
|
|
3385
|
+
/* @__PURE__ */ jsx7("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ jsx7(
|
|
3176
3386
|
"button",
|
|
3177
3387
|
{
|
|
3178
3388
|
type: "button",
|
|
@@ -3203,9 +3413,9 @@ var FloatingChatBox = ({
|
|
|
3203
3413
|
};
|
|
3204
3414
|
const renderFollowUps = (response) => {
|
|
3205
3415
|
if (!response?.followUpQuestions?.length) return null;
|
|
3206
|
-
return /* @__PURE__ */
|
|
3207
|
-
/* @__PURE__ */
|
|
3208
|
-
/* @__PURE__ */
|
|
3416
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
|
|
3417
|
+
/* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
|
|
3418
|
+
/* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ jsx7(
|
|
3209
3419
|
"button",
|
|
3210
3420
|
{
|
|
3211
3421
|
type: "button",
|
|
@@ -3234,8 +3444,8 @@ var FloatingChatBox = ({
|
|
|
3234
3444
|
)) })
|
|
3235
3445
|
] });
|
|
3236
3446
|
};
|
|
3237
|
-
const chatContent = /* @__PURE__ */
|
|
3238
|
-
/* @__PURE__ */
|
|
3447
|
+
const chatContent = /* @__PURE__ */ jsxs6("div", { style: styles.root, children: [
|
|
3448
|
+
/* @__PURE__ */ jsx7("style", { children: `
|
|
3239
3449
|
@keyframes zenitBlink {
|
|
3240
3450
|
0%, 49% { opacity: 1; }
|
|
3241
3451
|
50%, 100% { opacity: 0; }
|
|
@@ -3278,11 +3488,13 @@ var FloatingChatBox = ({
|
|
|
3278
3488
|
@media (max-width: 768px) {
|
|
3279
3489
|
.zenit-chat-panel.zenit-chat-panel--fullscreen {
|
|
3280
3490
|
position: fixed !important;
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3491
|
+
left: 0 !important;
|
|
3492
|
+
right: 0 !important;
|
|
3493
|
+
top: 4rem !important;
|
|
3494
|
+
bottom: 0 !important;
|
|
3495
|
+
width: 100% !important;
|
|
3496
|
+
max-width: 100% !important;
|
|
3497
|
+
height: auto !important;
|
|
3286
3498
|
border-radius: 0 !important;
|
|
3287
3499
|
display: flex !important;
|
|
3288
3500
|
flex-direction: column !important;
|
|
@@ -3305,7 +3517,7 @@ var FloatingChatBox = ({
|
|
|
3305
3517
|
}
|
|
3306
3518
|
}
|
|
3307
3519
|
` }),
|
|
3308
|
-
open && /* @__PURE__ */
|
|
3520
|
+
open && /* @__PURE__ */ jsxs6(
|
|
3309
3521
|
"div",
|
|
3310
3522
|
{
|
|
3311
3523
|
ref: chatBoxRef,
|
|
@@ -3315,10 +3527,10 @@ var FloatingChatBox = ({
|
|
|
3315
3527
|
...expanded ? styles.panelExpanded : styles.panelNormal
|
|
3316
3528
|
},
|
|
3317
3529
|
children: [
|
|
3318
|
-
/* @__PURE__ */
|
|
3319
|
-
/* @__PURE__ */
|
|
3320
|
-
/* @__PURE__ */
|
|
3321
|
-
/* @__PURE__ */
|
|
3530
|
+
/* @__PURE__ */ jsxs6("header", { style: styles.header, children: [
|
|
3531
|
+
/* @__PURE__ */ jsx7("h3", { style: styles.title, children: "Asistente Zenit AI" }),
|
|
3532
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.headerButtons, children: [
|
|
3533
|
+
/* @__PURE__ */ jsx7(
|
|
3322
3534
|
"button",
|
|
3323
3535
|
{
|
|
3324
3536
|
type: "button",
|
|
@@ -3331,10 +3543,10 @@ var FloatingChatBox = ({
|
|
|
3331
3543
|
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
3332
3544
|
},
|
|
3333
3545
|
"aria-label": expanded ? "Contraer" : "Expandir",
|
|
3334
|
-
children: expanded ? /* @__PURE__ */
|
|
3546
|
+
children: expanded ? /* @__PURE__ */ jsx7(CollapseIcon, {}) : /* @__PURE__ */ jsx7(ExpandIcon, {})
|
|
3335
3547
|
}
|
|
3336
3548
|
),
|
|
3337
|
-
/* @__PURE__ */
|
|
3549
|
+
/* @__PURE__ */ jsx7(
|
|
3338
3550
|
"button",
|
|
3339
3551
|
{
|
|
3340
3552
|
type: "button",
|
|
@@ -3347,20 +3559,20 @@ var FloatingChatBox = ({
|
|
|
3347
3559
|
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
3348
3560
|
},
|
|
3349
3561
|
"aria-label": "Cerrar",
|
|
3350
|
-
children: /* @__PURE__ */
|
|
3562
|
+
children: /* @__PURE__ */ jsx7(CloseIcon, {})
|
|
3351
3563
|
}
|
|
3352
3564
|
)
|
|
3353
3565
|
] })
|
|
3354
3566
|
] }),
|
|
3355
|
-
/* @__PURE__ */
|
|
3356
|
-
messages.map((message) => /* @__PURE__ */
|
|
3567
|
+
/* @__PURE__ */ jsxs6("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
|
|
3568
|
+
messages.map((message) => /* @__PURE__ */ jsx7(
|
|
3357
3569
|
"div",
|
|
3358
3570
|
{
|
|
3359
3571
|
style: {
|
|
3360
3572
|
...styles.messageWrapper,
|
|
3361
3573
|
alignItems: message.role === "user" ? "flex-end" : "flex-start"
|
|
3362
3574
|
},
|
|
3363
|
-
children: /* @__PURE__ */
|
|
3575
|
+
children: /* @__PURE__ */ jsxs6(
|
|
3364
3576
|
"div",
|
|
3365
3577
|
{
|
|
3366
3578
|
style: {
|
|
@@ -3368,7 +3580,7 @@ var FloatingChatBox = ({
|
|
|
3368
3580
|
...message.role === "user" ? styles.userMessage : styles.assistantMessage
|
|
3369
3581
|
},
|
|
3370
3582
|
children: [
|
|
3371
|
-
message.role === "assistant" ? /* @__PURE__ */
|
|
3583
|
+
message.role === "assistant" ? /* @__PURE__ */ jsx7(MarkdownRenderer, { content: message.content }) : message.content,
|
|
3372
3584
|
message.role === "assistant" && renderMetadata(message.response),
|
|
3373
3585
|
message.role === "assistant" && renderActions(message.response),
|
|
3374
3586
|
message.role === "assistant" && renderFollowUps(message.response)
|
|
@@ -3378,39 +3590,39 @@ var FloatingChatBox = ({
|
|
|
3378
3590
|
},
|
|
3379
3591
|
message.id
|
|
3380
3592
|
)),
|
|
3381
|
-
isStreaming && /* @__PURE__ */
|
|
3593
|
+
isStreaming && /* @__PURE__ */ jsx7(
|
|
3382
3594
|
"div",
|
|
3383
3595
|
{
|
|
3384
3596
|
style: {
|
|
3385
3597
|
...styles.messageWrapper,
|
|
3386
3598
|
alignItems: "flex-start"
|
|
3387
3599
|
},
|
|
3388
|
-
children: /* @__PURE__ */
|
|
3600
|
+
children: /* @__PURE__ */ jsx7(
|
|
3389
3601
|
"div",
|
|
3390
3602
|
{
|
|
3391
3603
|
style: {
|
|
3392
3604
|
...styles.messageBubble,
|
|
3393
3605
|
...styles.assistantMessage
|
|
3394
3606
|
},
|
|
3395
|
-
children: streamingText ? /* @__PURE__ */
|
|
3396
|
-
/* @__PURE__ */
|
|
3397
|
-
/* @__PURE__ */
|
|
3398
|
-
] }) : /* @__PURE__ */
|
|
3399
|
-
/* @__PURE__ */
|
|
3400
|
-
/* @__PURE__ */
|
|
3401
|
-
/* @__PURE__ */
|
|
3402
|
-
/* @__PURE__ */
|
|
3403
|
-
/* @__PURE__ */
|
|
3607
|
+
children: streamingText ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
3608
|
+
/* @__PURE__ */ jsx7(MarkdownRenderer, { content: streamingText }),
|
|
3609
|
+
/* @__PURE__ */ jsx7("span", { style: styles.cursor })
|
|
3610
|
+
] }) : /* @__PURE__ */ jsxs6("div", { style: styles.thinkingText, children: [
|
|
3611
|
+
/* @__PURE__ */ jsx7("span", { children: "Pensando" }),
|
|
3612
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.typingIndicator, children: [
|
|
3613
|
+
/* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
|
|
3614
|
+
/* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
|
|
3615
|
+
/* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot })
|
|
3404
3616
|
] })
|
|
3405
3617
|
] })
|
|
3406
3618
|
}
|
|
3407
3619
|
)
|
|
3408
3620
|
}
|
|
3409
3621
|
),
|
|
3410
|
-
/* @__PURE__ */
|
|
3622
|
+
/* @__PURE__ */ jsx7("div", { ref: messagesEndRef })
|
|
3411
3623
|
] }),
|
|
3412
|
-
/* @__PURE__ */
|
|
3413
|
-
/* @__PURE__ */
|
|
3624
|
+
/* @__PURE__ */ jsxs6("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
|
|
3625
|
+
/* @__PURE__ */ jsx7(
|
|
3414
3626
|
"textarea",
|
|
3415
3627
|
{
|
|
3416
3628
|
style: {
|
|
@@ -3427,7 +3639,7 @@ var FloatingChatBox = ({
|
|
|
3427
3639
|
disabled: !mapId || !baseUrl || isStreaming
|
|
3428
3640
|
}
|
|
3429
3641
|
),
|
|
3430
|
-
/* @__PURE__ */
|
|
3642
|
+
/* @__PURE__ */ jsx7(
|
|
3431
3643
|
"button",
|
|
3432
3644
|
{
|
|
3433
3645
|
type: "button",
|
|
@@ -3436,36 +3648,38 @@ var FloatingChatBox = ({
|
|
|
3436
3648
|
onClick: () => void handleSend(),
|
|
3437
3649
|
disabled: !canSend,
|
|
3438
3650
|
"aria-label": "Enviar mensaje",
|
|
3439
|
-
children: /* @__PURE__ */
|
|
3651
|
+
children: /* @__PURE__ */ jsx7(SendIcon, {})
|
|
3440
3652
|
}
|
|
3441
3653
|
)
|
|
3442
3654
|
] }),
|
|
3443
|
-
errorMessage && /* @__PURE__ */
|
|
3444
|
-
|
|
3445
|
-
!
|
|
3655
|
+
errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.errorText, children: errorMessage }),
|
|
3656
|
+
isStreaming && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Generando sugerencias..." }),
|
|
3657
|
+
!mapId && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
|
|
3658
|
+
!baseUrl && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
|
|
3446
3659
|
]
|
|
3447
3660
|
}
|
|
3448
3661
|
),
|
|
3449
|
-
!(hideButton && !open) && /* @__PURE__ */
|
|
3662
|
+
!(hideButton && !open) && /* @__PURE__ */ jsx7(
|
|
3450
3663
|
"button",
|
|
3451
3664
|
{
|
|
3452
3665
|
type: "button",
|
|
3453
3666
|
className: `zenit-ai-button ${open ? "open" : ""}${open && isMobile ? " zenit-ai-button--hidden-mobile" : ""}`,
|
|
3454
3667
|
style: {
|
|
3455
3668
|
...styles.floatingButton,
|
|
3456
|
-
...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed
|
|
3669
|
+
...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed,
|
|
3670
|
+
zIndex: open ? 100001 : 99999
|
|
3457
3671
|
},
|
|
3458
3672
|
onClick: () => setOpen((prev) => !prev),
|
|
3459
3673
|
"aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
|
|
3460
|
-
children: open ? /* @__PURE__ */
|
|
3461
|
-
/* @__PURE__ */
|
|
3462
|
-
!isMobile && /* @__PURE__ */
|
|
3674
|
+
children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
3675
|
+
/* @__PURE__ */ jsx7(ChatIcon, {}),
|
|
3676
|
+
!isMobile && /* @__PURE__ */ jsx7("span", { children: "Asistente IA" })
|
|
3463
3677
|
] })
|
|
3464
3678
|
}
|
|
3465
3679
|
)
|
|
3466
3680
|
] });
|
|
3467
3681
|
if (typeof document !== "undefined") {
|
|
3468
|
-
return
|
|
3682
|
+
return createPortal2(chatContent, document.body);
|
|
3469
3683
|
}
|
|
3470
3684
|
return chatContent;
|
|
3471
3685
|
};
|
|
@@ -3508,4 +3722,4 @@ export {
|
|
|
3508
3722
|
useSendMessageStream,
|
|
3509
3723
|
FloatingChatBox
|
|
3510
3724
|
};
|
|
3511
|
-
//# sourceMappingURL=chunk-
|
|
3725
|
+
//# sourceMappingURL=chunk-PCTRVN4O.mjs.map
|