@zendir/ui 0.2.2 → 0.2.4

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.
@@ -106,6 +106,20 @@ export interface LightSource {
106
106
  /** Optional label shown on hover (Leaflet) or in tooltip (Canvas). */
107
107
  label?: string;
108
108
  }
109
+ /**
110
+ * Developer-defined map layer shown in the Layers panel.
111
+ * The GroundTrackMap does not render the layer content — it only provides
112
+ * the toggle UI. The consumer app should listen to `onLayerChange` and
113
+ * render/hide its own overlay accordingly.
114
+ */
115
+ export interface MapLayerDef {
116
+ /** Unique identifier for the layer (e.g. 'heatmap', 'coverage') */
117
+ id: string;
118
+ /** Display label in the Layers panel */
119
+ label: string;
120
+ /** Whether the layer is on by default (default true) */
121
+ defaultEnabled?: boolean;
122
+ }
109
123
  export interface GroundTrackMapProps {
110
124
  /** Single satellite ground track (legacy API) */
111
125
  groundTrack?: GroundTrackPoint[];
@@ -131,6 +145,8 @@ export interface GroundTrackMapProps {
131
145
  showEquator?: boolean;
132
146
  /** Show recenter button (SRO compat) */
133
147
  showRecenterButton?: boolean;
148
+ /** Show a toggle button to switch between Dark and Satellite tile styles. Default true for Leaflet. */
149
+ showMapStyleToggle?: boolean;
134
150
  /** Whether data is still loading (SRO compat) */
135
151
  isLoading?: boolean;
136
152
  /** Message when there is no data (SRO compat) */
@@ -188,6 +204,19 @@ export interface GroundTrackMapProps {
188
204
  onPinUpdate?: (pin: MapPin) => void;
189
205
  /** Called when the user removes a pin */
190
206
  onPinRemove?: (pinId: string) => void;
207
+ /**
208
+ * Custom overlay layers shown in the Layers panel dropdown.
209
+ * Each layer gets a toggle switch. The GroundTrackMap does NOT render
210
+ * the layer content — it only provides the toggle UI.
211
+ * Listen to `onLayerChange` for state updates and render your own overlays.
212
+ */
213
+ customLayers?: MapLayerDef[];
214
+ /**
215
+ * Called when any layer is toggled (built-in or custom).
216
+ * `layerId` is one of: 'terminator', 'grid', or a custom layer id.
217
+ * `enabled` is the new toggle state.
218
+ */
219
+ onLayerChange?: (layerId: string, enabled: boolean) => void;
191
220
  }
192
- export declare function GroundTrackMap({ groundTrack, satellites, groundStations, accessMask, teamPaths, showTerminator, terminatorTime, showGrid, showLegend, showEquator, showRecenterButton, isLoading, emptyMessage, width, height, minHeight, defaultCenter, defaultZoom, className, onSatelliteClick, onStationClick, mapProvider, tileUrl, nightTileUrl, lightSources, pins, pinsEditable, onPinAdd, onPinUpdate, onPinRemove, }: GroundTrackMapProps): React.ReactElement;
221
+ export declare function GroundTrackMap({ groundTrack, satellites, groundStations, accessMask, teamPaths, showTerminator, terminatorTime, showGrid, showLegend, showEquator, showRecenterButton, showMapStyleToggle, isLoading, emptyMessage, width, height, minHeight, defaultCenter, defaultZoom, className, onSatelliteClick, onStationClick, mapProvider, tileUrl, nightTileUrl, lightSources, pins, pinsEditable, onPinAdd, onPinUpdate, onPinRemove, customLayers, onLayerChange, }: GroundTrackMapProps): React.ReactElement;
193
222
  export default GroundTrackMap;
@@ -122,6 +122,7 @@ function GroundTrackMap({
122
122
  showLegend = true,
123
123
  showEquator = true,
124
124
  showRecenterButton = false,
125
+ showMapStyleToggle = true,
125
126
  isLoading = false,
126
127
  emptyMessage = "No orbital data available",
127
128
  width = "100%",
@@ -140,7 +141,9 @@ function GroundTrackMap({
140
141
  pinsEditable = false,
141
142
  onPinAdd,
142
143
  onPinUpdate,
143
- onPinRemove
144
+ onPinRemove,
145
+ customLayers,
146
+ onLayerChange
144
147
  }) {
145
148
  const { tokens } = useTheme();
146
149
  const canvasRef = useRef(null);
@@ -273,6 +276,39 @@ function GroundTrackMap({
273
276
  }
274
277
  prevAlpha = zone.alpha;
275
278
  }
279
+ const terminatorEdge = calculateTerminatorContinuous(now, 0);
280
+ if (terminatorEdge.sunset.length > 2) {
281
+ ctx.save();
282
+ ctx.strokeStyle = "rgba(90, 142, 200, 0.12)";
283
+ ctx.lineWidth = 4;
284
+ for (const curve of [terminatorEdge.sunset, terminatorEdge.sunrise]) {
285
+ for (const offset of [-360, 0, 360]) {
286
+ ctx.beginPath();
287
+ for (let i = 0; i < curve.length; i++) {
288
+ const x = lonToX(curve[i][1] + offset, W);
289
+ const y = latToY(curve[i][0], H);
290
+ if (i === 0) ctx.moveTo(x, y);
291
+ else ctx.lineTo(x, y);
292
+ }
293
+ ctx.stroke();
294
+ }
295
+ }
296
+ ctx.strokeStyle = "rgba(122, 164, 212, 0.5)";
297
+ ctx.lineWidth = 1;
298
+ for (const curve of [terminatorEdge.sunset, terminatorEdge.sunrise]) {
299
+ for (const offset of [-360, 0, 360]) {
300
+ ctx.beginPath();
301
+ for (let i = 0; i < curve.length; i++) {
302
+ const x = lonToX(curve[i][1] + offset, W);
303
+ const y = latToY(curve[i][0], H);
304
+ if (i === 0) ctx.moveTo(x, y);
305
+ else ctx.lineTo(x, y);
306
+ }
307
+ ctx.stroke();
308
+ }
309
+ }
310
+ ctx.restore();
311
+ }
276
312
  }
277
313
  ctx.fillStyle = COLORS.land;
278
314
  ctx.strokeStyle = "rgba(100, 120, 160, 0.25)";
@@ -960,6 +996,7 @@ function GroundTrackMap({
960
996
  showLegend,
961
997
  showEquator,
962
998
  showRecenterButton,
999
+ showMapStyleToggle,
963
1000
  defaultCenter: leafletDefaultCenter,
964
1001
  defaultZoom: leafletDefaultZoom,
965
1002
  height,
@@ -976,7 +1013,9 @@ function GroundTrackMap({
976
1013
  pinsEditable,
977
1014
  onPinAdd,
978
1015
  onPinUpdate,
979
- onPinRemove
1016
+ onPinRemove,
1017
+ customLayers,
1018
+ onLayerChange
980
1019
  }
981
1020
  )
982
1021
  }