@zendir/ui 0.1.15 → 0.2.0
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 +39 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/react/astro/SimulationControls.js +3 -3
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.d.ts +56 -6
- package/dist/react/astro/UnifiedTimeline.js +628 -428
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/astro/index.d.ts +2 -2
- package/dist/react/charts/GroundTrackMap.d.ts +40 -1
- package/dist/react/charts/GroundTrackMap.js +98 -47
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/GroundTrackMapLeaflet.d.ts +11 -2
- package/dist/react/charts/GroundTrackMapLeaflet.js +128 -15
- package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
- package/dist/react/charts/index.d.ts +1 -1
- package/dist/react/charts/unified/theme.d.ts +7 -7
- package/dist/react/context/CategoryContext.d.ts +51 -0
- package/dist/react/context/CategoryContext.js +36 -0
- package/dist/react/context/CategoryContext.js.map +1 -0
- package/dist/react/context/index.d.ts +2 -0
- package/dist/react/index.d.ts +6 -4
- package/dist/react/types.d.ts +26 -0
- package/dist/react/types.js.map +1 -1
- package/dist/react/utils/categoryPalette.d.ts +43 -0
- package/dist/react/utils/categoryPalette.js +104 -0
- package/dist/react/utils/categoryPalette.js.map +1 -0
- package/dist/react/utils/index.d.ts +1 -0
- package/dist/react/utils/index.js.map +1 -1
- package/dist/react.js +6 -0
- package/dist/react.js.map +1 -1
- package/dist/style.css +49 -0
- package/package.json +1 -1
|
@@ -50,8 +50,8 @@ export type { NotificationProps, NotificationStatus } from './Notification';
|
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
52
|
export { UnifiedTimeline as Timeline } from './UnifiedTimeline';
|
|
53
|
-
export type { UnifiedTimelineProps as TimelineProps, TimelineEvent, TimelineTrackDef, TimelineViewMode, TimeFormat, TimelineFilter, } from './UnifiedTimeline';
|
|
54
|
-
export { getTimelineEventDisplayStatus, isTimelineFilterActive, matchesTimelineFilter, } from './UnifiedTimeline';
|
|
53
|
+
export type { UnifiedTimelineProps as TimelineProps, TimelineEvent, TimelineTeam, TimelineTrackDef, TimelineViewMode, TimeFormat, TimelineFilter, } from './UnifiedTimeline';
|
|
54
|
+
export { TIMELINE_FILTER_TEAM_LEGACY, TIMELINE_FILTER_TEAM_NONE, getTimelineEventDisplayStatus, getTimelineTeamAccent, getTimelineTeamDisplayLabel, getTimelineTeamFilterKey, isTimelineFilterActive, matchesTimelineFilter, } from './UnifiedTimeline';
|
|
55
55
|
/**
|
|
56
56
|
* Status type (shared across components)
|
|
57
57
|
* Follows Astro UX 6-level status semantics
|
|
@@ -62,6 +62,27 @@ export interface SROGroundStation {
|
|
|
62
62
|
longitude: number;
|
|
63
63
|
altitude?: number | null;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* User-placed point-of-interest pin on the map.
|
|
67
|
+
* Designed for collaborative scenarios where operators share annotated locations
|
|
68
|
+
* across terminals in real time (e.g. targets, waypoints, hazard markers).
|
|
69
|
+
*/
|
|
70
|
+
export interface MapPin {
|
|
71
|
+
/** Unique identifier (used as key and for update/remove callbacks) */
|
|
72
|
+
id: string;
|
|
73
|
+
/** Latitude in degrees (−90 … 90) */
|
|
74
|
+
latitude: number;
|
|
75
|
+
/** Longitude in degrees (−180 … 180) */
|
|
76
|
+
longitude: number;
|
|
77
|
+
/** Short label displayed next to the pin (e.g. "Target Alpha") */
|
|
78
|
+
label?: string;
|
|
79
|
+
/** Pin color — any CSS color string. Defaults to accent primary. */
|
|
80
|
+
color?: string;
|
|
81
|
+
/** Optional description shown in tooltip on hover */
|
|
82
|
+
description?: string;
|
|
83
|
+
/** Who placed the pin (display-only; not enforced) */
|
|
84
|
+
createdBy?: string;
|
|
85
|
+
}
|
|
65
86
|
export interface GroundTrackMapProps {
|
|
66
87
|
/** Single satellite ground track (legacy API) */
|
|
67
88
|
groundTrack?: GroundTrackPoint[];
|
|
@@ -75,6 +96,10 @@ export interface GroundTrackMapProps {
|
|
|
75
96
|
teamPaths?: TeamPath[];
|
|
76
97
|
/** Show day/night terminator */
|
|
77
98
|
showTerminator?: boolean;
|
|
99
|
+
/** Override the time used for the day/night terminator calculation.
|
|
100
|
+
* Defaults to wall-clock `new Date()` when omitted.
|
|
101
|
+
* Pass a simulation/mission UTC to keep the terminator in sync with a sim clock. */
|
|
102
|
+
terminatorTime?: Date;
|
|
78
103
|
/** Show grid lines */
|
|
79
104
|
showGrid?: boolean;
|
|
80
105
|
/** Show legend */
|
|
@@ -107,6 +132,20 @@ export interface GroundTrackMapProps {
|
|
|
107
132
|
mapProvider?: 'leaflet' | 'canvas';
|
|
108
133
|
/** Tile URL for Leaflet (default: CartoDB dark). Ignored when mapProvider is 'canvas'. */
|
|
109
134
|
tileUrl?: string;
|
|
135
|
+
/** Array of user-placed pins displayed on the map */
|
|
136
|
+
pins?: MapPin[];
|
|
137
|
+
/**
|
|
138
|
+
* Allow operators to place new pins by clicking the map.
|
|
139
|
+
* When true, a single-click on an empty area opens a pin creation flow.
|
|
140
|
+
* Requires `onPinAdd` to be set. Default false.
|
|
141
|
+
*/
|
|
142
|
+
pinsEditable?: boolean;
|
|
143
|
+
/** Called when the user places a new pin (provides lat/lon; consumer assigns id and persists) */
|
|
144
|
+
onPinAdd?: (pin: Omit<MapPin, 'id'>) => void;
|
|
145
|
+
/** Called when the user edits an existing pin (label, color, description change) */
|
|
146
|
+
onPinUpdate?: (pin: MapPin) => void;
|
|
147
|
+
/** Called when the user removes a pin */
|
|
148
|
+
onPinRemove?: (pinId: string) => void;
|
|
110
149
|
}
|
|
111
|
-
export declare function GroundTrackMap({ groundTrack, satellites, groundStations, accessMask, teamPaths, showTerminator, showGrid, showLegend, showEquator, showRecenterButton, isLoading, emptyMessage, width, height, minHeight, defaultCenter, defaultZoom, className, onSatelliteClick, onStationClick, mapProvider, tileUrl, }: GroundTrackMapProps): React.ReactElement;
|
|
150
|
+
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, pins, pinsEditable, onPinAdd, onPinUpdate, onPinRemove, }: GroundTrackMapProps): React.ReactElement;
|
|
112
151
|
export default GroundTrackMap;
|
|
@@ -68,15 +68,16 @@ function drawCoverageEllipse(ctx, centerLat, centerLon, radiusDeg, W, H, lonToX,
|
|
|
68
68
|
}
|
|
69
69
|
ctx.closePath();
|
|
70
70
|
}
|
|
71
|
-
function calculateTerminator(date, numPoints = 72) {
|
|
71
|
+
function calculateTerminator(date, depressionDeg = 0, numPoints = 72) {
|
|
72
72
|
const dayOfYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 864e5);
|
|
73
73
|
const declination = -23.44 * Math.cos(2 * Math.PI / 365 * (dayOfYear + 10));
|
|
74
74
|
const decRad = declination * Math.PI / 180;
|
|
75
75
|
const hourAngle = (date.getUTCHours() + date.getUTCMinutes() / 60) / 24 * 360 - 180;
|
|
76
|
+
const depRad = depressionDeg * Math.PI / 180;
|
|
76
77
|
const points = [];
|
|
77
78
|
for (let i = 0; i <= numPoints; i++) {
|
|
78
79
|
const latRad = (i / numPoints * 180 - 90) * (Math.PI / 180);
|
|
79
|
-
const cosH = -Math.
|
|
80
|
+
const cosH = -(Math.sin(depRad) + Math.sin(latRad) * Math.sin(decRad)) / (Math.cos(latRad) * Math.cos(decRad));
|
|
80
81
|
let lon;
|
|
81
82
|
if (cosH < -1) lon = hourAngle + 180;
|
|
82
83
|
else if (cosH > 1) lon = hourAngle;
|
|
@@ -94,6 +95,7 @@ function GroundTrackMap({
|
|
|
94
95
|
accessMask,
|
|
95
96
|
teamPaths,
|
|
96
97
|
showTerminator = true,
|
|
98
|
+
terminatorTime,
|
|
97
99
|
showGrid = true,
|
|
98
100
|
showLegend = true,
|
|
99
101
|
showEquator = true,
|
|
@@ -109,7 +111,12 @@ function GroundTrackMap({
|
|
|
109
111
|
onSatelliteClick,
|
|
110
112
|
onStationClick,
|
|
111
113
|
mapProvider = "leaflet",
|
|
112
|
-
tileUrl
|
|
114
|
+
tileUrl,
|
|
115
|
+
pins,
|
|
116
|
+
pinsEditable = false,
|
|
117
|
+
onPinAdd,
|
|
118
|
+
onPinUpdate,
|
|
119
|
+
onPinRemove
|
|
113
120
|
}) {
|
|
114
121
|
const { tokens } = useTheme();
|
|
115
122
|
const canvasRef = useRef(null);
|
|
@@ -194,55 +201,65 @@ function GroundTrackMap({
|
|
|
194
201
|
ctx.fillStyle = COLORS.background;
|
|
195
202
|
ctx.fillRect(0, 0, W, H);
|
|
196
203
|
if (showTerminator) {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
ctx.lineTo(edgeX, y);
|
|
209
|
-
ctx.closePath();
|
|
210
|
-
ctx.fill();
|
|
211
|
-
ctx.beginPath();
|
|
212
|
-
ctx.moveTo(W, y);
|
|
213
|
-
}
|
|
214
|
-
ctx.lineTo(x, y);
|
|
215
|
-
prevRX = x;
|
|
216
|
-
}
|
|
217
|
-
ctx.closePath();
|
|
218
|
-
ctx.fill();
|
|
204
|
+
const now = terminatorTime ?? /* @__PURE__ */ new Date();
|
|
205
|
+
const twilightZones = [
|
|
206
|
+
{ depression: 6, alpha: 0.08 },
|
|
207
|
+
// civil twilight
|
|
208
|
+
{ depression: 12, alpha: 0.1 },
|
|
209
|
+
// nautical twilight
|
|
210
|
+
{ depression: 18, alpha: 0.12 },
|
|
211
|
+
// astronomical twilight
|
|
212
|
+
{ depression: 90, alpha: 0.22 }
|
|
213
|
+
// full night (beyond 18°)
|
|
214
|
+
];
|
|
219
215
|
const normLon = (l) => {
|
|
220
216
|
let r = l;
|
|
221
217
|
while (r > 180) r -= 360;
|
|
222
218
|
while (r < -180) r += 360;
|
|
223
219
|
return r;
|
|
224
220
|
};
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (Math.abs(x - prevLX) > W * 0.5 && i < terminator.length - 1) {
|
|
234
|
-
const edgeX = prevLX < W / 2 ? 0 : W;
|
|
235
|
-
ctx.lineTo(edgeX, y);
|
|
236
|
-
ctx.closePath();
|
|
237
|
-
ctx.fill();
|
|
238
|
-
ctx.beginPath();
|
|
239
|
-
ctx.moveTo(0, y);
|
|
221
|
+
const fillNightSide = (terminator, isSunrise) => {
|
|
222
|
+
ctx.beginPath();
|
|
223
|
+
if (isSunrise) {
|
|
224
|
+
ctx.moveTo(0, 0);
|
|
225
|
+
ctx.lineTo(0, H);
|
|
226
|
+
} else {
|
|
227
|
+
ctx.moveTo(W, 0);
|
|
228
|
+
ctx.lineTo(W, H);
|
|
240
229
|
}
|
|
241
|
-
|
|
242
|
-
|
|
230
|
+
let prevX = isSunrise ? 0 : W;
|
|
231
|
+
for (let i = terminator.length - 1; i >= 0; i--) {
|
|
232
|
+
const tLon = isSunrise ? normLon(terminator[i].lon - 180) : terminator[i].lon;
|
|
233
|
+
const x = lonToX(tLon, W);
|
|
234
|
+
const y = latToY(terminator[i].lat, H);
|
|
235
|
+
if (Math.abs(x - prevX) > W * 0.5 && i < terminator.length - 1) {
|
|
236
|
+
const edgeX = isSunrise ? prevX < W / 2 ? 0 : W : prevX > W / 2 ? W : 0;
|
|
237
|
+
ctx.lineTo(edgeX, y);
|
|
238
|
+
ctx.closePath();
|
|
239
|
+
ctx.fill();
|
|
240
|
+
ctx.beginPath();
|
|
241
|
+
ctx.moveTo(isSunrise ? 0 : W, y);
|
|
242
|
+
}
|
|
243
|
+
ctx.lineTo(x, y);
|
|
244
|
+
prevX = x;
|
|
245
|
+
}
|
|
246
|
+
ctx.closePath();
|
|
247
|
+
ctx.fill();
|
|
248
|
+
};
|
|
249
|
+
let prevTerminator = null;
|
|
250
|
+
for (const zone of twilightZones) {
|
|
251
|
+
const dep = Math.min(zone.depression, 89);
|
|
252
|
+
const terminator = calculateTerminator(now, dep);
|
|
253
|
+
ctx.fillStyle = `rgba(0, 6, 24, ${zone.alpha})`;
|
|
254
|
+
if (prevTerminator) {
|
|
255
|
+
fillNightSide(terminator, false);
|
|
256
|
+
fillNightSide(terminator, true);
|
|
257
|
+
} else {
|
|
258
|
+
fillNightSide(terminator, false);
|
|
259
|
+
fillNightSide(terminator, true);
|
|
260
|
+
}
|
|
261
|
+
prevTerminator = terminator;
|
|
243
262
|
}
|
|
244
|
-
ctx.closePath();
|
|
245
|
-
ctx.fill();
|
|
246
263
|
}
|
|
247
264
|
ctx.fillStyle = COLORS.land;
|
|
248
265
|
ctx.strokeStyle = "rgba(100, 120, 160, 0.25)";
|
|
@@ -739,13 +756,41 @@ function GroundTrackMap({
|
|
|
739
756
|
ctx.fillText(item.label, legendX + 20, iy + 7);
|
|
740
757
|
});
|
|
741
758
|
}
|
|
742
|
-
if (
|
|
759
|
+
if (pins && pins.length > 0) {
|
|
760
|
+
pins.forEach((pin) => {
|
|
761
|
+
const px = lonToX(pin.longitude, W);
|
|
762
|
+
const py = latToY(pin.latitude, H);
|
|
763
|
+
const pinColor = pin.color || tokens.colors.accent.primary;
|
|
764
|
+
ctx.save();
|
|
765
|
+
ctx.shadowColor = "rgba(0,0,0,0.4)";
|
|
766
|
+
ctx.shadowBlur = 6;
|
|
767
|
+
ctx.shadowOffsetY = 2;
|
|
768
|
+
ctx.beginPath();
|
|
769
|
+
ctx.arc(px, py - 10, 6, Math.PI, 0);
|
|
770
|
+
ctx.lineTo(px, py);
|
|
771
|
+
ctx.closePath();
|
|
772
|
+
ctx.fillStyle = pinColor;
|
|
773
|
+
ctx.fill();
|
|
774
|
+
ctx.restore();
|
|
775
|
+
ctx.beginPath();
|
|
776
|
+
ctx.arc(px, py - 10, 2.5, 0, Math.PI * 2);
|
|
777
|
+
ctx.fillStyle = "#0d1323";
|
|
778
|
+
ctx.fill();
|
|
779
|
+
if (pin.label) {
|
|
780
|
+
ctx.fillStyle = "rgba(255,255,255,0.9)";
|
|
781
|
+
ctx.font = '10px "Inter", system-ui, sans-serif';
|
|
782
|
+
ctx.textAlign = "center";
|
|
783
|
+
ctx.fillText(pin.label, px, py - 20);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (allSatellites.length === 0 && groundStations.length === 0 && (!pins || pins.length === 0)) {
|
|
743
788
|
ctx.fillStyle = COLORS.text;
|
|
744
789
|
ctx.font = '12px "Inter", system-ui, sans-serif';
|
|
745
790
|
ctx.textAlign = "center";
|
|
746
791
|
ctx.fillText(emptyMessage, W / 2, H / 2);
|
|
747
792
|
}
|
|
748
|
-
}, [allSatellites, groundStations, height, showTerminator, showGrid, showLegend, showEquator, lonToX, latToY, COLORS, emptyMessage]);
|
|
793
|
+
}, [allSatellites, groundStations, height, showTerminator, terminatorTime, showGrid, showLegend, showEquator, lonToX, latToY, COLORS, emptyMessage, pins, tokens.colors.accent.primary]);
|
|
749
794
|
const handleMouseMove = useCallback((e) => {
|
|
750
795
|
const canvas = canvasRef.current;
|
|
751
796
|
if (!canvas) return;
|
|
@@ -861,6 +906,7 @@ function GroundTrackMap({
|
|
|
861
906
|
allSatellites,
|
|
862
907
|
groundStations,
|
|
863
908
|
showTerminator,
|
|
909
|
+
terminatorTime,
|
|
864
910
|
showGrid,
|
|
865
911
|
showLegend,
|
|
866
912
|
showEquator,
|
|
@@ -874,7 +920,12 @@ function GroundTrackMap({
|
|
|
874
920
|
tileUrl,
|
|
875
921
|
className,
|
|
876
922
|
onSatelliteClick,
|
|
877
|
-
onStationClick
|
|
923
|
+
onStationClick,
|
|
924
|
+
pins,
|
|
925
|
+
pinsEditable,
|
|
926
|
+
onPinAdd,
|
|
927
|
+
onPinUpdate,
|
|
928
|
+
onPinRemove
|
|
878
929
|
}
|
|
879
930
|
)
|
|
880
931
|
}
|