@zendir/ui 0.2.3 → 0.2.5
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/CHANGELOG.md +32 -0
- package/dist/react/charts/GroundTrackMap.d.ts +28 -1
- package/dist/react/charts/GroundTrackMap.js +6 -2
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/GroundTrackMapLeaflet.d.ts +6 -2
- package/dist/react/charts/GroundTrackMapLeaflet.js +318 -103
- package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
- package/dist/react/charts/groundTrackMapLeafletTiles.d.ts +10 -0
- package/dist/react/charts/groundTrackMapLeafletTiles.js +11 -1
- package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
- package/dist/react/charts/index.d.ts +2 -2
- package/dist/react/core/Container.js +23 -15
- package/dist/react/core/Container.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { GroundStation } from '../types';
|
|
3
|
-
import { SatelliteTrack, GroundStationEnhanced, SROGroundStation, MapPin, LightSource } from './GroundTrackMap';
|
|
3
|
+
import { SatelliteTrack, GroundStationEnhanced, SROGroundStation, MapPin, LightSource, MapLayerDef } from './GroundTrackMap';
|
|
4
4
|
|
|
5
5
|
export type GroundStationMapItem = GroundStation | GroundStationEnhanced | SROGroundStation;
|
|
6
6
|
export interface GroundTrackMapLeafletProps {
|
|
@@ -43,6 +43,10 @@ export interface GroundTrackMapLeafletProps {
|
|
|
43
43
|
onPinAdd?: (pin: Omit<MapPin, 'id'>) => void;
|
|
44
44
|
onPinUpdate?: (pin: MapPin) => void;
|
|
45
45
|
onPinRemove?: (pinId: string) => void;
|
|
46
|
+
/** Custom overlay layers for the Layers panel */
|
|
47
|
+
customLayers?: MapLayerDef[];
|
|
48
|
+
/** Called when any layer is toggled */
|
|
49
|
+
onLayerChange?: (layerId: string, enabled: boolean) => void;
|
|
46
50
|
}
|
|
47
|
-
export declare function GroundTrackMapLeaflet({ allSatellites, groundStations, showTerminator, terminatorTime, showGrid, showLegend, showEquator, showRecenterButton, showMapStyleToggle, defaultCenter, defaultZoom, height, width, minHeight, emptyMessage, tileUrl, nightTileUrl, lightSources, className, onSatelliteClick, onStationClick, pins, pinsEditable, onPinAdd, onPinUpdate, onPinRemove, }: GroundTrackMapLeafletProps): React.ReactElement;
|
|
51
|
+
export declare function GroundTrackMapLeaflet({ allSatellites, groundStations, showTerminator, terminatorTime, showGrid, showLegend, showEquator, showRecenterButton, showMapStyleToggle, defaultCenter, defaultZoom, height, width, minHeight, emptyMessage, tileUrl, nightTileUrl, lightSources, className, onSatelliteClick, onStationClick, pins, pinsEditable, onPinAdd, onPinUpdate, onPinRemove, customLayers, onLayerChange, }: GroundTrackMapLeafletProps): React.ReactElement;
|
|
48
52
|
export default GroundTrackMapLeaflet;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useState,
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState, useEffect, useCallback } from "react";
|
|
3
3
|
import L from "leaflet";
|
|
4
4
|
import "leaflet/dist/leaflet.css";
|
|
5
5
|
/* empty css */
|
|
6
6
|
import { geodesicCirclePoints, splitRingAtAntimeridian, segmentsWithWorldCopies, splitPolylineAtAntimeridian } from "./groundTrackMapLeafletUtils.js";
|
|
7
|
-
import {
|
|
7
|
+
import { TILE_PRESETS, getBasemapAttribution, FALLBACK_TILE, OSM_ATTRIBUTION } from "./groundTrackMapLeafletTiles.js";
|
|
8
8
|
import { useTheme } from "../theme/ThemeProvider.js";
|
|
9
9
|
const STATUS_COLOR_MAP = {
|
|
10
10
|
normal: "#56f000",
|
|
@@ -160,7 +160,9 @@ function GroundTrackMapLeaflet({
|
|
|
160
160
|
pinsEditable = false,
|
|
161
161
|
onPinAdd,
|
|
162
162
|
onPinUpdate,
|
|
163
|
-
onPinRemove
|
|
163
|
+
onPinRemove,
|
|
164
|
+
customLayers,
|
|
165
|
+
onLayerChange
|
|
164
166
|
}) {
|
|
165
167
|
const { tokens } = useTheme();
|
|
166
168
|
const containerRef = useRef(null);
|
|
@@ -170,9 +172,71 @@ function GroundTrackMapLeaflet({
|
|
|
170
172
|
const controlsRef = useRef([]);
|
|
171
173
|
const pinsGroupRef = useRef(null);
|
|
172
174
|
const [ready, setReady] = useState(false);
|
|
173
|
-
const [
|
|
175
|
+
const [layersPanelOpen, setLayersPanelOpen] = useState(false);
|
|
176
|
+
const layersPanelRef = useRef(null);
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!layersPanelOpen) return;
|
|
179
|
+
const handleClickOutside = (e) => {
|
|
180
|
+
if (layersPanelRef.current && !layersPanelRef.current.contains(e.target)) {
|
|
181
|
+
setLayersPanelOpen(false);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
185
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
186
|
+
}, [layersPanelOpen]);
|
|
187
|
+
const TILE_STORAGE_KEY = "zendir-map-tile-style";
|
|
174
188
|
const isExplicitTileUrl = tileUrl !== void 0;
|
|
189
|
+
const [tileStyle, setTileStyle] = useState(() => {
|
|
190
|
+
if (isExplicitTileUrl) return "dark";
|
|
191
|
+
try {
|
|
192
|
+
const saved = localStorage.getItem(TILE_STORAGE_KEY);
|
|
193
|
+
if (saved === "dark" || saved === "satellite") return saved;
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
return "dark";
|
|
197
|
+
});
|
|
198
|
+
const handleTileStyleChange = useCallback((style) => {
|
|
199
|
+
setTileStyle(style);
|
|
200
|
+
try {
|
|
201
|
+
localStorage.setItem(TILE_STORAGE_KEY, style);
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
}, []);
|
|
175
205
|
const effectiveTileUrl = isExplicitTileUrl ? tileUrl : TILE_PRESETS[tileStyle];
|
|
206
|
+
const [internalTerminator, setInternalTerminator] = useState(showTerminator);
|
|
207
|
+
const [internalGrid, setInternalGrid] = useState(showGrid);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
setInternalTerminator(showTerminator);
|
|
210
|
+
}, [showTerminator]);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
setInternalGrid(showGrid);
|
|
213
|
+
}, [showGrid]);
|
|
214
|
+
const [customLayerState, setCustomLayerState] = useState(() => {
|
|
215
|
+
const state = {};
|
|
216
|
+
for (const l of customLayers ?? []) {
|
|
217
|
+
state[l.id] = l.defaultEnabled ?? true;
|
|
218
|
+
}
|
|
219
|
+
return state;
|
|
220
|
+
});
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
setCustomLayerState((prev) => {
|
|
223
|
+
const next = { ...prev };
|
|
224
|
+
for (const l of customLayers ?? []) {
|
|
225
|
+
if (!(l.id in next)) next[l.id] = l.defaultEnabled ?? true;
|
|
226
|
+
}
|
|
227
|
+
return next;
|
|
228
|
+
});
|
|
229
|
+
}, [customLayers]);
|
|
230
|
+
const handleLayerToggle = useCallback((layerId, enabled) => {
|
|
231
|
+
if (layerId === "terminator") {
|
|
232
|
+
setInternalTerminator(enabled);
|
|
233
|
+
} else if (layerId === "grid") {
|
|
234
|
+
setInternalGrid(enabled);
|
|
235
|
+
} else {
|
|
236
|
+
setCustomLayerState((prev) => ({ ...prev, [layerId]: enabled }));
|
|
237
|
+
}
|
|
238
|
+
onLayerChange == null ? void 0 : onLayerChange(layerId, enabled);
|
|
239
|
+
}, [onLayerChange]);
|
|
176
240
|
const clearLayers = useCallback(() => {
|
|
177
241
|
var _a;
|
|
178
242
|
const map = mapRef.current;
|
|
@@ -204,7 +268,6 @@ function GroundTrackMapLeaflet({
|
|
|
204
268
|
maxBounds: [[-90, -540], [90, 540]],
|
|
205
269
|
maxBoundsViscosity: 1
|
|
206
270
|
});
|
|
207
|
-
L.control.zoom({ position: "topright" }).addTo(map);
|
|
208
271
|
map.attributionControl.setPrefix("");
|
|
209
272
|
const overlayGroup = L.layerGroup();
|
|
210
273
|
overlayGroup.addTo(map);
|
|
@@ -241,19 +304,13 @@ function GroundTrackMapLeaflet({
|
|
|
241
304
|
tileLayerRef.current.remove();
|
|
242
305
|
tileLayerRef.current = null;
|
|
243
306
|
}
|
|
244
|
-
try {
|
|
245
|
-
map.attributionControl.removeAttribution(CARTO_ATTRIBUTION);
|
|
246
|
-
map.attributionControl.removeAttribution(OSM_ATTRIBUTION);
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
307
|
const isCartoTiles = effectiveTileUrl.includes("cartocdn");
|
|
250
|
-
|
|
251
|
-
map.attributionControl.addAttribution(CARTO_ATTRIBUTION);
|
|
252
|
-
}
|
|
308
|
+
const basemapAttribution = getBasemapAttribution(effectiveTileUrl);
|
|
253
309
|
const tile = L.tileLayer(effectiveTileUrl, {
|
|
254
310
|
maxZoom: 19,
|
|
255
311
|
subdomains: isCartoTiles ? "abcd" : "abc",
|
|
256
|
-
crossOrigin: true
|
|
312
|
+
crossOrigin: true,
|
|
313
|
+
attribution: basemapAttribution || void 0
|
|
257
314
|
});
|
|
258
315
|
tile.addTo(map);
|
|
259
316
|
tile.bringToBack();
|
|
@@ -267,7 +324,8 @@ function GroundTrackMapLeaflet({
|
|
|
267
324
|
const fallback = L.tileLayer(FALLBACK_TILE, {
|
|
268
325
|
maxZoom: 19,
|
|
269
326
|
subdomains: "abc",
|
|
270
|
-
crossOrigin: true
|
|
327
|
+
crossOrigin: true,
|
|
328
|
+
attribution: OSM_ATTRIBUTION
|
|
271
329
|
});
|
|
272
330
|
fallback.addTo(map);
|
|
273
331
|
fallback.bringToBack();
|
|
@@ -279,7 +337,7 @@ function GroundTrackMapLeaflet({
|
|
|
279
337
|
if (!ready || !mapRef.current) return;
|
|
280
338
|
const map = mapRef.current;
|
|
281
339
|
clearLayers();
|
|
282
|
-
if (nightTileUrl &&
|
|
340
|
+
if (nightTileUrl && internalTerminator) {
|
|
283
341
|
const nightTile = L.tileLayer(nightTileUrl, {
|
|
284
342
|
maxZoom: 19,
|
|
285
343
|
crossOrigin: true,
|
|
@@ -288,7 +346,7 @@ function GroundTrackMapLeaflet({
|
|
|
288
346
|
});
|
|
289
347
|
addLayer(nightTile);
|
|
290
348
|
}
|
|
291
|
-
if (
|
|
349
|
+
if (internalTerminator) {
|
|
292
350
|
const now = terminatorTime ?? /* @__PURE__ */ new Date();
|
|
293
351
|
const BAND_STEP = 2;
|
|
294
352
|
const MAX_DEP = 24;
|
|
@@ -345,7 +403,7 @@ function GroundTrackMapLeaflet({
|
|
|
345
403
|
});
|
|
346
404
|
}
|
|
347
405
|
}
|
|
348
|
-
if (lightSources && lightSources.length > 0 &&
|
|
406
|
+
if (lightSources && lightSources.length > 0 && internalTerminator) {
|
|
349
407
|
const now = terminatorTime ?? /* @__PURE__ */ new Date();
|
|
350
408
|
const dayOfYear = Math.floor((now.getTime() - new Date(now.getFullYear(), 0, 0).getTime()) / 864e5);
|
|
351
409
|
const declination = -23.44 * Math.cos(2 * Math.PI / 365 * (dayOfYear + 10));
|
|
@@ -391,7 +449,7 @@ function GroundTrackMapLeaflet({
|
|
|
391
449
|
}
|
|
392
450
|
}
|
|
393
451
|
}
|
|
394
|
-
if (
|
|
452
|
+
if (internalGrid) {
|
|
395
453
|
const gridStyle = { color: "rgba(157, 112, 255, 0.12)", weight: 0.5, interactive: false };
|
|
396
454
|
[-360, 0, 360].forEach((offset) => {
|
|
397
455
|
for (let lon = -180; lon <= 180; lon += 30) {
|
|
@@ -583,11 +641,11 @@ function GroundTrackMapLeaflet({
|
|
|
583
641
|
ready,
|
|
584
642
|
allSatellites,
|
|
585
643
|
groundStations,
|
|
586
|
-
|
|
644
|
+
internalTerminator,
|
|
587
645
|
terminatorTime,
|
|
588
646
|
nightTileUrl,
|
|
589
647
|
lightSources,
|
|
590
|
-
|
|
648
|
+
internalGrid,
|
|
591
649
|
showEquator,
|
|
592
650
|
showLegend,
|
|
593
651
|
tokens.colors.text.secondary,
|
|
@@ -686,8 +744,42 @@ function GroundTrackMapLeaflet({
|
|
|
686
744
|
map.setView(defaultCenter, defaultZoom);
|
|
687
745
|
}
|
|
688
746
|
}, [allSatellites, groundStations, defaultCenter, defaultZoom]);
|
|
747
|
+
const handleZoomIn = useCallback(() => {
|
|
748
|
+
var _a;
|
|
749
|
+
(_a = mapRef.current) == null ? void 0 : _a.zoomIn();
|
|
750
|
+
}, []);
|
|
751
|
+
const handleZoomOut = useCallback(() => {
|
|
752
|
+
var _a;
|
|
753
|
+
(_a = mapRef.current) == null ? void 0 : _a.zoomOut();
|
|
754
|
+
}, []);
|
|
689
755
|
const resolvedMinHeight = minHeight || (typeof height === "number" ? `${height}px` : "400px");
|
|
690
756
|
const isEmpty = allSatellites.length === 0 && groundStations.length === 0 && (!pins || pins.length === 0);
|
|
757
|
+
const ctrlBtnBase = {
|
|
758
|
+
background: "rgba(20, 24, 38, 0.92)",
|
|
759
|
+
backdropFilter: "blur(8px)",
|
|
760
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
761
|
+
border: "1px solid rgba(120, 100, 180, 0.18)",
|
|
762
|
+
color: "#c8c0d8",
|
|
763
|
+
cursor: "pointer",
|
|
764
|
+
display: "flex",
|
|
765
|
+
alignItems: "center",
|
|
766
|
+
justifyContent: "center",
|
|
767
|
+
transition: "background 0.15s, border-color 0.15s"
|
|
768
|
+
};
|
|
769
|
+
const ctrlHover = (e) => {
|
|
770
|
+
e.currentTarget.style.background = "rgba(30, 34, 52, 0.95)";
|
|
771
|
+
e.currentTarget.style.borderColor = "rgba(157, 112, 255, 0.4)";
|
|
772
|
+
};
|
|
773
|
+
const ctrlLeave = (e) => {
|
|
774
|
+
e.currentTarget.style.background = "rgba(20, 24, 38, 0.92)";
|
|
775
|
+
e.currentTarget.style.borderColor = "rgba(120, 100, 180, 0.18)";
|
|
776
|
+
};
|
|
777
|
+
const overlayItems = [];
|
|
778
|
+
overlayItems.push({ id: "terminator", label: "Day / Night", enabled: internalTerminator });
|
|
779
|
+
overlayItems.push({ id: "grid", label: "Grid Lines", enabled: internalGrid });
|
|
780
|
+
for (const l of customLayers ?? []) {
|
|
781
|
+
overlayItems.push({ id: l.id, label: l.label, enabled: customLayerState[l.id] ?? (l.defaultEnabled ?? true) });
|
|
782
|
+
}
|
|
691
783
|
return /* @__PURE__ */ jsxs(
|
|
692
784
|
"div",
|
|
693
785
|
{
|
|
@@ -725,22 +817,22 @@ function GroundTrackMapLeaflet({
|
|
|
725
817
|
children: emptyMessage
|
|
726
818
|
}
|
|
727
819
|
),
|
|
728
|
-
|
|
820
|
+
/* @__PURE__ */ jsxs(
|
|
729
821
|
"div",
|
|
730
822
|
{
|
|
731
|
-
className: "zendir-map-controls",
|
|
732
823
|
style: {
|
|
733
824
|
position: "absolute",
|
|
734
|
-
|
|
735
|
-
|
|
825
|
+
top: 10,
|
|
826
|
+
right: 10,
|
|
736
827
|
zIndex: 1e3,
|
|
737
828
|
display: "flex",
|
|
738
829
|
flexDirection: "row",
|
|
739
|
-
gap:
|
|
740
|
-
alignItems: "flex-
|
|
830
|
+
gap: 4,
|
|
831
|
+
alignItems: "flex-start",
|
|
832
|
+
pointerEvents: "none"
|
|
741
833
|
},
|
|
742
834
|
children: [
|
|
743
|
-
showRecenterButton && /* @__PURE__ */
|
|
835
|
+
showRecenterButton && /* @__PURE__ */ jsx(
|
|
744
836
|
"button",
|
|
745
837
|
{
|
|
746
838
|
type: "button",
|
|
@@ -748,86 +840,209 @@ function GroundTrackMapLeaflet({
|
|
|
748
840
|
title: "Recenter map to fit all assets and ground stations",
|
|
749
841
|
"aria-label": "Recenter map",
|
|
750
842
|
style: {
|
|
751
|
-
|
|
752
|
-
backdropFilter: "blur(8px)",
|
|
753
|
-
WebkitBackdropFilter: "blur(8px)",
|
|
754
|
-
border: "1px solid rgba(120, 100, 180, 0.2)",
|
|
843
|
+
...ctrlBtnBase,
|
|
755
844
|
borderRadius: 4,
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
fontSize: 11,
|
|
845
|
+
padding: "6px 8px",
|
|
846
|
+
gap: 4,
|
|
847
|
+
fontSize: 10,
|
|
760
848
|
fontWeight: 500,
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
gap: 5,
|
|
764
|
-
transition: "background 0.15s, border-color 0.15s",
|
|
765
|
-
letterSpacing: "0.02em"
|
|
849
|
+
letterSpacing: "0.03em",
|
|
850
|
+
pointerEvents: "auto"
|
|
766
851
|
},
|
|
767
|
-
onMouseEnter:
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
e.currentTarget.style.borderColor = "rgba(120, 100, 180, 0.2)";
|
|
774
|
-
},
|
|
775
|
-
children: [
|
|
776
|
-
/* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
777
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
778
|
-
/* @__PURE__ */ jsx("path", { d: "M12 2v4M12 18v4M2 12h4M18 12h4" })
|
|
779
|
-
] }),
|
|
780
|
-
"Recenter"
|
|
781
|
-
]
|
|
852
|
+
onMouseEnter: ctrlHover,
|
|
853
|
+
onMouseLeave: ctrlLeave,
|
|
854
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", children: [
|
|
855
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
856
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2v4M12 18v4M2 12h4M18 12h4" })
|
|
857
|
+
] })
|
|
782
858
|
}
|
|
783
859
|
),
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
onClick: () => setTileStyle(style),
|
|
801
|
-
title: style === "dark" ? "Dark ops map" : "Satellite imagery",
|
|
802
|
-
"aria-label": style === "dark" ? "Dark map style" : "Satellite imagery",
|
|
803
|
-
"aria-pressed": tileStyle === style,
|
|
804
|
-
style: {
|
|
805
|
-
background: tileStyle === style ? "rgba(157, 112, 255, 0.22)" : "transparent",
|
|
806
|
-
border: "none",
|
|
807
|
-
borderRight: style === "dark" ? "1px solid rgba(120, 100, 180, 0.15)" : "none",
|
|
808
|
-
color: tileStyle === style ? "#d0c4ee" : "#7a748e",
|
|
809
|
-
cursor: "pointer",
|
|
810
|
-
padding: "5px 8px",
|
|
811
|
-
fontSize: 10,
|
|
812
|
-
fontWeight: tileStyle === style ? 600 : 400,
|
|
813
|
-
display: "flex",
|
|
814
|
-
alignItems: "center",
|
|
815
|
-
gap: 4,
|
|
816
|
-
transition: "all 0.15s ease",
|
|
817
|
-
letterSpacing: "0.02em"
|
|
818
|
-
},
|
|
819
|
-
children: [
|
|
820
|
-
style === "dark" ? /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" }) }) : /* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
821
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
822
|
-
/* @__PURE__ */ jsx("path", { d: "M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z" })
|
|
823
|
-
] }),
|
|
824
|
-
style === "dark" ? "Dark" : "Satellite"
|
|
825
|
-
]
|
|
860
|
+
/* @__PURE__ */ jsxs("div", { ref: layersPanelRef, style: { position: "relative", pointerEvents: "auto" }, children: [
|
|
861
|
+
/* @__PURE__ */ jsx(
|
|
862
|
+
"button",
|
|
863
|
+
{
|
|
864
|
+
type: "button",
|
|
865
|
+
onClick: () => setLayersPanelOpen((o) => !o),
|
|
866
|
+
title: "Map layers",
|
|
867
|
+
"aria-label": "Toggle map layers panel",
|
|
868
|
+
"aria-expanded": layersPanelOpen,
|
|
869
|
+
style: {
|
|
870
|
+
...ctrlBtnBase,
|
|
871
|
+
borderRadius: 4,
|
|
872
|
+
padding: "6px 8px",
|
|
873
|
+
pointerEvents: "auto",
|
|
874
|
+
borderColor: layersPanelOpen ? "rgba(157, 112, 255, 0.4)" : void 0,
|
|
875
|
+
background: layersPanelOpen ? "rgba(30, 34, 52, 0.95)" : ctrlBtnBase.background
|
|
826
876
|
},
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
877
|
+
onMouseEnter: ctrlHover,
|
|
878
|
+
onMouseLeave: ctrlLeave,
|
|
879
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", children: [
|
|
880
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2L2 7l10 5 10-5-10-5z" }),
|
|
881
|
+
/* @__PURE__ */ jsx("path", { d: "M2 17l10 5 10-5" }),
|
|
882
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12l10 5 10-5" })
|
|
883
|
+
] })
|
|
884
|
+
}
|
|
885
|
+
),
|
|
886
|
+
layersPanelOpen && /* @__PURE__ */ jsxs(
|
|
887
|
+
"div",
|
|
888
|
+
{
|
|
889
|
+
style: {
|
|
890
|
+
position: "absolute",
|
|
891
|
+
top: "calc(100% + 6px)",
|
|
892
|
+
right: 0,
|
|
893
|
+
minWidth: 180,
|
|
894
|
+
background: "rgba(16, 18, 30, 0.96)",
|
|
895
|
+
backdropFilter: "blur(16px)",
|
|
896
|
+
WebkitBackdropFilter: "blur(16px)",
|
|
897
|
+
border: "1px solid rgba(120, 100, 180, 0.18)",
|
|
898
|
+
borderRadius: 6,
|
|
899
|
+
padding: "8px 0",
|
|
900
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.5)"
|
|
901
|
+
},
|
|
902
|
+
children: [
|
|
903
|
+
!isExplicitTileUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
904
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
905
|
+
padding: "4px 12px 6px",
|
|
906
|
+
fontSize: 9,
|
|
907
|
+
fontWeight: 600,
|
|
908
|
+
letterSpacing: "0.08em",
|
|
909
|
+
textTransform: "uppercase",
|
|
910
|
+
color: "#7a748e"
|
|
911
|
+
}, children: "Base Map" }),
|
|
912
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 2, padding: "0 8px 8px" }, children: ["dark", "satellite"].map((style) => /* @__PURE__ */ jsxs(
|
|
913
|
+
"button",
|
|
914
|
+
{
|
|
915
|
+
type: "button",
|
|
916
|
+
onClick: () => handleTileStyleChange(style),
|
|
917
|
+
"aria-pressed": tileStyle === style,
|
|
918
|
+
style: {
|
|
919
|
+
flex: 1,
|
|
920
|
+
background: tileStyle === style ? "rgba(157, 112, 255, 0.18)" : "rgba(255,255,255,0.03)",
|
|
921
|
+
border: `1px solid ${tileStyle === style ? "rgba(157, 112, 255, 0.35)" : "rgba(120, 100, 180, 0.1)"}`,
|
|
922
|
+
borderRadius: 4,
|
|
923
|
+
color: tileStyle === style ? "#d0c4ee" : "#7a748e",
|
|
924
|
+
cursor: "pointer",
|
|
925
|
+
padding: "5px 6px",
|
|
926
|
+
fontSize: 10,
|
|
927
|
+
fontWeight: tileStyle === style ? 600 : 400,
|
|
928
|
+
display: "flex",
|
|
929
|
+
alignItems: "center",
|
|
930
|
+
justifyContent: "center",
|
|
931
|
+
gap: 4,
|
|
932
|
+
transition: "all 0.15s ease"
|
|
933
|
+
},
|
|
934
|
+
children: [
|
|
935
|
+
style === "dark" ? /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", children: /* @__PURE__ */ jsx("path", { d: "M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" }) }) : /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
936
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
937
|
+
/* @__PURE__ */ jsx("path", { d: "M2 12h20M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z" })
|
|
938
|
+
] }),
|
|
939
|
+
style === "dark" ? "Dark" : "Satellite"
|
|
940
|
+
]
|
|
941
|
+
},
|
|
942
|
+
style
|
|
943
|
+
)) }),
|
|
944
|
+
/* @__PURE__ */ jsx("div", { style: { height: 1, background: "rgba(120, 100, 180, 0.1)", margin: "0 8px" } })
|
|
945
|
+
] }),
|
|
946
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
947
|
+
padding: `${isExplicitTileUrl ? "4px" : "8px"} 12px 4px`,
|
|
948
|
+
fontSize: 9,
|
|
949
|
+
fontWeight: 600,
|
|
950
|
+
letterSpacing: "0.08em",
|
|
951
|
+
textTransform: "uppercase",
|
|
952
|
+
color: "#7a748e"
|
|
953
|
+
}, children: "Overlays" }),
|
|
954
|
+
overlayItems.map((item) => /* @__PURE__ */ jsxs(
|
|
955
|
+
"button",
|
|
956
|
+
{
|
|
957
|
+
type: "button",
|
|
958
|
+
onClick: () => handleLayerToggle(item.id, !item.enabled),
|
|
959
|
+
style: {
|
|
960
|
+
display: "flex",
|
|
961
|
+
alignItems: "center",
|
|
962
|
+
gap: 8,
|
|
963
|
+
width: "100%",
|
|
964
|
+
padding: "5px 12px",
|
|
965
|
+
background: "transparent",
|
|
966
|
+
border: "none",
|
|
967
|
+
color: item.enabled ? "#c8c0d8" : "#5a5470",
|
|
968
|
+
cursor: "pointer",
|
|
969
|
+
fontSize: 11,
|
|
970
|
+
textAlign: "left",
|
|
971
|
+
transition: "background 0.1s"
|
|
972
|
+
},
|
|
973
|
+
onMouseEnter: (e) => {
|
|
974
|
+
e.currentTarget.style.background = "rgba(157, 112, 255, 0.08)";
|
|
975
|
+
},
|
|
976
|
+
onMouseLeave: (e) => {
|
|
977
|
+
e.currentTarget.style.background = "transparent";
|
|
978
|
+
},
|
|
979
|
+
children: [
|
|
980
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
981
|
+
width: 14,
|
|
982
|
+
height: 14,
|
|
983
|
+
borderRadius: 3,
|
|
984
|
+
border: `1.5px solid ${item.enabled ? "rgba(157, 112, 255, 0.6)" : "rgba(120, 100, 180, 0.25)"}`,
|
|
985
|
+
background: item.enabled ? "rgba(157, 112, 255, 0.22)" : "transparent",
|
|
986
|
+
display: "flex",
|
|
987
|
+
alignItems: "center",
|
|
988
|
+
justifyContent: "center",
|
|
989
|
+
flexShrink: 0,
|
|
990
|
+
transition: "all 0.15s ease"
|
|
991
|
+
}, children: item.enabled && /* @__PURE__ */ jsx("svg", { width: "9", height: "9", viewBox: "0 0 12 12", fill: "none", stroke: "#d0c4ee", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M2 6l3 3 5-5" }) }) }),
|
|
992
|
+
item.label
|
|
993
|
+
]
|
|
994
|
+
},
|
|
995
|
+
item.id
|
|
996
|
+
))
|
|
997
|
+
]
|
|
998
|
+
}
|
|
999
|
+
)
|
|
1000
|
+
] }),
|
|
1001
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", pointerEvents: "auto" }, children: [
|
|
1002
|
+
/* @__PURE__ */ jsx(
|
|
1003
|
+
"button",
|
|
1004
|
+
{
|
|
1005
|
+
type: "button",
|
|
1006
|
+
onClick: handleZoomIn,
|
|
1007
|
+
title: "Zoom in",
|
|
1008
|
+
"aria-label": "Zoom in",
|
|
1009
|
+
style: {
|
|
1010
|
+
...ctrlBtnBase,
|
|
1011
|
+
borderRadius: "4px 4px 0 0",
|
|
1012
|
+
borderBottom: "none",
|
|
1013
|
+
width: 30,
|
|
1014
|
+
height: 28,
|
|
1015
|
+
fontSize: 16,
|
|
1016
|
+
fontWeight: 300,
|
|
1017
|
+
pointerEvents: "auto"
|
|
1018
|
+
},
|
|
1019
|
+
onMouseEnter: ctrlHover,
|
|
1020
|
+
onMouseLeave: ctrlLeave,
|
|
1021
|
+
children: "+"
|
|
1022
|
+
}
|
|
1023
|
+
),
|
|
1024
|
+
/* @__PURE__ */ jsx(
|
|
1025
|
+
"button",
|
|
1026
|
+
{
|
|
1027
|
+
type: "button",
|
|
1028
|
+
onClick: handleZoomOut,
|
|
1029
|
+
title: "Zoom out",
|
|
1030
|
+
"aria-label": "Zoom out",
|
|
1031
|
+
style: {
|
|
1032
|
+
...ctrlBtnBase,
|
|
1033
|
+
borderRadius: "0 0 4px 4px",
|
|
1034
|
+
width: 30,
|
|
1035
|
+
height: 28,
|
|
1036
|
+
fontSize: 16,
|
|
1037
|
+
fontWeight: 300,
|
|
1038
|
+
pointerEvents: "auto"
|
|
1039
|
+
},
|
|
1040
|
+
onMouseEnter: ctrlHover,
|
|
1041
|
+
onMouseLeave: ctrlLeave,
|
|
1042
|
+
children: "−"
|
|
1043
|
+
}
|
|
1044
|
+
)
|
|
1045
|
+
] })
|
|
831
1046
|
]
|
|
832
1047
|
}
|
|
833
1048
|
)
|