@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.
@@ -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, useCallback, useEffect } from "react";
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 { CARTO_ATTRIBUTION, OSM_ATTRIBUTION, TILE_PRESETS, FALLBACK_TILE } from "./groundTrackMapLeafletTiles.js";
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 [tileStyle, setTileStyle] = useState("dark");
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
- if (isCartoTiles) {
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 && showTerminator) {
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 (showTerminator) {
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 && showTerminator) {
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 (showGrid) {
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
- showTerminator,
644
+ internalTerminator,
587
645
  terminatorTime,
588
646
  nightTileUrl,
589
647
  lightSources,
590
- showGrid,
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
- (showRecenterButton || showMapStyleToggle && !isExplicitTileUrl) && /* @__PURE__ */ jsxs(
820
+ /* @__PURE__ */ jsxs(
729
821
  "div",
730
822
  {
731
- className: "zendir-map-controls",
732
823
  style: {
733
824
  position: "absolute",
734
- bottom: 28,
735
- left: 10,
825
+ top: 10,
826
+ right: 10,
736
827
  zIndex: 1e3,
737
828
  display: "flex",
738
829
  flexDirection: "row",
739
- gap: 6,
740
- alignItems: "flex-end"
830
+ gap: 4,
831
+ alignItems: "flex-start",
832
+ pointerEvents: "none"
741
833
  },
742
834
  children: [
743
- showRecenterButton && /* @__PURE__ */ jsxs(
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
- background: "rgba(20, 24, 38, 0.92)",
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
- color: "#c8c0d8",
757
- cursor: "pointer",
758
- padding: "6px 10px",
759
- fontSize: 11,
845
+ padding: "6px 8px",
846
+ gap: 4,
847
+ fontSize: 10,
760
848
  fontWeight: 500,
761
- display: "flex",
762
- alignItems: "center",
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: (e) => {
768
- e.currentTarget.style.background = "rgba(30, 34, 52, 0.95)";
769
- e.currentTarget.style.borderColor = "rgba(157, 112, 255, 0.4)";
770
- },
771
- onMouseLeave: (e) => {
772
- e.currentTarget.style.background = "rgba(20, 24, 38, 0.92)";
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
- showMapStyleToggle && !isExplicitTileUrl && /* @__PURE__ */ jsx(
785
- "div",
786
- {
787
- style: {
788
- background: "rgba(20, 24, 38, 0.92)",
789
- backdropFilter: "blur(8px)",
790
- WebkitBackdropFilter: "blur(8px)",
791
- border: "1px solid rgba(120, 100, 180, 0.2)",
792
- borderRadius: 4,
793
- display: "flex",
794
- overflow: "hidden"
795
- },
796
- children: ["dark", "satellite"].map((style) => /* @__PURE__ */ jsxs(
797
- "button",
798
- {
799
- type: "button",
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
- style
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
  )