@zendir/ui 0.1.15 → 0.2.1
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 +112 -60
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/GroundTrackMapLeaflet.d.ts +11 -2
- package/dist/react/charts/GroundTrackMapLeaflet.js +140 -39
- 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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @zendir/ui
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
#### SDK Category System (New)
|
|
8
|
+
- **`CategoryDef`** — shared interface for a named, colored data category (`id`, `color`, `label?`), used across Timeline, GroundTrackMap, and any future component.
|
|
9
|
+
- **`CategoryPalette`** — utility class that maps category IDs to stable colors and labels, with built-in fallback palettes for auto-assignment.
|
|
10
|
+
- **`CategoryProvider` / `useCategoryPalette`** — optional React Context for sharing a `CategoryPalette` and a custom display label (e.g. "Squad", "Division") across an entire dashboard. Fully opt-in; components work standalone without it.
|
|
11
|
+
- New Storybook documentation page (`SDK Category Concept`) with interactive examples, architecture diagrams, and integration guidance.
|
|
12
|
+
|
|
13
|
+
#### UnifiedTimeline
|
|
14
|
+
- **Team / category-aware events** — events now carry structured team identity (`id`, `color`, `label?`), enabling category-colored event bars and badges.
|
|
15
|
+
- **Multi-select team filter** — replaces the former boolean `teamColoredOnly` toggle; users can filter by specific teams via pill-style checkboxes.
|
|
16
|
+
- **`teamLabel` prop** — devs can rename "Team" to any domain-specific term (e.g. "Squad", "Division"); also readable from `CategoryProvider` context.
|
|
17
|
+
- **Day markers** — `showDayMarkers` draws labeled day boundaries on multi-day timelines.
|
|
18
|
+
- **Redesigned filter bar** — collapsible panel with Astro UX filter icon, search field moved to top header row, responsive breakpoints.
|
|
19
|
+
- **Performance** — bounding-box culling for `ChartView` events outside the visible viewport.
|
|
20
|
+
|
|
21
|
+
#### GroundTrackMap
|
|
22
|
+
- **Twilight terminator with gradient zones** — realistic Civil / Nautical / Astronomical / Full Night shading derived from solar depression angle, replacing the former hard-edge day/night line. Uses `depressionDeg` for precise boundaries.
|
|
23
|
+
- **Collaborative pins** — new `MapPin` interface and `pins` / `pinsEditable` / `onPinAdd` / `onPinUpdate` / `onPinRemove` props for placing colored points-of-interest synced across terminals.
|
|
24
|
+
- **Enhanced satellite visualization** — `showFootprint` / `footprintRadius` for sensor coverage circles, `futureTrackIndex` for solid-past / dashed-future track split, `passMarkers` for AOS/LOS diamonds.
|
|
25
|
+
- **Ground station antenna icons** — `dish`, `phased-array`, `omni`, and `relay` icon types with coverage circles and Astro status colors.
|
|
26
|
+
- **Legend overlay** — auto-generated satellite + ground station legend with status indicators.
|
|
27
|
+
- Category-aware pin colors via `CategoryProvider` integration.
|
|
28
|
+
|
|
29
|
+
#### SimulationControls
|
|
30
|
+
- Increased control-button background opacities for better visibility on dark themes.
|
|
31
|
+
|
|
32
|
+
### Documentation
|
|
33
|
+
- Comprehensive Storybook updates for Timeline (`WithCategoryProvider`, `MultiDayWithCategories` stories) and GroundTrackMap (`SDK Category Integration` section).
|
|
34
|
+
- New `CategoryConcept` Storybook page with `PaletteBasics`, `ProviderAndHook`, and `DynamicCategories` interactive stories.
|
|
35
|
+
|
|
36
|
+
### Breaking Changes
|
|
37
|
+
- `TimelineEvent.color` replaced by `TimelineEvent.team` (`TimelineTeam` / `CategoryDef`). Plain string colors are still accepted for backward compatibility but are treated as legacy entries.
|
|
38
|
+
- `TimelineFilter.teamColoredOnly` replaced by `TimelineFilter.teams` (multi-select `string[]`).
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
3
42
|
## 0.1.0
|
|
4
43
|
|
|
5
44
|
Initial public release.
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ import { CARD_ACCENT_COLORS, CardAccentProvider, SPACE_SYSTEM_COLORS, getAccentC
|
|
|
15
15
|
import { CATEGORY_ICONS, CATEGORY_LABELS, PROPERTY_PRESETS, createPropertyConfig, deriveBatteryStatus, deriveStatus, formatPropertyLabel, formatPropertyValue, getPropertiesByCategory, getPropertyConfig } from "./react/core/propertyConfig.js";
|
|
16
16
|
import { CHAT_RESPONSE_JSON_PROMPT, CHAT_RESPONSE_MCP_TOOL, CHAT_RESPONSE_TOOL_SCHEMA, CHAT_RESPONSE_YAML_PROMPT, CHAT_STATUS_RULES_PROMPT, ChatPanel, createChatResponseParser, parseChatResponse, parseMcpToolResult } from "./react/core/ChatPanel.js";
|
|
17
17
|
import { CardHeader } from "./react/core/CardHeader.js";
|
|
18
|
+
import { CategoryPalette } from "./react/utils/categoryPalette.js";
|
|
19
|
+
import { CategoryProvider, useCategoryPalette, useCategoryPaletteRequired } from "./react/context/CategoryContext.js";
|
|
18
20
|
import { Center } from "./react/core/layout/Center.js";
|
|
19
21
|
import { Checkbox } from "./react/core/Checkbox.js";
|
|
20
22
|
import { ClassificationBanner } from "./react/astro/ClassificationBanner.js";
|
|
@@ -88,6 +90,8 @@ export {
|
|
|
88
90
|
CHAT_STATUS_RULES_PROMPT,
|
|
89
91
|
CardAccentProvider,
|
|
90
92
|
CardHeader,
|
|
93
|
+
CategoryPalette,
|
|
94
|
+
CategoryProvider,
|
|
91
95
|
Center,
|
|
92
96
|
ChatPanel,
|
|
93
97
|
Checkbox,
|
|
@@ -241,6 +245,8 @@ export {
|
|
|
241
245
|
transitions,
|
|
242
246
|
useBreakpoint,
|
|
243
247
|
useCardAccent,
|
|
248
|
+
useCategoryPalette,
|
|
249
|
+
useCategoryPaletteRequired,
|
|
244
250
|
useCompactMode,
|
|
245
251
|
useConfirm,
|
|
246
252
|
useCopyToClipboard,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -55,7 +55,7 @@ const ControlButton = memo(function ControlButton2({
|
|
|
55
55
|
const size = small ? 28 : 32;
|
|
56
56
|
const iconSize = small ? 14 : 16;
|
|
57
57
|
const baseColor = danger ? tokens.colors.status.critical : active ? tokens.colors.status.normal : tokens.colors.text.primary;
|
|
58
|
-
const bgColor = active ? `${tokens.colors.status.normal}25` : "rgba(255,255,255,0.
|
|
58
|
+
const bgColor = active ? `${tokens.colors.status.normal}25` : "rgba(255,255,255,0.14)";
|
|
59
59
|
return /* @__PURE__ */ jsx(Tooltip, { content: label, placement: "bottom", children: /* @__PURE__ */ jsx(
|
|
60
60
|
"button",
|
|
61
61
|
{
|
|
@@ -71,7 +71,7 @@ const ControlButton = memo(function ControlButton2({
|
|
|
71
71
|
padding: 0,
|
|
72
72
|
border: "none",
|
|
73
73
|
borderRadius: tokens.borderRadius.md,
|
|
74
|
-
backgroundColor: disabled ? "rgba(255,255,255,0.
|
|
74
|
+
backgroundColor: disabled ? "rgba(255,255,255,0.06)" : bgColor,
|
|
75
75
|
color: disabled ? tokens.colors.text.tertiary : baseColor,
|
|
76
76
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
77
77
|
transition: "all 0.15s ease",
|
|
@@ -79,7 +79,7 @@ const ControlButton = memo(function ControlButton2({
|
|
|
79
79
|
},
|
|
80
80
|
onMouseEnter: (e) => {
|
|
81
81
|
if (!disabled) {
|
|
82
|
-
e.currentTarget.style.backgroundColor = active ? `${tokens.colors.status.normal}35` : "rgba(255,255,255,0.
|
|
82
|
+
e.currentTarget.style.backgroundColor = active ? `${tokens.colors.status.normal}35` : "rgba(255,255,255,0.22)";
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
onMouseLeave: (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimulationControls.js","sources":["../../../src/react/astro/SimulationControls.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - SimulationControls Component\r\n * \r\n * Space operations simulation playback controls following Astro UX patterns.\r\n * Provides play/pause/stop/step controls for simulation time management.\r\n * \r\n * Features:\r\n * - Play/Pause toggle with visual state feedback\r\n * - Stop (reset) functionality\r\n * - Step forward/backward controls\r\n * - Time scale (playback rate) adjustment\r\n * - Simulation time display integration\r\n * - AOS/LOS timer integration\r\n * - Keyboard accessibility\r\n * - AstroUXDS compliant styling\r\n * \r\n * Space Operations Terminology:\r\n * - SIM: Simulation mode indicator\r\n * - MET: Mission Elapsed Time\r\n * - Epoch: Simulation start time (t=0)\r\n * - Time Scale: Playback speed multiplier\r\n * \r\n * @example\r\n * ```tsx\r\n * import { SimulationControls } from '@zendir/ui/react';\r\n * \r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * onStop={handleStop}\r\n * timeScale={1}\r\n * onTimeScaleChange={setTimeScale}\r\n * currentTime={simulationTime}\r\n * epoch={epoch}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from 'react';\r\nimport { useTheme } from '../theme';\r\nimport { classNames, tabularNumsStyle, safeAccentText } from '../utils';\r\nimport { AstroIcon } from '../core/AstroIcon';\r\nimport { Tooltip } from '../core/Tooltip';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type SimulationState = 'stopped' | 'playing' | 'paused';\r\n\r\nexport interface SimulationControlsProps {\r\n /** Current simulation state */\r\n state?: SimulationState;\r\n /** Whether simulation is playing (alternative to state) */\r\n isPlaying?: boolean;\r\n /** Whether simulation is paused (used with isPlaying for 3-state) */\r\n isPaused?: boolean;\r\n \r\n // === Playback Callbacks ===\r\n /** Called when play is triggered */\r\n onPlay?: () => void;\r\n /** Called when pause is triggered */\r\n onPause?: () => void;\r\n /** Called when stop (reset) is triggered */\r\n onStop?: () => void;\r\n /** Called when step forward is triggered */\r\n onStepForward?: () => void;\r\n /** Called when step backward is triggered */\r\n onStepBackward?: () => void;\r\n \r\n // === Time Display ===\r\n /** Current simulation time */\r\n currentTime?: Date | string;\r\n /** Simulation epoch (t=0) */\r\n epoch?: Date | string;\r\n /** Current elapsed seconds from epoch */\r\n elapsedSeconds?: number;\r\n /** Show elapsed time display */\r\n showElapsedTime?: boolean;\r\n /** Show current time display */\r\n showCurrentTime?: boolean;\r\n /** Show date in time display */\r\n showDate?: boolean;\r\n \r\n // === Time Scale ===\r\n /** Current time scale factor (1 = realtime) */\r\n timeScale?: number;\r\n /** Callback when time scale changes */\r\n onTimeScaleChange?: (scale: number) => void;\r\n /** Available time scale presets */\r\n timeScalePresets?: number[];\r\n /** Show time scale controls */\r\n showTimeScale?: boolean;\r\n \r\n // === Step Size ===\r\n /** Step size in seconds */\r\n stepSize?: number;\r\n /** Callback when step size changes */\r\n onStepSizeChange?: (size: number) => void;\r\n /** Available step size presets in seconds */\r\n stepSizePresets?: number[];\r\n /** Show step controls */\r\n showStepControls?: boolean;\r\n \r\n // === Appearance ===\r\n /** Compact display mode */\r\n compact?: boolean;\r\n /** Small size variant */\r\n small?: boolean;\r\n /** Custom className */\r\n className?: string;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Show simulation status indicator */\r\n showStatus?: boolean;\r\n /** Custom label for simulation mode */\r\n label?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Constants\r\n// ============================================================================\r\n\r\nconst DEFAULT_TIME_SCALE_PRESETS = [0.1, 0.25, 0.5, 1, 2, 5, 10, 60, 3600];\r\nconst DEFAULT_STEP_SIZE_PRESETS = [1, 5, 10, 30, 60, 300, 600, 3600];\r\n\r\n// ============================================================================\r\n// Utility Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Format elapsed time as DD:HH:MM:SS\r\n */\r\nfunction formatElapsedTime(seconds: number): string {\r\n const absSeconds = Math.abs(seconds);\r\n const days = Math.floor(absSeconds / 86400);\r\n const hours = Math.floor((absSeconds % 86400) / 3600);\r\n const mins = Math.floor((absSeconds % 3600) / 60);\r\n const secs = Math.floor(absSeconds % 60);\r\n \r\n const sign = seconds < 0 ? '-' : '';\r\n \r\n if (days > 0) {\r\n return `${sign}${days}:${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\r\n }\r\n return `${sign}${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\r\n}\r\n\r\n/**\r\n * Format time scale for display\r\n */\r\nfunction formatTimeScale(scale: number): string {\r\n if (scale >= 3600) return `${scale / 3600}h/s`;\r\n if (scale >= 60) return `${scale / 60}m/s`;\r\n if (scale === 1) return '1×';\r\n if (scale < 1) return `${scale}×`;\r\n return `${scale}×`;\r\n}\r\n\r\n/**\r\n * Format step size for display\r\n */\r\nfunction formatStepSize(seconds: number): string {\r\n if (seconds >= 3600) return `${seconds / 3600}h`;\r\n if (seconds >= 60) return `${seconds / 60}m`;\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * Format time for display\r\n */\r\nfunction formatTimeDisplay(date: Date): string {\r\n const hours = date.getUTCHours().toString().padStart(2, '0');\r\n const mins = date.getUTCMinutes().toString().padStart(2, '0');\r\n const secs = date.getUTCSeconds().toString().padStart(2, '0');\r\n return `${hours}:${mins}:${secs}`;\r\n}\r\n\r\n/**\r\n * Format date for display\r\n */\r\nfunction formatDateDisplay(date: Date): string {\r\n const year = date.getUTCFullYear();\r\n const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');\r\n const day = date.getUTCDate().toString().padStart(2, '0');\r\n return `${year}-${month}-${day}`;\r\n}\r\n\r\n// ============================================================================\r\n// Sub-components\r\n// ============================================================================\r\n\r\ninterface ControlButtonProps {\r\n icon: string;\r\n label: string;\r\n onClick?: () => void;\r\n active?: boolean;\r\n disabled?: boolean;\r\n danger?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst ControlButton = memo(function ControlButton({\r\n icon,\r\n label,\r\n onClick,\r\n active = false,\r\n disabled = false,\r\n danger = false,\r\n tokens,\r\n small = false,\r\n}: ControlButtonProps) {\r\n const size = small ? 28 : 32;\r\n const iconSize = small ? 14 : 16;\r\n \r\n const baseColor = danger \r\n ? tokens.colors.status.critical \r\n : active \r\n ? tokens.colors.status.normal \r\n : tokens.colors.text.primary;\r\n \r\n const bgColor = active \r\n ? `${tokens.colors.status.normal}25`\r\n : 'rgba(255,255,255,0.08)';\r\n \r\n return (\r\n <Tooltip content={label} placement=\"bottom\">\r\n <button\r\n onClick={onClick}\r\n disabled={disabled}\r\n aria-label={label}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: size,\r\n height: size,\r\n padding: 0,\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.md,\r\n backgroundColor: disabled ? 'rgba(255,255,255,0.03)' : bgColor,\r\n color: disabled ? tokens.colors.text.tertiary : baseColor,\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.backgroundColor = active \r\n ? `${tokens.colors.status.normal}35`\r\n : 'rgba(255,255,255,0.15)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.backgroundColor = bgColor;\r\n }\r\n }}\r\n >\r\n <AstroIcon \r\n name={icon as any} \r\n size={iconSize} \r\n color={disabled ? tokens.colors.text.tertiary : baseColor}\r\n />\r\n </button>\r\n </Tooltip>\r\n );\r\n});\r\n\r\ninterface TimeScaleSelectorProps {\r\n value: number;\r\n presets: number[];\r\n onChange?: (scale: number) => void;\r\n disabled?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst TimeScaleSelector = memo(function TimeScaleSelector({\r\n value,\r\n presets,\r\n onChange,\r\n disabled = false,\r\n tokens,\r\n small = false,\r\n}: TimeScaleSelectorProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n \r\n return (\r\n <div style={{ position: 'relative' }}>\r\n <Tooltip content=\"Time Scale / Playback Speed\" placement=\"bottom\">\r\n <button\r\n onClick={() => !disabled && setIsOpen(!isOpen)}\r\n disabled={disabled}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n padding: small ? '4px 8px' : '6px 10px',\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.sm,\r\n backgroundColor: 'rgba(255,255,255,0.08)',\r\n color: value !== 1 ? tokens.colors.status.caution : tokens.colors.text.secondary,\r\n fontSize: small ? tokens.typography.fontSize.xxs : '0.6875rem', // 10px/11px in rem\r\n fontWeight: 700, // AstroUXDS bold for time display (critical info)\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n <AstroIcon name=\"fast-forward\" size={small ? 10 : 12} color=\"inherit\" />\r\n {formatTimeScale(value)}\r\n </button>\r\n </Tooltip>\r\n \r\n {isOpen && !disabled && (\r\n <>\r\n <div \r\n style={{ \r\n position: 'fixed', \r\n inset: 0, \r\n zIndex: 99 \r\n }} \r\n onClick={() => setIsOpen(false)} \r\n />\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: '100%',\r\n left: '50%',\r\n transform: 'translateX(-50%)',\r\n marginTop: 4,\r\n padding: 4,\r\n backgroundColor: tokens.colors.background.elevated,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: tokens.shadows.lg,\r\n zIndex: 100,\r\n minWidth: 80,\r\n }}\r\n >\r\n {presets.map((scale) => (\r\n <button\r\n key={scale}\r\n onClick={() => {\r\n onChange?.(scale);\r\n setIsOpen(false);\r\n }}\r\n style={{\r\n display: 'block',\r\n width: '100%',\r\n padding: '6px 12px',\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.sm,\r\n backgroundColor: value === scale ? tokens.colors.accent.primary : 'transparent',\r\n color: value === scale ? '#ffffff' : tokens.colors.text.secondary,\r\n fontSize: '0.6875rem', // 11px in rem\r\n fontWeight: value === scale ? 500 : 400, // AstroUXDS: 500 (medium) not 600\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n textAlign: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.1s ease',\r\n ...tabularNumsStyle,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (value !== scale) {\r\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = value === scale \r\n ? tokens.colors.accent.primary \r\n : 'transparent';\r\n }}\r\n >\r\n {formatTimeScale(scale)}\r\n </button>\r\n ))}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\ninterface SimulationTimeDisplayProps {\r\n currentTime?: Date;\r\n elapsedSeconds?: number;\r\n showElapsed?: boolean;\r\n showCurrent?: boolean;\r\n showDate?: boolean;\r\n isPlaying?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst SimulationTimeDisplay = memo(function SimulationTimeDisplay({\r\n currentTime,\r\n elapsedSeconds,\r\n showElapsed = true,\r\n showCurrent = true,\r\n showDate = false,\r\n isPlaying = false,\r\n tokens,\r\n small = false,\r\n}: SimulationTimeDisplayProps) {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n gap: 2,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n }}\r\n >\r\n {showDate && currentTime && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.tertiary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatDateDisplay(currentTime)}\r\n </span>\r\n )}\r\n \r\n {showCurrent && currentTime && (\r\n <span\r\n style={{\r\n fontSize: small ? tokens.typography.fontSize.lg : tokens.typography.fontSize.xl,\r\n fontWeight: tokens.typography.fontWeight.bold, // Bold (700) to match MissionClock\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.primary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatTimeDisplay(currentTime)}\r\n </span>\r\n )}\r\n \r\n {showElapsed && elapsedSeconds !== undefined && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.status.caution,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n T{elapsedSeconds >= 0 ? '+' : ''}{formatElapsedTime(elapsedSeconds)}\r\n </span>\r\n )}\r\n \r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.status.standby,\r\n textTransform: 'uppercase',\r\n letterSpacing: tokens.typography.letterSpacing.wide,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {isPlaying && (\r\n <span\r\n style={{\r\n width: 6,\r\n height: 6,\r\n borderRadius: '50%',\r\n backgroundColor: tokens.colors.accent.primary,\r\n animation: 'pulse 1.5s ease-in-out infinite',\r\n }}\r\n />\r\n )}\r\n SIM\r\n </span>\r\n </div>\r\n );\r\n});\r\n\r\n// ============================================================================\r\n// Main Component\r\n// ============================================================================\r\n\r\n/**\r\n * SimulationControls - Playback controls for space simulation\r\n * \r\n * Provides comprehensive simulation time control following Astro UX patterns.\r\n * Designed for mission control and space operations applications.\r\n * \r\n * @example\r\n * ```tsx\r\n * // Basic usage\r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={() => setIsPlaying(true)}\r\n * onPause={() => setIsPlaying(false)}\r\n * onStop={handleReset}\r\n * />\r\n * \r\n * // Full featured\r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * onStop={handleStop}\r\n * onStepForward={handleStepForward}\r\n * onStepBackward={handleStepBackward}\r\n * currentTime={simulationTime}\r\n * epoch={epoch}\r\n * elapsedSeconds={elapsed}\r\n * timeScale={timeScale}\r\n * onTimeScaleChange={setTimeScale}\r\n * showTimeScale\r\n * showStepControls\r\n * showElapsedTime\r\n * />\r\n * \r\n * // In AppBar center slot\r\n * <AppBar\r\n * appName=\"Mission Control\"\r\n * centerSlot={\r\n * <SimulationControls\r\n * compact\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * currentTime={simTime}\r\n * />\r\n * }\r\n * />\r\n * ```\r\n */\r\nexport const SimulationControls = memo(function SimulationControls({\r\n state,\r\n isPlaying: isPlayingProp,\r\n isPaused,\r\n onPlay,\r\n onPause,\r\n onStop,\r\n onStepForward,\r\n onStepBackward,\r\n currentTime: currentTimeProp,\r\n epoch,\r\n elapsedSeconds: elapsedSecondsProp,\r\n showElapsedTime = true,\r\n showCurrentTime = true,\r\n showDate = true,\r\n timeScale = 1,\r\n onTimeScaleChange,\r\n timeScalePresets = DEFAULT_TIME_SCALE_PRESETS,\r\n showTimeScale = true,\r\n stepSize = 60,\r\n onStepSizeChange: _onStepSizeChange,\r\n stepSizePresets: _stepSizePresets = DEFAULT_STEP_SIZE_PRESETS,\r\n showStepControls = true,\r\n compact = false,\r\n small = false,\r\n className = '',\r\n disabled = false,\r\n showStatus = true,\r\n label,\r\n}: SimulationControlsProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\r\n \r\n // Derive playing state from props\r\n const isPlaying = useMemo(() => {\r\n if (state) return state === 'playing';\r\n return isPlayingProp ?? false;\r\n }, [state, isPlayingProp]);\r\n \r\n const isStopped = useMemo(() => {\r\n if (state) return state === 'stopped';\r\n return !isPlayingProp && !isPaused;\r\n }, [state, isPlayingProp, isPaused]);\r\n \r\n // Parse current time\r\n const currentTime = useMemo(() => {\r\n if (!currentTimeProp) return undefined;\r\n return typeof currentTimeProp === 'string' ? new Date(currentTimeProp) : currentTimeProp;\r\n }, [currentTimeProp]);\r\n \r\n // Calculate elapsed seconds\r\n const elapsedSeconds = useMemo(() => {\r\n if (elapsedSecondsProp !== undefined) return elapsedSecondsProp;\r\n if (currentTime && epoch) {\r\n const epochDate = typeof epoch === 'string' ? new Date(epoch) : epoch;\r\n return (currentTime.getTime() - epochDate.getTime()) / 1000;\r\n }\r\n return undefined;\r\n }, [elapsedSecondsProp, currentTime, epoch]);\r\n \r\n // Handle play/pause toggle\r\n const handlePlayPause = useCallback(() => {\r\n if (isPlaying) {\r\n onPause?.();\r\n } else {\r\n onPlay?.();\r\n }\r\n }, [isPlaying, onPlay, onPause]);\r\n \r\n // Handle keyboard shortcuts\r\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\r\n if (disabled) return;\r\n \r\n switch (e.key) {\r\n case ' ':\r\n e.preventDefault();\r\n handlePlayPause();\r\n break;\r\n case 'Escape':\r\n e.preventDefault();\r\n onStop?.();\r\n break;\r\n case 'ArrowRight':\r\n e.preventDefault();\r\n onStepForward?.();\r\n break;\r\n case 'ArrowLeft':\r\n e.preventDefault();\r\n onStepBackward?.();\r\n break;\r\n }\r\n }, [disabled, handlePlayPause, onStop, onStepForward, onStepBackward]);\r\n \r\n return (\r\n <div\r\n className={classNames('zendir-simulation-controls', className)}\r\n role=\"group\"\r\n aria-label={label || 'Simulation Controls'}\r\n onKeyDown={handleKeyDown}\r\n tabIndex={0}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: compact ? tokens.spacing.sm : tokens.spacing.md,\r\n padding: compact \r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`,\r\n backgroundColor: isTransparentTheme \r\n ? 'rgba(0,0,0,0.3)' \r\n : tokens.colors.background.surface,\r\n ...(isTransparentTheme && { \r\n backdropFilter: 'blur(12px)', \r\n WebkitBackdropFilter: 'blur(12px)' as const \r\n }),\r\n ...(tokens.colors.border.cardStyle ?? { border: `1px solid ${tokens.colors.border.muted}` }),\r\n borderRadius: tokens.borderRadius.lg,\r\n outline: 'none',\r\n transition: tokens.animation.fast,\r\n }}\r\n >\r\n {/* Time Display (if showing time) */}\r\n {(showCurrentTime || showElapsedTime) && !compact && (\r\n <SimulationTimeDisplay\r\n currentTime={currentTime}\r\n elapsedSeconds={elapsedSeconds}\r\n showElapsed={showElapsedTime}\r\n showCurrent={showCurrentTime}\r\n showDate={showDate}\r\n isPlaying={isPlaying}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n \r\n {/* Compact time display */}\r\n {compact && currentTime && (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 8,\r\n }}\r\n >\r\n {showDate && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.tertiary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatDateDisplay(currentTime)}\r\n </span>\r\n )}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.lg,\r\n fontWeight: tokens.typography.fontWeight.bold, // Bold (700) to match MissionClock\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.primary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatTimeDisplay(currentTime)}\r\n </span>\r\n {showStatus && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.status.standby,\r\n textTransform: 'uppercase',\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {isPlaying && (\r\n <span\r\n style={{\r\n width: 6,\r\n height: 6,\r\n borderRadius: '50%',\r\n backgroundColor: tokens.colors.accent.primary,\r\n }}\r\n />\r\n )}\r\n SIM\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n \r\n {/* Separator */}\r\n {((showCurrentTime || showElapsedTime) || (compact && currentTime)) && (\r\n <div\r\n style={{\r\n width: 1,\r\n height: 24,\r\n backgroundColor: tokens.colors.border.muted,\r\n }}\r\n />\r\n )}\r\n \r\n {/* Transport Controls */}\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {/* Step Backward */}\r\n {showStepControls && (\r\n <ControlButton\r\n icon=\"skip-previous\"\r\n label={`Step backward ${formatStepSize(stepSize)}`}\r\n onClick={onStepBackward}\r\n disabled={disabled || !onStepBackward}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n \r\n {/* Stop */}\r\n <ControlButton\r\n icon=\"stop\"\r\n label=\"Stop / Reset\"\r\n onClick={onStop}\r\n disabled={disabled || !onStop}\r\n danger={!isStopped}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n \r\n {/* Play/Pause */}\r\n <Tooltip content={isPlaying ? 'Pause (Space)' : 'Play (Space)'} placement=\"bottom\">\r\n <button\r\n onClick={handlePlayPause}\r\n disabled={disabled}\r\n aria-label={isPlaying ? 'Pause simulation' : 'Play simulation'}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: small ? 36 : 44,\r\n height: small ? 36 : 44,\r\n padding: 0,\r\n border: isPlaying ? `2px solid ${tokens.colors.accent.primary}` : 'none',\r\n borderRadius: '50%',\r\n // Use accent primary for both states - playing has outline style\r\n backgroundColor: isPlaying \r\n ? 'rgba(138, 43, 226, 0.15)' // Subtle purple fill when playing\r\n : tokens.colors.accent.primary,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : '#ffffff',\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n boxShadow: isPlaying \r\n ? `0 0 16px ${tokens.colors.accent.primary}50`\r\n : `0 0 8px ${tokens.colors.accent.primary}40`,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.transform = 'scale(1.05)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = 'scale(1)';\r\n }}\r\n >\r\n <AstroIcon \r\n name={isPlaying ? 'pause' : 'play-arrow'} \r\n size={small ? 18 : 22} \r\n color={isPlaying ? tokens.colors.accent.primary : '#ffffff'}\r\n />\r\n </button>\r\n </Tooltip>\r\n \r\n {/* Step Forward */}\r\n {showStepControls && (\r\n <ControlButton\r\n icon=\"skip-next\"\r\n label={`Step forward ${formatStepSize(stepSize)}`}\r\n onClick={onStepForward}\r\n disabled={disabled || !onStepForward}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n </div>\r\n \r\n {/* Time Scale */}\r\n {showTimeScale && (\r\n <>\r\n <div\r\n style={{\r\n width: 1,\r\n height: 24,\r\n backgroundColor: tokens.colors.border.muted,\r\n }}\r\n />\r\n <TimeScaleSelector\r\n value={timeScale}\r\n presets={timeScalePresets}\r\n onChange={onTimeScaleChange}\r\n disabled={disabled || !onTimeScaleChange}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n </>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ============================================================================\r\n// Convenience Components\r\n// ============================================================================\r\n\r\n/**\r\n * MiniSimulationControls - Ultra-compact controls for AppBar\r\n */\r\nexport interface MiniSimulationControlsProps {\r\n isPlaying?: boolean;\r\n onPlay?: () => void;\r\n onPause?: () => void;\r\n onStop?: () => void;\r\n timeScale?: number;\r\n onTimeScaleChange?: (scale: number) => void;\r\n disabled?: boolean;\r\n className?: string;\r\n}\r\n\r\nexport const MiniSimulationControls = memo(function MiniSimulationControls({\r\n isPlaying = false,\r\n onPlay,\r\n onPause,\r\n onStop,\r\n timeScale = 1,\r\n onTimeScaleChange,\r\n disabled = false,\r\n className = '',\r\n}: MiniSimulationControlsProps): React.ReactElement {\r\n return (\r\n <SimulationControls\r\n isPlaying={isPlaying}\r\n onPlay={onPlay}\r\n onPause={onPause}\r\n onStop={onStop}\r\n timeScale={timeScale}\r\n onTimeScaleChange={onTimeScaleChange}\r\n disabled={disabled}\r\n compact\r\n small\r\n showCurrentTime={false}\r\n showElapsedTime={false}\r\n showDate={false}\r\n showStepControls={false}\r\n showTimeScale={Boolean(onTimeScaleChange)}\r\n className={className}\r\n />\r\n );\r\n});\r\n\r\n/**\r\n * SimulationControlsWithClock - Controls combined with MissionClock\r\n */\r\nexport interface SimulationControlsWithClockProps extends Omit<SimulationControlsProps, 'showCurrentTime' | 'showElapsedTime'> {\r\n /** Clock format */\r\n clockFormat?: '12h' | '24h';\r\n /** Timezone label */\r\n timezone?: string;\r\n}\r\n\r\nexport const SimulationControlsWithClock = memo(function SimulationControlsWithClock({\r\n clockFormat: _clockFormat = '24h',\r\n timezone = 'SIM',\r\n ...props\r\n}: SimulationControlsWithClockProps): React.ReactElement {\r\n return (\r\n <SimulationControls\r\n {...props}\r\n showCurrentTime\r\n showElapsedTime\r\n showDate\r\n label={`Simulation Controls - ${timezone}`}\r\n />\r\n );\r\n});\r\n\r\nexport default SimulationControls;\r\n"],"names":["ControlButton","TimeScaleSelector","SimulationTimeDisplay","SimulationControls","MiniSimulationControls","SimulationControlsWithClock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SimulationControls.js","sources":["../../../src/react/astro/SimulationControls.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - SimulationControls Component\r\n * \r\n * Space operations simulation playback controls following Astro UX patterns.\r\n * Provides play/pause/stop/step controls for simulation time management.\r\n * \r\n * Features:\r\n * - Play/Pause toggle with visual state feedback\r\n * - Stop (reset) functionality\r\n * - Step forward/backward controls\r\n * - Time scale (playback rate) adjustment\r\n * - Simulation time display integration\r\n * - AOS/LOS timer integration\r\n * - Keyboard accessibility\r\n * - AstroUXDS compliant styling\r\n * \r\n * Space Operations Terminology:\r\n * - SIM: Simulation mode indicator\r\n * - MET: Mission Elapsed Time\r\n * - Epoch: Simulation start time (t=0)\r\n * - Time Scale: Playback speed multiplier\r\n * \r\n * @example\r\n * ```tsx\r\n * import { SimulationControls } from '@zendir/ui/react';\r\n * \r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * onStop={handleStop}\r\n * timeScale={1}\r\n * onTimeScaleChange={setTimeScale}\r\n * currentTime={simulationTime}\r\n * epoch={epoch}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from 'react';\r\nimport { useTheme } from '../theme';\r\nimport { classNames, tabularNumsStyle, safeAccentText } from '../utils';\r\nimport { AstroIcon } from '../core/AstroIcon';\r\nimport { Tooltip } from '../core/Tooltip';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type SimulationState = 'stopped' | 'playing' | 'paused';\r\n\r\nexport interface SimulationControlsProps {\r\n /** Current simulation state */\r\n state?: SimulationState;\r\n /** Whether simulation is playing (alternative to state) */\r\n isPlaying?: boolean;\r\n /** Whether simulation is paused (used with isPlaying for 3-state) */\r\n isPaused?: boolean;\r\n \r\n // === Playback Callbacks ===\r\n /** Called when play is triggered */\r\n onPlay?: () => void;\r\n /** Called when pause is triggered */\r\n onPause?: () => void;\r\n /** Called when stop (reset) is triggered */\r\n onStop?: () => void;\r\n /** Called when step forward is triggered */\r\n onStepForward?: () => void;\r\n /** Called when step backward is triggered */\r\n onStepBackward?: () => void;\r\n \r\n // === Time Display ===\r\n /** Current simulation time */\r\n currentTime?: Date | string;\r\n /** Simulation epoch (t=0) */\r\n epoch?: Date | string;\r\n /** Current elapsed seconds from epoch */\r\n elapsedSeconds?: number;\r\n /** Show elapsed time display */\r\n showElapsedTime?: boolean;\r\n /** Show current time display */\r\n showCurrentTime?: boolean;\r\n /** Show date in time display */\r\n showDate?: boolean;\r\n \r\n // === Time Scale ===\r\n /** Current time scale factor (1 = realtime) */\r\n timeScale?: number;\r\n /** Callback when time scale changes */\r\n onTimeScaleChange?: (scale: number) => void;\r\n /** Available time scale presets */\r\n timeScalePresets?: number[];\r\n /** Show time scale controls */\r\n showTimeScale?: boolean;\r\n \r\n // === Step Size ===\r\n /** Step size in seconds */\r\n stepSize?: number;\r\n /** Callback when step size changes */\r\n onStepSizeChange?: (size: number) => void;\r\n /** Available step size presets in seconds */\r\n stepSizePresets?: number[];\r\n /** Show step controls */\r\n showStepControls?: boolean;\r\n \r\n // === Appearance ===\r\n /** Compact display mode */\r\n compact?: boolean;\r\n /** Small size variant */\r\n small?: boolean;\r\n /** Custom className */\r\n className?: string;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Show simulation status indicator */\r\n showStatus?: boolean;\r\n /** Custom label for simulation mode */\r\n label?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Constants\r\n// ============================================================================\r\n\r\nconst DEFAULT_TIME_SCALE_PRESETS = [0.1, 0.25, 0.5, 1, 2, 5, 10, 60, 3600];\r\nconst DEFAULT_STEP_SIZE_PRESETS = [1, 5, 10, 30, 60, 300, 600, 3600];\r\n\r\n// ============================================================================\r\n// Utility Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Format elapsed time as DD:HH:MM:SS\r\n */\r\nfunction formatElapsedTime(seconds: number): string {\r\n const absSeconds = Math.abs(seconds);\r\n const days = Math.floor(absSeconds / 86400);\r\n const hours = Math.floor((absSeconds % 86400) / 3600);\r\n const mins = Math.floor((absSeconds % 3600) / 60);\r\n const secs = Math.floor(absSeconds % 60);\r\n \r\n const sign = seconds < 0 ? '-' : '';\r\n \r\n if (days > 0) {\r\n return `${sign}${days}:${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\r\n }\r\n return `${sign}${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\r\n}\r\n\r\n/**\r\n * Format time scale for display\r\n */\r\nfunction formatTimeScale(scale: number): string {\r\n if (scale >= 3600) return `${scale / 3600}h/s`;\r\n if (scale >= 60) return `${scale / 60}m/s`;\r\n if (scale === 1) return '1×';\r\n if (scale < 1) return `${scale}×`;\r\n return `${scale}×`;\r\n}\r\n\r\n/**\r\n * Format step size for display\r\n */\r\nfunction formatStepSize(seconds: number): string {\r\n if (seconds >= 3600) return `${seconds / 3600}h`;\r\n if (seconds >= 60) return `${seconds / 60}m`;\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * Format time for display\r\n */\r\nfunction formatTimeDisplay(date: Date): string {\r\n const hours = date.getUTCHours().toString().padStart(2, '0');\r\n const mins = date.getUTCMinutes().toString().padStart(2, '0');\r\n const secs = date.getUTCSeconds().toString().padStart(2, '0');\r\n return `${hours}:${mins}:${secs}`;\r\n}\r\n\r\n/**\r\n * Format date for display\r\n */\r\nfunction formatDateDisplay(date: Date): string {\r\n const year = date.getUTCFullYear();\r\n const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');\r\n const day = date.getUTCDate().toString().padStart(2, '0');\r\n return `${year}-${month}-${day}`;\r\n}\r\n\r\n// ============================================================================\r\n// Sub-components\r\n// ============================================================================\r\n\r\ninterface ControlButtonProps {\r\n icon: string;\r\n label: string;\r\n onClick?: () => void;\r\n active?: boolean;\r\n disabled?: boolean;\r\n danger?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst ControlButton = memo(function ControlButton({\r\n icon,\r\n label,\r\n onClick,\r\n active = false,\r\n disabled = false,\r\n danger = false,\r\n tokens,\r\n small = false,\r\n}: ControlButtonProps) {\r\n const size = small ? 28 : 32;\r\n const iconSize = small ? 14 : 16;\r\n \r\n const baseColor = danger \r\n ? tokens.colors.status.critical \r\n : active \r\n ? tokens.colors.status.normal \r\n : tokens.colors.text.primary;\r\n \r\n const bgColor = active \r\n ? `${tokens.colors.status.normal}25`\r\n : 'rgba(255,255,255,0.14)';\r\n \r\n return (\r\n <Tooltip content={label} placement=\"bottom\">\r\n <button\r\n onClick={onClick}\r\n disabled={disabled}\r\n aria-label={label}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: size,\r\n height: size,\r\n padding: 0,\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.md,\r\n backgroundColor: disabled ? 'rgba(255,255,255,0.06)' : bgColor,\r\n color: disabled ? tokens.colors.text.tertiary : baseColor,\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.backgroundColor = active \r\n ? `${tokens.colors.status.normal}35`\r\n : 'rgba(255,255,255,0.22)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.backgroundColor = bgColor;\r\n }\r\n }}\r\n >\r\n <AstroIcon \r\n name={icon as any} \r\n size={iconSize} \r\n color={disabled ? tokens.colors.text.tertiary : baseColor}\r\n />\r\n </button>\r\n </Tooltip>\r\n );\r\n});\r\n\r\ninterface TimeScaleSelectorProps {\r\n value: number;\r\n presets: number[];\r\n onChange?: (scale: number) => void;\r\n disabled?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst TimeScaleSelector = memo(function TimeScaleSelector({\r\n value,\r\n presets,\r\n onChange,\r\n disabled = false,\r\n tokens,\r\n small = false,\r\n}: TimeScaleSelectorProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n \r\n return (\r\n <div style={{ position: 'relative' }}>\r\n <Tooltip content=\"Time Scale / Playback Speed\" placement=\"bottom\">\r\n <button\r\n onClick={() => !disabled && setIsOpen(!isOpen)}\r\n disabled={disabled}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n padding: small ? '4px 8px' : '6px 10px',\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.sm,\r\n backgroundColor: 'rgba(255,255,255,0.08)',\r\n color: value !== 1 ? tokens.colors.status.caution : tokens.colors.text.secondary,\r\n fontSize: small ? tokens.typography.fontSize.xxs : '0.6875rem', // 10px/11px in rem\r\n fontWeight: 700, // AstroUXDS bold for time display (critical info)\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n <AstroIcon name=\"fast-forward\" size={small ? 10 : 12} color=\"inherit\" />\r\n {formatTimeScale(value)}\r\n </button>\r\n </Tooltip>\r\n \r\n {isOpen && !disabled && (\r\n <>\r\n <div \r\n style={{ \r\n position: 'fixed', \r\n inset: 0, \r\n zIndex: 99 \r\n }} \r\n onClick={() => setIsOpen(false)} \r\n />\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: '100%',\r\n left: '50%',\r\n transform: 'translateX(-50%)',\r\n marginTop: 4,\r\n padding: 4,\r\n backgroundColor: tokens.colors.background.elevated,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: tokens.shadows.lg,\r\n zIndex: 100,\r\n minWidth: 80,\r\n }}\r\n >\r\n {presets.map((scale) => (\r\n <button\r\n key={scale}\r\n onClick={() => {\r\n onChange?.(scale);\r\n setIsOpen(false);\r\n }}\r\n style={{\r\n display: 'block',\r\n width: '100%',\r\n padding: '6px 12px',\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.sm,\r\n backgroundColor: value === scale ? tokens.colors.accent.primary : 'transparent',\r\n color: value === scale ? '#ffffff' : tokens.colors.text.secondary,\r\n fontSize: '0.6875rem', // 11px in rem\r\n fontWeight: value === scale ? 500 : 400, // AstroUXDS: 500 (medium) not 600\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n textAlign: 'center',\r\n cursor: 'pointer',\r\n transition: 'all 0.1s ease',\r\n ...tabularNumsStyle,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (value !== scale) {\r\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = value === scale \r\n ? tokens.colors.accent.primary \r\n : 'transparent';\r\n }}\r\n >\r\n {formatTimeScale(scale)}\r\n </button>\r\n ))}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\ninterface SimulationTimeDisplayProps {\r\n currentTime?: Date;\r\n elapsedSeconds?: number;\r\n showElapsed?: boolean;\r\n showCurrent?: boolean;\r\n showDate?: boolean;\r\n isPlaying?: boolean;\r\n tokens: ReturnType<typeof useTheme>['tokens'];\r\n small?: boolean;\r\n}\r\n\r\nconst SimulationTimeDisplay = memo(function SimulationTimeDisplay({\r\n currentTime,\r\n elapsedSeconds,\r\n showElapsed = true,\r\n showCurrent = true,\r\n showDate = false,\r\n isPlaying = false,\r\n tokens,\r\n small = false,\r\n}: SimulationTimeDisplayProps) {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexDirection: 'column',\r\n alignItems: 'center',\r\n gap: 2,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n }}\r\n >\r\n {showDate && currentTime && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.tertiary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatDateDisplay(currentTime)}\r\n </span>\r\n )}\r\n \r\n {showCurrent && currentTime && (\r\n <span\r\n style={{\r\n fontSize: small ? tokens.typography.fontSize.lg : tokens.typography.fontSize.xl,\r\n fontWeight: tokens.typography.fontWeight.bold, // Bold (700) to match MissionClock\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.primary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatTimeDisplay(currentTime)}\r\n </span>\r\n )}\r\n \r\n {showElapsed && elapsedSeconds !== undefined && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.status.caution,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n T{elapsedSeconds >= 0 ? '+' : ''}{formatElapsedTime(elapsedSeconds)}\r\n </span>\r\n )}\r\n \r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.status.standby,\r\n textTransform: 'uppercase',\r\n letterSpacing: tokens.typography.letterSpacing.wide,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {isPlaying && (\r\n <span\r\n style={{\r\n width: 6,\r\n height: 6,\r\n borderRadius: '50%',\r\n backgroundColor: tokens.colors.accent.primary,\r\n animation: 'pulse 1.5s ease-in-out infinite',\r\n }}\r\n />\r\n )}\r\n SIM\r\n </span>\r\n </div>\r\n );\r\n});\r\n\r\n// ============================================================================\r\n// Main Component\r\n// ============================================================================\r\n\r\n/**\r\n * SimulationControls - Playback controls for space simulation\r\n * \r\n * Provides comprehensive simulation time control following Astro UX patterns.\r\n * Designed for mission control and space operations applications.\r\n * \r\n * @example\r\n * ```tsx\r\n * // Basic usage\r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={() => setIsPlaying(true)}\r\n * onPause={() => setIsPlaying(false)}\r\n * onStop={handleReset}\r\n * />\r\n * \r\n * // Full featured\r\n * <SimulationControls\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * onStop={handleStop}\r\n * onStepForward={handleStepForward}\r\n * onStepBackward={handleStepBackward}\r\n * currentTime={simulationTime}\r\n * epoch={epoch}\r\n * elapsedSeconds={elapsed}\r\n * timeScale={timeScale}\r\n * onTimeScaleChange={setTimeScale}\r\n * showTimeScale\r\n * showStepControls\r\n * showElapsedTime\r\n * />\r\n * \r\n * // In AppBar center slot\r\n * <AppBar\r\n * appName=\"Mission Control\"\r\n * centerSlot={\r\n * <SimulationControls\r\n * compact\r\n * isPlaying={isPlaying}\r\n * onPlay={handlePlay}\r\n * onPause={handlePause}\r\n * currentTime={simTime}\r\n * />\r\n * }\r\n * />\r\n * ```\r\n */\r\nexport const SimulationControls = memo(function SimulationControls({\r\n state,\r\n isPlaying: isPlayingProp,\r\n isPaused,\r\n onPlay,\r\n onPause,\r\n onStop,\r\n onStepForward,\r\n onStepBackward,\r\n currentTime: currentTimeProp,\r\n epoch,\r\n elapsedSeconds: elapsedSecondsProp,\r\n showElapsedTime = true,\r\n showCurrentTime = true,\r\n showDate = true,\r\n timeScale = 1,\r\n onTimeScaleChange,\r\n timeScalePresets = DEFAULT_TIME_SCALE_PRESETS,\r\n showTimeScale = true,\r\n stepSize = 60,\r\n onStepSizeChange: _onStepSizeChange,\r\n stepSizePresets: _stepSizePresets = DEFAULT_STEP_SIZE_PRESETS,\r\n showStepControls = true,\r\n compact = false,\r\n small = false,\r\n className = '',\r\n disabled = false,\r\n showStatus = true,\r\n label,\r\n}: SimulationControlsProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\r\n \r\n // Derive playing state from props\r\n const isPlaying = useMemo(() => {\r\n if (state) return state === 'playing';\r\n return isPlayingProp ?? false;\r\n }, [state, isPlayingProp]);\r\n \r\n const isStopped = useMemo(() => {\r\n if (state) return state === 'stopped';\r\n return !isPlayingProp && !isPaused;\r\n }, [state, isPlayingProp, isPaused]);\r\n \r\n // Parse current time\r\n const currentTime = useMemo(() => {\r\n if (!currentTimeProp) return undefined;\r\n return typeof currentTimeProp === 'string' ? new Date(currentTimeProp) : currentTimeProp;\r\n }, [currentTimeProp]);\r\n \r\n // Calculate elapsed seconds\r\n const elapsedSeconds = useMemo(() => {\r\n if (elapsedSecondsProp !== undefined) return elapsedSecondsProp;\r\n if (currentTime && epoch) {\r\n const epochDate = typeof epoch === 'string' ? new Date(epoch) : epoch;\r\n return (currentTime.getTime() - epochDate.getTime()) / 1000;\r\n }\r\n return undefined;\r\n }, [elapsedSecondsProp, currentTime, epoch]);\r\n \r\n // Handle play/pause toggle\r\n const handlePlayPause = useCallback(() => {\r\n if (isPlaying) {\r\n onPause?.();\r\n } else {\r\n onPlay?.();\r\n }\r\n }, [isPlaying, onPlay, onPause]);\r\n \r\n // Handle keyboard shortcuts\r\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\r\n if (disabled) return;\r\n \r\n switch (e.key) {\r\n case ' ':\r\n e.preventDefault();\r\n handlePlayPause();\r\n break;\r\n case 'Escape':\r\n e.preventDefault();\r\n onStop?.();\r\n break;\r\n case 'ArrowRight':\r\n e.preventDefault();\r\n onStepForward?.();\r\n break;\r\n case 'ArrowLeft':\r\n e.preventDefault();\r\n onStepBackward?.();\r\n break;\r\n }\r\n }, [disabled, handlePlayPause, onStop, onStepForward, onStepBackward]);\r\n \r\n return (\r\n <div\r\n className={classNames('zendir-simulation-controls', className)}\r\n role=\"group\"\r\n aria-label={label || 'Simulation Controls'}\r\n onKeyDown={handleKeyDown}\r\n tabIndex={0}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: compact ? tokens.spacing.sm : tokens.spacing.md,\r\n padding: compact \r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`,\r\n backgroundColor: isTransparentTheme \r\n ? 'rgba(0,0,0,0.3)' \r\n : tokens.colors.background.surface,\r\n ...(isTransparentTheme && { \r\n backdropFilter: 'blur(12px)', \r\n WebkitBackdropFilter: 'blur(12px)' as const \r\n }),\r\n ...(tokens.colors.border.cardStyle ?? { border: `1px solid ${tokens.colors.border.muted}` }),\r\n borderRadius: tokens.borderRadius.lg,\r\n outline: 'none',\r\n transition: tokens.animation.fast,\r\n }}\r\n >\r\n {/* Time Display (if showing time) */}\r\n {(showCurrentTime || showElapsedTime) && !compact && (\r\n <SimulationTimeDisplay\r\n currentTime={currentTime}\r\n elapsedSeconds={elapsedSeconds}\r\n showElapsed={showElapsedTime}\r\n showCurrent={showCurrentTime}\r\n showDate={showDate}\r\n isPlaying={isPlaying}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n \r\n {/* Compact time display */}\r\n {compact && currentTime && (\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 8,\r\n }}\r\n >\r\n {showDate && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.tertiary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatDateDisplay(currentTime)}\r\n </span>\r\n )}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.lg,\r\n fontWeight: tokens.typography.fontWeight.bold, // Bold (700) to match MissionClock\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.primary,\r\n ...tabularNumsStyle,\r\n }}\r\n >\r\n {formatTimeDisplay(currentTime)}\r\n </span>\r\n {showStatus && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.status.standby,\r\n textTransform: 'uppercase',\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {isPlaying && (\r\n <span\r\n style={{\r\n width: 6,\r\n height: 6,\r\n borderRadius: '50%',\r\n backgroundColor: tokens.colors.accent.primary,\r\n }}\r\n />\r\n )}\r\n SIM\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n \r\n {/* Separator */}\r\n {((showCurrentTime || showElapsedTime) || (compact && currentTime)) && (\r\n <div\r\n style={{\r\n width: 1,\r\n height: 24,\r\n backgroundColor: tokens.colors.border.muted,\r\n }}\r\n />\r\n )}\r\n \r\n {/* Transport Controls */}\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n }}\r\n >\r\n {/* Step Backward */}\r\n {showStepControls && (\r\n <ControlButton\r\n icon=\"skip-previous\"\r\n label={`Step backward ${formatStepSize(stepSize)}`}\r\n onClick={onStepBackward}\r\n disabled={disabled || !onStepBackward}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n \r\n {/* Stop */}\r\n <ControlButton\r\n icon=\"stop\"\r\n label=\"Stop / Reset\"\r\n onClick={onStop}\r\n disabled={disabled || !onStop}\r\n danger={!isStopped}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n \r\n {/* Play/Pause */}\r\n <Tooltip content={isPlaying ? 'Pause (Space)' : 'Play (Space)'} placement=\"bottom\">\r\n <button\r\n onClick={handlePlayPause}\r\n disabled={disabled}\r\n aria-label={isPlaying ? 'Pause simulation' : 'Play simulation'}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: small ? 36 : 44,\r\n height: small ? 36 : 44,\r\n padding: 0,\r\n border: isPlaying ? `2px solid ${tokens.colors.accent.primary}` : 'none',\r\n borderRadius: '50%',\r\n // Use accent primary for both states - playing has outline style\r\n backgroundColor: isPlaying \r\n ? 'rgba(138, 43, 226, 0.15)' // Subtle purple fill when playing\r\n : tokens.colors.accent.primary,\r\n color: isPlaying ? safeAccentText(tokens.colors.accent.primary) : '#ffffff',\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n transition: 'all 0.15s ease',\r\n opacity: disabled ? 0.5 : 1,\r\n boxShadow: isPlaying \r\n ? `0 0 16px ${tokens.colors.accent.primary}50`\r\n : `0 0 8px ${tokens.colors.accent.primary}40`,\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!disabled) {\r\n e.currentTarget.style.transform = 'scale(1.05)';\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = 'scale(1)';\r\n }}\r\n >\r\n <AstroIcon \r\n name={isPlaying ? 'pause' : 'play-arrow'} \r\n size={small ? 18 : 22} \r\n color={isPlaying ? tokens.colors.accent.primary : '#ffffff'}\r\n />\r\n </button>\r\n </Tooltip>\r\n \r\n {/* Step Forward */}\r\n {showStepControls && (\r\n <ControlButton\r\n icon=\"skip-next\"\r\n label={`Step forward ${formatStepSize(stepSize)}`}\r\n onClick={onStepForward}\r\n disabled={disabled || !onStepForward}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n )}\r\n </div>\r\n \r\n {/* Time Scale */}\r\n {showTimeScale && (\r\n <>\r\n <div\r\n style={{\r\n width: 1,\r\n height: 24,\r\n backgroundColor: tokens.colors.border.muted,\r\n }}\r\n />\r\n <TimeScaleSelector\r\n value={timeScale}\r\n presets={timeScalePresets}\r\n onChange={onTimeScaleChange}\r\n disabled={disabled || !onTimeScaleChange}\r\n tokens={tokens}\r\n small={small}\r\n />\r\n </>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ============================================================================\r\n// Convenience Components\r\n// ============================================================================\r\n\r\n/**\r\n * MiniSimulationControls - Ultra-compact controls for AppBar\r\n */\r\nexport interface MiniSimulationControlsProps {\r\n isPlaying?: boolean;\r\n onPlay?: () => void;\r\n onPause?: () => void;\r\n onStop?: () => void;\r\n timeScale?: number;\r\n onTimeScaleChange?: (scale: number) => void;\r\n disabled?: boolean;\r\n className?: string;\r\n}\r\n\r\nexport const MiniSimulationControls = memo(function MiniSimulationControls({\r\n isPlaying = false,\r\n onPlay,\r\n onPause,\r\n onStop,\r\n timeScale = 1,\r\n onTimeScaleChange,\r\n disabled = false,\r\n className = '',\r\n}: MiniSimulationControlsProps): React.ReactElement {\r\n return (\r\n <SimulationControls\r\n isPlaying={isPlaying}\r\n onPlay={onPlay}\r\n onPause={onPause}\r\n onStop={onStop}\r\n timeScale={timeScale}\r\n onTimeScaleChange={onTimeScaleChange}\r\n disabled={disabled}\r\n compact\r\n small\r\n showCurrentTime={false}\r\n showElapsedTime={false}\r\n showDate={false}\r\n showStepControls={false}\r\n showTimeScale={Boolean(onTimeScaleChange)}\r\n className={className}\r\n />\r\n );\r\n});\r\n\r\n/**\r\n * SimulationControlsWithClock - Controls combined with MissionClock\r\n */\r\nexport interface SimulationControlsWithClockProps extends Omit<SimulationControlsProps, 'showCurrentTime' | 'showElapsedTime'> {\r\n /** Clock format */\r\n clockFormat?: '12h' | '24h';\r\n /** Timezone label */\r\n timezone?: string;\r\n}\r\n\r\nexport const SimulationControlsWithClock = memo(function SimulationControlsWithClock({\r\n clockFormat: _clockFormat = '24h',\r\n timezone = 'SIM',\r\n ...props\r\n}: SimulationControlsWithClockProps): React.ReactElement {\r\n return (\r\n <SimulationControls\r\n {...props}\r\n showCurrentTime\r\n showElapsedTime\r\n showDate\r\n label={`Simulation Controls - ${timezone}`}\r\n />\r\n );\r\n});\r\n\r\nexport default SimulationControls;\r\n"],"names":["ControlButton","TimeScaleSelector","SimulationTimeDisplay","SimulationControls","MiniSimulationControls","SimulationControlsWithClock"],"mappings":";;;;;;AA4HA,MAAM,6BAA6B,CAAC,KAAK,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI;AACzE,MAAM,4BAA4B,CAAC,GAAG,GAAG,IAAI,IAAI,IAAI,KAAK,KAAK,IAAI;AASnE,SAAS,kBAAkB,SAAyB;AAClD,QAAM,aAAa,KAAK,IAAI,OAAO;AACnC,QAAM,OAAO,KAAK,MAAM,aAAa,KAAK;AAC1C,QAAM,QAAQ,KAAK,MAAO,aAAa,QAAS,IAAI;AACpD,QAAM,OAAO,KAAK,MAAO,aAAa,OAAQ,EAAE;AAChD,QAAM,OAAO,KAAK,MAAM,aAAa,EAAE;AAEvC,QAAM,OAAO,UAAU,IAAI,MAAM;AAEjC,MAAI,OAAO,GAAG;AACZ,WAAO,GAAG,IAAI,GAAG,IAAI,IAAI,MAAM,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC;AAAA,EACpI;AACA,SAAO,GAAG,IAAI,GAAG,MAAM,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,WAAW,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC;AAC5H;AAKA,SAAS,gBAAgB,OAAuB;AAC9C,MAAI,SAAS,KAAM,QAAO,GAAG,QAAQ,IAAI;AACzC,MAAI,SAAS,GAAI,QAAO,GAAG,QAAQ,EAAE;AACrC,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,QAAQ,EAAG,QAAO,GAAG,KAAK;AAC9B,SAAO,GAAG,KAAK;AACjB;AAKA,SAAS,eAAe,SAAyB;AAC/C,MAAI,WAAW,KAAM,QAAO,GAAG,UAAU,IAAI;AAC7C,MAAI,WAAW,GAAI,QAAO,GAAG,UAAU,EAAE;AACzC,SAAO,GAAG,OAAO;AACnB;AAKA,SAAS,kBAAkB,MAAoB;AAC7C,QAAM,QAAQ,KAAK,YAAA,EAAc,WAAW,SAAS,GAAG,GAAG;AAC3D,QAAM,OAAO,KAAK,cAAA,EAAgB,WAAW,SAAS,GAAG,GAAG;AAC5D,QAAM,OAAO,KAAK,cAAA,EAAgB,WAAW,SAAS,GAAG,GAAG;AAC5D,SAAO,GAAG,KAAK,IAAI,IAAI,IAAI,IAAI;AACjC;AAKA,SAAS,kBAAkB,MAAoB;AAC7C,QAAM,OAAO,KAAK,eAAA;AAClB,QAAM,SAAS,KAAK,YAAA,IAAgB,GAAG,WAAW,SAAS,GAAG,GAAG;AACjE,QAAM,MAAM,KAAK,WAAA,EAAa,WAAW,SAAS,GAAG,GAAG;AACxD,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAChC;AAiBA,MAAM,gBAAgB,KAAK,SAASA,eAAc;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT;AAAA,EACA,QAAQ;AACV,GAAuB;AACrB,QAAM,OAAO,QAAQ,KAAK;AAC1B,QAAM,WAAW,QAAQ,KAAK;AAE9B,QAAM,YAAY,SACd,OAAO,OAAO,OAAO,WACrB,SACE,OAAO,OAAO,OAAO,SACrB,OAAO,OAAO,KAAK;AAEzB,QAAM,UAAU,SACZ,GAAG,OAAO,OAAO,OAAO,MAAM,OAC9B;AAEJ,SACE,oBAAC,SAAA,EAAQ,SAAS,OAAO,WAAU,UACjC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,cAAY;AAAA,MACZ,OAAO;AAAA,QACL,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,cAAc,OAAO,aAAa;AAAA,QAClC,iBAAiB,WAAW,2BAA2B;AAAA,QACvD,OAAO,WAAW,OAAO,OAAO,KAAK,WAAW;AAAA,QAChD,QAAQ,WAAW,gBAAgB;AAAA,QACnC,YAAY;AAAA,QACZ,SAAS,WAAW,MAAM;AAAA,MAAA;AAAA,MAE5B,cAAc,CAAC,MAAM;AACnB,YAAI,CAAC,UAAU;AACb,YAAE,cAAc,MAAM,kBAAkB,SACpC,GAAG,OAAO,OAAO,OAAO,MAAM,OAC9B;AAAA,QACN;AAAA,MACF;AAAA,MACA,cAAc,CAAC,MAAM;AACnB,YAAI,CAAC,UAAU;AACb,YAAE,cAAc,MAAM,kBAAkB;AAAA,QAC1C;AAAA,MACF;AAAA,MAEA,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO,WAAW,OAAO,OAAO,KAAK,WAAW;AAAA,QAAA;AAAA,MAAA;AAAA,IAClD;AAAA,EAAA,GAEJ;AAEJ,CAAC;AAWD,MAAM,oBAAoB,KAAK,SAASC,mBAAkB;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAA2B;AACzB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAE1C,8BACG,OAAA,EAAI,OAAO,EAAE,UAAU,cACtB,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAQ,SAAQ,+BAA8B,WAAU,UACvD,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS,MAAM,CAAC,YAAY,UAAU,CAAC,MAAM;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,KAAK;AAAA,UACL,SAAS,QAAQ,YAAY;AAAA,UAC7B,QAAQ;AAAA,UACR,cAAc,OAAO,aAAa;AAAA,UAClC,iBAAiB;AAAA,UACjB,OAAO,UAAU,IAAI,OAAO,OAAO,OAAO,UAAU,OAAO,OAAO,KAAK;AAAA,UACvE,UAAU,QAAQ,OAAO,WAAW,SAAS,MAAM;AAAA;AAAA,UACnD,YAAY;AAAA;AAAA,UACZ,YAAY,OAAO,WAAW,WAAW;AAAA,UACzC,QAAQ,WAAW,gBAAgB;AAAA,UACnC,YAAY;AAAA,UACZ,SAAS,WAAW,MAAM;AAAA,UAC1B,GAAG;AAAA,QAAA;AAAA,QAGL,UAAA;AAAA,UAAA,oBAAC,WAAA,EAAU,MAAK,gBAAe,MAAM,QAAQ,KAAK,IAAI,OAAM,UAAA,CAAU;AAAA,UACrE,gBAAgB,KAAK;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAE1B;AAAA,IAEC,UAAU,CAAC,YACV,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAO;AAAA,YACL,UAAU;AAAA,YACV,OAAO;AAAA,YACP,QAAQ;AAAA,UAAA;AAAA,UAEV,SAAS,MAAM,UAAU,KAAK;AAAA,QAAA;AAAA,MAAA;AAAA,MAEhC;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAO;AAAA,YACL,UAAU;AAAA,YACV,KAAK;AAAA,YACL,MAAM;AAAA,YACN,WAAW;AAAA,YACX,WAAW;AAAA,YACX,SAAS;AAAA,YACT,iBAAiB,OAAO,OAAO,WAAW;AAAA,YAC1C,QAAQ,aAAa,OAAO,OAAO,OAAO,OAAO;AAAA,YACjD,cAAc,OAAO,aAAa;AAAA,YAClC,WAAW,OAAO,QAAQ;AAAA,YAC1B,QAAQ;AAAA,YACR,UAAU;AAAA,UAAA;AAAA,UAGX,UAAA,QAAQ,IAAI,CAAC,UACZ;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,SAAS,MAAM;AACb,qDAAW;AACX,0BAAU,KAAK;AAAA,cACjB;AAAA,cACA,OAAO;AAAA,gBACL,SAAS;AAAA,gBACT,OAAO;AAAA,gBACP,SAAS;AAAA,gBACT,QAAQ;AAAA,gBACR,cAAc,OAAO,aAAa;AAAA,gBAClC,iBAAiB,UAAU,QAAQ,OAAO,OAAO,OAAO,UAAU;AAAA,gBAClE,OAAO,UAAU,QAAQ,YAAY,OAAO,OAAO,KAAK;AAAA,gBACxD,UAAU;AAAA;AAAA,gBACV,YAAY,UAAU,QAAQ,MAAM;AAAA;AAAA,gBACpC,YAAY,OAAO,WAAW,WAAW;AAAA,gBACzC,WAAW;AAAA,gBACX,QAAQ;AAAA,gBACR,YAAY;AAAA,gBACZ,GAAG;AAAA,cAAA;AAAA,cAEL,cAAc,CAAC,MAAM;AACnB,oBAAI,UAAU,OAAO;AACnB,oBAAE,cAAc,MAAM,kBAAkB;AAAA,gBAC1C;AAAA,cACF;AAAA,cACA,cAAc,CAAC,MAAM;AACnB,kBAAE,cAAc,MAAM,kBAAkB,UAAU,QAC9C,OAAO,OAAO,OAAO,UACrB;AAAA,cACN;AAAA,cAEC,0BAAgB,KAAK;AAAA,YAAA;AAAA,YAhCjB;AAAA,UAAA,CAkCR;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ,CAAC;AAaD,MAAM,wBAAwB,KAAK,SAASC,uBAAsB;AAAA,EAChE;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AAAA,EACA,QAAQ;AACV,GAA+B;AAC7B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO;AAAA,QACL,SAAS;AAAA,QACT,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,KAAK;AAAA,QACL,SAAS,GAAG,OAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE;AAAA,MAAA;AAAA,MAGnD,UAAA;AAAA,QAAA,YAAY,eACX;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU,OAAO,WAAW,SAAS;AAAA,cACrC,YAAY,OAAO,WAAW,WAAW;AAAA,cACzC,OAAO,OAAO,OAAO,KAAK;AAAA,cAC1B,GAAG;AAAA,YAAA;AAAA,YAGJ,4BAAkB,WAAW;AAAA,UAAA;AAAA,QAAA;AAAA,QAIjC,eAAe,eACd;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU,QAAQ,OAAO,WAAW,SAAS,KAAK,OAAO,WAAW,SAAS;AAAA,cAC7E,YAAY,OAAO,WAAW,WAAW;AAAA;AAAA,cACzC,YAAY,OAAO,WAAW,WAAW;AAAA,cACzC,OAAO,OAAO,OAAO,KAAK;AAAA,cAC1B,GAAG;AAAA,YAAA;AAAA,YAGJ,4BAAkB,WAAW;AAAA,UAAA;AAAA,QAAA;AAAA,QAIjC,eAAe,mBAAmB,UACjC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU,OAAO,WAAW,SAAS;AAAA,cACrC,YAAY,OAAO,WAAW,WAAW;AAAA,cACzC,OAAO,OAAO,OAAO,OAAO;AAAA,cAC5B,GAAG;AAAA,YAAA;AAAA,YAEN,UAAA;AAAA,cAAA;AAAA,cACG,kBAAkB,IAAI,MAAM;AAAA,cAAI,kBAAkB,cAAc;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAItE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU,OAAO,WAAW,SAAS;AAAA,cACrC,YAAY,OAAO,WAAW,WAAW;AAAA,cACzC,OAAO,YAAY,eAAe,OAAO,OAAO,OAAO,OAAO,IAAI,OAAO,OAAO,OAAO;AAAA,cACvF,eAAe;AAAA,cACf,eAAe,OAAO,WAAW,cAAc;AAAA,cAC/C,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,KAAK;AAAA,YAAA;AAAA,YAGN,UAAA;AAAA,cAAA,aACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,OAAO;AAAA,oBACP,QAAQ;AAAA,oBACR,cAAc;AAAA,oBACd,iBAAiB,OAAO,OAAO,OAAO;AAAA,oBACtC,WAAW;AAAA,kBAAA;AAAA,gBACb;AAAA,cAAA;AAAA,cAEF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAuDM,MAAM,qBAAqB,KAAK,SAASC,oBAAmB;AAAA,EACjE;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AAAA,EACA,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,iBAAiB,mBAAmB;AAAA,EACpC,mBAAmB;AAAA,EACnB,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb;AACF,GAAgD;AAC9C,QAAM,EAAE,QAAQ,MAAA,IAAU,SAAA;AAC1B,QAAM,qBAAqB,UAAU,iBAAiB,UAAU,sBAAsB,UAAU;AAGhG,QAAM,YAAY,QAAQ,MAAM;AAC9B,QAAI,cAAc,UAAU;AAC5B,WAAO,iBAAiB;AAAA,EAC1B,GAAG,CAAC,OAAO,aAAa,CAAC;AAEzB,QAAM,YAAY,QAAQ,MAAM;AAC9B,QAAI,cAAc,UAAU;AAC5B,WAAO,CAAC,iBAAiB,CAAC;AAAA,EAC5B,GAAG,CAAC,OAAO,eAAe,QAAQ,CAAC;AAGnC,QAAM,cAAc,QAAQ,MAAM;AAChC,QAAI,CAAC,gBAAiB,QAAO;AAC7B,WAAO,OAAO,oBAAoB,WAAW,IAAI,KAAK,eAAe,IAAI;AAAA,EAC3E,GAAG,CAAC,eAAe,CAAC;AAGpB,QAAM,iBAAiB,QAAQ,MAAM;AACnC,QAAI,uBAAuB,OAAW,QAAO;AAC7C,QAAI,eAAe,OAAO;AACxB,YAAM,YAAY,OAAO,UAAU,WAAW,IAAI,KAAK,KAAK,IAAI;AAChE,cAAQ,YAAY,QAAA,IAAY,UAAU,aAAa;AAAA,IACzD;AACA,WAAO;AAAA,EACT,GAAG,CAAC,oBAAoB,aAAa,KAAK,CAAC;AAG3C,QAAM,kBAAkB,YAAY,MAAM;AACxC,QAAI,WAAW;AACb;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,QAAQ,OAAO,CAAC;AAG/B,QAAM,gBAAgB,YAAY,CAAC,MAA2B;AAC5D,QAAI,SAAU;AAEd,YAAQ,EAAE,KAAA;AAAA,MACR,KAAK;AACH,UAAE,eAAA;AACF,wBAAA;AACA;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF;AACA;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF;AACA;AAAA,MACF,KAAK;AACH,UAAE,eAAA;AACF;AACA;AAAA,IAAA;AAAA,EAEN,GAAG,CAAC,UAAU,iBAAiB,QAAQ,eAAe,cAAc,CAAC;AAErE,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,WAAW,8BAA8B,SAAS;AAAA,MAC7D,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MACrB,WAAW;AAAA,MACX,UAAU;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,KAAK,UAAU,OAAO,QAAQ,KAAK,OAAO,QAAQ;AAAA,QAClD,SAAS,UACL,GAAG,OAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE,KACzC,GAAG,OAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE;AAAA,QAC7C,iBAAiB,qBACb,oBACA,OAAO,OAAO,WAAW;AAAA,QAC7B,GAAI,sBAAsB;AAAA,UACxB,gBAAgB;AAAA,UAChB,sBAAsB;AAAA,QAAA;AAAA,QAExB,GAAI,OAAO,OAAO,OAAO,aAAa,EAAE,QAAQ,aAAa,OAAO,OAAO,OAAO,KAAK,GAAA;AAAA,QACvF,cAAc,OAAO,aAAa;AAAA,QAClC,SAAS;AAAA,QACT,YAAY,OAAO,UAAU;AAAA,MAAA;AAAA,MAI7B,UAAA;AAAA,SAAA,mBAAmB,oBAAoB,CAAC,WACxC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,aAAa;AAAA,YACb,aAAa;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAAA,QAAA;AAAA,QAKH,WAAW,eACV;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,KAAK;AAAA,YAAA;AAAA,YAGN,UAAA;AAAA,cAAA,YACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,UAAU,OAAO,WAAW,SAAS;AAAA,oBACrC,YAAY,OAAO,WAAW,WAAW;AAAA,oBACzC,OAAO,OAAO,OAAO,KAAK;AAAA,oBAC1B,GAAG;AAAA,kBAAA;AAAA,kBAGJ,4BAAkB,WAAW;AAAA,gBAAA;AAAA,cAAA;AAAA,cAGlC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,UAAU,OAAO,WAAW,SAAS;AAAA,oBACrC,YAAY,OAAO,WAAW,WAAW;AAAA;AAAA,oBACzC,YAAY,OAAO,WAAW,WAAW;AAAA,oBACzC,OAAO,OAAO,OAAO,KAAK;AAAA,oBAC1B,GAAG;AAAA,kBAAA;AAAA,kBAGJ,4BAAkB,WAAW;AAAA,gBAAA;AAAA,cAAA;AAAA,cAE/B,cACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,UAAU,OAAO,WAAW,SAAS;AAAA,oBACrC,YAAY,OAAO,WAAW,WAAW;AAAA,oBACzC,OAAO,YAAY,eAAe,OAAO,OAAO,OAAO,OAAO,IAAI,OAAO,OAAO,OAAO;AAAA,oBACvF,eAAe;AAAA,oBACf,SAAS;AAAA,oBACT,YAAY;AAAA,oBACZ,KAAK;AAAA,kBAAA;AAAA,kBAGN,UAAA;AAAA,oBAAA,aACC;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,OAAO;AAAA,0BACL,OAAO;AAAA,0BACP,QAAQ;AAAA,0BACR,cAAc;AAAA,0BACd,iBAAiB,OAAO,OAAO,OAAO;AAAA,wBAAA;AAAA,sBACxC;AAAA,oBAAA;AAAA,oBAEF;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YAEJ;AAAA,UAAA;AAAA,QAAA;AAAA,SAMH,mBAAmB,mBAAqB,WAAW,gBACpD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,iBAAiB,OAAO,OAAO,OAAO;AAAA,YAAA;AAAA,UACxC;AAAA,QAAA;AAAA,QAKJ;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,KAAK;AAAA,YAAA;AAAA,YAIN,UAAA;AAAA,cAAA,oBACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAO,iBAAiB,eAAe,QAAQ,CAAC;AAAA,kBAChD,SAAS;AAAA,kBACT,UAAU,YAAY,CAAC;AAAA,kBACvB;AAAA,kBACA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAKJ;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAM;AAAA,kBACN,SAAS;AAAA,kBACT,UAAU,YAAY,CAAC;AAAA,kBACvB,QAAQ,CAAC;AAAA,kBACT;AAAA,kBACA;AAAA,gBAAA;AAAA,cAAA;AAAA,kCAID,SAAA,EAAQ,SAAS,YAAY,kBAAkB,gBAAgB,WAAU,UACxE,UAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,SAAS;AAAA,kBACT;AAAA,kBACA,cAAY,YAAY,qBAAqB;AAAA,kBAC7C,OAAO;AAAA,oBACL,SAAS;AAAA,oBACT,YAAY;AAAA,oBACZ,gBAAgB;AAAA,oBAChB,OAAO,QAAQ,KAAK;AAAA,oBACpB,QAAQ,QAAQ,KAAK;AAAA,oBACrB,SAAS;AAAA,oBACT,QAAQ,YAAY,aAAa,OAAO,OAAO,OAAO,OAAO,KAAK;AAAA,oBAClE,cAAc;AAAA;AAAA,oBAEd,iBAAiB,YACb,6BACA,OAAO,OAAO,OAAO;AAAA,oBACzB,OAAO,YAAY,eAAe,OAAO,OAAO,OAAO,OAAO,IAAI;AAAA,oBAClE,QAAQ,WAAW,gBAAgB;AAAA,oBACnC,YAAY;AAAA,oBACZ,SAAS,WAAW,MAAM;AAAA,oBAC1B,WAAW,YACP,YAAY,OAAO,OAAO,OAAO,OAAO,OACxC,WAAW,OAAO,OAAO,OAAO,OAAO;AAAA,kBAAA;AAAA,kBAE7C,cAAc,CAAC,MAAM;AACnB,wBAAI,CAAC,UAAU;AACb,wBAAE,cAAc,MAAM,YAAY;AAAA,oBACpC;AAAA,kBACF;AAAA,kBACA,cAAc,CAAC,MAAM;AACnB,sBAAE,cAAc,MAAM,YAAY;AAAA,kBACpC;AAAA,kBAEA,UAAA;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBACC,MAAM,YAAY,UAAU;AAAA,sBAC5B,MAAM,QAAQ,KAAK;AAAA,sBACnB,OAAO,YAAY,OAAO,OAAO,OAAO,UAAU;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBACpD;AAAA,cAAA,GAEJ;AAAA,cAGC,oBACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAO,gBAAgB,eAAe,QAAQ,CAAC;AAAA,kBAC/C,SAAS;AAAA,kBACT,UAAU,YAAY,CAAC;AAAA,kBACvB;AAAA,kBACA;AAAA,gBAAA;AAAA,cAAA;AAAA,YACF;AAAA,UAAA;AAAA,QAAA;AAAA,QAKH,iBACC,qBAAA,UAAA,EACE,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,OAAO;AAAA,gBACL,OAAO;AAAA,gBACP,QAAQ;AAAA,gBACR,iBAAiB,OAAO,OAAO,OAAO;AAAA,cAAA;AAAA,YACxC;AAAA,UAAA;AAAA,UAEF;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,OAAO;AAAA,cACP,SAAS;AAAA,cACT,UAAU;AAAA,cACV,UAAU,YAAY,CAAC;AAAA,cACvB;AAAA,cACA;AAAA,YAAA;AAAA,UAAA;AAAA,QACF,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAoBM,MAAM,yBAAyB,KAAK,SAASC,wBAAuB;AAAA,EACzE,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AACd,GAAoD;AAClD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAO;AAAA,MACP,OAAK;AAAA,MACL,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,UAAU;AAAA,MACV,kBAAkB;AAAA,MAClB,eAAe,QAAQ,iBAAiB;AAAA,MACxC;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAYM,MAAM,8BAA8B,KAAK,SAASC,6BAA4B;AAAA,EACnF,aAAa,eAAe;AAAA,EAC5B,WAAW;AAAA,EACX,GAAG;AACL,GAAyD;AACvD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACJ,iBAAe;AAAA,MACf,iBAAe;AAAA,MACf,UAAQ;AAAA,MACR,OAAO,yBAAyB,QAAQ;AAAA,IAAA;AAAA,EAAA;AAG9C,CAAC;"}
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { CategoryDef } from '../types';
|
|
2
3
|
|
|
4
|
+
/** Filter key for events with no team accent (use with `TimelineFilter.teams`). */
|
|
5
|
+
export declare const TIMELINE_FILTER_TEAM_NONE = "__zendir_team_none__";
|
|
6
|
+
/** Filter key for legacy accent color only (no structured `team.id`). */
|
|
7
|
+
export declare const TIMELINE_FILTER_TEAM_LEGACY = "__zendir_team_legacy__";
|
|
3
8
|
export type TimelineViewMode = 'list' | 'chart' | 'scatter';
|
|
9
|
+
/**
|
|
10
|
+
* Team / category identity on the timeline.
|
|
11
|
+
*
|
|
12
|
+
* Extends the SDK-level `CategoryDef` with `label` kept optional for
|
|
13
|
+
* backward compatibility — existing code that passes `{ id, color }` without
|
|
14
|
+
* a label continues to compile and run unchanged.
|
|
15
|
+
*
|
|
16
|
+
* Because it extends `CategoryDef`, a `TimelineTeam` value can be used
|
|
17
|
+
* anywhere a `CategoryDef` is expected (e.g. `CategoryPalette` constructor).
|
|
18
|
+
*/
|
|
19
|
+
export interface TimelineTeam extends Omit<CategoryDef, 'label'> {
|
|
20
|
+
/** Human-readable name (tooltips, search, filter pills). Optional for backward compat. */
|
|
21
|
+
label?: string;
|
|
22
|
+
}
|
|
4
23
|
export interface TimelineEvent {
|
|
5
24
|
/** Unique identifier */
|
|
6
25
|
id: string;
|
|
@@ -30,11 +49,27 @@ export interface TimelineEvent {
|
|
|
30
49
|
arguments?: Record<string, string | number | boolean>;
|
|
31
50
|
/** Custom metadata */
|
|
32
51
|
metadata?: Record<string, unknown>;
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
52
|
+
/**
|
|
53
|
+
* Team assignment (preferred): id + accent color (+ optional label).
|
|
54
|
+
* List: thin top stripe. Chart/Gantt: thin top stripe; status via Astro shape + hover.
|
|
55
|
+
* Scatter: outer ring. Does NOT override status fill.
|
|
56
|
+
*/
|
|
57
|
+
team?: TimelineTeam;
|
|
58
|
+
/**
|
|
59
|
+
* @deprecated Prefer `team: { id, color, label? }`. If set without `team`, still used as the accent color (legacy).
|
|
60
|
+
*/
|
|
36
61
|
color?: string;
|
|
37
62
|
}
|
|
63
|
+
/** Resolved team accent for rendering (from `event.team` or legacy `event.color`). */
|
|
64
|
+
export declare function getTimelineTeamAccent(event: TimelineEvent): {
|
|
65
|
+
color: string;
|
|
66
|
+
id?: string | number;
|
|
67
|
+
label?: string;
|
|
68
|
+
} | null;
|
|
69
|
+
/** Label for team row in tooltips (id / label / arguments.Team / badge). */
|
|
70
|
+
export declare function getTimelineTeamDisplayLabel(event: TimelineEvent, accent: NonNullable<ReturnType<typeof getTimelineTeamAccent>>): string;
|
|
71
|
+
/** Stable key for team filter pills (`TimelineFilter.teams`). */
|
|
72
|
+
export declare function getTimelineTeamFilterKey(event: TimelineEvent): string;
|
|
38
73
|
export interface TimelineTrackDef {
|
|
39
74
|
/** Track identifier */
|
|
40
75
|
id: string;
|
|
@@ -53,7 +88,14 @@ export interface TimelineFilter {
|
|
|
53
88
|
search?: string;
|
|
54
89
|
/** Duration shape: point (no end or zero length), range (has end after start), all */
|
|
55
90
|
eventShape?: 'all' | 'point' | 'range';
|
|
56
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Filter by team keys from `getTimelineTeamFilterKey` (e.g. `"42"`, `"alpha"`).
|
|
93
|
+
* Include `TIMELINE_FILTER_TEAM_NONE` / `TIMELINE_FILTER_TEAM_LEGACY` when those buckets exist in data.
|
|
94
|
+
*/
|
|
95
|
+
teams?: string[];
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated Use `teams` with explicit keys. When true (and `teams` is empty), only events with a team accent match.
|
|
98
|
+
*/
|
|
57
99
|
teamColoredOnly?: boolean;
|
|
58
100
|
}
|
|
59
101
|
/** Display status used for filtering and markers (badgeVariant overrides raw status when set). */
|
|
@@ -93,9 +135,9 @@ export interface UnifiedTimelineProps {
|
|
|
93
135
|
zoomable?: boolean;
|
|
94
136
|
/** Initial zoom level (1 = 100%) */
|
|
95
137
|
initialZoom?: number;
|
|
96
|
-
/** Show
|
|
138
|
+
/** Show filter & search controls in header (default true) */
|
|
97
139
|
showFilters?: boolean;
|
|
98
|
-
/**
|
|
140
|
+
/** Start with the filter panel expanded (default false) */
|
|
99
141
|
filtersDefaultExpanded?: boolean;
|
|
100
142
|
/** When any filter is active, hide chart/scatter track rows that have no matching events */
|
|
101
143
|
hideEmptyTracksWhenFiltered?: boolean;
|
|
@@ -117,6 +159,14 @@ export interface UnifiedTimelineProps {
|
|
|
117
159
|
onPlayheadChange?: (time: Date) => void;
|
|
118
160
|
/** Show day markers at midnight */
|
|
119
161
|
showDayMarkers?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Override the label used for the "Teams" secondary-category filter section.
|
|
164
|
+
* Useful when the `team` field on events represents squads, categories, groups, etc.
|
|
165
|
+
* Defaults to `"Teams"`. The value is also used for ARIA labels and tooltip text.
|
|
166
|
+
* @example teamLabel="Squads"
|
|
167
|
+
* @example teamLabel="Categories"
|
|
168
|
+
*/
|
|
169
|
+
teamLabel?: string;
|
|
120
170
|
}
|
|
121
171
|
export declare const UnifiedTimeline: React.NamedExoticComponent<UnifiedTimelineProps>;
|
|
122
172
|
export default UnifiedTimeline;
|