@zendir/ui 0.2.20 → 0.3.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 +192 -1
- package/README.md +70 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.js +57 -41
- package/dist/index.js.map +1 -1
- package/dist/react/3d/CesiumCaptureSource.d.ts +119 -0
- package/dist/react/3d/CesiumCaptureSource.js +307 -0
- package/dist/react/3d/CesiumCaptureSource.js.map +1 -0
- package/dist/react/3d/ZenSpace3D.js +1253 -0
- package/dist/react/3d/ZenSpace3D.js.map +1 -0
- package/dist/react/3d/ZenSpace3DCesium.js +579 -0
- package/dist/react/3d/ZenSpace3DCesium.js.map +1 -0
- package/dist/react/3d/ZenSpace3DTypes.d.ts +28 -1
- package/dist/react/3d/ZenSpace3DUtils.d.ts +17 -173
- package/dist/react/3d/ZenSpace3DUtils.js +28 -0
- package/dist/react/3d/ZenSpace3DUtils.js.map +1 -0
- package/dist/react/3d/capturePngAnalysis.d.ts +16 -0
- package/dist/react/3d/index.d.ts +10 -12
- package/dist/react/3d/threeLoader.js +18 -0
- package/dist/react/3d/threeLoader.js.map +1 -0
- package/dist/react/astro/MonitoringIcon.js +1 -1
- package/dist/react/astro/MonitoringIcon.js.map +1 -1
- package/dist/react/astro/SimulationControls.js +2 -2
- package/dist/react/astro/SimulationControls.js.map +1 -1
- package/dist/react/astro/UnifiedTimeline.js +4 -4
- package/dist/react/astro/UnifiedTimeline.js.map +1 -1
- package/dist/react/charts/GroundTrackMap.d.ts +2 -15
- package/dist/react/charts/GroundTrackMap.js +1 -1
- package/dist/react/charts/GroundTrackMap.js.map +1 -1
- package/dist/react/charts/unified/AstroChart.js +34 -13
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/chatgpt/AppCard.d.ts +0 -4
- package/dist/react/chatgpt/index.d.ts +0 -19
- package/dist/react/context/SpatialSelectionContext.d.ts +40 -0
- package/dist/react/context/SpatialSelectionContext.js +10 -0
- package/dist/react/context/SpatialSelectionContext.js.map +1 -0
- package/dist/react/context/index.d.ts +2 -0
- package/dist/react/core/{DataTable.d.ts → data/DataTable.d.ts} +1 -1
- package/dist/react/core/{DataTable.js → data/DataTable.js} +4 -4
- package/dist/react/core/data/DataTable.js.map +1 -0
- package/dist/react/core/{DataValue.d.ts → data/DataValue.d.ts} +2 -2
- package/dist/react/core/{DataValue.js → data/DataValue.js} +2 -2
- package/dist/react/core/data/DataValue.js.map +1 -0
- package/dist/react/core/{propertyConfig.d.ts → data/propertyConfig.d.ts} +2 -2
- package/dist/react/core/data/propertyConfig.js.map +1 -0
- package/dist/react/core/{AstroIcon.js → display/AstroIcon.js} +1 -1
- package/dist/react/core/display/AstroIcon.js.map +1 -0
- package/dist/react/core/{Badge.d.ts → display/Badge.d.ts} +1 -1
- package/dist/react/core/{Badge.js → display/Badge.js} +2 -2
- package/dist/react/core/display/Badge.js.map +1 -0
- package/dist/react/core/{CardHeader.d.ts → display/CardHeader.d.ts} +1 -1
- package/dist/react/core/{CardHeader.js → display/CardHeader.js} +2 -2
- package/dist/react/core/display/CardHeader.js.map +1 -0
- package/dist/react/core/{Container.d.ts → display/Container.d.ts} +1 -1
- package/dist/react/core/{Container.js → display/Container.js} +3 -3
- package/dist/react/core/display/Container.js.map +1 -0
- package/dist/react/core/{CopyButton.js → display/CopyButton.js} +1 -1
- package/dist/react/core/display/CopyButton.js.map +1 -0
- package/dist/react/core/{GlassCard.d.ts → display/GlassCard.d.ts} +1 -1
- package/dist/react/core/{GlassCard.js → display/GlassCard.js} +2 -2
- package/dist/react/core/display/GlassCard.js.map +1 -0
- package/dist/react/core/{HeaderIconWithStatus.d.ts → display/HeaderIconWithStatus.d.ts} +1 -1
- package/dist/react/core/{HeaderIconWithStatus.js → display/HeaderIconWithStatus.js} +1 -1
- package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -0
- package/dist/react/core/{Icon.d.ts → display/Icon.d.ts} +1 -1
- package/dist/react/core/{Icon.js → display/Icon.js} +1 -1
- package/dist/react/core/display/Icon.js.map +1 -0
- package/dist/react/core/{Typography.d.ts → display/Typography.d.ts} +13 -4
- package/dist/react/core/{Typography.js → display/Typography.js} +1 -1
- package/dist/react/core/display/Typography.js.map +1 -0
- package/dist/react/core/{ConfirmDialog.js → feedback/ConfirmDialog.js} +1 -1
- package/dist/react/core/feedback/ConfirmDialog.js.map +1 -0
- package/dist/react/core/{Dialog.js → feedback/Dialog.js} +2 -2
- package/dist/react/core/feedback/Dialog.js.map +1 -0
- package/dist/react/core/{Toast.js → feedback/Toast.js} +3 -3
- package/dist/react/core/feedback/Toast.js.map +1 -0
- package/dist/react/core/index.d.ts +85 -83
- package/dist/react/core/{Button.js → inputs/Button.js} +2 -2
- package/dist/react/core/inputs/Button.js.map +1 -0
- package/dist/react/core/{Checkbox.js → inputs/Checkbox.js} +2 -2
- package/dist/react/core/inputs/Checkbox.js.map +1 -0
- package/dist/react/core/{Input.d.ts → inputs/Input.d.ts} +1 -1
- package/dist/react/core/{Input.js → inputs/Input.js} +3 -3
- package/dist/react/core/inputs/Input.js.map +1 -0
- package/dist/react/core/{LimitsBar.js → inputs/LimitsBar.js} +1 -1
- package/dist/react/core/inputs/LimitsBar.js.map +1 -0
- package/dist/react/core/{NumberInput.d.ts → inputs/NumberInput.d.ts} +2 -2
- package/dist/react/core/{NumberInput.js → inputs/NumberInput.js} +3 -3
- package/dist/react/core/inputs/NumberInput.js.map +1 -0
- package/dist/react/core/{PinInput.js → inputs/PinInput.js} +2 -2
- package/dist/react/core/inputs/PinInput.js.map +1 -0
- package/dist/react/core/{Select.js → inputs/Select.js} +3 -3
- package/dist/react/core/inputs/Select.js.map +1 -0
- package/dist/react/core/{Toggle.js → inputs/Toggle.js} +2 -2
- package/dist/react/core/inputs/Toggle.js.map +1 -0
- package/dist/react/core/{AppBar.d.ts → navigation/AppBar.d.ts} +1 -1
- package/dist/react/core/{AppBar.js → navigation/AppBar.js} +7 -7
- package/dist/react/core/navigation/AppBar.js.map +1 -0
- package/dist/react/core/{Pagination.js → navigation/Pagination.js} +2 -2
- package/dist/react/core/navigation/Pagination.js.map +1 -0
- package/dist/react/core/{SideNav.d.ts → navigation/SideNav.d.ts} +1 -1
- package/dist/react/core/{SideNav.js → navigation/SideNav.js} +8 -9
- package/dist/react/core/navigation/SideNav.js.map +1 -0
- package/dist/react/core/{Tabs.js → navigation/Tabs.js} +2 -2
- package/dist/react/core/navigation/Tabs.js.map +1 -0
- package/dist/react/core/{Popover.js → overlays/Popover.js} +1 -1
- package/dist/react/core/overlays/Popover.js.map +1 -0
- package/dist/react/core/{SidePanel.js → overlays/SidePanel.js} +7 -7
- package/dist/react/core/overlays/SidePanel.js.map +1 -0
- package/dist/react/core/{Tooltip.js → overlays/Tooltip.js} +2 -2
- package/dist/react/core/overlays/Tooltip.js.map +1 -0
- package/dist/react/core/{ActivityPlanner.js → widgets/ActivityPlanner.js} +1 -1
- package/dist/react/core/widgets/ActivityPlanner.js.map +1 -0
- package/dist/react/core/widgets/Capture.d.ts +140 -0
- package/dist/react/core/widgets/Capture.js +804 -0
- package/dist/react/core/widgets/Capture.js.map +1 -0
- package/dist/react/core/{ChatPanel.d.ts → widgets/ChatPanel.d.ts} +1 -1
- package/dist/react/core/{ChatPanel.js → widgets/ChatPanel.js} +5 -4
- package/dist/react/core/widgets/ChatPanel.js.map +1 -0
- package/dist/react/core/{ColorPickerPanel.d.ts → widgets/ColorPickerPanel.d.ts} +1 -1
- package/dist/react/core/{ColorPickerPanel.js → widgets/ColorPickerPanel.js} +3 -3
- package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -0
- package/dist/react/core/{CommandBuilder.js → widgets/CommandBuilder.js} +1 -1
- package/dist/react/core/widgets/CommandBuilder.js.map +1 -0
- package/dist/react/core/{ConnectionForm.d.ts → widgets/ConnectionForm.d.ts} +1 -1
- package/dist/react/core/{ConnectionForm.js → widgets/ConnectionForm.js} +2 -2
- package/dist/react/core/widgets/ConnectionForm.js.map +1 -0
- package/dist/react/core/{FileExplorer.js → widgets/FileExplorer.js} +2 -2
- package/dist/react/core/widgets/FileExplorer.js.map +1 -0
- package/dist/react/core/{HexViewer.js → widgets/HexViewer.js} +1 -1
- package/dist/react/core/widgets/HexViewer.js.map +1 -0
- package/dist/react/core/{ImageGallery.d.ts → widgets/ImageGallery.d.ts} +1 -1
- package/dist/react/core/{ImageGallery.js → widgets/ImageGallery.js} +3 -3
- package/dist/react/core/widgets/ImageGallery.js.map +1 -0
- package/dist/react/core/{LogViewer.d.ts → widgets/LogViewer.d.ts} +13 -3
- package/dist/react/core/{LogViewer.js → widgets/LogViewer.js} +28 -8
- package/dist/react/core/widgets/LogViewer.js.map +1 -0
- package/dist/react/core/{MessageStream.d.ts → widgets/MessageStream.d.ts} +2 -2
- package/dist/react/core/{MessageStream.js → widgets/MessageStream.js} +4 -4
- package/dist/react/core/widgets/MessageStream.js.map +1 -0
- package/dist/react/core/{MissionCalendar.js → widgets/MissionCalendar.js} +2 -2
- package/dist/react/core/widgets/MissionCalendar.js.map +1 -0
- package/dist/react/core/{PacketViewer.js → widgets/PacketViewer.js} +1 -1
- package/dist/react/core/widgets/PacketViewer.js.map +1 -0
- package/dist/react/core/widgets/capture-placeholder.png.js +5 -0
- package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -0
- package/dist/react/hooks/index.d.ts +9 -11
- package/dist/react/hooks/useAccessWindows.d.ts +15 -19
- package/dist/react/hooks/useGroundTrackHistory.d.ts +34 -0
- package/dist/react/hooks/useSimulationScene.d.ts +141 -0
- package/dist/react/hooks/useSimulationScene.js +401 -0
- package/dist/react/hooks/useSimulationScene.js.map +1 -0
- package/dist/react/hooks/useZendirSession.d.ts +44 -69
- package/dist/react/index.d.ts +10 -5
- package/dist/react/panels/LayerControlPanel.d.ts +54 -0
- package/dist/react/panels/LayerControlPanel.js +184 -0
- package/dist/react/panels/LayerControlPanel.js.map +1 -0
- package/dist/react/panels/ObjectInventoryPanel.d.ts +57 -0
- package/dist/react/panels/ObjectInventoryPanel.js +261 -0
- package/dist/react/panels/ObjectInventoryPanel.js.map +1 -0
- package/dist/react/panels/index.d.ts +15 -0
- package/dist/react/theme/ThemeProvider.d.ts +2 -0
- package/dist/react/theme/ThemeProvider.js +50 -72
- package/dist/react/theme/ThemeProvider.js.map +1 -1
- package/dist/react/types.d.ts +32 -3
- package/dist/react/types.js.map +1 -1
- package/dist/react.js +57 -41
- package/dist/react.js.map +1 -1
- package/dist/shaders/atmosphere.frag.js +5 -0
- package/dist/shaders/atmosphere.frag.js.map +1 -0
- package/dist/shaders/atmosphere.vert.js +5 -0
- package/dist/shaders/atmosphere.vert.js.map +1 -0
- package/dist/shaders/stars.frag.js +5 -0
- package/dist/shaders/stars.frag.js.map +1 -0
- package/dist/shaders/stars.vert.js +5 -0
- package/dist/shaders/stars.vert.js.map +1 -0
- package/dist/style.css +6 -4
- package/dist/tokens/css-vars.d.ts +91 -0
- package/dist/tokens/css-vars.js +228 -0
- package/dist/tokens/css-vars.js.map +1 -0
- package/dist/tokens/index.d.ts +71 -18
- package/dist/tokens/index.js +206 -97
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/tokens.css +50 -50
- package/package.json +27 -22
- package/sdk-stub.js +10 -5
- package/dist/react/3d/EarthViewer.d.ts +0 -46
- package/dist/react/3d/SolarSystemViewer.d.ts +0 -43
- package/dist/react/chatgpt/ChatGPTCard.d.ts +0 -6
- package/dist/react/core/ActivityPlanner.js.map +0 -1
- package/dist/react/core/AppBar.js.map +0 -1
- package/dist/react/core/AstroIcon.js.map +0 -1
- package/dist/react/core/Badge.js.map +0 -1
- package/dist/react/core/Button.js.map +0 -1
- package/dist/react/core/CardHeader.js.map +0 -1
- package/dist/react/core/ChatPanel.js.map +0 -1
- package/dist/react/core/Checkbox.js.map +0 -1
- package/dist/react/core/ColorPickerPanel.js.map +0 -1
- package/dist/react/core/CommandBuilder.js.map +0 -1
- package/dist/react/core/ConfirmDialog.js.map +0 -1
- package/dist/react/core/ConnectionForm.js.map +0 -1
- package/dist/react/core/Container.js.map +0 -1
- package/dist/react/core/CopyButton.js.map +0 -1
- package/dist/react/core/DataTable.js.map +0 -1
- package/dist/react/core/DataValue.js.map +0 -1
- package/dist/react/core/Dialog.js.map +0 -1
- package/dist/react/core/FileExplorer.js.map +0 -1
- package/dist/react/core/GlassCard.js.map +0 -1
- package/dist/react/core/HeaderIconWithStatus.js.map +0 -1
- package/dist/react/core/HexViewer.js.map +0 -1
- package/dist/react/core/Icon.js.map +0 -1
- package/dist/react/core/ImageGallery.js.map +0 -1
- package/dist/react/core/Input.js.map +0 -1
- package/dist/react/core/LimitsBar.js.map +0 -1
- package/dist/react/core/LogViewer.js.map +0 -1
- package/dist/react/core/MessageStream.js.map +0 -1
- package/dist/react/core/MissionCalendar.js.map +0 -1
- package/dist/react/core/NumberInput.js.map +0 -1
- package/dist/react/core/PacketViewer.js.map +0 -1
- package/dist/react/core/Pagination.js.map +0 -1
- package/dist/react/core/PinInput.js.map +0 -1
- package/dist/react/core/Popover.js.map +0 -1
- package/dist/react/core/Select.js.map +0 -1
- package/dist/react/core/SideNav.js.map +0 -1
- package/dist/react/core/SidePanel.js.map +0 -1
- package/dist/react/core/Tabs.js.map +0 -1
- package/dist/react/core/Toast.js.map +0 -1
- package/dist/react/core/Toggle.js.map +0 -1
- package/dist/react/core/Tooltip.js.map +0 -1
- package/dist/react/core/Typography.js.map +0 -1
- package/dist/react/core/propertyConfig.js.map +0 -1
- package/dist/react/hooks/useSimulationTime.d.ts +0 -61
- package/dist/react/hooks/useSpacecraftPosition.d.ts +0 -50
- package/dist/react/hooks/useTelemetry.d.ts +0 -55
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- /package/dist/react/core/{propertyConfig.js → data/propertyConfig.js} +0 -0
- /package/dist/react/core/{AstroIcon.d.ts → display/AstroIcon.d.ts} +0 -0
- /package/dist/react/core/{CopyButton.d.ts → display/CopyButton.d.ts} +0 -0
- /package/dist/react/core/{ConfirmDialog.d.ts → feedback/ConfirmDialog.d.ts} +0 -0
- /package/dist/react/core/{Dialog.d.ts → feedback/Dialog.d.ts} +0 -0
- /package/dist/react/core/{Toast.d.ts → feedback/Toast.d.ts} +0 -0
- /package/dist/react/core/{Button.d.ts → inputs/Button.d.ts} +0 -0
- /package/dist/react/core/{Checkbox.d.ts → inputs/Checkbox.d.ts} +0 -0
- /package/dist/react/core/{LimitsBar.d.ts → inputs/LimitsBar.d.ts} +0 -0
- /package/dist/react/core/{PinInput.d.ts → inputs/PinInput.d.ts} +0 -0
- /package/dist/react/core/{Select.d.ts → inputs/Select.d.ts} +0 -0
- /package/dist/react/core/{Toggle.d.ts → inputs/Toggle.d.ts} +0 -0
- /package/dist/react/core/{Pagination.d.ts → navigation/Pagination.d.ts} +0 -0
- /package/dist/react/core/{Tabs.d.ts → navigation/Tabs.d.ts} +0 -0
- /package/dist/react/core/{Popover.d.ts → overlays/Popover.d.ts} +0 -0
- /package/dist/react/core/{SidePanel.d.ts → overlays/SidePanel.d.ts} +0 -0
- /package/dist/react/core/{Tooltip.d.ts → overlays/Tooltip.d.ts} +0 -0
- /package/dist/react/core/{ActivityPlanner.d.ts → widgets/ActivityPlanner.d.ts} +0 -0
- /package/dist/react/core/{CommandBuilder.d.ts → widgets/CommandBuilder.d.ts} +0 -0
- /package/dist/react/core/{FileExplorer.d.ts → widgets/FileExplorer.d.ts} +0 -0
- /package/dist/react/core/{HexViewer.d.ts → widgets/HexViewer.d.ts} +0 -0
- /package/dist/react/core/{MissionCalendar.d.ts → widgets/MissionCalendar.d.ts} +0 -0
- /package/dist/react/core/{PacketViewer.d.ts → widgets/PacketViewer.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppBar.js","sources":["../../../src/react/core/AppBar.tsx"],"sourcesContent":["/**\n * @zendir/ui - AppBar Component\n * \n * Enterprise-grade application bar with integrated theme controls, \n * compact mode toggle, and display settings management.\n * \n * Features:\n * - Customizable branding (logo, name, domain, version)\n * - Dark/light mode toggle\n * - Theme variant selector\n * - Color picker (accent + glass tint)\n * - Compact mode toggle (affects all cards globally)\n * - Integrates with DisplaySettingsProvider\n * - Responsive design\n * - AstroUXDS compliant\n * \n * @example\n * ```tsx\n * import { AppBar, ThemeProvider, DisplaySettingsProvider } from '@zendir/ui';\n * \n * <ThemeProvider>\n * <DisplaySettingsProvider>\n * <AppBar\n * appName=\"Mission Control\"\n * appDomain=\"NASA\"\n * appVersion=\"1.0.0\"\n * showThemeControls\n * showCompactToggle\n * />\n * <App />\n * </DisplaySettingsProvider>\n * </ThemeProvider>\n * ```\n */\n\nimport React, { memo, useMemo, useState, useRef, useEffect, type ReactNode } from 'react';\nimport { useTheme, type ThemeVariant, type ThemeMode } from '../theme';\nimport { useDisplaySettingsOptional } from '../context/DisplaySettingsContext';\nimport { classNames, safeAccentText } from '../utils';\nimport { AstroIcon } from './AstroIcon';\nimport { ColorPickerPanel } from './ColorPickerPanel';\nimport { Tooltip } from './Tooltip';\nimport { useBreakpoint } from './layout/useBreakpoint';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AppBarBranding {\n /** Logo image URL (takes precedence over text) */\n logoUrl?: string;\n /** Logo as React node (takes precedence over logoUrl) */\n logo?: ReactNode;\n /** Logo alt text for accessibility */\n logoAlt?: string;\n /** Logo height in pixels (default: 28) */\n logoHeight?: number;\n /** Link URL when clicking on branding */\n href?: string;\n}\n\nexport interface AppBarProps {\n /** Application name */\n appName?: string;\n /** Application domain/organization (shown above app name) */\n appDomain?: string;\n /** Application version (shown smaller) */\n appVersion?: string;\n /** Logo/branding configuration */\n branding?: AppBarBranding;\n \n // Control visibility\n /** Show theme controls (mode toggle, theme selector, color picker) */\n showThemeControls?: boolean;\n /** Show compact mode toggle */\n showCompactToggle?: boolean;\n /** Show color picker (only visible for transparent themes) */\n showColorPicker?: boolean;\n /** Show theme variant selector */\n showThemeSelector?: boolean;\n /** Show only mode toggle (hide other theme controls) */\n showModeOnly?: boolean;\n /** Use compact dropdown for theme controls instead of buttons */\n compactThemeControls?: boolean;\n \n // Default values (for when controls are hidden)\n /** Default theme to apply if not already set */\n defaultTheme?: ThemeVariant;\n /** Default mode to apply if not already set */\n defaultMode?: ThemeMode;\n /** Default compact mode */\n defaultCompactMode?: boolean;\n \n // Slots\n /** Left slot content (after branding, or only content when showBranding is false) */\n leftSlot?: ReactNode;\n /** Center slot content */\n centerSlot?: ReactNode;\n /** Right slot content (before theme controls) */\n rightSlot?: ReactNode;\n /** Optional title shown above right slot (e.g. \"Data\" for metrics/refresh section) */\n rightSectionTitle?: string;\n \n // Layout (operator / mission-control style)\n /** Show branding block (app name, logo). Set false for control-only bars. */\n showBranding?: boolean;\n /** Let center slot grow to fill space and stay centered (e.g. simulation controls). */\n centerGrow?: boolean;\n /** Bar variant: default (52px) or dense (44px min height, tighter padding/gap). */\n variant?: 'default' | 'dense';\n \n // Callbacks\n /** Callback when theme changes */\n onThemeChange?: (theme: ThemeVariant) => void;\n /** Callback when mode changes */\n onModeChange?: (mode: ThemeMode) => void;\n /** Callback when compact mode changes */\n onCompactModeChange?: (compact: boolean) => void;\n \n // Styling\n /** Custom className */\n className?: string;\n /** Custom height (default: 52) */\n height?: number;\n /** Sticky positioning */\n sticky?: boolean;\n}\n\n// =============================================================================\n// Theme Variants for Selector\n// =============================================================================\n\nconst THEME_VARIANTS: { key: ThemeVariant; label: string; tooltip: string }[] = [\n { key: 'hybrid', label: 'Zen (Hybrid)', tooltip: 'Default theme - Astro status colors with purple accents' },\n { key: 'purple-hue', label: 'Zen (Purple Hue)', tooltip: 'Purple accent theme - vibrant purple tones' },\n { key: 'astro', label: 'Astro', tooltip: 'Astro UX Design System theme - dark blue tones' },\n { key: 'transparent', label: 'Glass', tooltip: 'Glassmorphism theme - transparent with blur effects' },\n { key: 'transparent-bold', label: 'Bold', tooltip: 'Bold glass theme - stronger transparency effects' },\n { key: 'transparent-minimal', label: 'Minimal', tooltip: 'Minimal glass theme - ultra-subtle borders, clean surfaces' },\n];\n\n// =============================================================================\n// Mode Toggle Component\n// =============================================================================\n\ninterface ModeToggleProps {\n mode: ThemeMode;\n onToggle: () => void;\n tokens: any;\n}\n\nconst ModeToggle = memo(function ModeToggle({ mode, onToggle, tokens }: ModeToggleProps) {\n const isDark = mode === 'dark';\n const tooltipText = isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode';\n \n return (\n <Tooltip content={tooltipText} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={tooltipText}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n padding: 0,\n border: 'none',\n borderRadius: tokens.borderRadius.md,\n backgroundColor: 'rgba(255,255,255,0.08)',\n color: tokens.colors.text.primary,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.15)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.08)';\n }}\n >\n <AstroIcon \n name={isDark ? 'brightness-3' : 'brightness-high'} \n size={16} \n color={tokens.colors.text.primary}\n />\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Compact Mode Toggle Component\n// =============================================================================\n\ninterface CompactToggleProps {\n isCompact: boolean;\n onToggle: () => void;\n tokens: any;\n}\n\nconst CompactToggle = memo(function CompactToggle({ isCompact, onToggle, tokens }: CompactToggleProps) {\n const tooltipText = isCompact ? 'Switch to Expanded View' : 'Switch to Compact View';\n \n return (\n <Tooltip content={tooltipText} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={tooltipText}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n padding: 0,\n border: 'none',\n borderRadius: tokens.borderRadius.md,\n backgroundColor: isCompact ? 'rgba(139, 92, 246, 0.25)' : 'rgba(255,255,255,0.08)',\n color: isCompact ? '#a78bfa' : tokens.colors.text.primary,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = isCompact \n ? 'rgba(139, 92, 246, 0.35)' \n : 'rgba(255,255,255,0.15)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = isCompact \n ? 'rgba(139, 92, 246, 0.25)' \n : 'rgba(255,255,255,0.08)';\n }}\n >\n <AstroIcon \n name={isCompact ? 'view-comfy' : 'view-module'} \n size={16} \n color={isCompact ? '#a78bfa' : tokens.colors.text.primary}\n />\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Theme Selector Component\n// =============================================================================\n\ninterface ThemeSelectorProps {\n currentTheme: ThemeVariant;\n onSelect: (theme: ThemeVariant) => void;\n tokens: any;\n}\n\nconst ThemeSelector = memo(function ThemeSelector({ currentTheme, onSelect, tokens }: ThemeSelectorProps) {\n // WCAG AA: compute a safe background for selected theme buttons (white text)\n const safeAccentBg = useMemo(() => {\n const hex = tokens.colors.accent.primary.replace('#', '');\n if (hex.length < 6) return tokens.colors.accent.primary;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n if ((1.05 / (L + 0.05)) >= 4.5) return tokens.colors.accent.primary;\n const factor = 0.88;\n const dr = Math.min(255, Math.round(r * 255 * factor));\n const dg = Math.min(255, Math.round(g * 255 * factor));\n const db = Math.min(255, Math.round(b * 255 * factor));\n return `#${dr.toString(16).padStart(2, '0')}${dg.toString(16).padStart(2, '0')}${db.toString(16).padStart(2, '0')}`;\n }, [tokens.colors.accent.primary]);\n\n // Group themes for better organization\n const coreThemes = THEME_VARIANTS.filter(t => !t.key.startsWith('transparent'));\n const glassThemes = THEME_VARIANTS.filter(t => t.key.startsWith('transparent'));\n \n return (\n <div \n style={{ \n display: 'flex', \n alignItems: 'center', \n gap: 4,\n padding: 2,\n backgroundColor: 'rgba(255,255,255,0.05)',\n borderRadius: tokens.borderRadius.md,\n }}\n role=\"group\"\n aria-label=\"Theme selector\"\n >\n {/* Core themes */}\n {coreThemes.map(({ key, label, tooltip }) => (\n <Tooltip key={key} content={tooltip} placement=\"bottom\">\n <button\n onClick={() => onSelect(key)}\n aria-label={`Select ${label} theme`}\n style={{\n padding: '4px 10px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? safeAccentBg \n : 'transparent',\n color: currentTheme === key \n ? '#ffffff' \n : tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: currentTheme === key ? 500 : 400, // AstroUXDS: 500 (medium) not 600\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? safeAccentBg \n : 'transparent';\n }}\n >\n {label}\n </button>\n </Tooltip>\n ))}\n \n {/* Separator */}\n <div style={{ width: 1, height: 16, backgroundColor: 'rgba(255,255,255,0.15)', margin: '0 2px' }} />\n \n {/* Glass themes */}\n {glassThemes.map(({ key, label, tooltip }) => (\n <Tooltip key={key} content={tooltip} placement=\"bottom\">\n <button\n onClick={() => onSelect(key)}\n aria-label={`Select ${label} theme`}\n style={{\n padding: '4px 10px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? safeAccentBg \n : 'transparent',\n color: currentTheme === key \n ? '#ffffff' \n : tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: currentTheme === key ? 500 : 400, // AstroUXDS: 500 (medium) not 600\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? safeAccentBg \n : 'transparent';\n }}\n >\n {label}\n </button>\n </Tooltip>\n ))}\n </div>\n );\n});\n\n// =============================================================================\n// Accent Borders Toggle Component\n// =============================================================================\n\ninterface AccentBordersToggleProps {\n enabled: boolean;\n onToggle: () => void;\n tokens: any;\n}\n\nconst AccentBordersToggle = memo(function AccentBordersToggle({ enabled, onToggle, tokens }: AccentBordersToggleProps) {\n return (\n <Tooltip content={enabled ? 'Accent borders enabled - Click to disable' : 'Accent borders disabled - Click to enable'} placement=\"bottom\">\n <button\n onClick={onToggle}\n aria-label={enabled ? 'Disable accent borders' : 'Enable accent borders'}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n padding: '4px 8px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: enabled \n ? `${tokens.colors.accent.primary}30`\n : 'rgba(255,255,255,0.05)',\n color: enabled \n ? safeAccentText(tokens.colors.accent.primary) \n : tokens.colors.text.tertiary,\n fontSize: '0.625rem', // 10px in rem\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = enabled \n ? `${tokens.colors.accent.primary}40`\n : 'rgba(255,255,255,0.1)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = enabled \n ? `${tokens.colors.accent.primary}30`\n : 'rgba(255,255,255,0.05)';\n }}\n >\n <span style={{ \n width: 8, \n height: 8, \n borderRadius: 2,\n border: `2px solid ${enabled ? tokens.colors.accent.primary : tokens.colors.text.tertiary}`,\n backgroundColor: 'transparent',\n }} />\n <span>Borders</span>\n </button>\n </Tooltip>\n );\n});\n\n// =============================================================================\n// Theme Controls Dropdown (Compact)\n// =============================================================================\n\ninterface ThemeControlsDropdownProps {\n currentTheme: ThemeVariant;\n mode: ThemeMode;\n onThemeSelect: (theme: ThemeVariant) => void;\n onModeToggle: () => void;\n accentBordersEnabled?: boolean;\n onAccentBordersToggle?: () => void;\n showAccentBorders?: boolean;\n tokens: any;\n}\n\nconst ThemeControlsDropdown = memo(function ThemeControlsDropdown({\n currentTheme,\n mode,\n onThemeSelect,\n onModeToggle,\n accentBordersEnabled = true,\n onAccentBordersToggle,\n showAccentBorders = false,\n tokens,\n}: ThemeControlsDropdownProps) {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n \n // Close on click outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n \n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [isOpen]);\n \n // Get current theme label\n const currentLabel = THEME_VARIANTS.find(t => t.key === currentTheme)?.label || 'Theme';\n \n // Group themes\n const coreThemes = THEME_VARIANTS.filter(t => !t.key.startsWith('transparent'));\n const glassThemes = THEME_VARIANTS.filter(t => t.key.startsWith('transparent'));\n \n return (\n <div ref={dropdownRef} style={{ position: 'relative' }}>\n {/* Trigger Button */}\n <Tooltip content=\"Theme settings\" placement=\"bottom\">\n <button\n onClick={() => setIsOpen(!isOpen)}\n aria-label=\"Theme settings\"\n aria-expanded={isOpen}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '6px 10px',\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n backgroundColor: isOpen ? tokens.colors.background.elevated : 'transparent',\n color: tokens.colors.text.secondary,\n fontSize: '0.6875rem', // 11px in rem\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n }}\n onMouseEnter={(e) => {\n if (!isOpen) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n if (!isOpen) {\n e.currentTarget.style.backgroundColor = 'transparent';\n }\n }}\n >\n <AstroIcon name=\"settings\" size={14} color={tokens.colors.text.secondary} />\n <span>{currentLabel}</span>\n <AstroIcon \n name={isOpen ? 'arrow-up' : 'arrow-down'} \n size={10} \n color={tokens.colors.text.tertiary} \n />\n </button>\n </Tooltip>\n \n {/* Dropdown Panel */}\n {isOpen && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n right: 0,\n marginTop: 4,\n minWidth: 220,\n padding: tokens.spacing.sm,\n backgroundColor: tokens.colors.background.surface,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.lg,\n boxShadow: '0 8px 32px rgba(0,0,0,0.3)',\n zIndex: 1000,\n }}\n >\n {/* Theme Section */}\n <div style={{ marginBottom: tokens.spacing.sm }}>\n <div style={{ \n fontSize: '0.625rem', // 10px in rem \n color: tokens.colors.text.muted, \n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n marginBottom: 8,\n paddingLeft: 4,\n }}>\n Theme\n </div>\n \n {/* Core Themes */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {coreThemes.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => {\n onThemeSelect(key);\n setIsOpen(false);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent',\n color: currentTheme === key \n ? tokens.colors.accent.primary\n : tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n fontWeight: currentTheme === key ? 600 : 400,\n cursor: 'pointer',\n textAlign: 'left',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent';\n }}\n >\n {currentTheme === key && (\n <AstroIcon name=\"check\" size={12} color={tokens.colors.accent.primary} />\n )}\n <span style={{ marginLeft: currentTheme === key ? 0 : 20 }}>{label}</span>\n </button>\n ))}\n </div>\n \n {/* Separator */}\n <div style={{ \n height: 1, \n backgroundColor: tokens.colors.border.muted, \n margin: `${tokens.spacing.xs} 0`,\n }} />\n \n {/* Glass Themes */}\n <div style={{ \n fontSize: '0.625rem', // 10px in rem \n color: tokens.colors.text.muted, \n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n marginBottom: 6,\n paddingLeft: 4,\n }}>\n Glass\n </div>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {glassThemes.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => {\n onThemeSelect(key);\n setIsOpen(false);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent',\n color: currentTheme === key \n ? tokens.colors.accent.primary\n : tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n fontWeight: currentTheme === key ? 600 : 400,\n cursor: 'pointer',\n textAlign: 'left',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n if (currentTheme !== key) {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = currentTheme === key \n ? `${tokens.colors.accent.primary}20`\n : 'transparent';\n }}\n >\n {currentTheme === key && (\n <AstroIcon name=\"check\" size={12} color={tokens.colors.accent.primary} />\n )}\n <span style={{ marginLeft: currentTheme === key ? 0 : 20 }}>{label}</span>\n </button>\n ))}\n </div>\n </div>\n \n {/* Separator */}\n <div style={{ \n height: 1, \n backgroundColor: tokens.colors.border.muted, \n margin: `${tokens.spacing.sm} 0`,\n }} />\n \n {/* Options Section */}\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n {/* Mode Toggle */}\n <button\n onClick={() => {\n onModeToggle();\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: 'transparent',\n color: tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n cursor: 'pointer',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n }}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <AstroIcon \n name={mode === 'dark' ? 'brightness-high' : 'brightness-low'} \n size={14} \n color={tokens.colors.text.secondary} \n />\n {mode === 'dark' ? 'Dark Mode' : 'Light Mode'}\n </span>\n <div style={{\n width: 32,\n height: 18,\n borderRadius: 9,\n backgroundColor: mode === 'dark' ? tokens.colors.accent.primary : tokens.colors.border.default,\n padding: 2,\n transition: 'all 0.15s ease',\n }}>\n <div style={{\n width: 14,\n height: 14,\n borderRadius: '50%',\n backgroundColor: '#fff',\n transition: 'all 0.15s ease',\n transform: mode === 'dark' ? 'translateX(14px)' : 'translateX(0)',\n }} />\n </div>\n </button>\n \n {/* Accent Borders (only for glass themes) */}\n {showAccentBorders && onAccentBordersToggle && (\n <button\n onClick={onAccentBordersToggle}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 12px',\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: 'transparent',\n color: tokens.colors.text.primary,\n fontSize: '0.75rem', // 12px in rem\n cursor: 'pointer',\n transition: 'all 0.1s ease',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = tokens.colors.background.elevated;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n }}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <span style={{ \n width: 14, \n height: 14, \n borderRadius: 3,\n border: `2px solid ${tokens.colors.text.secondary}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}>\n {accentBordersEnabled && (\n <AstroIcon name=\"check\" size={10} color={tokens.colors.text.secondary} />\n )}\n </span>\n Accent Borders\n </span>\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n );\n});\n\n// =============================================================================\n// AppBar Component\n// =============================================================================\n\n// =============================================================================\n// Section header for right slot (operator-style \"Data\" header)\n// =============================================================================\n\ninterface SectionHeaderProps {\n title: string;\n tokens: { typography: any; spacing: any; colors: any; borders: any };\n}\n\nconst SectionHeader = memo(function SectionHeader({ title, tokens }: SectionHeaderProps) {\n return (\n <div style={{ textAlign: 'center', marginBottom: tokens.spacing.xs }}>\n <span style={{\n fontSize: tokens.typography.fontSize.xxs,\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.interactive?.default ?? tokens.colors.text.secondary,\n fontWeight: tokens.typography.fontWeight.bold,\n }}>\n {title}\n </span>\n <div style={{\n width: '100%',\n height: tokens.borders.width.thin,\n background: tokens.colors.interactive?.default ?? tokens.colors.text.secondary,\n opacity: 0.5,\n marginTop: tokens.spacing.xs,\n }} />\n </div>\n );\n});\n\n// =============================================================================\n// AppBar Component\n// =============================================================================\n\nexport const AppBar = memo(function AppBar({\n appName = 'Zendir',\n appDomain,\n appVersion,\n branding,\n showThemeControls = true,\n showCompactToggle = true,\n showColorPicker = true,\n showThemeSelector = true,\n showModeOnly = false,\n compactThemeControls = false,\n defaultTheme,\n defaultMode,\n defaultCompactMode,\n leftSlot,\n centerSlot,\n rightSlot,\n rightSectionTitle,\n showBranding = true,\n centerGrow = false,\n variant = 'default',\n onThemeChange,\n onModeChange,\n onCompactModeChange,\n className = '',\n height = 52,\n sticky = true,\n}: AppBarProps): React.ReactElement {\n const { tokens, theme, mode, setTheme, setMode, toggleMode } = useTheme();\n const { isMobile } = useBreakpoint();\n const displaySettings = useDisplaySettingsOptional();\n \n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n \n // Get compact mode from context or default\n const compactMode = displaySettings?.compactMode ?? defaultCompactMode ?? false;\n \n // Apply defaults on first render if specified\n React.useEffect(() => {\n if (defaultTheme && theme !== defaultTheme) {\n setTheme(defaultTheme);\n onThemeChange?.(defaultTheme);\n }\n if (defaultMode && mode !== defaultMode) {\n setMode(defaultMode);\n onModeChange?.(defaultMode);\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n \n // Handle mode toggle\n const handleModeToggle = () => {\n toggleMode();\n onModeChange?.(mode === 'dark' ? 'light' : 'dark');\n };\n \n // Handle theme change\n const handleThemeChange = (newTheme: ThemeVariant) => {\n setTheme(newTheme);\n onThemeChange?.(newTheme);\n };\n \n // Handle compact mode toggle\n const handleCompactToggle = () => {\n if (displaySettings) {\n displaySettings.toggleCompactMode();\n onCompactModeChange?.(!compactMode);\n } else {\n onCompactModeChange?.(!compactMode);\n }\n };\n\n // Variant-driven layout (operator / mission-control style)\n const isDense = variant === 'dense';\n const effectiveHeight = height ?? (isDense ? 44 : 52);\n const paddingH = isDense\n ? (isMobile ? tokens.spacing.xs : tokens.spacing.sm)\n : (isMobile ? tokens.spacing.md : tokens.spacing.lg);\n const paddingV = isDense ? tokens.spacing.xs : 0;\n const gap = isDense ? (isMobile ? tokens.spacing.sm : tokens.spacing.md) : tokens.spacing.sm;\n const minHeight = isDense && isMobile ? 44 : undefined;\n \n // Render branding section\n const renderBranding = () => {\n // Custom logo node\n if (branding?.logo) {\n const content = branding.logo;\n if (branding.href) {\n return (\n <a href={branding.href} style={{ display: 'flex', alignItems: 'center', textDecoration: 'none' }}>\n {content}\n </a>\n );\n }\n return <>{content}</>;\n }\n \n // Logo image URL\n if (branding?.logoUrl) {\n const img = (\n <img\n src={branding.logoUrl}\n alt={branding.logoAlt || appName}\n style={{\n height: branding.logoHeight || 28,\n width: 'auto',\n objectFit: 'contain',\n }}\n />\n );\n \n if (branding.href) {\n return (\n <a href={branding.href} style={{ display: 'flex', alignItems: 'center', textDecoration: 'none' }}>\n {img}\n </a>\n );\n }\n return img;\n }\n \n // Text branding\n return (\n <div style={{ display: 'flex', alignItems: 'baseline', gap: tokens.spacing.sm }}>\n {appDomain && (\n <span style={{\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: 400,\n color: tokens.colors.text.secondary,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}>\n {appDomain}\n </span>\n )}\n <span style={{\n fontSize: tokens.typography.fontSize.lg,\n fontWeight: 500, // AstroUXDS medium for app title\n color: tokens.colors.text.primary,\n }}>\n {appName}\n </span>\n {appVersion && (\n <span style={{\n fontSize: tokens.typography.fontSize.xs,\n color: tokens.colors.text.tertiary,\n }}>\n v{appVersion.replace(/^v/, '')}\n </span>\n )}\n </div>\n );\n };\n \n return (\n <header\n className={classNames('zendir-app-bar', className)}\n role=\"banner\"\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n height: effectiveHeight,\n padding: paddingV ? `${paddingV} ${paddingH}` : `0 ${paddingH}`,\n backgroundColor: isTransparentTheme \n ? 'rgba(0,0,0,0.4)' \n : tokens.colors.background.surface,\n ...(isTransparentTheme && { \n backdropFilter: 'blur(16px)', \n WebkitBackdropFilter: 'blur(16px)' as const \n }),\n borderBottom: tokens.borders.divider,\n color: tokens.colors.text.primary,\n fontFamily: tokens.typography.fontFamily.primary,\n gap,\n ...(minHeight && { minHeight }),\n ...(sticky && {\n position: 'sticky',\n top: 0,\n zIndex: 100,\n }),\n }}\n >\n {/* Left section: Branding (optional) + leftSlot */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: isDense ? gap : (isMobile ? tokens.spacing.md : tokens.spacing.lg),\n flex: '1 1 0',\n minWidth: 0,\n }}\n >\n {showBranding && renderBranding()}\n {leftSlot}\n </div>\n \n {/* Center section (optionally grows to fill and center content) */}\n {centerSlot && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flex: centerGrow ? '1 1 0' : '0 0 auto',\n minWidth: 0,\n }}\n >\n {centerSlot}\n </div>\n )}\n \n {/* Right section: optional section title + rightSlot + theme controls + compact toggle */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: tokens.spacing.sm,\n flex: '1 1 0',\n minWidth: 0,\n }}\n >\n {rightSectionTitle != null && rightSectionTitle !== '' ? (\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>\n <SectionHeader title={rightSectionTitle} tokens={tokens} />\n {rightSlot}\n </div>\n ) : (\n rightSlot\n )}\n \n {/* Theme Controls Group */}\n {showThemeControls && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: isMobile ? 6 : 8,\n marginLeft: isMobile ? tokens.spacing.xs : tokens.spacing.sm,\n minWidth: 0,\n }}\n >\n {/* Compact Dropdown Mode */}\n {compactThemeControls ? (\n <ThemeControlsDropdown\n currentTheme={theme}\n mode={mode}\n onThemeSelect={handleThemeChange}\n onModeToggle={handleModeToggle}\n accentBordersEnabled={displaySettings?.accentBorders ?? true}\n onAccentBordersToggle={displaySettings?.toggleAccentBorders}\n showAccentBorders={isTransparentTheme && !!displaySettings}\n tokens={tokens}\n />\n ) : (\n <>\n {/* Theme Selector */}\n {showThemeSelector && !showModeOnly && (\n <ThemeSelector\n currentTheme={theme}\n onSelect={handleThemeChange}\n tokens={tokens}\n />\n )}\n \n {/* Accent Borders Toggle (only for transparent themes) */}\n {!showModeOnly && isTransparentTheme && displaySettings && (\n <AccentBordersToggle\n enabled={displaySettings.accentBorders}\n onToggle={displaySettings.toggleAccentBorders}\n tokens={tokens}\n />\n )}\n \n {/* Color Picker (only for transparent themes) */}\n {showColorPicker && !showModeOnly && isTransparentTheme && displaySettings && (\n <ColorPickerPanel\n accentColor={displaySettings.accentColor}\n glassTint={displaySettings.glassTint}\n colorTarget={displaySettings.colorTarget}\n onAccentChange={displaySettings.setAccentColor}\n onGlassTintChange={displaySettings.setGlassTint}\n onTargetChange={displaySettings.setColorTarget}\n onColorSelect={displaySettings.setColor}\n compact\n />\n )}\n \n {/* Dark/Light Mode Toggle */}\n <ModeToggle \n mode={mode}\n onToggle={handleModeToggle}\n tokens={tokens}\n />\n </>\n )}\n </div>\n )}\n \n {/* Compact Mode Toggle */}\n {showCompactToggle && (\n <CompactToggle\n isCompact={compactMode}\n onToggle={handleCompactToggle}\n tokens={tokens}\n />\n )}\n </div>\n </header>\n );\n});\n\nexport default AppBar;\n"],"names":["ModeToggle","CompactToggle","ThemeSelector","AccentBordersToggle","ThemeControlsDropdown","SectionHeader","AppBar"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AstroIcon.js","sources":["../../../src/react/core/AstroIcon.tsx"],"sourcesContent":["/**\n * @zendir/ui - AstroIcon Component\n * \n * Wrapper around official @astrouxds/react RuxIcon component.\n * Provides all 400+ official Astro UXD icons with consistent API.\n * \n * SECURITY: Uses dynamic import with proper error handling.\n * FLEXIBILITY: Works with or without @astrouxds/react installed.\n * \n * @see https://www.astrouxds.com/components/icon-library/\n * \n * @example\n * ```tsx\n * import { AstroIcon } from '@zendir/ui/react';\n * \n * <AstroIcon name=\"satellite\" />\n * <AstroIcon name=\"antenna\" size=\"large\" />\n * <AstroIcon name=\"thermal\" color=\"#ff0000\" />\n * ```\n */\n\nimport React, { memo, useState, useEffect, useRef } from 'react';\nimport { useTheme } from '../theme';\nimport { Icon, isValidIconName } from './Icon';\n\n/**\n * All official Astro UXD icon names\n * @see https://www.astrouxds.com/components/icon-library/\n */\nexport type AstroIconName =\n // === Astro Space/Mission Icons (Custom by Rocket Communications) ===\n | 'altitude'\n | 'antenna'\n | 'antenna-off'\n | 'antenna-receive'\n | 'antenna-transmit'\n | 'equipment'\n | 'hardware'\n | 'mission'\n | 'netcom'\n | 'payload'\n | 'processor'\n | 'processor-alt'\n | 'propulsion-power'\n | 'satellite-off'\n | 'satellite-receive'\n | 'satellite-transmit'\n | 'solar'\n | 'thermal'\n // === Actions ===\n | 'settings'\n | 'search'\n | 'home'\n | 'info'\n | 'help'\n | 'delete'\n | 'edit'\n | 'lock'\n | 'lock-open'\n | 'visibility'\n | 'visibility-off'\n | 'refresh'\n | 'sync'\n | 'download'\n | 'upload'\n | 'fullscreen'\n | 'fullscreen-exit'\n | 'zoom-in'\n | 'zoom-out'\n | 'schedule'\n | 'timeline'\n | 'today'\n | 'event'\n | 'bookmark'\n | 'favorite'\n | 'star'\n | 'check-circle'\n | 'done'\n | 'done-all'\n | 'print'\n | 'open-in-new'\n | 'launch'\n | 'power-settings-new'\n | 'history'\n | 'cached'\n | 'build'\n | 'code'\n | 'dns'\n | 'language'\n | 'input'\n | 'label'\n | 'list'\n | 'track-changes'\n | 'verified-user'\n | 'work'\n // === Alerts ===\n | 'add-alert'\n | 'error'\n | 'error-outline'\n | 'notification-important'\n | 'warning'\n // === AV ===\n | 'play-arrow'\n | 'pause'\n | 'stop'\n | 'skip-next'\n | 'skip-previous'\n | 'fast-forward'\n | 'fast-rewind'\n | 'replay'\n | 'loop'\n | 'shuffle'\n | 'volume-up'\n | 'volume-down'\n | 'volume-off'\n | 'volume-mute'\n | 'mic'\n | 'mic-off'\n | 'videocam'\n | 'videocam-off'\n | 'movie'\n | 'music-note'\n | 'equalizer'\n | 'library-music'\n | 'queue-music'\n | 'subscriptions'\n | 'fiber-new'\n | 'fiber-manual-record'\n | 'fiber-smart-record'\n // === Communication ===\n | 'call'\n | 'call-end'\n | 'call-made'\n | 'call-received'\n | 'call-missed'\n | 'call-split'\n | 'chat'\n | 'chat-bubble'\n | 'comment'\n | 'contacts'\n | 'dialer-sip'\n | 'email'\n | 'forum'\n | 'import-export'\n | 'live-help'\n | 'location-off'\n | 'location-on'\n | 'message'\n | 'phone'\n | 'phonelink-erase'\n | 'phonelink-lock'\n | 'phonelink-ring'\n | 'portable-wifi-off'\n | 'present-to-all'\n | 'ring-volume'\n | 'screen-share'\n | 'speaker-phone'\n | 'stay-primary-landscape'\n | 'stay-primary-portrait'\n | 'stop-screen-share'\n | 'swap-calls'\n | 'textsms'\n | 'voicemail'\n | 'vpn-key'\n // === Content ===\n | 'add'\n | 'add-box'\n | 'add-circle'\n | 'add-circle-outline'\n | 'archive'\n | 'backspace'\n | 'block'\n | 'clear'\n | 'content-copy'\n | 'content-cut'\n | 'content-paste'\n | 'create'\n | 'drafts'\n | 'filter-list'\n | 'flag'\n | 'forward'\n | 'gesture'\n | 'inbox'\n | 'link'\n | 'link-off'\n | 'low-priority'\n | 'mail'\n | 'markunread'\n | 'move-to-inbox'\n | 'next-week'\n | 'redo'\n | 'remove'\n | 'remove-circle'\n | 'remove-circle-outline'\n | 'reply'\n | 'reply-all'\n | 'report'\n | 'save'\n | 'select-all'\n | 'send'\n | 'sort'\n | 'text-format'\n | 'unarchive'\n | 'undo'\n | 'weekend'\n // === Device ===\n | 'access-alarm'\n | 'access-alarms'\n | 'access-time'\n | 'add-alarm'\n | 'airplanemode-active'\n | 'airplanemode-inactive'\n | 'battery-20'\n | 'battery-30'\n | 'battery-50'\n | 'battery-60'\n | 'battery-80'\n | 'battery-90'\n | 'battery-alert'\n | 'battery-charging-20'\n | 'battery-charging-30'\n | 'battery-charging-50'\n | 'battery-charging-60'\n | 'battery-charging-80'\n | 'battery-charging-90'\n | 'battery-charging-full'\n | 'battery-full'\n | 'battery-std'\n | 'battery-unknown'\n | 'bluetooth'\n | 'bluetooth-connected'\n | 'bluetooth-disabled'\n | 'bluetooth-searching'\n | 'brightness-auto'\n | 'brightness-high'\n | 'brightness-low'\n | 'brightness-medium'\n | 'data-usage'\n | 'developer-mode'\n | 'devices'\n | 'dvr'\n | 'gps-fixed'\n | 'gps-not-fixed'\n | 'gps-off'\n | 'graphic-eq'\n | 'location-disabled'\n | 'location-searching'\n | 'network-cell'\n | 'network-wifi'\n | 'nfc'\n | 'screen-lock-landscape'\n | 'screen-lock-portrait'\n | 'screen-lock-rotation'\n | 'screen-rotation'\n | 'sd-storage'\n | 'settings-system-daydream'\n | 'signal-cellular-0-bar'\n | 'signal-cellular-1-bar'\n | 'signal-cellular-2-bar'\n | 'signal-cellular-3-bar'\n | 'signal-cellular-4-bar'\n | 'signal-cellular-connected-no-internet-0-bar'\n | 'signal-cellular-connected-no-internet-1-bar'\n | 'signal-cellular-connected-no-internet-2-bar'\n | 'signal-cellular-connected-no-internet-3-bar'\n | 'signal-cellular-connected-no-internet-4-bar'\n | 'signal-cellular-no-sim'\n | 'signal-cellular-null'\n | 'signal-cellular-off'\n | 'signal-wifi-0-bar'\n | 'signal-wifi-1-bar'\n | 'signal-wifi-1-bar-lock'\n | 'signal-wifi-2-bar'\n | 'signal-wifi-2-bar-lock'\n | 'signal-wifi-3-bar'\n | 'signal-wifi-3-bar-lock'\n | 'signal-wifi-4-bar'\n | 'signal-wifi-4-bar-lock'\n | 'signal-wifi-off'\n | 'storage'\n | 'usb'\n | 'wallpaper'\n | 'widgets'\n | 'wifi-lock'\n | 'wifi-tethering'\n // === Editor ===\n | 'attach-file'\n | 'attach-money'\n | 'border-all'\n | 'border-bottom'\n | 'border-clear'\n | 'border-color'\n | 'border-horizontal'\n | 'border-inner'\n | 'border-left'\n | 'border-outer'\n | 'border-right'\n | 'border-style'\n | 'border-top'\n | 'border-vertical'\n | 'bubble-chart'\n | 'drag-handle'\n | 'format-align-center'\n | 'format-align-justify'\n | 'format-align-left'\n | 'format-align-right'\n | 'format-bold'\n | 'format-clear'\n | 'format-color-fill'\n | 'format-color-reset'\n | 'format-color-text'\n | 'format-indent-decrease'\n | 'format-indent-increase'\n | 'format-italic'\n | 'format-line-spacing'\n | 'format-list-bulleted'\n | 'format-list-numbered'\n | 'format-paint'\n | 'format-quote'\n | 'format-shapes'\n | 'format-size'\n | 'format-strikethrough'\n | 'format-textdirection-l-to-r'\n | 'format-textdirection-r-to-l'\n | 'format-underlined'\n | 'functions'\n | 'highlight'\n | 'insert-chart'\n | 'insert-comment'\n | 'insert-drive-file'\n | 'insert-emoticon'\n | 'insert-invitation'\n | 'insert-link'\n | 'insert-photo'\n | 'linear-scale'\n | 'merge-type'\n | 'mode-comment'\n | 'mode-edit'\n | 'monetization-on'\n | 'money-off'\n | 'multiline-chart'\n | 'pie-chart'\n | 'pie-chart-outlined'\n | 'publish'\n | 'short-text'\n | 'show-chart'\n | 'space-bar'\n | 'strikethrough-s'\n | 'table-chart'\n | 'text-fields'\n | 'title'\n | 'vertical-align-bottom'\n | 'vertical-align-center'\n | 'vertical-align-top'\n | 'wrap-text'\n // === File ===\n | 'attachment'\n | 'cloud'\n | 'cloud-circle'\n | 'cloud-done'\n | 'cloud-download'\n | 'cloud-off'\n | 'cloud-queue'\n | 'cloud-upload'\n | 'create-new-folder'\n | 'file-download'\n | 'file-upload'\n | 'folder'\n | 'folder-open'\n | 'folder-shared'\n // === Hardware ===\n | 'cast'\n | 'cast-connected'\n | 'computer'\n | 'desktop-mac'\n | 'desktop-windows'\n | 'developer-board'\n | 'device-hub'\n | 'devices-other'\n | 'dock'\n | 'gamepad'\n | 'headset'\n | 'headset-mic'\n | 'keyboard'\n | 'keyboard-arrow-down'\n | 'keyboard-arrow-left'\n | 'keyboard-arrow-right'\n | 'keyboard-arrow-up'\n | 'keyboard-backspace'\n | 'keyboard-capslock'\n | 'keyboard-hide'\n | 'keyboard-return'\n | 'keyboard-tab'\n | 'keyboard-voice'\n | 'laptop'\n | 'laptop-chromebook'\n | 'laptop-mac'\n | 'laptop-windows'\n | 'memory'\n | 'mouse'\n | 'phone-android'\n | 'phone-iphone'\n | 'phonelink'\n | 'phonelink-off'\n | 'power-input'\n | 'router'\n | 'scanner'\n | 'security'\n | 'sim-card'\n | 'smartphone'\n | 'speaker'\n | 'speaker-group'\n | 'tablet'\n | 'tablet-android'\n | 'tablet-mac'\n | 'toys'\n | 'tv'\n | 'videogame-asset'\n | 'watch'\n // === Image ===\n | 'add-a-photo'\n | 'add-to-photos'\n | 'adjust'\n | 'assistant'\n | 'assistant-photo'\n | 'audiotrack'\n | 'blur-circular'\n | 'blur-linear'\n | 'blur-off'\n | 'blur-on'\n | 'brightness-1'\n | 'brightness-2'\n | 'brightness-3'\n | 'brightness-4'\n | 'brightness-5'\n | 'brightness-6'\n | 'brightness-7'\n | 'broken-image'\n | 'brush'\n | 'burst-mode'\n | 'camera'\n | 'camera-alt'\n | 'camera-front'\n | 'camera-rear'\n | 'camera-roll'\n | 'center-focus-strong'\n | 'center-focus-weak'\n | 'collections'\n | 'collections-bookmark'\n | 'color-lens'\n | 'colorize'\n | 'compare'\n | 'control-point'\n | 'control-point-duplicate'\n | 'crop'\n | 'crop-16-9'\n | 'crop-3-2'\n | 'crop-5-4'\n | 'crop-7-5'\n | 'crop-din'\n | 'crop-free'\n | 'crop-landscape'\n | 'crop-original'\n | 'crop-portrait'\n | 'crop-rotate'\n | 'crop-square'\n | 'dehaze'\n | 'details'\n | 'edit'\n | 'exposure'\n | 'exposure-neg-1'\n | 'exposure-neg-2'\n | 'exposure-plus-1'\n | 'exposure-plus-2'\n | 'exposure-zero'\n | 'filter'\n | 'filter-1'\n | 'filter-2'\n | 'filter-3'\n | 'filter-4'\n | 'filter-5'\n | 'filter-6'\n | 'filter-7'\n | 'filter-8'\n | 'filter-9'\n | 'filter-9-plus'\n | 'filter-b-and-w'\n | 'filter-center-focus'\n | 'filter-drama'\n | 'filter-frames'\n | 'filter-hdr'\n | 'filter-none'\n | 'filter-tilt-shift'\n | 'filter-vintage'\n | 'flare'\n | 'flash-auto'\n | 'flash-off'\n | 'flash-on'\n | 'flip'\n | 'gradient'\n | 'grain'\n | 'grid-off'\n | 'grid-on'\n | 'hdr-off'\n | 'hdr-on'\n | 'hdr-strong'\n | 'hdr-weak'\n | 'healing'\n | 'image'\n | 'image-aspect-ratio'\n | 'iso'\n | 'landscape'\n | 'leak-add'\n | 'leak-remove'\n | 'lens'\n | 'linked-camera'\n | 'looks'\n | 'looks-3'\n | 'looks-4'\n | 'looks-5'\n | 'looks-6'\n | 'looks-one'\n | 'looks-two'\n | 'loupe'\n | 'monochrome-photos'\n | 'movie-creation'\n | 'movie-filter'\n | 'music-note'\n | 'nature'\n | 'nature-people'\n | 'navigate-before'\n | 'navigate-next'\n | 'palette'\n | 'panorama'\n | 'panorama-fish-eye'\n | 'panorama-horizontal'\n | 'panorama-vertical'\n | 'panorama-wide-angle'\n | 'photo'\n | 'photo-album'\n | 'photo-camera'\n | 'photo-filter'\n | 'photo-library'\n | 'photo-size-select-actual'\n | 'photo-size-select-large'\n | 'photo-size-select-small'\n | 'picture-as-pdf'\n | 'portrait'\n | 'remove-red-eye'\n | 'rotate-90-degrees-ccw'\n | 'rotate-left'\n | 'rotate-right'\n | 'slideshow'\n | 'straighten'\n | 'style'\n | 'switch-camera'\n | 'switch-video'\n | 'tag-faces'\n | 'texture'\n | 'timelapse'\n | 'timer'\n | 'timer-10'\n | 'timer-3'\n | 'timer-off'\n | 'tonality'\n | 'transform'\n | 'tune'\n | 'view-comfy'\n | 'view-compact'\n | 'vignette'\n | 'wb-auto'\n | 'wb-cloudy'\n | 'wb-incandescent'\n | 'wb-iridescent'\n | 'wb-sunny'\n // === Maps ===\n | 'add-location'\n | 'beenhere'\n | 'directions'\n | 'directions-bike'\n | 'directions-boat'\n | 'directions-bus'\n | 'directions-car'\n | 'directions-railway'\n | 'directions-run'\n | 'directions-subway'\n | 'directions-transit'\n | 'directions-walk'\n | 'edit-location'\n | 'ev-station'\n | 'explore'\n | 'flight'\n | 'hotel'\n | 'layers'\n | 'layers-clear'\n | 'local-activity'\n | 'local-airport'\n | 'local-atm'\n | 'local-bar'\n | 'local-cafe'\n | 'local-car-wash'\n | 'local-convenience-store'\n | 'local-dining'\n | 'local-drink'\n | 'local-florist'\n | 'local-gas-station'\n | 'local-grocery-store'\n | 'local-hospital'\n | 'local-hotel'\n | 'local-laundry-service'\n | 'local-library'\n | 'local-mall'\n | 'local-movies'\n | 'local-offer'\n | 'local-parking'\n | 'local-pharmacy'\n | 'local-phone'\n | 'local-pizza'\n | 'local-play'\n | 'local-post-office'\n | 'local-printshop'\n | 'local-see'\n | 'local-shipping'\n | 'local-taxi'\n | 'map'\n | 'my-location'\n | 'navigation'\n | 'near-me'\n | 'person-pin'\n | 'person-pin-circle'\n | 'pin-drop'\n | 'place'\n | 'rate-review'\n | 'restaurant'\n | 'restaurant-menu'\n | 'satellite'\n | 'store-mall-directory'\n | 'streetview'\n | 'subway'\n | 'terrain'\n | 'traffic'\n | 'train'\n | 'tram'\n | 'transfer-within-a-station'\n | 'zoom-out-map'\n // === Navigation ===\n | 'apps'\n | 'arrow-back'\n | 'arrow-downward'\n | 'arrow-drop-down'\n | 'arrow-drop-down-circle'\n | 'arrow-drop-up'\n | 'arrow-forward'\n | 'arrow-upward'\n | 'cancel'\n | 'check'\n | 'chevron-left'\n | 'chevron-right'\n | 'close'\n | 'expand-less'\n | 'expand-more'\n | 'first-page'\n | 'fullscreen'\n | 'fullscreen-exit'\n | 'last-page'\n | 'menu'\n | 'more-horiz'\n | 'more-vert'\n | 'refresh'\n | 'subdirectory-arrow-left'\n | 'subdirectory-arrow-right'\n | 'unfold-less'\n | 'unfold-more'\n // === Notification ===\n | 'adb'\n | 'airline-seat-flat'\n | 'airline-seat-flat-angled'\n | 'airline-seat-individual-suite'\n | 'airline-seat-legroom-extra'\n | 'airline-seat-legroom-normal'\n | 'airline-seat-legroom-reduced'\n | 'airline-seat-recline-extra'\n | 'airline-seat-recline-normal'\n | 'bluetooth-audio'\n | 'confirmation-number'\n | 'disc-full'\n | 'do-not-disturb'\n | 'do-not-disturb-alt'\n | 'do-not-disturb-off'\n | 'do-not-disturb-on'\n | 'drive-eta'\n | 'enhanced-encryption'\n | 'event-available'\n | 'event-busy'\n | 'event-note'\n | 'folder-special'\n | 'live-tv'\n | 'mms'\n | 'more'\n | 'network-check'\n | 'network-locked'\n | 'no-encryption'\n | 'ondemand-video'\n | 'personal-video'\n | 'phone-bluetooth-speaker'\n | 'phone-forwarded'\n | 'phone-in-talk'\n | 'phone-locked'\n | 'phone-missed'\n | 'phone-paused'\n | 'power'\n | 'power-off'\n | 'priority-high'\n | 'sd-card'\n | 'sim-card-alert'\n | 'sms'\n | 'sms-failed'\n | 'sync'\n | 'sync-disabled'\n | 'sync-problem'\n | 'system-update'\n | 'tap-and-play'\n | 'time-to-leave'\n | 'vibration'\n | 'voice-chat'\n | 'vpn-lock'\n | 'wc'\n | 'wifi'\n | 'wifi-off'\n // === Places ===\n | 'ac-unit'\n | 'airport-shuttle'\n | 'all-inclusive'\n | 'beach-access'\n | 'business-center'\n | 'casino'\n | 'child-care'\n | 'child-friendly'\n | 'fitness-center'\n | 'free-breakfast'\n | 'golf-course'\n | 'hot-tub'\n | 'kitchen'\n | 'pool'\n | 'room-service'\n | 'rv-hookup'\n | 'smoke-free'\n | 'smoking-rooms'\n | 'spa'\n // === Social ===\n | 'cake'\n | 'domain'\n | 'group'\n | 'group-add'\n | 'location-city'\n | 'mood'\n | 'mood-bad'\n | 'notifications'\n | 'notifications-active'\n | 'notifications-none'\n | 'notifications-off'\n | 'notifications-paused'\n | 'pages'\n | 'party-mode'\n | 'people'\n | 'people-outline'\n | 'person'\n | 'person-add'\n | 'person-outline'\n | 'plus-one'\n | 'poll'\n | 'public'\n | 'school'\n | 'sentiment-dissatisfied'\n | 'sentiment-neutral'\n | 'sentiment-satisfied'\n | 'sentiment-very-dissatisfied'\n | 'sentiment-very-satisfied'\n | 'share'\n | 'whatshot'\n // === Toggle ===\n | 'check-box'\n | 'check-box-outline-blank'\n | 'indeterminate-check-box'\n | 'radio-button-checked'\n | 'radio-button-unchecked'\n | 'star'\n | 'star-border'\n | 'star-half'\n // === Space Operations Aliases (mapped to existing icons) ===\n // Orbit & Trajectory\n | 'orbit' | 'trajectory' | 'orbital'\n // Ground Operations\n | 'ground-station' | 'ground-control' | 'dish' | 'tracking-station'\n // Communication\n | 'uplink' | 'downlink' | 'command' | 'telemetry' | 'comms' | 'rf' | 'link' | 'signal' | 'data-link'\n // Satellite States\n | 'satellite' | 'spacecraft' | 'vehicle' | 'sat' | 'sc'\n // Power Systems\n | 'power' | 'battery' | 'energy' | 'solar-array' | 'solar-panel' | 'photovoltaic'\n // Propulsion\n | 'thruster' | 'engine' | 'burn' | 'maneuver' | 'delta-v' | 'rcs'\n // Thermal\n | 'temperature' | 'heat' | 'cooling' | 'heater'\n // Attitude & Control\n | 'attitude' | 'pointing' | 'orientation' | 'adcs' | 'gyro' | 'gyroscope'\n // Navigation\n | 'gps' | 'gnss' | 'position' | 'location' | 'ephemeris'\n // Status & Health\n | 'health' | 'status' | 'nominal' | 'anomaly' | 'fault' | 'safe-mode' | 'emergency' | 'critical' | 'caution'\n // Time & Scheduling\n | 'pass' | 'contact' | 'access' | 'window' | 'aos' | 'los' | 'eclipse' | 'sunlight' | 'shadow'\n // Subsystems\n | 'subsystem' | 'computer' | 'obc' | 'cdh' | 'flight-computer' | 'sensor' | 'instrument'\n // Launch & Deployment\n | 'launch' | 'rocket' | 'liftoff' | 'deployment' | 'deploy' | 'separation'\n // Operations\n | 'ops' | 'operations' | 'control' | 'automation' | 'sequence' | 'procedure'\n // Data & Storage\n | 'storage' | 'memory' | 'recorder' | 'playback' | 'record'\n // Monitoring\n | 'monitor' | 'dashboard' | 'console' | 'display'\n // Crew\n | 'astronaut' | 'crew' | 'eva'\n // Catch-all for any other icon (flexibility)\n | (string & {});\n\nexport type AstroIconSize = 'extra-small' | 'small' | 'normal' | 'large';\n\nexport interface AstroIconProps {\n /** Icon name from Astro UXD library */\n name: AstroIconName;\n /** Icon size preset or custom pixel value */\n size?: AstroIconSize | number;\n /** Custom color (hex, rgb, or CSS color) */\n color?: string;\n /** Accessibility label (required for screen readers) */\n label?: string;\n /** Additional CSS class */\n className?: string;\n /** Additional inline styles */\n style?: React.CSSProperties;\n /** Click handler */\n onClick?: () => void;\n /** Test ID for testing */\n 'data-testid'?: string;\n}\n\n// Size mapping for fallback mode\nconst SIZE_MAP: Record<AstroIconSize, number> = {\n 'extra-small': 16,\n 'small': 24,\n 'normal': 32,\n 'large': 48,\n};\n\n/**\n * Space Operations Icon Aliases\n * Maps common space terminology to existing Astro icons.\n * Provides better developer experience with intuitive naming.\n */\nconst ICON_ALIASES: Record<string, string> = {\n // === Orbit & Trajectory ===\n 'orbit': '360', // Orbital path\n 'trajectory': '360', // Flight path\n 'orbital': '360', // Orbital mechanics\n \n // === Ground Operations ===\n 'ground-station': 'antenna', // Ground station\n 'ground-control': 'antenna', // Mission control\n 'dish': 'antenna', // Antenna dish\n 'tracking-station': 'antenna', // Tracking facility\n \n // === Communication ===\n 'uplink': 'antenna-transmit', // Command upload\n 'downlink': 'antenna-receive', // Data download\n 'command': 'antenna-transmit', // Send command\n 'telemetry': 'antenna-receive', // Receive telemetry\n 'comms': 'netcom', // Communications\n 'rf': 'antenna', // Radio frequency\n 'link': 'netcom', // Communication link\n 'signal': 'antenna-transmit', // Signal transmission\n 'data-link': 'netcom', // Data link\n \n // === Satellite States ===\n 'satellite': 'satellite-transmit', // Default satellite (active)\n 'spacecraft': 'satellite-transmit', // Spacecraft\n 'vehicle': 'satellite-transmit', // Space vehicle\n 'sat': 'satellite-transmit', // Abbreviation\n 'sc': 'satellite-transmit', // Spacecraft abbreviation\n \n // === Power Systems ===\n 'power': 'propulsion-power', // Power system\n 'battery': 'battery-full', // Battery status\n 'energy': 'propulsion-power', // Energy management\n 'solar-array': 'solar', // Solar arrays\n 'solar-panel': 'solar', // Solar panels\n 'photovoltaic': 'solar', // PV system\n \n // === Propulsion ===\n 'thruster': 'propulsion-power', // Thruster\n 'engine': 'propulsion-power', // Engine\n 'burn': 'propulsion-power', // Burn/maneuver\n 'maneuver': 'propulsion-power', // Orbital maneuver\n 'delta-v': 'propulsion-power', // Delta-V\n 'rcs': 'propulsion-power', // Reaction control system\n \n // === Thermal ===\n 'temperature': 'thermal', // Temperature\n 'heat': 'thermal', // Heat management\n 'cooling': 'thermal', // Cooling system\n 'heater': 'thermal', // Heater\n \n // === Attitude & Control ===\n 'attitude': 'explore', // Attitude control\n 'pointing': 'explore', // Pointing direction\n 'orientation': 'explore', // Spacecraft orientation\n 'adcs': 'explore', // Attitude determination & control\n 'gyro': 'explore', // Gyroscope\n 'gyroscope': 'explore', // Gyroscope\n \n // === Navigation ===\n 'gps': 'gps-fixed', // GPS/GNSS\n 'gnss': 'gps-fixed', // Global navigation\n 'position': 'gps-fixed', // Position\n 'location': 'place', // Location\n 'ephemeris': 'timeline', // Ephemeris data\n \n // === Status & Health ===\n 'health': 'favorite', // System health\n 'status': 'info', // Status indicator\n 'nominal': 'check-circle', // Nominal/OK\n 'anomaly': 'error', // Anomaly detected\n 'fault': 'error', // Fault condition\n 'safe-mode': 'warning', // Safe mode\n 'emergency': 'warning', // Emergency\n 'critical': 'error', // Critical status\n 'caution': 'warning', // Caution\n \n // === Time & Scheduling ===\n 'pass': 'schedule', // Satellite pass\n 'contact': 'schedule', // Ground contact\n 'access': 'schedule', // Access window\n 'window': 'schedule', // Time window\n 'aos': 'schedule', // Acquisition of signal\n 'los': 'schedule', // Loss of signal\n 'eclipse': 'brightness-3', // Eclipse period\n 'sunlight': 'wb-sunny', // In sunlight\n 'shadow': 'brightness-3', // In shadow\n \n // === Subsystems ===\n 'subsystem': 'developer-board', // Generic subsystem\n 'computer': 'memory', // Onboard computer\n 'obc': 'memory', // Onboard computer\n 'cdh': 'memory', // Command & data handling\n 'flight-computer': 'memory', // Flight computer\n 'sensor': 'settings-input-component', // Sensor\n 'instrument': 'settings-input-component', // Instrument\n \n // === Launch & Deployment ===\n 'launch': 'flight-takeoff', // Launch\n 'rocket': 'flight-takeoff', // Rocket\n 'liftoff': 'flight-takeoff', // Liftoff\n 'deployment': 'open-in-new', // Deployment\n 'deploy': 'open-in-new', // Deploy\n 'separation': 'call-split', // Stage separation\n \n // === Operations ===\n 'ops': 'build', // Operations\n 'operations': 'build', // Operations\n 'control': 'tune', // Control\n 'automation': 'autorenew', // Automation\n 'sequence': 'playlist-play', // Command sequence\n 'procedure': 'list', // Procedure\n \n // === Data & Storage ===\n 'storage': 'sd-storage', // Data storage\n 'memory': 'memory', // Memory\n 'recorder': 'fiber-dvr', // Data recorder\n 'playback': 'play-arrow', // Playback\n 'record': 'fiber-manual-record', // Recording\n \n // === Monitoring ===\n 'monitor': 'desktop-mac', // Monitoring\n 'dashboard': 'dashboard', // Dashboard\n 'console': 'desktop-windows', // Console\n 'display': 'tv', // Display\n \n // === Crew (if applicable) ===\n 'astronaut': 'person', // Astronaut\n 'crew': 'group', // Crew\n 'eva': 'directions-walk', // Extra-vehicular activity\n};\n\n/**\n * Resolve icon alias to actual Astro icon name\n */\nfunction resolveIconAlias(name: string): string {\n return ICON_ALIASES[name] || name;\n}\n\n/**\n * Get all available icon aliases\n */\nexport function getIconAliases(): Record<string, string> {\n return { ...ICON_ALIASES };\n}\n\n/**\n * Check if an icon name is an alias\n */\nexport function isIconAlias(name: string): boolean {\n return name in ICON_ALIASES;\n}\n\n/**\n * All available Astro UXD icon names (official icons, not aliases)\n * This is the comprehensive list from the Astro UXD icon library\n */\nexport const ASTRO_ICON_NAMES: readonly string[] = [\n // === Astro Space/Mission Icons (Custom by Rocket Communications) ===\n 'altitude', 'antenna', 'antenna-off', 'antenna-receive', 'antenna-transmit',\n 'equipment', 'hardware', 'mission', 'netcom', 'payload',\n 'processor', 'processor-alt', 'propulsion-power', 'satellite-off',\n 'satellite-receive', 'satellite-transmit', 'solar', 'thermal',\n // === Actions ===\n 'settings', 'search', 'home', 'info', 'help', 'delete', 'edit',\n 'lock', 'lock-open', 'visibility', 'visibility-off', 'refresh', 'sync',\n 'download', 'upload', 'fullscreen', 'fullscreen-exit', 'zoom-in', 'zoom-out',\n 'schedule', 'timeline', 'today', 'event', 'bookmark', 'favorite', 'star',\n 'check-circle', 'done', 'done-all', 'print', 'open-in-new', 'launch',\n 'power-settings-new', 'history', 'cached', 'build', 'code', 'dns',\n 'language', 'input', 'label', 'list', 'track-changes', 'verified-user', 'work',\n // === Alerts ===\n 'add-alert', 'error', 'error-outline', 'notification-important', 'warning',\n // === AV ===\n 'play-arrow', 'pause', 'stop', 'skip-next', 'skip-previous',\n 'fast-forward', 'fast-rewind', 'replay', 'loop', 'shuffle',\n 'volume-up', 'volume-down', 'volume-off', 'volume-mute',\n 'mic', 'mic-off', 'videocam', 'videocam-off', 'movie',\n 'music-note', 'equalizer', 'library-music', 'queue-music',\n // === Communication ===\n 'call', 'call-end', 'chat', 'chat-bubble', 'chat-bubble-outline',\n 'comment', 'contacts', 'email', 'mail-outline', 'message',\n 'phone', 'voicemail', 'vpn-key', 'forum', 'live-help',\n 'location-on', 'location-off', 'business', 'import-contacts',\n // === Content ===\n 'add', 'remove', 'add-circle', 'add-circle-outline', 'remove-circle',\n 'remove-circle-outline', 'clear', 'content-copy', 'content-cut', 'content-paste',\n 'create', 'flag', 'forward', 'reply', 'reply-all',\n 'save', 'save-alt', 'select-all', 'send', 'undo', 'redo',\n 'filter-list', 'sort', 'drafts', 'link', 'link-off',\n // === Device ===\n 'battery-full', 'battery-charging-full', 'battery-alert', 'battery-unknown',\n 'bluetooth', 'bluetooth-disabled', 'bluetooth-connected', 'bluetooth-searching',\n 'brightness-1', 'brightness-2', 'brightness-3', 'brightness-4',\n 'brightness-5', 'brightness-6', 'brightness-7', 'brightness-auto',\n 'brightness-high', 'brightness-low', 'brightness-medium',\n 'gps-fixed', 'gps-not-fixed', 'gps-off', 'graphic-eq',\n 'network-cell', 'network-wifi', 'nfc', 'screen-rotation',\n 'sd-storage', 'settings-system-daydream', 'signal-cellular-4-bar',\n 'signal-cellular-connected-no-internet-4-bar', 'signal-cellular-no-sim',\n 'signal-cellular-null', 'signal-cellular-off', 'signal-wifi-4-bar',\n 'signal-wifi-4-bar-lock', 'signal-wifi-off', 'storage',\n 'usb', 'wifi', 'wifi-lock', 'wifi-off', 'wifi-tethering',\n // === Editor ===\n 'attach-file', 'attach-money', 'border-all', 'border-bottom',\n 'border-clear', 'border-color', 'border-horizontal', 'border-inner',\n 'border-left', 'border-outer', 'border-right', 'border-style',\n 'border-top', 'border-vertical', 'format-align-center', 'format-align-justify',\n 'format-align-left', 'format-align-right', 'format-bold', 'format-clear',\n 'format-color-fill', 'format-color-reset', 'format-color-text',\n 'format-indent-decrease', 'format-indent-increase', 'format-italic',\n 'format-line-spacing', 'format-list-bulleted', 'format-list-numbered',\n 'format-paint', 'format-quote', 'format-size', 'format-strikethrough',\n 'format-textdirection-l-to-r', 'format-textdirection-r-to-l',\n 'format-underlined', 'functions', 'insert-chart', 'insert-comment',\n 'insert-drive-file', 'insert-emoticon', 'insert-invitation', 'insert-link',\n 'insert-photo', 'merge-type', 'mode-comment', 'mode-edit',\n 'publish', 'short-text', 'space-bar', 'strikethrough-s',\n 'text-fields', 'title', 'vertical-align-bottom', 'vertical-align-center',\n 'vertical-align-top', 'wrap-text',\n // === File ===\n 'attachment', 'cloud', 'cloud-circle', 'cloud-done', 'cloud-download',\n 'cloud-off', 'cloud-queue', 'cloud-upload', 'create-new-folder',\n 'file-download', 'file-upload', 'folder', 'folder-open', 'folder-shared',\n // === Hardware ===\n 'cast', 'cast-connected', 'computer', 'desktop-mac', 'desktop-windows',\n 'developer-board', 'device-hub', 'devices-other', 'dock', 'gamepad',\n 'headset', 'headset-mic', 'keyboard', 'keyboard-arrow-down',\n 'keyboard-arrow-left', 'keyboard-arrow-right', 'keyboard-arrow-up',\n 'keyboard-backspace', 'keyboard-capslock', 'keyboard-hide',\n 'keyboard-return', 'keyboard-tab', 'keyboard-voice', 'laptop',\n 'laptop-chromebook', 'laptop-mac', 'laptop-windows', 'memory',\n 'mouse', 'phone-android', 'phone-iphone', 'phonelink', 'phonelink-off',\n 'power-input', 'router', 'scanner', 'security', 'sim-card',\n 'smartphone', 'speaker', 'speaker-group', 'tablet', 'tablet-android',\n 'tablet-mac', 'toys', 'tv', 'watch',\n // === Image ===\n 'add-a-photo', 'add-photo-alternate', 'add-to-photos', 'adjust',\n 'brightness-1', 'broken-image', 'brush', 'burst-mode', 'camera',\n 'camera-alt', 'camera-front', 'camera-rear', 'camera-roll',\n 'center-focus-strong', 'center-focus-weak', 'collections',\n 'collections-bookmark', 'color-lens', 'colorize', 'compare',\n 'control-point', 'control-point-duplicate', 'crop', 'crop-16-9',\n 'crop-3-2', 'crop-5-4', 'crop-7-5', 'crop-din', 'crop-free',\n 'crop-landscape', 'crop-original', 'crop-portrait', 'crop-rotate',\n 'crop-square', 'dehaze', 'details', 'edit', 'exposure',\n 'exposure-neg-1', 'exposure-neg-2', 'exposure-plus-1', 'exposure-plus-2',\n 'exposure-zero', 'filter', 'filter-1', 'filter-2', 'filter-3',\n 'filter-4', 'filter-5', 'filter-6', 'filter-7', 'filter-8', 'filter-9',\n 'filter-9-plus', 'filter-b-and-w', 'filter-center-focus', 'filter-drama',\n 'filter-frames', 'filter-hdr', 'filter-none', 'filter-tilt-shift',\n 'filter-vintage', 'flare', 'flash-auto', 'flash-off', 'flash-on',\n 'flip', 'gradient', 'grain', 'grid-off', 'grid-on', 'hdr-off',\n 'hdr-on', 'hdr-strong', 'hdr-weak', 'healing', 'image',\n 'image-aspect-ratio', 'image-search', 'iso', 'landscape', 'leak-add',\n 'leak-remove', 'lens', 'linked-camera', 'looks', 'looks-3',\n 'looks-4', 'looks-5', 'looks-6', 'looks-one', 'looks-two', 'loupe',\n 'monochrome-photos', 'movie-creation', 'movie-filter', 'music-off',\n 'music-on', 'navigate-before', 'navigate-next', 'nature', 'nature-people',\n 'palette', 'panorama', 'panorama-fish-eye', 'panorama-horizontal',\n 'panorama-vertical', 'panorama-wide-angle', 'photo', 'photo-album',\n 'photo-camera', 'photo-filter', 'photo-library', 'photo-size-select-actual',\n 'photo-size-select-large', 'photo-size-select-small', 'picture-as-pdf',\n 'portrait', 'remove-red-eye', 'rotate-90-degrees-ccw', 'rotate-left',\n 'rotate-right', 'slideshow', 'straighten', 'style', 'switch-camera',\n 'switch-video', 'tag-faces', 'texture', 'timelapse', 'timer',\n 'timer-10', 'timer-3', 'timer-off', 'tonality', 'transform', 'tune',\n 'view-comfy', 'view-compact', 'vignette', 'wb-auto', 'wb-cloudy',\n 'wb-incandescent', 'wb-iridescent', 'wb-sunny',\n // === Maps ===\n 'add-location', 'beenhere', 'directions', 'directions-bike',\n 'directions-boat', 'directions-bus', 'directions-car',\n 'directions-railway', 'directions-run', 'directions-subway',\n 'directions-transit', 'directions-walk', 'edit-location', 'ev-station',\n 'flight', 'hotel', 'layers', 'layers-clear', 'local-activity',\n 'local-airport', 'local-atm', 'local-bar', 'local-cafe', 'local-car-wash',\n 'local-convenience-store', 'local-dining', 'local-drink', 'local-florist',\n 'local-gas-station', 'local-grocery-store', 'local-hospital',\n 'local-hotel', 'local-laundry-service', 'local-library', 'local-mall',\n 'local-movies', 'local-offer', 'local-parking', 'local-pharmacy',\n 'local-phone', 'local-pizza', 'local-play', 'local-post-office',\n 'local-printshop', 'local-see', 'local-shipping', 'local-taxi', 'map',\n 'my-location', 'navigation', 'near-me', 'person-pin', 'person-pin-circle',\n 'pin-drop', 'place', 'rate-review', 'restaurant', 'restaurant-menu',\n 'satellite', 'store-mall-directory', 'streetview', 'subway', 'terrain',\n 'traffic', 'train', 'tram', 'transfer-within-a-station', 'zoom-out-map',\n // === Navigation ===\n 'apps', 'arrow-back', 'arrow-downward', 'arrow-drop-down',\n 'arrow-drop-down-circle', 'arrow-drop-up', 'arrow-forward', 'arrow-upward',\n 'cancel', 'check', 'chevron-left', 'chevron-right', 'close',\n 'expand-less', 'expand-more', 'first-page', 'last-page', 'menu',\n 'more-horiz', 'more-vert', 'subdirectory-arrow-left',\n 'subdirectory-arrow-right', 'unfold-less', 'unfold-more',\n // === Notification ===\n 'adb', 'airline-seat-flat', 'airline-seat-flat-angled',\n 'airline-seat-individual-suite', 'airline-seat-legroom-extra',\n 'airline-seat-legroom-normal', 'airline-seat-legroom-reduced',\n 'airline-seat-recline-extra', 'airline-seat-recline-normal',\n 'bluetooth-audio', 'confirmation-number', 'disc-full', 'do-not-disturb',\n 'do-not-disturb-alt', 'do-not-disturb-off', 'do-not-disturb-on',\n 'drive-eta', 'enhanced-encryption', 'event-available', 'event-busy',\n 'event-note', 'folder-special', 'live-tv', 'mms', 'more',\n 'network-check', 'network-locked', 'no-encryption', 'ondemand-video',\n 'personal-video', 'phone-bluetooth-speaker', 'phone-callback',\n 'phone-forwarded', 'phone-in-talk', 'phone-locked', 'phone-missed',\n 'phone-paused', 'power', 'power-off', 'priority-high', 'sd-card',\n 'sim-card-alert', 'sms', 'sms-failed', 'sync-disabled', 'sync-problem',\n 'system-update', 'tap-and-play', 'time-to-leave', 'vibration',\n 'voice-chat', 'vpn-lock', 'wc', 'wifi', 'notifications', 'notifications-active',\n 'notifications-none', 'notifications-off', 'notifications-paused',\n // === Places ===\n 'ac-unit', 'airport-shuttle', 'all-inclusive', 'beach-access',\n 'business-center', 'casino', 'child-care', 'child-friendly',\n 'fitness-center', 'free-breakfast', 'golf-course', 'hot-tub',\n 'kitchen', 'meeting-room', 'no-meeting-room', 'pool', 'room-service',\n 'rv-hookup', 'smoke-free', 'smoking-rooms', 'spa',\n // === Social ===\n 'cake', 'domain', 'group', 'group-add', 'location-city', 'mood',\n 'mood-bad', 'notifications', 'pages', 'party-mode', 'people',\n 'people-outline', 'person', 'person-add', 'person-outline',\n 'plus-one', 'poll', 'public', 'school', 'sentiment-dissatisfied',\n 'sentiment-neutral', 'sentiment-satisfied', 'sentiment-very-dissatisfied',\n 'sentiment-very-satisfied', 'share', 'thumb-down', 'thumb-up',\n 'whatshot',\n // === Toggle ===\n 'check-box', 'check-box-outline-blank', 'indeterminate-check-box',\n 'radio-button-checked', 'radio-button-unchecked', 'star', 'star-border', 'star-half',\n // === Additional common icons ===\n '360', 'autorenew', 'call-split', 'explore', 'extension', 'face',\n 'feedback', 'fiber-dvr', 'fiber-manual-record', 'fiber-new', 'fiber-pin',\n 'fiber-smart-record', 'get-app', 'grade', 'highlight-off', 'http',\n 'https', 'important-devices', 'lightbulb', 'lightbulb-outline',\n 'perm-camera-mic', 'perm-contact-calendar', 'perm-data-setting',\n 'perm-device-information', 'perm-identity', 'perm-media', 'perm-phone-msg',\n 'perm-scan-wifi', 'pets', 'playlist-add', 'playlist-add-check', 'playlist-play',\n 'pregnant-woman', 'query-builder', 'question-answer', 'receipt', 'record-voice-over',\n 'redeem', 'report-problem', 'restore', 'restore-page', 'room', 'rounded-corner',\n 'rowing', 'settings-applications', 'settings-backup-restore', 'settings-bluetooth',\n 'settings-brightness', 'settings-cell', 'settings-ethernet', 'settings-input-antenna',\n 'settings-input-component', 'settings-input-composite', 'settings-input-hdmi',\n 'settings-input-svideo', 'settings-overscan', 'settings-phone', 'settings-power',\n 'settings-remote', 'settings-voice', 'shop', 'shop-two', 'shopping-basket',\n 'shopping-cart', 'speaker-notes', 'speaker-notes-off', 'spellcheck', 'stars',\n 'subject', 'supervisor-account', 'swap-horiz', 'swap-vert', 'swap-vertical-circle',\n 'system-update-alt', 'tab', 'tab-unselected', 'theaters', 'thumb-down',\n 'thumb-up', 'thumbs-up-down', 'toc', 'toll', 'touch-app', 'translate',\n 'trending-down', 'trending-flat', 'trending-up', 'turned-in', 'turned-in-not',\n 'update', 'view-agenda', 'view-array', 'view-carousel', 'view-column',\n 'view-day', 'view-headline', 'view-list', 'view-module', 'view-quilt',\n 'view-stream', 'view-week', 'visibility', 'visibility-off', 'watch-later',\n 'youtube-searched-for', 'aspect-ratio', 'assignment', 'assignment-ind',\n 'assignment-late', 'assignment-return', 'assignment-returned', 'assignment-turned-in',\n 'backup', 'book', 'flight-takeoff', 'flight-land',\n] as const;\n\n/**\n * Get all available Astro icon names\n */\nexport function getAstroIconNames(): readonly string[] {\n return ASTRO_ICON_NAMES;\n}\n\n/**\n * Get all available icon names including aliases\n */\nexport function getAllIconNames(): { icons: readonly string[]; aliases: Record<string, string> } {\n return {\n icons: ASTRO_ICON_NAMES,\n aliases: ICON_ALIASES,\n };\n}\n\n// Global state for RuxIcon loading with subscription pattern\ntype LoadState = 'pending' | 'loading' | 'loaded' | 'failed';\nlet loadState: LoadState = 'pending';\nlet RuxIconComponent: React.ComponentType<any> | null = null;\nconst subscribers = new Set<() => void>();\n\nfunction subscribe(callback: () => void) {\n subscribers.add(callback);\n return () => subscribers.delete(callback);\n}\n\nfunction notifySubscribers() {\n subscribers.forEach(cb => cb());\n}\n\n/**\n * Safely load RuxIcon from @astrouxds/react\n * Returns null if not available\n */\nasync function loadRuxIcon(): Promise<React.ComponentType<any> | null> {\n if (loadState === 'loaded') {\n return RuxIconComponent;\n }\n \n if (loadState === 'failed') {\n return null;\n }\n \n if (loadState === 'loading') {\n // Wait for the existing load to complete\n return new Promise(resolve => {\n const unsub = subscribe(() => {\n unsub();\n resolve(RuxIconComponent);\n });\n });\n }\n \n loadState = 'loading';\n \n try {\n // Dynamic import for tree-shaking and optional dependency\n const astroModule = await import('@astrouxds/react');\n RuxIconComponent = astroModule.RuxIcon;\n loadState = 'loaded';\n notifySubscribers();\n return RuxIconComponent;\n } catch (err) {\n loadState = 'failed';\n notifySubscribers();\n return null;\n }\n}\n\n/**\n * Fallback icon component when @astrouxds/react is not installed.\n * Uses built-in Icon SVGs when the name matches, otherwise shows a generic placeholder.\n */\nfunction FallbackIcon({\n name,\n size,\n color,\n label,\n className,\n style,\n onClick,\n 'data-testid': testId,\n}: AstroIconProps) {\n const { tokens } = useTheme();\n const actualSize = typeof size === 'number' ? size : SIZE_MAP[size || 'normal'];\n\n if (isValidIconName(name)) {\n return (\n <Icon\n name={name}\n size={actualSize}\n color={color}\n className={className}\n aria-label={label || name}\n onClick={onClick}\n style={style}\n />\n );\n }\n\n return (\n <span\n role=\"img\"\n aria-label={label || name}\n className={className}\n data-testid={testId}\n onClick={onClick}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={onClick ? (e) => e.key === 'Enter' && onClick() : undefined}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: actualSize,\n height: actualSize,\n fontSize: actualSize * 0.4,\n fontFamily: tokens.typography.fontFamily.mono,\n fontWeight: 700,\n color: color || tokens.colors.status.off,\n backgroundColor: tokens.colors.background.surface,\n border: `1px solid ${tokens.colors.border.default}`,\n borderRadius: 4,\n cursor: onClick ? 'pointer' : 'default',\n userSelect: 'none',\n ...style,\n }}\n title={`Icon: ${name} (install @astrouxds/react for official icons)`}\n >\n <svg\n width={actualSize * 0.6}\n height={actualSize * 0.6}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" fill=\"currentColor\" />\n </svg>\n </span>\n );\n}\n\n/**\n * AstroIcon - Official Astro UXD Icons\n * \n * Displays icons from the official Astro UX Design System.\n * Automatically uses @astrouxds/react if installed, otherwise shows fallback.\n * \n * @example\n * ```tsx\n * // Basic usage\n * <AstroIcon name=\"satellite\" />\n * \n * // With size\n * <AstroIcon name=\"antenna\" size=\"large\" />\n * <AstroIcon name=\"thermal\" size={48} />\n * \n * // With color\n * <AstroIcon name=\"warning\" color=\"var(--color-status-caution)\" />\n * \n * // With click handler\n * <AstroIcon name=\"settings\" onClick={() => openSettings()} />\n * ```\n */\nexport const AstroIcon = memo(function AstroIcon(props: AstroIconProps) {\n const {\n name,\n size = 'normal',\n color,\n label,\n className,\n style,\n onClick,\n 'data-testid': testId,\n } = props;\n \n const [, forceUpdate] = useState(0);\n const [currentState, setCurrentState] = useState(loadState);\n const mountedRef = useRef(true);\n \n // Subscribe to load state changes and trigger loading\n useEffect(() => {\n mountedRef.current = true;\n \n // Subscribe to state changes\n const unsubscribe = subscribe(() => {\n if (mountedRef.current) {\n setCurrentState(loadState);\n forceUpdate(n => n + 1);\n }\n });\n \n // Trigger load if not yet started\n if (loadState === 'pending') {\n loadRuxIcon();\n }\n \n return () => {\n mountedRef.current = false;\n unsubscribe();\n };\n }, []);\n \n // If still loading, show a subtle placeholder\n if (currentState === 'pending' || currentState === 'loading') {\n const actualSize = typeof size === 'number' ? size : SIZE_MAP[size];\n return (\n <span\n style={{\n display: 'inline-block',\n width: actualSize,\n height: actualSize,\n ...style,\n }}\n className={className}\n aria-hidden=\"true\"\n />\n );\n }\n \n // Resolve icon alias to actual Astro icon name\n const resolvedName = resolveIconAlias(name);\n \n // If @astrouxds/react is available, use official RuxIcon\n if (RuxIconComponent) {\n const RuxIcon = RuxIconComponent;\n const pixelSize = typeof size === 'number' ? size : SIZE_MAP[size];\n \n // Map numeric size to closest Astro size preset for RuxIcon\n // This helps RuxIcon render at approximately the right size before CSS kicks in\n const getClosestAstroSize = (px: number): AstroIconSize => {\n if (px <= 18) return 'extra-small'; // 16px\n if (px <= 28) return 'small'; // 24px\n if (px <= 40) return 'normal'; // 32px\n return 'large'; // 48px\n };\n const astroSize = typeof size === 'number' ? getClosestAstroSize(size) : size;\n \n // Default to off/grey color if no color specified\n const iconColor = color || 'var(--color-status-off, #a4abb6)';\n \n return (\n <span\n role=\"img\"\n aria-label={label || name}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: pixelSize,\n height: pixelSize,\n minWidth: pixelSize,\n minHeight: pixelSize,\n flexShrink: 0,\n // Don't use overflow:hidden - it clips the icon SVG\n ...style,\n }}\n className={className}\n >\n <RuxIcon\n icon={resolvedName}\n size={astroSize}\n label={label || name}\n data-testid={testId}\n onClick={onClick}\n style={{\n // Force the RuxIcon to respect our exact pixel size\n '--iconDefaultSize': `${pixelSize}px`,\n '--iconSize': `${pixelSize}px`,\n '--size': `${pixelSize}px`,\n color: iconColor,\n width: `${pixelSize}px`,\n height: `${pixelSize}px`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...(onClick && { cursor: 'pointer' }),\n } as React.CSSProperties}\n />\n </span>\n );\n }\n \n // Fallback when @astrouxds/react is not installed\n return <FallbackIcon {...props} name={resolvedName} />;\n});\n\n/**\n * Check if @astrouxds/react is available\n * Useful for conditional rendering or feature detection\n */\nexport function isAstroIconsAvailable(): boolean {\n return loadState === 'loaded' && RuxIconComponent !== null;\n}\n\n/**\n * Preload the Astro icons module\n * Call this early in your app to ensure icons are ready\n */\nexport async function preloadAstroIcons(): Promise<boolean> {\n const component = await loadRuxIcon();\n return component !== null;\n}\n\nexport default AstroIcon;\n"],"names":["AstroIcon"],"mappings":";;;;AAi1BA,MAAM,WAA0C;AAAA,EAC9C,eAAe;AAAA,EACf,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AACX;AAOA,MAAM,eAAuC;AAAA;AAAA,EAE3C,SAAS;AAAA;AAAA,EACT,cAAc;AAAA;AAAA,EACd,WAAW;AAAA;AAAA;AAAA,EAGX,kBAAkB;AAAA;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAClB,QAAQ;AAAA;AAAA,EACR,oBAAoB;AAAA;AAAA;AAAA,EAGpB,UAAU;AAAA;AAAA,EACV,YAAY;AAAA;AAAA,EACZ,WAAW;AAAA;AAAA,EACX,aAAa;AAAA;AAAA,EACb,SAAS;AAAA;AAAA,EACT,MAAM;AAAA;AAAA,EACN,QAAQ;AAAA;AAAA,EACR,UAAU;AAAA;AAAA,EACV,aAAa;AAAA;AAAA;AAAA,EAGb,aAAa;AAAA;AAAA,EACb,cAAc;AAAA;AAAA,EACd,WAAW;AAAA;AAAA,EACX,OAAO;AAAA;AAAA,EACP,MAAM;AAAA;AAAA;AAAA,EAGN,SAAS;AAAA;AAAA,EACT,WAAW;AAAA;AAAA,EACX,UAAU;AAAA;AAAA,EACV,eAAe;AAAA;AAAA,EACf,eAAe;AAAA;AAAA,EACf,gBAAgB;AAAA;AAAA;AAAA,EAGhB,YAAY;AAAA;AAAA,EACZ,UAAU;AAAA;AAAA,EACV,QAAQ;AAAA;AAAA,EACR,YAAY;AAAA;AAAA,EACZ,WAAW;AAAA;AAAA,EACX,OAAO;AAAA;AAAA;AAAA,EAGP,eAAe;AAAA;AAAA,EACf,QAAQ;AAAA;AAAA,EACR,WAAW;AAAA;AAAA,EACX,UAAU;AAAA;AAAA;AAAA,EAGV,YAAY;AAAA;AAAA,EACZ,YAAY;AAAA;AAAA,EACZ,eAAe;AAAA;AAAA,EACf,QAAQ;AAAA;AAAA,EACR,QAAQ;AAAA;AAAA,EACR,aAAa;AAAA;AAAA;AAAA,EAGb,OAAO;AAAA;AAAA,EACP,QAAQ;AAAA;AAAA,EACR,YAAY;AAAA;AAAA,EACZ,YAAY;AAAA;AAAA,EACZ,aAAa;AAAA;AAAA;AAAA,EAGb,UAAU;AAAA;AAAA,EACV,UAAU;AAAA;AAAA,EACV,WAAW;AAAA;AAAA,EACX,WAAW;AAAA;AAAA,EACX,SAAS;AAAA;AAAA,EACT,aAAa;AAAA;AAAA,EACb,aAAa;AAAA;AAAA,EACb,YAAY;AAAA;AAAA,EACZ,WAAW;AAAA;AAAA;AAAA,EAGX,QAAQ;AAAA;AAAA,EACR,WAAW;AAAA;AAAA,EACX,UAAU;AAAA;AAAA,EACV,UAAU;AAAA;AAAA,EACV,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,WAAW;AAAA;AAAA,EACX,YAAY;AAAA;AAAA,EACZ,UAAU;AAAA;AAAA;AAAA,EAGV,aAAa;AAAA;AAAA,EACb,YAAY;AAAA;AAAA,EACZ,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,mBAAmB;AAAA;AAAA,EACnB,UAAU;AAAA;AAAA,EACV,cAAc;AAAA;AAAA;AAAA,EAGd,UAAU;AAAA;AAAA,EACV,UAAU;AAAA;AAAA,EACV,WAAW;AAAA;AAAA,EACX,cAAc;AAAA;AAAA,EACd,UAAU;AAAA;AAAA,EACV,cAAc;AAAA;AAAA;AAAA,EAGd,OAAO;AAAA;AAAA,EACP,cAAc;AAAA;AAAA,EACd,WAAW;AAAA;AAAA,EACX,cAAc;AAAA;AAAA,EACd,YAAY;AAAA;AAAA,EACZ,aAAa;AAAA;AAAA;AAAA,EAGb,WAAW;AAAA;AAAA,EACX,UAAU;AAAA;AAAA,EACV,YAAY;AAAA;AAAA,EACZ,YAAY;AAAA;AAAA,EACZ,UAAU;AAAA;AAAA;AAAA,EAGV,WAAW;AAAA;AAAA,EACX,aAAa;AAAA;AAAA,EACb,WAAW;AAAA;AAAA,EACX,WAAW;AAAA;AAAA;AAAA,EAGX,aAAa;AAAA;AAAA,EACb,QAAQ;AAAA;AAAA,EACR,OAAO;AAAA;AACT;AAKA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO,aAAa,IAAI,KAAK;AAC/B;AA6OA,IAAI,YAAuB;AAC3B,IAAI,mBAAoD;AACxD,MAAM,kCAAkB,IAAA;AAExB,SAAS,UAAU,UAAsB;AACvC,cAAY,IAAI,QAAQ;AACxB,SAAO,MAAM,YAAY,OAAO,QAAQ;AAC1C;AAEA,SAAS,oBAAoB;AAC3B,cAAY,QAAQ,CAAA,OAAM,GAAA,CAAI;AAChC;AAMA,eAAe,cAAwD;AACrE,MAAI,cAAc,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,WAAW;AAE3B,WAAO,IAAI,QAAQ,CAAA,YAAW;AAC5B,YAAM,QAAQ,UAAU,MAAM;AAC5B,cAAA;AACA,gBAAQ,gBAAgB;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,cAAY;AAEZ,MAAI;AAEF,UAAM,cAAc,MAAM,OAAO,kBAAkB;AACnD,uBAAmB,YAAY;AAC/B,gBAAY;AACZ,sBAAA;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,gBAAY;AACZ,sBAAA;AACA,WAAO;AAAA,EACT;AACF;AAMA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAmB;AACjB,QAAM,EAAE,OAAA,IAAW,SAAA;AACnB,QAAM,aAAa,OAAO,SAAS,WAAW,OAAO,SAAS,QAAQ,QAAQ;AAE9E,MAAI,gBAAgB,IAAI,GAAG;AACzB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,cAAY,SAAS;AAAA,QACrB;AAAA,QACA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MACrB;AAAA,MACA,eAAa;AAAA,MACb;AAAA,MACA,UAAU,UAAU,IAAI;AAAA,MACxB,WAAW,UAAU,CAAC,MAAM,EAAE,QAAQ,WAAW,YAAY;AAAA,MAC7D,OAAO;AAAA,QACL,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,UAAU,aAAa;AAAA,QACvB,YAAY,OAAO,WAAW,WAAW;AAAA,QACzC,YAAY;AAAA,QACZ,OAAO,SAAS,OAAO,OAAO,OAAO;AAAA,QACrC,iBAAiB,OAAO,OAAO,WAAW;AAAA,QAC1C,QAAQ,aAAa,OAAO,OAAO,OAAO,OAAO;AAAA,QACjD,cAAc;AAAA,QACd,QAAQ,UAAU,YAAY;AAAA,QAC9B,YAAY;AAAA,QACZ,GAAG;AAAA,MAAA;AAAA,MAEL,OAAO,SAAS,IAAI;AAAA,MAEpB,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAO,aAAa;AAAA,UACpB,QAAQ,aAAa;AAAA,UACrB,SAAQ;AAAA,UACR,MAAK;AAAA,UAEL,UAAA;AAAA,YAAA,oBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM;AAAA,YACpG,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI,MAAK,eAAA,CAAe;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACpD;AAAA,EAAA;AAGN;AAwBO,MAAM,YAAY,KAAK,SAASA,WAAU,OAAuB;AACtE,QAAM;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EAAA,IACb;AAEJ,QAAM,GAAG,WAAW,IAAI,SAAS,CAAC;AAClC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,SAAS;AAC1D,QAAM,aAAa,OAAO,IAAI;AAG9B,YAAU,MAAM;AACd,eAAW,UAAU;AAGrB,UAAM,cAAc,UAAU,MAAM;AAClC,UAAI,WAAW,SAAS;AACtB,wBAAgB,SAAS;AACzB,oBAAY,CAAA,MAAK,IAAI,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AAGD,QAAI,cAAc,WAAW;AAC3B,kBAAA;AAAA,IACF;AAEA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,kBAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAA,CAAE;AAGL,MAAI,iBAAiB,aAAa,iBAAiB,WAAW;AAC5D,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,SAAS,IAAI;AAClE,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,OAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,GAAG;AAAA,QAAA;AAAA,QAEL;AAAA,QACA,eAAY;AAAA,MAAA;AAAA,IAAA;AAAA,EAGlB;AAGA,QAAM,eAAe,iBAAiB,IAAI;AAG1C,MAAI,kBAAkB;AACpB,UAAM,UAAU;AAChB,UAAM,YAAY,OAAO,SAAS,WAAW,OAAO,SAAS,IAAI;AAIjE,UAAM,sBAAsB,CAAC,OAA8B;AACzD,UAAI,MAAM,GAAI,QAAO;AACrB,UAAI,MAAM,GAAI,QAAO;AACrB,UAAI,MAAM,GAAI,QAAO;AACrB,aAAO;AAAA,IACT;AACA,UAAM,YAAY,OAAO,SAAS,WAAW,oBAAoB,IAAI,IAAI;AAGzE,UAAM,YAAY,SAAS;AAE3B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY,SAAS;AAAA,QACrB,OAAO;AAAA,UACL,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,gBAAgB;AAAA,UAChB,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,WAAW;AAAA,UACX,YAAY;AAAA;AAAA,UAEZ,GAAG;AAAA,QAAA;AAAA,QAEL;AAAA,QAEA,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO,SAAS;AAAA,YAChB,eAAa;AAAA,YACb;AAAA,YACA,OAAO;AAAA;AAAA,cAEL,qBAAqB,GAAG,SAAS;AAAA,cACjC,cAAc,GAAG,SAAS;AAAA,cAC1B,UAAU,GAAG,SAAS;AAAA,cACtB,OAAO;AAAA,cACP,OAAO,GAAG,SAAS;AAAA,cACnB,QAAQ,GAAG,SAAS;AAAA,cACpB,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,GAAI,WAAW,EAAE,QAAQ,UAAA;AAAA,YAAU;AAAA,UACrC;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAGN;AAGA,SAAO,oBAAC,cAAA,EAAc,GAAG,OAAO,MAAM,cAAc;AACtD,CAAC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sources":["../../../src/react/core/Badge.tsx"],"sourcesContent":["/**\n * @zendir/ui - Badge/Tag Component\n * \n * Badge and tag following Astro UX Design System.\n * \n * @example\n * ```tsx\n * <Badge>Default</Badge>\n * <Badge status=\"normal\">Active</Badge>\n * <Badge variant=\"outline\" status=\"caution\">Warning</Badge>\n * <Badge icon={<Icon name=\"satellite\" size={12} />}>Satellite</Badge>\n * ```\n */\n\nimport React, { memo } from 'react';\nimport { useTheme } from '../theme';\nimport { classNames, type StatusLevel } from '../utils';\n\nexport type BadgeVariant = 'filled' | 'outline' | 'subtle';\nexport type BadgeSize = 'small' | 'medium';\n\nexport interface BadgeProps {\n /** Visual variant */\n variant?: BadgeVariant;\n /** Size */\n size?: BadgeSize;\n /** Status-based coloring */\n status?: StatusLevel;\n /** Custom color */\n color?: string;\n /** Icon before text */\n icon?: React.ReactNode;\n /** Removable (shows X button) */\n onRemove?: () => void;\n /** Children */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n /** Click handler */\n onClick?: () => void;\n}\n\nexport const Badge = memo(function Badge({\n variant = 'filled',\n size = 'small',\n status,\n color,\n icon,\n onRemove,\n children,\n className = '',\n onClick,\n}: BadgeProps): React.ReactElement {\n const { tokens } = useTheme();\n \n // Determine base color\n const baseColor = color ?? (status ? tokens.colors.status[status] : tokens.colors.accent.primary);\n\n // WCAG AA: choose black or white text based on background luminance\n // Uses relative luminance formula (WCAG 2.1 SC 1.4.3)\n const getContrastText = (bg: string): string => {\n const hex = bg.replace('#', '');\n if (hex.length < 6) return '#ffffff';\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n // If luminance > 0.179, background is bright → use dark text\n return L > 0.179 ? '#000000' : '#ffffff';\n };\n \n const sizeConfig = {\n small: {\n padding: `2px ${tokens.spacing.xs}`,\n fontSize: tokens.typography.fontSize.xs,\n height: '20px',\n },\n medium: {\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\n fontSize: tokens.typography.fontSize.sm,\n height: '26px',\n },\n };\n const config = sizeConfig[size];\n \n const variantStyles: Record<BadgeVariant, React.CSSProperties> = {\n filled: {\n backgroundColor: baseColor,\n color: getContrastText(baseColor), // WCAG AA: auto dark/light text based on bg luminance\n border: 'none',\n },\n outline: {\n backgroundColor: 'transparent',\n color: baseColor,\n border: `1px solid ${baseColor}`,\n },\n subtle: {\n backgroundColor: `${baseColor}20`,\n color: baseColor,\n border: 'none',\n },\n };\n \n const isInteractive = !!onClick;\n \n return (\n <span\n className={classNames('zendir-badge', `zendir-badge--${variant}`, className)}\n onClick={onClick}\n role={isInteractive ? 'button' : undefined}\n tabIndex={isInteractive ? 0 : undefined}\n onKeyDown={onClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } } : undefined}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: tokens.spacing.xs,\n padding: config.padding,\n height: config.height,\n fontSize: config.fontSize,\n fontWeight: tokens.typography.fontWeight.medium,\n fontFamily: tokens.typography.fontFamily.primary,\n borderRadius: tokens.borderRadius.full,\n cursor: isInteractive ? 'pointer' : 'default',\n transition: `all ${tokens.animation.fast}`,\n whiteSpace: 'nowrap',\n ...variantStyles[variant],\n }}\n >\n {icon}\n {children}\n {onRemove && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n aria-label=\"Remove\"\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n /* Visual size stays 14px; touch target expanded to 24px via padding (WCAG 2.5.8) */\n padding: '5px',\n marginLeft: tokens.spacing.xxs,\n width: '24px',\n height: '24px',\n backgroundColor: 'transparent',\n border: 'none',\n borderRadius: '50%',\n cursor: 'pointer',\n color: 'inherit',\n opacity: 0.7,\n transition: `opacity ${tokens.animation.fast}`,\n boxSizing: 'border-box',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.opacity = '1';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.opacity = '0.7';\n }}\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" />\n </svg>\n </button>\n )}\n </span>\n );\n});\n\nexport default Badge;\n"],"names":["Badge"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","sources":["../../../src/react/core/Button.tsx"],"sourcesContent":["/**\n * @zendir/ui - Button Component\n * \n * Enterprise-grade button following Astro UX Design System with Zendir purple accents.\n * \n * Astro UX Compliance:\n * - Three visual variants: primary, secondary, borderless\n * - Size variants: small, medium, large\n * - Sentence case capitalization\n * - Focus ring for accessibility (WCAG 2.1 AA)\n * - Right-align in button groups (primary on right)\n * \n * Zendir Enhancements:\n * - Purple accent colors\n * - Smooth micro-interactions\n * - Subtle glow effects\n * - Modern glassmorphism hover states\n * \n * @example\n * ```tsx\n * <Button>Primary Action</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button variant=\"borderless\" icon={<Icon name=\"settings\" />}>Settings</Button>\n * <Button loading>Saving...</Button>\n * ```\n */\n\nimport React, { memo, forwardRef, useState, useMemo, useRef, useLayoutEffect, useCallback } from 'react';\nimport ReactDOM from 'react-dom';\nimport { useTheme } from '../theme';\nimport { classNames, safeAccentText } from '../utils';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'borderless';\nexport type ButtonSize = 'small' | 'medium' | 'large';\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual variant */\n variant?: ButtonVariant;\n /** Size variant */\n size?: ButtonSize;\n /** Icon to display before text */\n icon?: React.ReactNode;\n /** Icon to display after text */\n iconEnd?: React.ReactNode;\n /** Show loading spinner */\n loading?: boolean;\n /** Full width button */\n fullWidth?: boolean;\n /** Tooltip text (shows on hover) */\n tooltip?: string;\n /** Custom className */\n className?: string;\n /** Children */\n children?: React.ReactNode;\n}\n\nexport const Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n variant = 'primary',\n size = 'medium',\n icon,\n iconEnd,\n loading = false,\n fullWidth = false,\n tooltip,\n disabled,\n className = '',\n children,\n style,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ...rest\n },\n ref\n): React.ReactElement {\n const { tokens, theme } = useTheme();\n \n // Spin keyframe is used by loading spinner only (injected there)\n\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const [isHovered, setIsHovered] = useState(false);\n const [isActive, setIsActive] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [showTooltip, setShowTooltip] = useState(false);\n \n const isDisabled = disabled || loading;\n const transparentDefault = tokens.colors.interactive.transparentDefault;\n const transparentHover = tokens.colors.interactive.transparentHover;\n\n // WCAG AA: compute a button-safe background from accent.primary\n // If white text (#fff) on the accent bg doesn't meet 4.5:1, darken it proportionally.\n const safeAccentBg = useMemo(() => {\n const hex = tokens.colors.accent.primary.replace('#', '');\n if (hex.length < 6) return tokens.colors.accent.primary;\n const toLinear = (c: number) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const L = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);\n if ((1.05 / (L + 0.05)) >= 4.5) return tokens.colors.accent.primary;\n // Darken just enough: target L for ~4.7:1 contrast with white\n const factor = 0.88;\n const dr = Math.min(255, Math.round(r * 255 * factor));\n const dg = Math.min(255, Math.round(g * 255 * factor));\n const db = Math.min(255, Math.round(b * 255 * factor));\n return `#${dr.toString(16).padStart(2, '0')}${dg.toString(16).padStart(2, '0')}${db.toString(16).padStart(2, '0')}`;\n }, [tokens.colors.accent.primary]);\n\n // WCAG AA safe accent for use as foreground text on dark backgrounds\n const safeAccentFg = useMemo(() => safeAccentText(tokens.colors.accent.primary), [tokens.colors.accent.primary]);\n \n const sizeConfig = {\n small: {\n padding: `${tokens.spacing.xxs} ${tokens.spacing.smd}`,\n fontSize: tokens.typography.fontSize.xs,\n height: '24px',\n gap: tokens.spacing.xs,\n iconSize: 13,\n borderRadius: tokens.borderRadius.sm,\n },\n medium: {\n padding: `${tokens.spacing.xxs} ${tokens.spacing.md}`,\n fontSize: tokens.typography.fontSize.sm,\n height: '28px',\n gap: tokens.spacing.xs,\n iconSize: 15,\n borderRadius: tokens.borderRadius.md,\n },\n large: {\n padding: `${tokens.spacing.xs} ${tokens.spacing.lg}`,\n fontSize: tokens.typography.fontSize.base,\n height: '34px',\n gap: tokens.spacing.sm,\n iconSize: 17,\n borderRadius: tokens.borderRadius.md,\n },\n };\n \n const config = sizeConfig[size];\n \n // Variant base styles (transparent theme: purple-hue transparent bg, smooth fade on hover)\n const getVariantStyles = (): React.CSSProperties => {\n const baseTransition = `${tokens.animation.normal}, opacity ${tokens.animation.duration.normal}ms ${tokens.animation.easing.default}`;\n \n if (isTransparentTheme && transparentDefault && transparentHover) {\n switch (variant) {\n case 'primary':\n return {\n backgroundColor: isHovered && !isDisabled ? transparentHover : transparentDefault,\n color: tokens.colors.text.primary,\n border: `1px solid ${isHovered && !isDisabled ? 'rgba(139, 92, 246, 0.5)' : 'rgba(139, 92, 246, 0.25)'}`,\n boxShadow: isHovered && !isDisabled ? '0 0 10px rgba(139, 92, 246, 0.15)' : 'none',\n transition: baseTransition,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n };\n case 'secondary':\n return {\n backgroundColor: isHovered && !isDisabled \n ? `${tokens.colors.accent.primary}15` // Use accent color with opacity instead of transparentHover\n : transparentDefault,\n color: safeAccentFg,\n border: `1px solid ${isHovered && !isDisabled ? `${tokens.colors.accent.primary}70` : `${tokens.colors.accent.primary}30`}`,\n boxShadow: 'none',\n transition: baseTransition,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n };\n case 'borderless':\n return {\n backgroundColor: isHovered && !isDisabled ? transparentHover : transparentDefault,\n color: safeAccentFg,\n border: 'none',\n boxShadow: 'none',\n transition: baseTransition,\n backdropFilter: 'blur(6px)',\n WebkitBackdropFilter: 'blur(6px)',\n };\n }\n }\n \n switch (variant) {\n case 'primary':\n return {\n backgroundColor: isHovered && !isDisabled\n ? tokens.colors.interactive.hover\n : safeAccentBg,\n color: '#ffffff',\n border: 'none',\n boxShadow: isHovered && !isDisabled\n ? `0 2px 8px -2px ${tokens.colors.accent.primary}35`\n : 'none',\n transition: baseTransition,\n };\n \n case 'secondary':\n return {\n backgroundColor: isHovered && !isDisabled\n ? `${tokens.colors.accent.primary}12`\n : 'transparent',\n color: isHovered && !isDisabled ? safeAccentFg : safeAccentFg,\n border: `${tokens.borders.width.medium} solid ${isHovered && !isDisabled ? tokens.colors.accent.primary : `${tokens.colors.accent.primary}80`}`,\n boxShadow: 'none',\n transition: baseTransition,\n };\n \n case 'borderless':\n return {\n backgroundColor: isHovered && !isDisabled\n ? `${tokens.colors.accent.primary}10`\n : 'transparent',\n color: safeAccentFg,\n border: `${tokens.borders.width.medium} solid transparent`,\n boxShadow: 'none',\n transition: baseTransition,\n };\n }\n };\n \n const focusStyles: React.CSSProperties = isFocused ? {\n outline: 'none',\n boxShadow: tokens.borders.focusRing.button,\n } : {};\n\n const activeStyles: React.CSSProperties = isActive && !isDisabled ? {\n filter: 'brightness(0.85)',\n transform: 'scale(0.98)',\n boxShadow: 'none',\n } : {};\n \n const baseStyle: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: config.gap,\n padding: config.padding,\n height: config.height,\n fontSize: config.fontSize,\n fontWeight: 400,\n fontFamily: tokens.typography.fontFamily.primary,\n letterSpacing: '0.02em',\n borderRadius: config.borderRadius,\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n opacity: isDisabled ? 0.5 : 1,\n outline: 'none',\n textDecoration: 'none',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n width: fullWidth ? '100%' : 'auto',\n position: 'relative',\n ...getVariantStyles(),\n ...focusStyles,\n ...activeStyles,\n ...style,\n };\n \n // Loading spinner with smooth animation\n const spinner = (\n <svg\n width={config.iconSize}\n height={config.iconSize}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n style={{\n animation: 'zendir-spin 0.8s cubic-bezier(0.4, 0, 0.6, 1) infinite',\n }}\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeDasharray=\"31.4 31.4\"\n strokeDashoffset=\"10\"\n opacity=\"0.9\"\n />\n <style>\n {`@keyframes zendir-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`}\n </style>\n </svg>\n );\n \n // Portal-based tooltip: position fixed relative to viewport, never clipped\n const internalRef = useRef<HTMLButtonElement>(null);\n const tooltipNodeRef = useRef<HTMLDivElement>(null);\n const [tooltipPos, setTooltipPos] = useState<{ top: number; left: number } | null>(null);\n\n const mergedRef = useCallback((node: HTMLButtonElement | null) => {\n (internalRef as React.MutableRefObject<HTMLButtonElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = node;\n }, [ref]);\n\n useLayoutEffect(() => {\n if (!showTooltip || !tooltip || !internalRef.current) {\n setTooltipPos(null);\n return;\n }\n const btn = internalRef.current.getBoundingClientRect();\n const gap = 8;\n let top = btn.top - gap;\n let left = btn.left + btn.width / 2;\n // Adjust after tooltip renders so we can measure its width\n if (tooltipNodeRef.current) {\n const tt = tooltipNodeRef.current.getBoundingClientRect();\n top = btn.top - tt.height - gap;\n left = btn.left + (btn.width - tt.width) / 2;\n if (left < 8) left = 8;\n if (left + tt.width > window.innerWidth - 8) left = window.innerWidth - tt.width - 8;\n if (top < 8) top = btn.bottom + gap; // flip below if no room above\n }\n setTooltipPos({ top, left });\n }, [showTooltip, tooltip]);\n\n const tooltipPortal = tooltip && showTooltip && ReactDOM.createPortal(\n <div\n ref={tooltipNodeRef}\n role=\"tooltip\"\n style={{\n position: 'fixed',\n top: tooltipPos ? tooltipPos.top : -9999,\n left: tooltipPos ? tooltipPos.left : -9999,\n padding: `${tokens.spacing.xs} ${tokens.spacing.smd}`,\n fontSize: '0.75rem',\n fontWeight: 500,\n color: tokens.colors.text.primary,\n backgroundColor: tokens.colors.background.elevated,\n border: tokens.borders.divider,\n borderRadius: tokens.borderRadius.md,\n boxShadow: tokens.shadows.md,\n whiteSpace: 'nowrap',\n zIndex: 2147483100,\n pointerEvents: 'none',\n opacity: tooltipPos ? 1 : 0,\n animation: tooltipPos ? `zendir-btn-tip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both` : 'none',\n }}\n >\n {tooltip}\n <style>\n {`@keyframes zendir-btn-tip { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }`}\n </style>\n </div>,\n document.body\n );\n\n return (\n <>\n <button\n ref={mergedRef}\n type=\"button\"\n disabled={isDisabled}\n className={classNames('zendir-button', `zendir-button--${variant}`, `zendir-button--${size}`, className)}\n style={baseStyle}\n title={tooltip && !showTooltip ? tooltip : undefined}\n onMouseDown={() => setIsActive(true)}\n onMouseUp={() => setIsActive(false)}\n onMouseEnter={(e) => {\n setIsHovered(true);\n setShowTooltip(true);\n onMouseEnter?.(e);\n }}\n onMouseLeave={(e) => {\n setIsHovered(false);\n setIsActive(false);\n setShowTooltip(false);\n onMouseLeave?.(e);\n }}\n onFocus={(e) => {\n setIsFocused(true);\n onFocus?.(e);\n }}\n onBlur={(e) => {\n setIsFocused(false);\n onBlur?.(e);\n }}\n {...rest}\n >\n {loading ? spinner : icon}\n {children && <span>{children}</span>}\n {iconEnd && !loading && iconEnd}\n </button>\n {tooltipPortal}\n </>\n );\n}));\n\nexport default Button;\n"],"names":["Button"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CardHeader.js","sources":["../../../src/react/core/CardHeader.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - CardHeader Component\r\n * \r\n * Unified card header component for consistent styling across all cards.\r\n * Uses HeaderIconWithStatus for status-aware icon display.\r\n * \r\n * Features:\r\n * - Consistent header styling across all theme variants (Regular, Bold, Minimal)\r\n * - Integrated HeaderIconWithStatus for status indicator\r\n * - Theme-aware accent colors and styling\r\n * - Optional badge/tag display\r\n * - Optional subtitle\r\n * - Accessibility compliant\r\n * \r\n * @example\r\n * ```tsx\r\n * <CardHeader\r\n * icon=\"satellite-transmit\"\r\n * title=\"ISS\"\r\n * subtitle=\"SC-001\"\r\n * status=\"normal\"\r\n * badge=\"LEO\"\r\n * badgeColor=\"#3E3CFF\"\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useMemo } from 'react';\r\nimport { useTheme } from '../theme';\r\nimport { HeaderIconWithStatus } from './HeaderIconWithStatus';\r\nimport { AstroIcon, type AstroIconName } from './AstroIcon';\r\nimport { Icon } from './Icon';\r\nimport { type StatusLevel, getStatusColor, safeAccentText } from '../utils';\r\n\r\n// =============================================================================\r\n// Status Colors (matching Astro UXD)\r\n// =============================================================================\r\n\r\nconst STATUS_COLORS: Record<StatusLevel, string> = {\r\n off: '#a4abb6',\r\n standby: '#2dccff',\r\n normal: '#56f000',\r\n caution: '#fce83a',\r\n serious: '#ffb302',\r\n critical: '#ff3838',\r\n};\r\n\r\n// =============================================================================\r\n// StatusBadge - Compact status indicator for accent lines\r\n// =============================================================================\r\n\r\ninterface StatusBadgeProps {\r\n status: StatusLevel;\r\n size?: 'bold' | 'minimal';\r\n}\r\n\r\nfunction StatusBadge({ status, size = 'bold' }: StatusBadgeProps): React.ReactElement {\r\n const color = STATUS_COLORS[status];\r\n const label = status.charAt(0).toUpperCase() + status.slice(1);\r\n const isBold = size === 'bold';\r\n \r\n // Render status shape\r\n const renderShape = () => {\r\n const s = isBold ? 6 : 5;\r\n switch (status) {\r\n case 'critical':\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <polygon points=\"6,11 1,2 11,2\" fill={color} />\r\n </svg>\r\n );\r\n case 'serious':\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <polygon points=\"6,1 11,6 6,11 1,6\" fill={color} />\r\n </svg>\r\n );\r\n case 'caution':\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" fill={color} />\r\n </svg>\r\n );\r\n case 'standby':\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <circle cx=\"6\" cy=\"6\" r=\"3.5\" fill=\"none\" stroke={color} strokeWidth=\"2\" />\r\n </svg>\r\n );\r\n case 'off':\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <circle cx=\"6\" cy=\"6\" r=\"3\" fill={color} />\r\n </svg>\r\n );\r\n case 'normal':\r\n default:\r\n return (\r\n <svg viewBox=\"0 0 12 12\" width={s} height={s}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n );\r\n }\r\n };\r\n \r\n return (\r\n <span style={{\r\n display: 'inline-flex',\r\n alignItems: 'center',\r\n gap: isBold ? '6px' : '4px',\r\n padding: isBold ? '3px 10px' : '2px 8px',\r\n borderRadius: '12px',\r\n fontSize: isBold ? '0.6875rem' : '0.625rem', // 11px / 10px in rem (AstroUXDS compliant)\r\n fontWeight: 500, // AstroUXDS: medium weight\r\n backgroundColor: `${color}20`,\r\n color: color,\r\n flexShrink: 0,\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.3px',\r\n }}>\r\n {renderShape()}\r\n {label}\r\n </span>\r\n );\r\n}\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface CardHeaderProps {\r\n /** Icon name (AstroIcon) */\r\n icon: AstroIconName | string;\r\n /** Main title text */\r\n title: string;\r\n /** Optional subtitle text */\r\n subtitle?: string;\r\n /** Status level - determines icon color and status indicator */\r\n status?: StatusLevel;\r\n /** Badge text (e.g., orbit type, category) */\r\n badge?: string;\r\n /** Badge background color */\r\n badgeColor?: string;\r\n /** Show pin indicator */\r\n isPinned?: boolean;\r\n /** Icon size override */\r\n iconSize?: number;\r\n /** Custom icon color (overrides status-based coloring) */\r\n iconColor?: string;\r\n /** Hide status indicator (show plain icon) */\r\n hideStatusIndicator?: boolean;\r\n /** Additional className */\r\n className?: string;\r\n /** Additional styles */\r\n style?: React.CSSProperties;\r\n /** Slot for additional content on the right side */\r\n rightSlot?: React.ReactNode;\r\n}\r\n\r\n// =============================================================================\r\n// CardHeader Component\r\n// =============================================================================\r\n\r\nexport const CardHeader = memo(function CardHeader({\r\n icon,\r\n title,\r\n subtitle,\r\n status = 'off',\r\n badge,\r\n badgeColor,\r\n isPinned,\r\n iconSize,\r\n iconColor,\r\n hideStatusIndicator = false,\r\n className,\r\n style,\r\n rightSlot,\r\n}: CardHeaderProps) {\r\n const { tokens, theme } = useTheme();\r\n \r\n // Theme detection\r\n const _isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\r\n const isBoldVariant = theme === 'transparent-bold';\r\n const isMinimalVariant = theme === 'transparent-minimal';\r\n const _useGlassMode = isBoldVariant || isMinimalVariant;\r\n \r\n // Glass accent colors — use safeAccentText for text to meet WCAG AA 4.5:1\r\n const glassAccentColor = useMemo(() => safeAccentText(tokens.colors.accent.primary), [tokens.colors.accent.primary]);\r\n const glassAccentRaw = tokens.colors.accent.primary; // raw color for non-text usage (gradients, glows)\r\n const glassAccentMuted = `${glassAccentRaw}66`;\r\n \r\n // Status color (for icon when showing status)\r\n const statusColor = iconColor ?? (status ? getStatusColor(status) : tokens.colors.text.secondary);\r\n \r\n // Single card heading system (tokens.layout.card.heading)\r\n const headingConfig = tokens.layout?.card?.heading ?? {\r\n iconSize: 20,\r\n iconSizeCompact: 16,\r\n titleFontSize: '1.125rem',\r\n titleFontWeight: 500,\r\n gap: 8,\r\n };\r\n const defaultIconSize = isMinimalVariant ? headingConfig.iconSizeCompact : headingConfig.iconSize;\r\n const finalIconSize = iconSize ?? defaultIconSize;\r\n const titleFontSize = headingConfig.titleFontSize;\r\n const titleFontWeight = headingConfig.titleFontWeight;\r\n const iconTitleGap = headingConfig.gap;\r\n \r\n // Badge color (use safe accent text if not provided) — ensures 4.5:1 contrast\r\n const safeBadgeColor = useMemo(() => badgeColor ? safeAccentText(badgeColor) : safeAccentText(tokens.colors.accent.primary), [badgeColor, tokens.colors.accent.primary]);\r\n const finalBadgeColor = safeBadgeColor;\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // BOLD VARIANT\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n if (isBoldVariant) {\r\n return (\r\n <header \r\n className={className}\r\n style={{ \r\n position: 'relative', \r\n zIndex: 1, \r\n marginBottom: tokens.spacing.sm,\r\n ...style,\r\n }}\r\n >\r\n <div style={{ \r\n display: 'flex', \r\n alignItems: 'center', \r\n justifyContent: 'space-between',\r\n }}>\r\n <div style={{ display: 'flex', alignItems: 'center', gap: iconTitleGap }}>\r\n {/* Icon with status badge — only when icon is provided */}\r\n {icon && (hideStatusIndicator ? (\r\n <AstroIcon name={icon as AstroIconName} size={finalIconSize} color={statusColor} />\r\n ) : (\r\n <HeaderIconWithStatus \r\n icon={icon as AstroIconName} \r\n size={finalIconSize} \r\n status={status}\r\n />\r\n ))}\r\n \r\n {/* Title with accent color — card heading system */}\r\n <h3 style={{\r\n margin: 0,\r\n fontSize: titleFontSize,\r\n fontWeight: titleFontWeight,\r\n color: glassAccentColor,\r\n letterSpacing: '0.5px',\r\n textShadow: `0 0 20px ${glassAccentMuted}`,\r\n }}>\r\n {title}\r\n </h3>\r\n \r\n {/* Pin indicator */}\r\n {isPinned && (\r\n <Icon name=\"lock\" size={12} color={tokens.colors.accent.primary} />\r\n )}\r\n </div>\r\n \r\n {/* Right side: Badge + rightSlot */}\r\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.sm }}>\r\n {badge && (\r\n <span style={{\r\n backgroundColor: `${finalBadgeColor}20`,\r\n color: finalBadgeColor,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n borderRadius: tokens.borderRadius.sm,\r\n flexShrink: 0,\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n }}>\r\n {badge}\r\n </span>\r\n )}\r\n {rightSlot}\r\n </div>\r\n </div>\r\n \r\n {/* Subtitle if provided */}\r\n {subtitle && (\r\n <span style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.tertiary,\r\n marginLeft: finalIconSize + parseInt(tokens.spacing.sm),\r\n }}>\r\n {subtitle}\r\n </span>\r\n )}\r\n \r\n {/* Accent line with optional status badge */}\r\n <div style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacing.sm,\r\n marginTop: tokens.spacing.sm,\r\n marginBottom: tokens.spacing.xs,\r\n }}>\r\n <div style={{\r\n flex: 1,\r\n height: '2px',\r\n background: `linear-gradient(90deg, ${glassAccentRaw} 0%, ${glassAccentMuted} 70%, ${glassAccentMuted}20 100%)`,\r\n boxShadow: `0 0 8px ${glassAccentMuted}`,\r\n }} />\r\n {/* Status badge at end of line if status is set and not 'off' */}\r\n {status && status !== 'off' && !hideStatusIndicator && (\r\n <StatusBadge status={status} size=\"bold\" />\r\n )}\r\n </div>\r\n </header>\r\n );\r\n }\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // MINIMAL VARIANT\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n if (isMinimalVariant) {\r\n return (\r\n <header\r\n className={className}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n marginBottom: tokens.spacing.sm,\r\n position: 'relative',\r\n zIndex: 1,\r\n ...style,\r\n }}\r\n >\r\n {/* Icon with status badge — only when icon is provided */}\r\n {icon && (hideStatusIndicator ? (\r\n <AstroIcon \r\n name={icon as AstroIconName} \r\n size={finalIconSize} \r\n color={statusColor} \r\n style={{ marginRight: tokens.spacing.sm }} \r\n />\r\n ) : (\r\n <HeaderIconWithStatus \r\n icon={icon as AstroIconName} \r\n size={finalIconSize} \r\n status={status}\r\n style={{ marginRight: tokens.spacing.sm }}\r\n />\r\n ))}\r\n \r\n {/* Title with accent color — card heading system */}\r\n <h3 style={{\r\n margin: 0,\r\n fontSize: titleFontSize,\r\n fontWeight: titleFontWeight,\r\n color: glassAccentColor,\r\n textShadow: `0 0 16px ${glassAccentMuted}`,\r\n letterSpacing: '0.02em',\r\n whiteSpace: 'nowrap',\r\n marginRight: '12px',\r\n }}>\r\n {title}\r\n </h3>\r\n \r\n {/* Pin indicator */}\r\n {isPinned && (\r\n <Icon name=\"lock\" size={10} color={tokens.colors.accent.primary} style={{ marginRight: '8px' }} />\r\n )}\r\n \r\n {/* Accent line with optional status badge */}\r\n <div style={{\r\n flex: 1,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacing.sm,\r\n minWidth: 40,\r\n }}>\r\n <div style={{\r\n flex: 1,\r\n height: '1px',\r\n background: `linear-gradient(90deg, ${glassAccentRaw} 0%, ${glassAccentMuted} 70%, ${glassAccentMuted}20 100%)`,\r\n }} />\r\n {/* Status badge at end of line if status is set and not 'off' */}\r\n {status && status !== 'off' && !hideStatusIndicator && !badge && (\r\n <StatusBadge status={status} size=\"minimal\" />\r\n )}\r\n </div>\r\n \r\n {/* Badge (takes precedence over status badge in minimal view for space) */}\r\n {badge && (\r\n <span style={{\r\n backgroundColor: `${finalBadgeColor}20`,\r\n color: finalBadgeColor,\r\n padding: '2px 6px',\r\n borderRadius: tokens.borderRadius.sm,\r\n flexShrink: 0,\r\n fontSize: tokens.typography.fontSize.xxs, // 0.625rem / 10px (AstroUXDS compact)\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n marginLeft: '8px',\r\n }}>\r\n {badge}\r\n </span>\r\n )}\r\n \r\n {rightSlot}\r\n </header>\r\n );\r\n }\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // REGULAR THEMES (Dark, Light, Simple Glass)\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n return (\r\n <header\r\n className={className}\r\n style={{\r\n display: 'flex',\r\n justifyContent: 'space-between',\r\n alignItems: 'center',\r\n marginBottom: tokens.spacing.md,\r\n ...style,\r\n }}\r\n >\r\n <div style={{ display: 'flex', alignItems: 'center', gap: iconTitleGap }}>\r\n {/* Icon with status badge — only when icon is provided */}\r\n {icon && (hideStatusIndicator ? (\r\n <AstroIcon name={icon as AstroIconName} size={finalIconSize} color={statusColor} />\r\n ) : (\r\n <HeaderIconWithStatus \r\n icon={icon as AstroIconName} \r\n size={finalIconSize} \r\n status={status}\r\n />\r\n ))}\r\n\r\n <div style={{ minWidth: 0 }}>\r\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: titleFontSize,\r\n fontWeight: titleFontWeight,\r\n lineHeight: 1.2,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {title}\r\n </h3>\r\n \r\n {/* Pin indicator */}\r\n {isPinned && (\r\n <Icon name=\"lock\" size={12} color={tokens.colors.accent.primary} />\r\n )}\r\n </div>\r\n \r\n {subtitle && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n {subtitle}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Right side: Status badge + Badge + rightSlot */}\r\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.sm }}>\r\n {/* Status badge - consistent with Container styling */}\r\n {status && status !== 'off' && !hideStatusIndicator && !rightSlot && (\r\n <StatusBadge status={status} size=\"bold\" />\r\n )}\r\n {badge && (\r\n <span\r\n style={{\r\n backgroundColor: `${finalBadgeColor}20`,\r\n color: finalBadgeColor,\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n borderRadius: tokens.borderRadius.sm,\r\n flexShrink: 0,\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.medium,\r\n }}\r\n >\r\n {badge}\r\n </span>\r\n )}\r\n {rightSlot}\r\n </div>\r\n </header>\r\n );\r\n});\r\n\r\nexport default CardHeader;\r\n"],"names":["CardHeader"],"mappings":"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"}
|