@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":"Popover.js","sources":["../../../src/react/core/Popover.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Popover & Menu Components\r\n * \r\n * General-purpose popover for dropdown menus, sort controls, action menus,\r\n * and contextual information. Portal-rendered with auto-positioning.\r\n * \r\n * Astro UX Compliance:\r\n * - Consistent with Dialog/Tooltip styling\r\n * - Focus trap for keyboard navigation\r\n * - Escape to close\r\n * - Click outside to close\r\n * - Reduced motion support\r\n * \r\n * @example\r\n * ```tsx\r\n * <Popover\r\n * trigger={<Button variant=\"secondary\">Sort</Button>}\r\n * placement=\"bottom-start\"\r\n * >\r\n * <Menu>\r\n * <Menu.Item onClick={...}>Sort by Time</Menu.Item>\r\n * <Menu.Item onClick={...}>Sort by Name</Menu.Item>\r\n * <Menu.Divider />\r\n * <Menu.Item onClick={...} destructive>Clear All</Menu.Item>\r\n * </Menu>\r\n * </Popover>\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useRef, useEffect, useCallback, createContext, useContext } from 'react';\r\nimport { useTheme } from '../theme';\r\n\r\n// ─── Popover ─────────────────────────────────────────────────────────────────\r\n\r\nexport type PopoverPlacement = \r\n | 'top' | 'top-start' | 'top-end'\r\n | 'bottom' | 'bottom-start' | 'bottom-end'\r\n | 'left' | 'left-start' | 'left-end'\r\n | 'right' | 'right-start' | 'right-end';\r\n\r\nexport interface PopoverProps {\r\n /** Trigger element */\r\n trigger: React.ReactElement;\r\n /** Popover content */\r\n children: React.ReactNode;\r\n /** Placement relative to trigger */\r\n placement?: PopoverPlacement;\r\n /** Open on hover instead of click */\r\n hover?: boolean;\r\n /** Controlled open state */\r\n open?: boolean;\r\n /** Controlled open change */\r\n onOpenChange?: (open: boolean) => void;\r\n /** Close on content click */\r\n closeOnClick?: boolean;\r\n /** Width: 'trigger' matches trigger width, number for px, 'auto' */\r\n width?: 'trigger' | 'auto' | number;\r\n /** Custom style for popover panel */\r\n style?: React.CSSProperties;\r\n}\r\n\r\nconst PopoverContext = createContext<{ close: () => void }>({ close: () => {} });\r\n\r\nexport const Popover = memo(function Popover({\r\n trigger,\r\n children,\r\n placement = 'bottom-start',\r\n hover = false,\r\n open: controlledOpen,\r\n onOpenChange,\r\n closeOnClick = true,\r\n width = 'auto',\r\n style,\r\n}: PopoverProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\r\n const [internalOpen, setInternalOpen] = useState(false);\r\n const triggerRef = useRef<HTMLDivElement>(null);\r\n const popoverRef = useRef<HTMLDivElement>(null);\r\n const hoverTimeout = useRef<ReturnType<typeof setTimeout>>();\r\n \r\n const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;\r\n \r\n const setOpen = useCallback((val: boolean) => {\r\n if (controlledOpen === undefined) setInternalOpen(val);\r\n onOpenChange?.(val);\r\n }, [controlledOpen, onOpenChange]);\r\n \r\n const close = useCallback(() => setOpen(false), [setOpen]);\r\n \r\n // Click outside\r\n useEffect(() => {\r\n if (!isOpen) return;\r\n const handler = (e: MouseEvent) => {\r\n if (\r\n triggerRef.current && !triggerRef.current.contains(e.target as Node) &&\r\n popoverRef.current && !popoverRef.current.contains(e.target as Node)\r\n ) {\r\n close();\r\n }\r\n };\r\n document.addEventListener('mousedown', handler);\r\n return () => document.removeEventListener('mousedown', handler);\r\n }, [isOpen, close]);\r\n \r\n // Escape key + cleanup hoverTimeout on unmount\r\n useEffect(() => {\r\n if (!isOpen) return;\r\n const handler = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') close();\r\n };\r\n document.addEventListener('keydown', handler);\r\n return () => document.removeEventListener('keydown', handler);\r\n }, [isOpen, close]);\r\n \r\n useEffect(() => {\r\n return () => { clearTimeout(hoverTimeout.current); };\r\n }, []);\r\n \r\n // Position calculation\r\n const [position, setPosition] = useState<React.CSSProperties>({});\r\n \r\n useEffect(() => {\r\n if (!isOpen || !triggerRef.current) return;\r\n const gap = 8;\r\n const viewportPadding = 8;\r\n\r\n const updatePosition = () => {\r\n const rect = triggerRef.current?.getBoundingClientRect();\r\n if (!rect) return;\r\n\r\n const panelWidth = width === 'trigger'\r\n ? rect.width\r\n : typeof width === 'number'\r\n ? width\r\n : (popoverRef.current?.offsetWidth ?? 220);\r\n const panelHeight = popoverRef.current?.offsetHeight ?? 220;\r\n\r\n let left = rect.left;\r\n let top = rect.bottom + gap;\r\n\r\n if (placement.startsWith('top')) {\r\n top = rect.top - panelHeight - gap;\r\n } else if (placement.startsWith('bottom')) {\r\n top = rect.bottom + gap;\r\n } else if (placement.startsWith('left')) {\r\n left = rect.left - panelWidth - gap;\r\n } else if (placement.startsWith('right')) {\r\n left = rect.right + gap;\r\n }\r\n\r\n if (placement.startsWith('top') || placement.startsWith('bottom')) {\r\n if (placement.endsWith('-start')) {\r\n left = rect.left;\r\n } else if (placement.endsWith('-end')) {\r\n left = rect.right - panelWidth;\r\n } else {\r\n left = rect.left + (rect.width - panelWidth) / 2;\r\n }\r\n } else {\r\n if (placement.endsWith('-start')) {\r\n top = rect.top;\r\n } else if (placement.endsWith('-end')) {\r\n top = rect.bottom - panelHeight;\r\n } else {\r\n top = rect.top + (rect.height - panelHeight) / 2;\r\n }\r\n }\r\n\r\n left = Math.max(viewportPadding, Math.min(left, window.innerWidth - panelWidth - viewportPadding));\r\n top = Math.max(viewportPadding, Math.min(top, window.innerHeight - panelHeight - viewportPadding));\r\n\r\n const pos: React.CSSProperties = {\r\n position: 'fixed',\r\n zIndex: 9990,\r\n left,\r\n top,\r\n };\r\n\r\n if (width === 'trigger') {\r\n pos.width = rect.width;\r\n pos.minWidth = rect.width;\r\n } else if (typeof width === 'number') {\r\n pos.width = width;\r\n }\r\n\r\n setPosition(pos);\r\n };\r\n\r\n const raf = requestAnimationFrame(updatePosition);\r\n window.addEventListener('resize', updatePosition);\r\n window.addEventListener('scroll', updatePosition, true);\r\n return () => {\r\n cancelAnimationFrame(raf);\r\n window.removeEventListener('resize', updatePosition);\r\n window.removeEventListener('scroll', updatePosition, true);\r\n };\r\n }, [isOpen, placement, width]);\r\n \r\n const triggerProps: Record<string, (() => void) | undefined> = {};\r\n if (hover) {\r\n triggerProps.onMouseEnter = () => {\r\n clearTimeout(hoverTimeout.current);\r\n setOpen(true);\r\n };\r\n triggerProps.onMouseLeave = () => {\r\n hoverTimeout.current = setTimeout(close, 150);\r\n };\r\n } else {\r\n triggerProps.onClick = () => setOpen(!isOpen);\r\n }\r\n \r\n const popoverHoverProps = hover ? {\r\n onMouseEnter: () => clearTimeout(hoverTimeout.current),\r\n onMouseLeave: () => { hoverTimeout.current = setTimeout(close, 150); },\r\n } : {};\r\n \r\n return (\r\n <PopoverContext.Provider value={{ close }}>\r\n <div ref={triggerRef} style={{ display: 'inline-flex' }} aria-expanded={isOpen} aria-haspopup=\"true\" {...triggerProps}>\r\n {trigger}\r\n </div>\r\n \r\n {isOpen && (\r\n <div\r\n ref={popoverRef}\r\n role=\"dialog\"\r\n onClick={closeOnClick ? close : undefined}\r\n {...popoverHoverProps}\r\n style={{\r\n ...position,\r\n backgroundColor: isTransparentTheme\r\n ? 'rgba(15, 12, 30, 0.85)'\r\n : tokens.colors.background.elevated,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: tokens.shadows.lg,\r\n padding: tokens.spacing.xs,\r\n maxWidth: 'min(320px, calc(100vw - 16px))',\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n animation: `zendir-popover-enter 150ms ${tokens.animation.easing.default}`,\r\n ...(isTransparentTheme ? {\r\n backdropFilter: 'blur(16px)',\r\n WebkitBackdropFilter: 'blur(16px)',\r\n } : {}),\r\n ...style,\r\n }}\r\n >\r\n {children}\r\n </div>\r\n )}\r\n \r\n <style>{`\r\n @keyframes zendir-popover-enter {\r\n from { opacity: 0; transform: translateY(-4px) scale(0.98); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n `}</style>\r\n </PopoverContext.Provider>\r\n );\r\n});\r\n\r\n// ─── Menu ────────────────────────────────────────────────────────────────────\r\n\r\nexport interface MenuProps {\r\n children: React.ReactNode;\r\n style?: React.CSSProperties;\r\n}\r\n\r\nconst MenuRoot = memo(function Menu({ children, style }: MenuProps): React.ReactElement {\r\n return (\r\n <div role=\"menu\" style={{ minWidth: 'min(160px, calc(100vw - 32px))', ...style }}>\r\n {children}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Menu.Item ───────────────────────────────────────────────────────────────\r\n\r\nexport interface MenuItemProps {\r\n /** Click handler */\r\n onClick?: () => void;\r\n /** Icon (ReactNode or IconName string) */\r\n icon?: React.ReactNode;\r\n /** Label text */\r\n children: React.ReactNode;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Destructive action (red text) */\r\n destructive?: boolean;\r\n /** Keyboard shortcut hint */\r\n shortcut?: string;\r\n}\r\n\r\nconst MenuItem = memo(function MenuItem({\r\n onClick,\r\n icon,\r\n children,\r\n disabled = false,\r\n destructive = false,\r\n shortcut,\r\n}: MenuItemProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { close } = useContext(PopoverContext);\r\n const [isHovered, setIsHovered] = useState(false);\r\n \r\n const handleClick = useCallback(() => {\r\n if (disabled) return;\r\n onClick?.();\r\n close();\r\n }, [disabled, onClick, close]);\r\n \r\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\r\n const item = e.currentTarget;\r\n const siblings = item.parentElement?.querySelectorAll('[role=\"menuitem\"]:not([disabled])');\r\n if (!siblings) return;\r\n const items = Array.from(siblings) as HTMLElement[];\r\n const idx = items.indexOf(item as HTMLElement);\r\n \r\n if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n items[(idx + 1) % items.length]?.focus();\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n items[(idx - 1 + items.length) % items.length]?.focus();\r\n } else if (e.key === 'Home') {\r\n e.preventDefault();\r\n items[0]?.focus();\r\n } else if (e.key === 'End') {\r\n e.preventDefault();\r\n items[items.length - 1]?.focus();\r\n }\r\n }, []);\r\n \r\n const color = destructive\r\n ? tokens.colors.status.critical\r\n : disabled\r\n ? tokens.colors.text.tertiary\r\n : tokens.colors.text.primary;\r\n \r\n return (\r\n <button\r\n role=\"menuitem\"\r\n tabIndex={disabled ? -1 : 0}\r\n onClick={handleClick}\r\n onKeyDown={handleKeyDown}\r\n disabled={disabled}\r\n onMouseEnter={() => setIsHovered(true)}\r\n onMouseLeave={() => setIsHovered(false)}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: tokens.spacing.sm,\r\n width: '100%',\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.sm}`,\r\n minHeight: 44,\r\n border: 'none',\r\n background: isHovered && !disabled\r\n ? `${destructive ? tokens.colors.status.critical : tokens.colors.accent.primary}12`\r\n : 'transparent',\r\n color,\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n cursor: disabled ? 'not-allowed' : 'pointer',\r\n borderRadius: tokens.borderRadius.sm,\r\n textAlign: 'left',\r\n transition: tokens.animation.fast,\r\n opacity: disabled ? 0.5 : 1,\r\n outline: 'none',\r\n }}\r\n >\r\n {icon && <span style={{ flexShrink: 0, display: 'flex' }}>{icon}</span>}\r\n <span style={{ flex: 1 }}>{children}</span>\r\n {shortcut && (\r\n <span style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n flexShrink: 0,\r\n }}>\r\n {shortcut}\r\n </span>\r\n )}\r\n </button>\r\n );\r\n});\r\n\r\n// ─── Menu.Divider ────────────────────────────────────────────────────────────\r\n\r\nconst MenuDivider = memo(function MenuDivider(): React.ReactElement {\r\n const { tokens } = useTheme();\r\n return (\r\n <div\r\n role=\"separator\"\r\n style={{\r\n height: '1px',\r\n backgroundColor: tokens.colors.border.muted,\r\n margin: `${tokens.spacing.xs} 0`,\r\n }}\r\n />\r\n );\r\n});\r\n\r\n// ─── Menu.Label ──────────────────────────────────────────────────────────────\r\n\r\nconst MenuLabel = memo(function MenuLabel({ children }: { children: React.ReactNode }): React.ReactElement {\r\n const { tokens } = useTheme();\r\n return (\r\n <div style={{\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: 'uppercase',\r\n letterSpacing: '0.06em',\r\n }}>\r\n {children}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Compound Export ─────────────────────────────────────────────────────────\r\n\r\ntype MenuComponent = typeof MenuRoot & {\r\n Item: typeof MenuItem;\r\n Divider: typeof MenuDivider;\r\n Label: typeof MenuLabel;\r\n};\r\n\r\nexport const Menu: MenuComponent = Object.assign(MenuRoot, {\r\n Item: MenuItem,\r\n Divider: MenuDivider,\r\n Label: MenuLabel,\r\n});\r\n\r\nexport default Popover;\r\n"],"names":["Popover","Menu","MenuItem","MenuDivider","MenuLabel"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","sources":["../../../src/react/core/Select.tsx"],"sourcesContent":["/**\n * @zendir/ui - Select Component\n * \n * Dropdown select following Astro UX Design System.\n * \n * @example\n * ```tsx\n * <Select\n * label=\"Spacecraft\"\n * options={[\n * { value: 'sat-001', label: 'Explorer-1' },\n * { value: 'sat-002', label: 'Voyager-2' },\n * ]}\n * value={selected}\n * onChange={setSelected}\n * />\n * ```\n */\n\nimport React, { memo, forwardRef, useState, useId, useRef, useEffect, useLayoutEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useTheme } from '../theme';\nimport { classNames } from '../utils';\nimport type { LabelPlacement } from './Input';\nimport { useBreakpoint } from './layout/useBreakpoint';\n\nexport interface SelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SelectProps {\n /** Label text or ReactNode (supports rich content like tooltip icons) */\n label?: React.ReactNode;\n /** Options array */\n options: SelectOption[];\n /** Selected value */\n value?: string;\n /** Change handler */\n onChange?: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Helper text */\n helperText?: string;\n /** Error message */\n error?: string;\n /** Disabled state */\n disabled?: boolean;\n /** Full width */\n fullWidth?: boolean;\n /** Size variant */\n size?: 'small' | 'medium' | 'large';\n /** Custom className */\n className?: string;\n /**\n * Label placement style.\n * - `'outlined'` — label sits on the input border (notched/Material-style). **Default.**\n * - `'above'` — label sits above the input with a gap (classic stack).\n */\n labelPlacement?: LabelPlacement;\n /** Required indicator */\n required?: boolean;\n}\n\nexport const Select = memo(forwardRef<HTMLDivElement, SelectProps>(function Select(\n {\n label,\n options,\n value,\n onChange,\n placeholder = 'Select...',\n helperText,\n error,\n disabled = false,\n fullWidth = false,\n size = 'medium',\n className = '',\n labelPlacement = 'outlined',\n required,\n },\n ref\n): React.ReactElement {\n const { tokens, theme } = useTheme();\n const { isMobile } = useBreakpoint();\n const [isOpen, setIsOpen] = useState(false);\n const [focused, setFocused] = useState(false);\n const [dropdownRect, setDropdownRect] = useState<{ top: number; left: number; width: number; openUp?: boolean } | null>(null);\n const selectRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLUListElement>(null);\n const generatedId = useId();\n \n // Transparent theme detection (purple-hue transparent bg)\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const _transparentDefault = tokens.colors.interactive.transparentDefault;\n const _transparentInputBg = tokens.colors.interactive.transparentInputBg;\n \n const hasError = !!error;\n const selectedOption = options.find(o => o.value === value);\n const isOutlined = labelPlacement === 'outlined';\n \n const sizeConfig = {\n small: { height: tokens.elementSize.sm, fontSize: tokens.typography.fontSize.xs, padding: tokens.spacing.sm, inputPaddingLeft: 8 },\n medium: { height: tokens.elementSize.md, fontSize: tokens.typography.fontSize.sm, padding: tokens.spacing.md, inputPaddingLeft: 16 },\n large: { height: tokens.elementSize.lg, fontSize: tokens.typography.fontSize.base, padding: tokens.spacing.lg, inputPaddingLeft: 24 },\n };\n const config = sizeConfig[size];\n const controlHeight = isMobile ? '44px' : config.height;\n const optionRowHeight = isMobile\n ? 40\n : (size === 'small' ? 30 : size === 'medium' ? 34 : 38);\n \n // Position dropdown when open (for portal) - keep in viewport\n const DROPDOWN_MAX_HEIGHT = 200;\n const GAP = 4;\n useLayoutEffect(() => {\n if (!isOpen || !triggerRef.current) {\n setDropdownRect(null);\n return;\n }\n const update = () => {\n if (triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom - GAP;\n const spaceAbove = rect.top - GAP;\n const openUp = spaceBelow < Math.min(DROPDOWN_MAX_HEIGHT, options.length * optionRowHeight) && spaceAbove > spaceBelow;\n setDropdownRect({\n top: openUp ? rect.top - GAP : rect.bottom + GAP,\n left: rect.left,\n width: rect.width,\n openUp,\n });\n }\n };\n update();\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n return () => {\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [isOpen, options.length, optionRowHeight]);\n\n // Close dropdown on outside click (include portaled dropdown)\n useEffect(() => {\n if (!isOpen) return;\n \n const handleClickOutside = (e: MouseEvent) => {\n const target = e.target as Node;\n // Check if click is inside the trigger or the portaled dropdown\n if (\n selectRef.current?.contains(target) ||\n dropdownRef.current?.contains(target)\n ) {\n return;\n }\n setIsOpen(false);\n };\n // Use click instead of mousedown to avoid race condition with option onClick\n document.addEventListener('click', handleClickOutside, true);\n return () => document.removeEventListener('click', handleClickOutside, true);\n }, [isOpen]);\n \n const computedBorder = hasError\n ? tokens.borders.input.error\n : focused || isOpen\n ? tokens.borders.input.focus\n : tokens.borders.input.default;\n \n // Background for the outlined label \"notch\"\n const labelBg = isTransparentTheme\n ? tokens.colors.background.base\n : tokens.colors.background.surface;\n \n // Outlined label font sizes\n const outlinedLabelSizeMap = { small: '0.625rem', medium: '0.6875rem', large: '0.75rem' };\n const outlinedLabelLeftMap = { small: '2px', medium: '10px', large: '18px' };\n \n // Shared label content\n const labelContent = (\n <>\n {label}\n {required && (\n <span style={{ color: tokens.colors.status.critical, marginLeft: '2px' }}>*</span>\n )}\n </>\n );\n\n return (\n <div\n ref={ref}\n className={classNames('zendir-select-wrapper', className)}\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: tokens.spacing.xs,\n width: fullWidth ? '100%' : undefined,\n }}\n >\n {/* Label: 'above' variant */}\n {label && !isOutlined && (\n <span\n id={`${generatedId}-label`}\n style={{\n fontSize: tokens.typography.fontSize.xs,\n fontWeight: tokens.typography.fontWeight.medium,\n color: tokens.colors.text.secondary,\n }}\n >\n {labelContent}\n </span>\n )}\n \n <div ref={selectRef} style={{ position: 'relative' }}>\n {/* Label: 'outlined' variant — positioned on the trigger border */}\n {label && isOutlined && (\n <span\n id={`${generatedId}-label`}\n style={{\n position: 'absolute',\n top: 0,\n left: outlinedLabelLeftMap[size],\n transform: 'translateY(-50%)',\n backgroundColor: labelBg,\n padding: `0 ${tokens.spacing.xs}`,\n fontSize: outlinedLabelSizeMap[size],\n fontWeight: tokens.typography.fontWeight.medium,\n color: focused || isOpen\n ? (hasError ? tokens.colors.status.critical : tokens.colors.accent.primary)\n : hasError\n ? tokens.colors.status.critical\n : tokens.colors.text.secondary,\n zIndex: 1,\n pointerEvents: 'none',\n transition: `color ${tokens.animation.fast}`,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n letterSpacing: '0.01em',\n }}\n >\n {labelContent}\n </span>\n )}\n \n <button\n ref={triggerRef}\n type=\"button\"\n disabled={disabled}\n onClick={() => !disabled && setIsOpen(!isOpen)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-labelledby={label ? `${generatedId}-label` : undefined}\n aria-describedby={(helperText || error) ? `${generatedId}-hint` : undefined}\n style={{\n width: '100%',\n height: controlHeight,\n padding: `0 ${config.padding}`,\n fontSize: config.fontSize,\n fontFamily: tokens.typography.fontFamily.primary,\n color: selectedOption ? tokens.colors.text.primary : tokens.colors.text.muted,\n backgroundColor: isTransparentTheme ? 'rgba(10, 15, 25, 0.4)' : tokens.colors.background.surface,\n border: computedBorder,\n borderRadius: tokens.borderRadius.md,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n textAlign: 'left',\n outline: 'none',\n transition: `border-color ${tokens.animation.fast}`,\n boxShadow: focused || isOpen ? tokens.borders.focusRing.default : 'none',\n }}\n >\n <span>{selectedOption?.label ?? placeholder}</span>\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill={tokens.colors.text.muted}\n style={{\n transform: isOpen ? 'rotate(180deg)' : 'rotate(0)',\n transition: `transform ${tokens.animation.fast}`,\n }}\n >\n <path d=\"M7 10l5 5 5-5z\" />\n </svg>\n </button>\n \n {isOpen && dropdownRect &&\n createPortal(\n <ul\n ref={dropdownRef}\n role=\"listbox\"\n style={{\n position: 'fixed',\n top: dropdownRect.openUp ? undefined : dropdownRect.top,\n bottom: dropdownRect.openUp ? window.innerHeight - dropdownRect.top : undefined,\n left: dropdownRect.left,\n width: dropdownRect.width,\n padding: tokens.spacing.xs,\n backgroundColor: isTransparentTheme ? 'rgba(139, 92, 246, 0.12)' : tokens.colors.background.elevated,\n backdropFilter: isTransparentTheme ? 'blur(12px)' : undefined,\n WebkitBackdropFilter: isTransparentTheme ? 'blur(12px)' : undefined,\n border: tokens.borders.dropdown,\n borderRadius: tokens.borderRadius.md,\n boxShadow: `${tokens.shadows.lg}, 0 0 30px ${tokens.colors.accent.primary}10`,\n zIndex: 10000,\n maxHeight: '200px',\n overflowY: 'auto',\n listStyle: 'none',\n margin: 0,\n animation: `zendir-dropdown ${tokens.animation.duration.normal}ms ${tokens.animation.easing.default} both`,\n }}\n >\n <style>{`@keyframes zendir-dropdown { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }`}</style>\n {options.map((option) => (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={option.value === value}\n tabIndex={option.disabled ? -1 : 0}\n onMouseDown={(e) => {\n // Prevent the document click handler from firing before we process this\n e.stopPropagation();\n }}\n onClick={(e) => {\n e.stopPropagation();\n if (!option.disabled) {\n onChange?.(option.value);\n setIsOpen(false);\n }\n }}\n onKeyDown={(e) => {\n if (option.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onChange?.(option.value);\n setIsOpen(false);\n }\n }}\n style={{\n padding: `0 ${tokens.spacing.md}`,\n height: optionRowHeight,\n minHeight: optionRowHeight,\n display: 'flex',\n alignItems: 'center',\n fontSize: config.fontSize,\n lineHeight: 1.2,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n color: option.disabled ? tokens.colors.text.muted : tokens.colors.text.primary,\n backgroundColor: option.value === value ? `${tokens.colors.accent.primary}15` : 'transparent',\n borderRadius: tokens.borderRadius.sm,\n cursor: option.disabled ? 'not-allowed' : 'pointer',\n opacity: option.disabled ? 0.5 : 1,\n transition: `background-color ${tokens.animation.fast}`,\n }}\n onMouseEnter={(e) => {\n if (!option.disabled) {\n e.currentTarget.style.backgroundColor = `${tokens.colors.accent.primary}10`;\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor =\n option.value === value ? `${tokens.colors.accent.primary}15` : 'transparent';\n }}\n >\n {option.label}\n </li>\n ))}\n </ul>,\n document.body\n )}\n </div>\n \n {(helperText || error) && (\n <span\n id={`${generatedId}-hint`}\n role={hasError ? 'alert' : undefined}\n style={{\n fontSize: tokens.typography.fontSize.xs,\n color: hasError ? tokens.colors.status.critical : tokens.colors.text.muted,\n }}\n >\n {error || helperText}\n </span>\n )}\n </div>\n );\n}));\n\nexport default Select;\n"],"names":["Select"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SideNav.js","sources":["../../../src/react/core/SideNav.tsx"],"sourcesContent":["/**\n * @zendir/ui - SideNav Component\n * \n * Persistent sidebar navigation for operator dashboards. Compound component\n * pattern with Header, Item, Section, Divider, and Footer subcomponents.\n * \n * Responsive: Full sidebar on desktop, hamburger overlay on mobile.\n * \n * Astro UX Compliance:\n * - Persistent side navigation pattern (AstroUXDS Navigation)\n * - Status indicator integration\n * - Active state highlighting with accent color\n * - Keyboard navigation support\n * - Reduced motion support\n * \n * @example\n * ```tsx\n * <SideNav>\n * <SideNav.Header logo={<Icon name=\"satellite\" size={24} />} title=\"Space Range\" badge=\"Operator\" />\n * <SideNav.Section title=\"Operations\">\n * <SideNav.Item icon=\"controls\" label=\"Controls\" description=\"System command interface\" href=\"/controls\" active />\n * <SideNav.Item icon=\"telemetry\" label=\"Telemetry\" href=\"/telemetry\" badge={3} tag=\"LIVE\" tagVariant=\"success\" />\n * <SideNav.Item icon=\"images\" label=\"Images\" href=\"/images\" />\n * </SideNav.Section>\n * <SideNav.Divider />\n * <SideNav.Section title=\"Analysis\">\n * <SideNav.Item icon=\"chart\" label=\"Plots\" href=\"/plots\" />\n * <SideNav.Item icon=\"map\" label=\"Map\" href=\"/map\" />\n * </SideNav.Section>\n * <SideNav.Footer>\n * <SideNav.Item icon=\"settings\" label=\"Settings\" href=\"/settings\" />\n * </SideNav.Footer>\n * </SideNav>\n * ```\n */\n\nimport React, { memo, useState, createContext, useContext, useCallback, useEffect } from 'react';\nimport { useTheme } from '../theme';\nimport { safeAccentText } from '../utils';\nimport { Icon } from './Icon';\nimport type { IconName } from './Icon';\n\n// ─── Astro UX Status Shape ───────────────────────────────────────────────────\n\nconst SIDENAV_STATUS_COLORS: Record<string, string> = {\n off: '#a4abb6',\n standby: '#2dccff',\n normal: '#56f000',\n caution: '#fce83a',\n serious: '#ffb302',\n critical: '#ff3838',\n};\n\n/** Astro UX dual-coded status shape (color + geometry). */\nfunction NavStatusShape({ status, size = 8 }: { status: string; size?: number }) {\n const color = SIDENAV_STATUS_COLORS[status] ?? SIDENAV_STATUS_COLORS.off;\n const glow = `${color}50`;\n const style = { flexShrink: 0 as const, filter: `drop-shadow(0 0 3px ${glow})` };\n\n switch (status) {\n case 'caution':\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" fill={color} /></svg>;\n case 'serious':\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><polygon points=\"6,1 11,6 6,11 1,6\" fill={color} /></svg>;\n case 'critical':\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><polygon points=\"6,11 1,2 11,2\" fill={color} /></svg>;\n case 'standby':\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><circle cx=\"6\" cy=\"6\" r=\"3.5\" fill=\"none\" stroke={color} strokeWidth=\"2\" /></svg>;\n case 'off':\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><circle cx=\"6\" cy=\"6\" r=\"3\" fill={color} /></svg>;\n default: // normal\n return <svg viewBox=\"0 0 12 12\" width={size} height={size} style={style} aria-hidden=\"true\"><circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} /></svg>;\n }\n}\n\n// ─── Responsive Breakpoints ──────────────────────────────────────────────────\n\nconst SIDENAV_BREAKPOINTS = {\n mobile: 768,\n tablet: 1024,\n} as const;\n\ntype SideNavMode = 'desktop' | 'tablet' | 'mobile';\n\nfunction getSideNavMode(width: number): SideNavMode {\n if (width < SIDENAV_BREAKPOINTS.mobile) return 'mobile';\n if (width < SIDENAV_BREAKPOINTS.tablet) return 'tablet';\n return 'desktop';\n}\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\ninterface SideNavContextValue {\n collapsed: boolean;\n mobileOpen: boolean;\n mode: SideNavMode;\n showCollapseToggle: boolean;\n setMobileOpen: (open: boolean) => void;\n toggleCollapse: () => void;\n}\n\nconst SideNavContext = createContext<SideNavContextValue>({\n collapsed: false,\n mobileOpen: false,\n mode: 'desktop',\n showCollapseToggle: true,\n setMobileOpen: () => {},\n toggleCollapse: () => {},\n});\n\n// ─── SideNav ─────────────────────────────────────────────────────────────────\n\nexport interface SideNavProps {\n /** Collapsed mode (icon-only). When omitted, auto-collapses on tablet viewports. */\n collapsed?: boolean;\n /** Callback when collapsed state changes (via toggle button or responsive breakpoint). */\n onCollapsedChange?: (collapsed: boolean) => void;\n /** Show a collapse/expand toggle button in the sidebar (default true). */\n showCollapseToggle?: boolean;\n /** Width in pixels (default 260) */\n width?: number;\n /** Collapsed width in pixels (default 64) */\n collapsedWidth?: number;\n /**\n * Mobile viewport behavior.\n * - `'drawer'` (default): hamburger button with slide-out overlay drawer.\n * - `'collapsed'`: persistent collapsed icon-only strip (same as tablet).\n */\n mobileVariant?: 'drawer' | 'collapsed';\n /** Children (SideNav.Header, SideNav.Item, SideNav.Section, SideNav.Footer) */\n children?: React.ReactNode;\n /** Custom style */\n style?: React.CSSProperties;\n}\n\nconst SideNavRoot = memo(function SideNav({\n collapsed,\n onCollapsedChange,\n showCollapseToggle = true,\n width = 260,\n collapsedWidth = 64,\n mobileVariant = 'drawer',\n children,\n style,\n}: SideNavProps): React.ReactElement {\n const { tokens, theme } = useTheme();\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const [mobileOpen, setMobileOpen] = useState(false);\n const [userCollapsed, setUserCollapsed] = useState<boolean | null>(null);\n const [viewMode, setViewMode] = useState<SideNavMode>(() => {\n if (typeof window !== 'undefined') return getSideNavMode(window.innerWidth);\n return 'desktop';\n });\n \n useEffect(() => {\n if (typeof window === 'undefined') return;\n let rafId: number;\n const handleResize = () => {\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => setViewMode(getSideNavMode(window.innerWidth)));\n };\n window.addEventListener('resize', handleResize, { passive: true });\n return () => {\n window.removeEventListener('resize', handleResize);\n cancelAnimationFrame(rafId);\n };\n }, []);\n\n // When mobileVariant='collapsed', promote mobile to tablet (collapsed inline strip)\n const effectiveMode = mobileVariant === 'collapsed' && viewMode === 'mobile' ? 'tablet' : viewMode;\n\n // Reset user toggle when crossing breakpoints\n useEffect(() => {\n setUserCollapsed(null);\n }, [effectiveMode]);\n\n // Close mobile drawer when switching away from mobile\n useEffect(() => {\n if (effectiveMode !== 'mobile' && mobileOpen) setMobileOpen(false);\n }, [effectiveMode, mobileOpen]);\n \n // Close mobile nav on escape\n useEffect(() => {\n if (!mobileOpen) return;\n const handleEsc = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setMobileOpen(false);\n };\n document.addEventListener('keydown', handleEsc);\n return () => document.removeEventListener('keydown', handleEsc);\n }, [mobileOpen]);\n\n // Resolve collapsed: explicit prop > user toggle > auto (tablet = collapsed)\n const autoCollapsed = effectiveMode === 'tablet';\n const isCollapsed = collapsed !== undefined\n ? collapsed\n : userCollapsed !== null\n ? userCollapsed\n : autoCollapsed;\n const isMobile = effectiveMode === 'mobile';\n const navWidth = isCollapsed ? collapsedWidth : width;\n\n const handleToggleCollapse = useCallback(() => {\n const next = !isCollapsed;\n setUserCollapsed(next);\n onCollapsedChange?.(next);\n }, [isCollapsed, onCollapsedChange]);\n \n const navStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n width: isMobile ? 280 : navWidth,\n height: '100%',\n backgroundColor: isTransparentTheme\n ? 'rgba(15, 12, 30, 0.7)'\n : tokens.colors.background.surface,\n borderRight: `1px solid ${tokens.colors.border.muted}`,\n transition: 'width 0.25s ease, transform 0.25s ease',\n overflowX: 'hidden',\n overflowY: 'auto',\n flexShrink: 0,\n fontFamily: tokens.typography.fontFamily.primary,\n ...(isTransparentTheme ? {\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n } : {}),\n ...style,\n };\n \n const contextValue: SideNavContextValue = { collapsed: isCollapsed, mobileOpen, mode: effectiveMode, showCollapseToggle, setMobileOpen, toggleCollapse: handleToggleCollapse };\n \n // Mobile: hamburger button + overlay drawer\n if (isMobile) {\n return (\n <SideNavContext.Provider value={contextValue}>\n {/* Hamburger button */}\n <button\n aria-label=\"Open navigation\"\n onClick={() => setMobileOpen(true)}\n style={{\n position: 'fixed',\n top: 6,\n left: 6,\n zIndex: 1001,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n border: 'none',\n borderRadius: tokens.borderRadius.sm,\n backgroundColor: 'transparent',\n color: tokens.colors.text.primary,\n cursor: 'pointer',\n padding: 0,\n transition: tokens.animation.fast,\n }}\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\">\n <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\" />\n <line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\" />\n <line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\" />\n </svg>\n </button>\n \n {/* Overlay */}\n <div\n aria-hidden=\"true\"\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1002,\n backgroundColor: `${tokens.colors.background.base}99`,\n backdropFilter: 'blur(4px)',\n opacity: mobileOpen ? 1 : 0,\n pointerEvents: mobileOpen ? 'auto' : 'none',\n transition: 'opacity 0.25s ease',\n }}\n onClick={() => setMobileOpen(false)}\n />\n \n {/* Slide-out nav */}\n <nav\n role=\"navigation\"\n aria-label=\"Main navigation\"\n style={{\n ...navStyle,\n position: 'fixed',\n top: 0,\n left: 0,\n zIndex: 1003,\n transform: mobileOpen ? 'translateX(0)' : 'translateX(-100%)',\n boxShadow: mobileOpen ? tokens.shadows.xl : 'none',\n }}\n >\n {children}\n </nav>\n </SideNavContext.Provider>\n );\n }\n \n return (\n <SideNavContext.Provider value={contextValue}>\n <nav role=\"navigation\" aria-label=\"Main navigation\" style={navStyle}>\n {children}\n </nav>\n </SideNavContext.Provider>\n );\n});\n\n// ─── Header ──────────────────────────────────────────────────────────────────\n\n/** Default height for the logo row so dashboard / operator / other apps align the same mark. */\nexport const SIDENAV_HEADER_LOGO_SLOT_HEIGHT_PX = 44;\n\nexport interface SideNavHeaderProps {\n /** Logo element (Icon, image, or ReactNode) */\n logo?: React.ReactNode;\n /** Compact logo shown when sidebar is collapsed or on tablet (e.g., just the icon mark) */\n collapsedLogo?: React.ReactNode;\n /** App title */\n title?: string;\n /** Subtitle or version */\n subtitle?: string;\n /** Role badge (e.g., \"Operator\", \"Admin\") */\n badge?: string;\n /** Badge variant for color */\n badgeVariant?: 'info' | 'success' | 'warning' | 'caution';\n /**\n * Fixed height (px) for the top logo band. Title, subtitle, and badge render below this band\n * so different logo assets still line up across apps (dashboard vs operator).\n */\n logoSlotHeight?: number;\n /** Children override (advanced: replaces default header content entirely) */\n children?: React.ReactNode;\n}\n\n/** Chevron toggle button for collapsing/expanding the sidebar. */\nfunction CollapseToggleButton() {\n const { tokens } = useTheme();\n const { collapsed, toggleCollapse } = useContext(SideNavContext);\n const [hovered, setHovered] = useState(false);\n\n return (\n <button\n type=\"button\"\n aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n onClick={toggleCollapse}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 28,\n height: 28,\n border: `1px solid ${hovered ? tokens.colors.border.focus : tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n backgroundColor: hovered ? `${tokens.colors.accent.primary}15` : 'transparent',\n color: hovered ? tokens.colors.accent.primary : tokens.colors.text.tertiary,\n cursor: 'pointer',\n flexShrink: 0,\n padding: 0,\n transition: tokens.animation.fast,\n outline: 'none',\n }}\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n transition: 'transform 0.25s ease',\n transform: collapsed ? 'rotate(180deg)' : 'rotate(0deg)',\n }}\n >\n <polyline points=\"15 18 9 12 15 6\" />\n </svg>\n </button>\n );\n}\n\nconst SideNavHeader = memo(function SideNavHeader({\n logo,\n collapsedLogo,\n title,\n subtitle,\n badge,\n badgeVariant = 'info',\n logoSlotHeight = SIDENAV_HEADER_LOGO_SLOT_HEIGHT_PX,\n children,\n}: SideNavHeaderProps): React.ReactElement {\n const { tokens } = useTheme();\n const { collapsed, mode, showCollapseToggle, toggleCollapse } = useContext(SideNavContext);\n const displayLogo = collapsed && collapsedLogo ? collapsedLogo : logo;\n const showToggle = showCollapseToggle && mode !== 'mobile';\n const hasMeta = Boolean(title || subtitle || badge);\n const slotH = logoSlotHeight;\n\n const badgeColors: Record<string, string> = {\n info: tokens.colors.accent.primary,\n success: tokens.colors.status.normal,\n warning: tokens.colors.status.caution,\n caution: tokens.colors.status.serious,\n };\n\n const metaBlock = !collapsed && hasMeta ? (\n <div style={{ flex: 1, minWidth: 0, width: '100%' }}>\n {title ? (\n <div style={{\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: tokens.typography.fontWeight.bold,\n color: tokens.colors.text.primary,\n lineHeight: tokens.typography.lineHeight.tight,\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}>\n {title}\n </div>\n ) : null}\n {(subtitle || badge) ? (\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.xs, marginTop: title ? '2px' : 0, flexWrap: 'wrap' }}>\n {subtitle ? (\n <span style={{\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.tertiary,\n }}>\n {subtitle}\n </span>\n ) : null}\n {badge ? (\n <span style={{\n fontSize: '0.6rem',\n fontWeight: tokens.typography.fontWeight.bold,\n color: badgeColors[badgeVariant] || safeAccentText(tokens.colors.accent.primary),\n backgroundColor: `${badgeColors[badgeVariant] || tokens.colors.accent.primary}18`,\n border: `1px solid ${badgeColors[badgeVariant] || tokens.colors.accent.primary}30`,\n padding: '1px 6px',\n borderRadius: tokens.borderRadius.sm,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}>\n {badge}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null;\n\n const logoSlot = displayLogo ? (\n <div\n style={{\n height: slotH,\n minHeight: slotH,\n maxHeight: slotH,\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: collapsed ? 'center' : 'flex-start',\n overflow: 'hidden',\n boxSizing: 'border-box',\n marginBottom: !collapsed && hasMeta && displayLogo ? tokens.spacing.sm : 0,\n }}\n >\n <div\n style={{\n maxHeight: '100%',\n maxWidth: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: collapsed ? 'center' : 'flex-start',\n minWidth: 0,\n cursor: collapsed && showToggle ? 'pointer' : undefined,\n }}\n onClick={collapsed && showToggle ? toggleCollapse : undefined}\n >\n {displayLogo}\n </div>\n </div>\n ) : null;\n\n if (children) {\n return (\n <div style={{\n padding: `${tokens.spacing.md} 16px ${tokens.spacing.md} 15px`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n flexShrink: 0,\n boxSizing: 'border-box',\n position: 'relative',\n }}>\n {children}\n </div>\n );\n }\n\n // Collapsed: centered logo in the same fixed slot height as expanded (visual parity with operator).\n if (collapsed) {\n return (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n padding: `${tokens.spacing.md} ${tokens.spacing.sm}`,\n minHeight: slotH + 24,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n flexShrink: 0,\n boxSizing: 'border-box',\n position: 'relative',\n justifyContent: 'center',\n }}>\n {logoSlot}\n {showToggle ? (\n <div style={{ position: 'absolute', right: 4, top: '50%', transform: 'translateY(-50%)' }}>\n <CollapseToggleButton />\n </div>\n ) : null}\n </div>\n );\n }\n\n return (\n <div style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'stretch',\n padding: `${tokens.spacing.md} 16px ${tokens.spacing.md} 15px`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n flexShrink: 0,\n boxSizing: 'border-box',\n position: 'relative',\n }}>\n {showToggle ? (\n <div style={{ position: 'absolute', top: tokens.spacing.sm, right: tokens.spacing.sm, zIndex: 1 }}>\n <CollapseToggleButton />\n </div>\n ) : null}\n <div style={{ paddingRight: showToggle ? 36 : 0, width: '100%', minWidth: 0 }}>\n {logoSlot}\n {metaBlock}\n </div>\n </div>\n );\n});\n\n// ─── Item ────────────────────────────────────────────────────────────────────\n\nexport interface SideNavItemProps {\n /** Icon name from zendir-ui icon library */\n icon?: IconName | React.ReactNode;\n /** Label text */\n label: string;\n /** Description text displayed beneath the label (hidden in collapsed mode) */\n description?: string;\n /** Small tag/chip displayed after the label (e.g. \"v2\", \"NEW\", \"BETA\") */\n tag?: string;\n /** Tag color variant */\n tagVariant?: 'default' | 'info' | 'success' | 'warning' | 'danger';\n /** Link href (renders <a>) */\n href?: string;\n /** Click handler (renders <button>) */\n onClick?: () => void;\n /** Active state */\n active?: boolean;\n /** Disabled state */\n disabled?: boolean;\n /** Notification badge count */\n badge?: number;\n /** External link indicator */\n external?: boolean;\n /**\n * Astro UX status level — renders a dual-coded indicator (color + shape).\n * Shapes per official Astro UXDS: normal = ● filled circle, standby = ◎ ring,\n * caution = ■ square, serious = ◆ diamond, critical = ▼ triangle, off = · small circle.\n */\n status?: 'off' | 'standby' | 'normal' | 'caution' | 'serious' | 'critical';\n /** Optional status label shown as tooltip or text next to status shape */\n statusLabel?: string;\n /** Right-side slot — arbitrary ReactNode rendered at the end of the item row */\n suffix?: React.ReactNode;\n}\n\nconst TAG_COLORS: Record<string, { bg: string; fg: string; border: string }> = {\n default: { bg: '#ffffff10', fg: '#9590a8', border: '#ffffff15' },\n info: { bg: '#8a2be218', fg: '#c4a0ff', border: '#8a2be230' },\n success: { bg: '#56f00018', fg: '#56f000', border: '#56f00030' },\n warning: { bg: '#fce83a18', fg: '#fce83a', border: '#fce83a30' },\n danger: { bg: '#ff383818', fg: '#ff3838', border: '#ff383830' },\n};\n\nconst SideNavItem = memo(function SideNavItem({\n icon,\n label,\n description,\n tag,\n tagVariant = 'default',\n href,\n onClick,\n active = false,\n disabled = false,\n badge,\n external = false,\n status,\n statusLabel,\n suffix,\n}: SideNavItemProps): React.ReactElement {\n const { tokens } = useTheme();\n const { collapsed, setMobileOpen } = useContext(SideNavContext);\n const [isHovered, setIsHovered] = useState(false);\n \n const handleClick = useCallback((e: React.MouseEvent) => {\n if (disabled) return;\n if (onClick && href && !e.metaKey && !e.ctrlKey && !e.shiftKey) {\n e.preventDefault();\n }\n setMobileOpen(false);\n onClick?.();\n }, [disabled, onClick, href, setMobileOpen]);\n \n // Status-aware accent: when an item has a status, tint active/hover with its color\n const statusColor = status ? (SIDENAV_STATUS_COLORS[status] ?? undefined) : undefined;\n const accentColor = statusColor && active ? statusColor : safeAccentText(tokens.colors.accent.primary);\n \n const hasDescription = !!description && !collapsed;\n \n const iconElement = typeof icon === 'string'\n ? <Icon name={icon as IconName} size={hasDescription ? 22 : 20} color={active ? accentColor : tokens.colors.text.secondary} />\n : icon;\n \n const itemStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: hasDescription ? 'flex-start' : 'center',\n gap: tokens.spacing.sm,\n padding: collapsed\n ? `${tokens.spacing.sm} 0`\n : hasDescription\n ? `10px 16px 10px 12px`\n : `${tokens.spacing.sm} 16px ${tokens.spacing.sm} 12px`,\n justifyContent: collapsed ? 'center' : 'flex-start',\n color: active\n ? accentColor\n : disabled\n ? tokens.colors.text.tertiary\n : tokens.colors.text.secondary,\n backgroundColor: active\n ? `${accentColor}12`\n : isHovered && !disabled\n ? `${accentColor}08`\n : 'transparent',\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: active ? tokens.typography.fontWeight.semibold : tokens.typography.fontWeight.normal,\n fontFamily: tokens.typography.fontFamily.primary,\n cursor: disabled ? 'not-allowed' : 'pointer',\n textDecoration: 'none',\n borderTop: 'none',\n borderRight: 'none',\n borderBottom: 'none',\n borderLeft: active\n ? `3px solid ${accentColor}`\n : '3px solid transparent',\n outline: 'none',\n width: '100%',\n boxSizing: 'border-box',\n transition: tokens.animation.fast,\n position: 'relative',\n opacity: disabled ? 0.5 : 1,\n };\n \n const tagColors = TAG_COLORS[tagVariant] ?? TAG_COLORS.default;\n \n const content = (\n <>\n {/* Icon + collapsed status overlay */}\n {iconElement && (\n <span style={{ flexShrink: 0, display: 'flex', position: 'relative', marginTop: hasDescription ? '2px' : 0 }}>\n {iconElement}\n {/* In collapsed mode, show a small status shape overlaid on the icon (top-left) */}\n {collapsed && status && (\n <span\n style={{\n position: 'absolute',\n top: -3,\n left: -4,\n lineHeight: 0,\n }}\n title={statusLabel ?? status}\n >\n <NavStatusShape status={status} size={7} />\n </span>\n )}\n </span>\n )}\n {!collapsed && (\n <>\n {/* Label + description block */}\n <span style={{ flex: 1, minWidth: 0, overflow: 'hidden' }}>\n <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n <span style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {label}\n </span>\n {tag && (\n <span style={{\n fontSize: '0.6rem',\n fontWeight: tokens.typography.fontWeight.bold,\n color: tagColors.fg,\n backgroundColor: tagColors.bg,\n border: `1px solid ${tagColors.border}`,\n padding: '1px 5px',\n borderRadius: tokens.borderRadius.sm,\n textTransform: 'uppercase',\n letterSpacing: '0.04em',\n flexShrink: 0,\n lineHeight: '1.3',\n whiteSpace: 'nowrap',\n }}>\n {tag}\n </span>\n )}\n </span>\n {description && (\n <span style={{\n display: 'block',\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.tertiary,\n lineHeight: tokens.typography.lineHeight.normal,\n marginTop: '2px',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n fontWeight: tokens.typography.fontWeight.normal,\n }}>\n {description}\n </span>\n )}\n </span>\n {/* Right-side trailing elements — badge, status, suffix, external icon */}\n {(badge !== undefined && badge > 0 || status || suffix || external) && (\n <span style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 8,\n flexShrink: 0,\n marginTop: hasDescription ? '2px' : 0,\n }}>\n {/* Badge */}\n {badge !== undefined && badge > 0 && (\n <span style={{\n fontSize: '0.65rem',\n fontWeight: tokens.typography.fontWeight.bold,\n color: '#fff',\n backgroundColor: tokens.colors.status.critical,\n borderRadius: tokens.borderRadius.full,\n minWidth: '18px',\n height: '18px',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '0 5px',\n flexShrink: 0,\n lineHeight: 1,\n boxSizing: 'border-box',\n }}>\n {badge > 99 ? '99+' : badge}\n </span>\n )}\n {/* Status */}\n {status && (\n <span\n style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }}\n role=\"status\"\n aria-label={`Status: ${statusLabel ?? status}`}\n title={statusLabel ?? status}\n >\n <NavStatusShape status={status} size={8} />\n </span>\n )}\n {/* Suffix */}\n {suffix && (\n <span style={{ flexShrink: 0, display: 'inline-flex', alignItems: 'center' }}>\n {suffix}\n </span>\n )}\n {/* External */}\n {external && (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" style={{ flexShrink: 0, opacity: 0.5 }}>\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n <polyline points=\"15 3 21 3 21 9\" />\n <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />\n </svg>\n )}\n </span>\n )}\n </>\n )}\n </>\n );\n \n const props = {\n style: itemStyle,\n onMouseEnter: () => setIsHovered(true),\n onMouseLeave: () => setIsHovered(false),\n title: collapsed ? label : undefined,\n 'aria-current': active ? ('page' as const) : undefined,\n 'aria-disabled': disabled,\n };\n \n if (href && !disabled) {\n return (\n <a href={href} onClick={handleClick} target={external ? '_blank' : undefined} rel={external ? 'noopener noreferrer' : undefined} {...props}>\n {content}\n </a>\n );\n }\n \n return (\n <button type=\"button\" onClick={handleClick} disabled={disabled} {...props}>\n {content}\n </button>\n );\n});\n\n// ─── Section ─────────────────────────────────────────────────────────────────\n\nexport interface SideNavSectionProps {\n /** Section title */\n title?: string;\n /** Children (SideNav.Item elements) */\n children?: React.ReactNode;\n}\n\nconst SideNavSection = memo(function SideNavSection({\n title,\n children,\n}: SideNavSectionProps): React.ReactElement {\n const { tokens } = useTheme();\n const { collapsed } = useContext(SideNavContext);\n \n return (\n <div style={{ marginTop: tokens.spacing.sm }}>\n {title && !collapsed && (\n <div style={{\n padding: `${tokens.spacing.xs} 16px ${tokens.spacing.xs} 15px`,\n fontSize: tokens.typography.fontSize.xxs,\n fontWeight: tokens.typography.fontWeight.bold,\n color: tokens.colors.text.tertiary,\n textTransform: 'uppercase',\n letterSpacing: '0.08em',\n }}>\n {title}\n </div>\n )}\n {collapsed && title && (\n <div style={{\n width: '60%',\n height: '1px',\n backgroundColor: tokens.colors.border.muted,\n margin: `${tokens.spacing.xs} auto`,\n }} />\n )}\n {children}\n </div>\n );\n});\n\n// ─── Divider ─────────────────────────────────────────────────────────────────\n\nexport interface SideNavDividerProps {\n /** Optional label shown in the center of the divider line */\n label?: string;\n}\n\nconst SideNavDivider = memo(function SideNavDivider({\n label,\n}: SideNavDividerProps): React.ReactElement {\n const { tokens } = useTheme();\n const { collapsed } = useContext(SideNavContext);\n\n if (collapsed) {\n return (\n <div style={{\n width: '60%',\n height: '1px',\n backgroundColor: tokens.colors.border.muted,\n margin: `${tokens.spacing.sm} auto`,\n }} />\n );\n }\n\n if (label) {\n return (\n <div style={{\n display: 'flex',\n alignItems: 'center',\n gap: tokens.spacing.sm,\n padding: `${tokens.spacing.sm} 16px ${tokens.spacing.sm} 15px`,\n }}>\n <div style={{ flex: 1, height: '1px', backgroundColor: tokens.colors.border.muted }} />\n <span style={{\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.tertiary,\n textTransform: 'uppercase',\n letterSpacing: '0.06em',\n whiteSpace: 'nowrap',\n }}>\n {label}\n </span>\n <div style={{ flex: 1, height: '1px', backgroundColor: tokens.colors.border.muted }} />\n </div>\n );\n }\n\n return (\n <div style={{\n height: '1px',\n backgroundColor: tokens.colors.border.muted,\n margin: `${tokens.spacing.sm} 16px ${tokens.spacing.sm} 15px`,\n }} />\n );\n});\n\n// ─── Footer ──────────────────────────────────────────────────────────────────\n\nexport interface SideNavFooterProps {\n children?: React.ReactNode;\n}\n\nconst SideNavFooter = memo(function SideNavFooter({\n children,\n}: SideNavFooterProps): React.ReactElement {\n const { tokens } = useTheme();\n \n return (\n <div style={{\n marginTop: 'auto',\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n paddingTop: tokens.spacing.xs,\n paddingBottom: tokens.spacing.xs,\n }}>\n {children}\n </div>\n );\n});\n\n// ─── Compound Export ─────────────────────────────────────────────────────────\n\ntype SideNavComponent = typeof SideNavRoot & {\n Header: typeof SideNavHeader;\n Item: typeof SideNavItem;\n Section: typeof SideNavSection;\n Divider: typeof SideNavDivider;\n Footer: typeof SideNavFooter;\n};\n\nexport const SideNav: SideNavComponent = Object.assign(SideNavRoot, {\n Header: SideNavHeader,\n Item: SideNavItem,\n Section: SideNavSection,\n Divider: SideNavDivider,\n Footer: SideNavFooter,\n});\n\nexport default SideNav;\n"],"names":["SideNav","SideNavHeader","SideNavItem","SideNavSection","SideNavDivider","SideNavFooter"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SidePanel.js","sources":["../../../src/react/core/SidePanel.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - SidePanel Component\r\n * \r\n * A sliding panel that pushes content instead of overlaying it.\r\n * Designed to be placed as a flex sibling so the parent layout\r\n * naturally adjusts when the panel opens/closes.\r\n * \r\n * Features:\r\n * - Push-content behavior (flex sibling, content adjusts automatically)\r\n * - Smooth slide-in/out animation\r\n * - Configurable width, position (left/right)\r\n * - Optional overlay backdrop (for modal-like behavior)\r\n * - Theme-integrated via useTheme()\r\n * - Accessible (focus trap, keyboard support)\r\n * - Close on escape key\r\n * \r\n * @example\r\n * ```tsx\r\n * // Push-content pattern: SidePanel as flex sibling\r\n * <div style={{ display: 'flex', height: '100vh' }}>\r\n * <main style={{ flex: 1, transition: 'all 0.3s ease' }}>\r\n * {children}\r\n * </main>\r\n * <SidePanel\r\n * open={isChatOpen}\r\n * onClose={() => setIsChatOpen(false)}\r\n * title=\"Chat Assistant\"\r\n * width={380}\r\n * >\r\n * <ChatPanel messages={messages} onSend={handleSend} />\r\n * </SidePanel>\r\n * </div>\r\n * ```\r\n */\r\n\r\nimport React, { memo, useEffect, useRef, useCallback, useState } from 'react';\r\nimport { useTheme } from '../theme';\r\nimport { classNames } from '../utils';\r\nimport { useBreakpoint } from './layout/useBreakpoint';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type SidePanelPosition = 'left' | 'right';\r\n\r\nexport interface SidePanelProps {\r\n /** Whether the panel is open */\r\n open: boolean;\r\n /** Called when the panel should close */\r\n onClose: () => void;\r\n /** Panel title displayed in the header */\r\n title?: string;\r\n /** Panel width in px or CSS string (default: 380) */\r\n width?: number | string;\r\n /** Which side the panel slides in from (default: 'right') */\r\n position?: SidePanelPosition;\r\n /** Show a close button in the header (default: true) */\r\n showCloseButton?: boolean;\r\n /** Close when pressing Escape (default: true) */\r\n closeOnEscape?: boolean;\r\n /** Show a semi-transparent overlay behind the panel (default: false) */\r\n overlay?: boolean;\r\n /** Close when clicking the overlay (default: true, only applies when overlay=true) */\r\n closeOnOverlayClick?: boolean;\r\n /** Panel content */\r\n children: React.ReactNode;\r\n /** Optional header actions (rendered in header, right of title) */\r\n headerActions?: React.ReactNode;\r\n /** Optional leading element (rendered in header, left of title — useful for collapse icons) */\r\n headerLeading?: React.ReactNode;\r\n /** Called when the header bar is clicked (useful for collapse-on-click) */\r\n onHeaderClick?: () => void;\r\n /** Custom style overrides for the header bar */\r\n headerStyle?: React.CSSProperties;\r\n /** Custom className */\r\n className?: string;\r\n /** Custom style overrides for the panel container */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// ============================================================================\r\n// Unique animation ID\r\n// ============================================================================\r\n\r\nlet sidePanelInstanceCount = 0;\r\n\r\n// ============================================================================\r\n// Component\r\n// ============================================================================\r\n\r\nexport const SidePanel = memo(function SidePanel({\r\n open,\r\n onClose,\r\n title,\r\n width = 380,\r\n position = 'right',\r\n showCloseButton = true,\r\n closeOnEscape = true,\r\n overlay = false,\r\n closeOnOverlayClick = true,\r\n children,\r\n headerActions,\r\n headerLeading,\r\n onHeaderClick,\r\n headerStyle: headerStyleProp,\r\n className = '',\r\n style: styleProp,\r\n}: SidePanelProps): React.ReactElement | null {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const [animState, setAnimState] = useState<'closed' | 'opening' | 'open' | 'closing'>('closed');\r\n const _instanceId = useRef(++sidePanelInstanceCount);\r\n\r\n const { isMobile } = useBreakpoint();\r\n const resolvedWidth = isMobile ? '100%' : (typeof width === 'number' ? `${width}px` : width);\r\n\r\n // Animate open/close\r\n useEffect(() => {\r\n if (open) {\r\n setAnimState('opening');\r\n const timer = setTimeout(() => setAnimState('open'), 30);\r\n return () => clearTimeout(timer);\r\n } else {\r\n if (animState === 'open' || animState === 'opening') {\r\n setAnimState('closing');\r\n const timer = setTimeout(() => setAnimState('closed'), 550);\r\n return () => clearTimeout(timer);\r\n }\r\n }\r\n }, [open]);\r\n\r\n // Escape key handler\r\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\r\n if (closeOnEscape && e.key === 'Escape') {\r\n onClose();\r\n }\r\n }, [closeOnEscape, onClose]);\r\n\r\n useEffect(() => {\r\n if (open) {\r\n document.addEventListener('keydown', handleKeyDown);\r\n return () => document.removeEventListener('keydown', handleKeyDown);\r\n }\r\n }, [open, handleKeyDown]);\r\n\r\n // Don't render at all when fully closed\r\n if (animState === 'closed') return null;\r\n\r\n const isVisible = animState === 'open';\r\n const slideFrom = position === 'right' ? 'translateX(100%)' : 'translateX(-100%)';\r\n\r\n return (\r\n <>\r\n {/* Optional overlay */}\r\n {overlay && (\r\n <div\r\n className=\"zendir-sidepanel-overlay\"\r\n onClick={closeOnOverlayClick ? onClose : undefined}\r\n style={{\r\n position: 'fixed',\r\n inset: 0,\r\n backgroundColor: isTransparentTheme ? `${tokens.colors.background.base}4D` : `${tokens.colors.background.base}66`,\r\n backdropFilter: isTransparentTheme ? 'blur(2px)' : undefined,\r\n WebkitBackdropFilter: isTransparentTheme ? 'blur(2px)' : undefined,\r\n zIndex: 999,\r\n opacity: isVisible ? 1 : 0,\r\n transition: 'opacity 0.3s ease',\r\n pointerEvents: isVisible ? 'auto' : 'none',\r\n }}\r\n />\r\n )}\r\n\r\n {/* Panel */}\r\n <div\r\n ref={panelRef}\r\n className={classNames('zendir-sidepanel', className)}\r\n style={{\r\n width: isVisible || animState === 'opening' ? resolvedWidth : '0px',\r\n minWidth: isVisible || animState === 'opening' ? resolvedWidth : '0px',\r\n maxWidth: resolvedWidth,\r\n height: '100%',\r\n overflow: 'hidden',\r\n transition: 'width 0.5s cubic-bezier(0.4, 0, 0.2, 1), min-width 0.5s cubic-bezier(0.4, 0, 0.2, 1)',\r\n flexShrink: 0,\r\n position: 'relative',\r\n zIndex: overlay ? 1000 : 'auto',\r\n ...(overlay && { position: 'fixed', top: 0, [position]: 0 }),\r\n ...styleProp,\r\n }}\r\n >\r\n <div\r\n style={{\r\n width: resolvedWidth,\r\n height: '100%',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n backgroundColor: isTransparentTheme\r\n ? 'rgba(15, 15, 35, 0.85)'\r\n : tokens.colors.background.surface,\r\n ...(isTransparentTheme && {\r\n backdropFilter: 'blur(16px)',\r\n WebkitBackdropFilter: 'blur(16px)',\r\n }),\r\n borderLeft: position === 'right' ? tokens.borders.divider : undefined,\r\n borderRight: position === 'left' ? tokens.borders.divider : undefined,\r\n transform: isVisible ? 'translateX(0)' : slideFrom,\r\n transition: 'transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)',\r\n overflow: 'hidden',\r\n }}\r\n >\r\n {/* Header */}\r\n {(title || showCloseButton || headerActions || headerLeading) && (\r\n <div\r\n onClick={onHeaderClick}\r\n role={onHeaderClick ? 'button' : undefined}\r\n tabIndex={onHeaderClick ? 0 : undefined}\r\n onKeyDown={onHeaderClick ? (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onHeaderClick(); } } : undefined}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'space-between',\r\n padding: `${tokens.spacing.md} ${tokens.spacing.md}`,\r\n borderBottom: tokens.borders.divider,\r\n flexShrink: 0,\r\n gap: tokens.spacing.sm,\r\n ...(onHeaderClick && { cursor: 'pointer' }),\r\n ...headerStyleProp,\r\n }}\r\n >\r\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.sm, flex: 1, minWidth: 0 }}>\r\n {headerLeading}\r\n {title && (\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: tokens.typography.fontSize.base,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n whiteSpace: 'nowrap',\r\n overflow: 'hidden',\r\n textOverflow: 'ellipsis',\r\n }}\r\n >\r\n {title}\r\n </h3>\r\n )}\r\n </div>\r\n <div style={{ display: 'flex', alignItems: 'center', gap: tokens.spacing.xs, flexShrink: 0 }}>\r\n {headerActions}\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n aria-label=\"Close panel\"\r\n style={{\r\n padding: tokens.spacing.sm,\r\n minWidth: 44,\r\n minHeight: 44,\r\n backgroundColor: 'transparent',\r\n border: 'none',\r\n borderRadius: tokens.borderRadius.sm,\r\n cursor: 'pointer',\r\n color: tokens.colors.text.muted ?? tokens.colors.text.tertiary,\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n transition: `color 0.15s ease, background-color 0.15s ease`,\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.color = tokens.colors.text.primary;\r\n e.currentTarget.style.backgroundColor = tokens.colors.background.base;\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.color = tokens.colors.text.muted ?? tokens.colors.text.tertiary;\r\n e.currentTarget.style.backgroundColor = 'transparent';\r\n }}\r\n >\r\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\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\" />\r\n </svg>\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Content */}\r\n <div\r\n style={{\r\n flex: 1,\r\n overflow: 'auto',\r\n minHeight: 0,\r\n }}\r\n >\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n});\r\n\r\nexport default SidePanel;\r\n"],"names":["SidePanel"],"mappings":";;;;;AAqFA,IAAI,yBAAyB;AAMtB,MAAM,YAAY,KAAK,SAASA,WAAU;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,sBAAsB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,OAAO;AACT,GAA8C;AAC5C,QAAM,EAAE,QAAQ,MAAA,IAAU,SAAA;AAC1B,QAAM,qBAAqB,UAAU,iBAAiB,UAAU,sBAAsB,UAAU;AAChG,QAAM,WAAW,OAAuB,IAAI;AAC5C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAoD,QAAQ;AAC1E,SAAO,EAAE,sBAAsB;AAEnD,QAAM,EAAE,SAAA,IAAa,cAAA;AACrB,QAAM,gBAAgB,WAAW,SAAU,OAAO,UAAU,WAAW,GAAG,KAAK,OAAO;AAGtF,YAAU,MAAM;AACd,QAAI,MAAM;AACR,mBAAa,SAAS;AACtB,YAAM,QAAQ,WAAW,MAAM,aAAa,MAAM,GAAG,EAAE;AACvD,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC,OAAO;AACL,UAAI,cAAc,UAAU,cAAc,WAAW;AACnD,qBAAa,SAAS;AACtB,cAAM,QAAQ,WAAW,MAAM,aAAa,QAAQ,GAAG,GAAG;AAC1D,eAAO,MAAM,aAAa,KAAK;AAAA,MACjC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAGT,QAAM,gBAAgB,YAAY,CAAC,MAAqB;AACtD,QAAI,iBAAiB,EAAE,QAAQ,UAAU;AACvC,cAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,eAAe,OAAO,CAAC;AAE3B,YAAU,MAAM;AACd,QAAI,MAAM;AACR,eAAS,iBAAiB,WAAW,aAAa;AAClD,aAAO,MAAM,SAAS,oBAAoB,WAAW,aAAa;AAAA,IACpE;AAAA,EACF,GAAG,CAAC,MAAM,aAAa,CAAC;AAGxB,MAAI,cAAc,SAAU,QAAO;AAEnC,QAAM,YAAY,cAAc;AAChC,QAAM,YAAY,aAAa,UAAU,qBAAqB;AAE9D,SACE,qBAAA,UAAA,EAEG,UAAA;AAAA,IAAA,WACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,sBAAsB,UAAU;AAAA,QACzC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,iBAAiB,qBAAqB,GAAG,OAAO,OAAO,WAAW,IAAI,OAAO,GAAG,OAAO,OAAO,WAAW,IAAI;AAAA,UAC7G,gBAAgB,qBAAqB,cAAc;AAAA,UACnD,sBAAsB,qBAAqB,cAAc;AAAA,UACzD,QAAQ;AAAA,UACR,SAAS,YAAY,IAAI;AAAA,UACzB,YAAY;AAAA,UACZ,eAAe,YAAY,SAAS;AAAA,QAAA;AAAA,MACtC;AAAA,IAAA;AAAA,IAKJ;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAW,WAAW,oBAAoB,SAAS;AAAA,QACnD,OAAO;AAAA,UACL,OAAO,aAAa,cAAc,YAAY,gBAAgB;AAAA,UAC9D,UAAU,aAAa,cAAc,YAAY,gBAAgB;AAAA,UACjE,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,UAAU;AAAA,UACV,QAAQ,UAAU,MAAO;AAAA,UACzB,GAAI,WAAW,EAAE,UAAU,SAAS,KAAK,GAAG,CAAC,QAAQ,GAAG,EAAA;AAAA,UACxD,GAAG;AAAA,QAAA;AAAA,QAGL,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,eAAe;AAAA,cACf,iBAAiB,qBACb,2BACA,OAAO,OAAO,WAAW;AAAA,cAC7B,GAAI,sBAAsB;AAAA,gBACxB,gBAAgB;AAAA,gBAChB,sBAAsB;AAAA,cAAA;AAAA,cAExB,YAAY,aAAa,UAAU,OAAO,QAAQ,UAAU;AAAA,cAC5D,aAAa,aAAa,SAAS,OAAO,QAAQ,UAAU;AAAA,cAC5D,WAAW,YAAY,kBAAkB;AAAA,cACzC,YAAY;AAAA,cACZ,UAAU;AAAA,YAAA;AAAA,YAIV,UAAA;AAAA,eAAA,SAAS,mBAAmB,iBAAiB,kBAC7C;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,SAAS;AAAA,kBACT,MAAM,gBAAgB,WAAW;AAAA,kBACjC,UAAU,gBAAgB,IAAI;AAAA,kBAC9B,WAAW,gBAAgB,CAAC,MAA2B;AAAE,wBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AAAE,wBAAE,eAAA;AAAkB,oCAAA;AAAA,oBAAiB;AAAA,kBAAE,IAAI;AAAA,kBAC9I,OAAO;AAAA,oBACL,SAAS;AAAA,oBACT,YAAY;AAAA,oBACZ,gBAAgB;AAAA,oBAChB,SAAS,GAAG,OAAO,QAAQ,EAAE,IAAI,OAAO,QAAQ,EAAE;AAAA,oBAClD,cAAc,OAAO,QAAQ;AAAA,oBAC7B,YAAY;AAAA,oBACZ,KAAK,OAAO,QAAQ;AAAA,oBACpB,GAAI,iBAAiB,EAAE,QAAQ,UAAA;AAAA,oBAC/B,GAAG;AAAA,kBAAA;AAAA,kBAGL,UAAA;AAAA,oBAAA,qBAAC,OAAA,EAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG,UAAU,KAC7F,UAAA;AAAA,sBAAA;AAAA,sBACA,SACC;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,OAAO;AAAA,4BACL,QAAQ;AAAA,4BACR,UAAU,OAAO,WAAW,SAAS;AAAA,4BACrC,YAAY,OAAO,WAAW,WAAW;AAAA,4BACzC,OAAO,OAAO,OAAO,KAAK;AAAA,4BAC1B,YAAY;AAAA,4BACZ,UAAU;AAAA,4BACV,cAAc;AAAA,0BAAA;AAAA,0BAGf,UAAA;AAAA,wBAAA;AAAA,sBAAA;AAAA,oBACH,GAEJ;AAAA,oBACA,qBAAC,OAAA,EAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,OAAO,QAAQ,IAAI,YAAY,KACtF,UAAA;AAAA,sBAAA;AAAA,sBACA,mBACC;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,MAAK;AAAA,0BACL,SAAS;AAAA,0BACT,cAAW;AAAA,0BACX,OAAO;AAAA,4BACL,SAAS,OAAO,QAAQ;AAAA,4BACxB,UAAU;AAAA,4BACV,WAAW;AAAA,4BACX,iBAAiB;AAAA,4BACjB,QAAQ;AAAA,4BACR,cAAc,OAAO,aAAa;AAAA,4BAClC,QAAQ;AAAA,4BACR,OAAO,OAAO,OAAO,KAAK,SAAS,OAAO,OAAO,KAAK;AAAA,4BACtD,SAAS;AAAA,4BACT,YAAY;AAAA,4BACZ,gBAAgB;AAAA,4BAChB,YAAY;AAAA,0BAAA;AAAA,0BAEd,cAAc,CAAC,MAAM;AACnB,8BAAE,cAAc,MAAM,QAAQ,OAAO,OAAO,KAAK;AACjD,8BAAE,cAAc,MAAM,kBAAkB,OAAO,OAAO,WAAW;AAAA,0BACnE;AAAA,0BACA,cAAc,CAAC,MAAM;AACnB,8BAAE,cAAc,MAAM,QAAQ,OAAO,OAAO,KAAK,SAAS,OAAO,OAAO,KAAK;AAC7E,8BAAE,cAAc,MAAM,kBAAkB;AAAA,0BAC1C;AAAA,0BAEA,UAAA,oBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,gBACnD,UAAA,oBAAC,QAAA,EAAK,GAAE,yGAAwG,EAAA,CAClH;AAAA,wBAAA;AAAA,sBAAA;AAAA,oBACF,EAAA,CAEJ;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAKJ;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO;AAAA,oBACL,MAAM;AAAA,oBACN,UAAU;AAAA,oBACV,WAAW;AAAA,kBAAA;AAAA,kBAGZ;AAAA,gBAAA;AAAA,cAAA;AAAA,YACH;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EACF,GACF;AAEJ,CAAC;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.js","sources":["../../../src/react/core/Tabs.tsx"],"sourcesContent":["/**\n * @zendir/ui - Tabs Component\n * \n * Tab navigation following Astro UX Design System.\n * \n * @example\n * ```tsx\n * <Tabs value={activeTab} onChange={setActiveTab}>\n * <Tabs.List>\n * <Tabs.Tab value=\"overview\">Overview</Tabs.Tab>\n * <Tabs.Tab value=\"telemetry\">Telemetry</Tabs.Tab>\n * <Tabs.Tab value=\"commands\">Commands</Tabs.Tab>\n * </Tabs.List>\n * <Tabs.Panel value=\"overview\">Overview content</Tabs.Panel>\n * <Tabs.Panel value=\"telemetry\">Telemetry content</Tabs.Panel>\n * <Tabs.Panel value=\"commands\">Commands content</Tabs.Panel>\n * </Tabs>\n * ```\n */\n\nimport React, { memo, createContext, useContext, useId } from 'react';\nimport { useTheme } from '../theme';\nimport { classNames, safeAccentText } from '../utils';\n\ninterface TabsContextValue {\n value: string;\n onChange: (value: string) => void;\n baseId: string;\n}\n\nconst TabsContext = createContext<TabsContextValue | null>(null);\n\nconst useTabsContext = () => {\n const context = useContext(TabsContext);\n if (!context) {\n throw new Error('Tabs components must be used within a Tabs provider');\n }\n return context;\n};\n\nexport interface TabsProps {\n /** Active tab value */\n value: string;\n /** Change handler */\n onChange: (value: string) => void;\n /** Children */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n}\n\nexport interface TabsListProps {\n /** Children (Tab components) */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n}\n\nexport interface TabProps {\n /** Tab value */\n value: string;\n /** Disabled state */\n disabled?: boolean;\n /** Icon before label */\n icon?: React.ReactNode;\n /** Children (label) */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n}\n\nexport interface TabsPanelProps {\n /** Panel value (must match Tab value) */\n value: string;\n /** Children */\n children: React.ReactNode;\n /** Custom className */\n className?: string;\n}\n\nconst TabsList = memo(function TabsList({\n children,\n className = '',\n}: TabsListProps): React.ReactElement {\n const { tokens } = useTheme();\n \n return (\n <div\n role=\"tablist\"\n className={classNames('zendir-tabs-list', className)}\n style={{\n display: 'flex',\n gap: tokens.spacing.xs,\n borderBottom: tokens.borders.divider,\n marginBottom: tokens.spacing.md,\n }}\n >\n {children}\n </div>\n );\n});\n\nconst Tab = memo(function Tab({\n value,\n disabled = false,\n icon,\n children,\n className = '',\n}: TabProps): React.ReactElement {\n const { tokens } = useTheme();\n const { value: activeValue, onChange, baseId } = useTabsContext();\n \n const isActive = value === activeValue;\n \n return (\n <button\n type=\"button\"\n role=\"tab\"\n id={`${baseId}-tab-${value}`}\n aria-controls={`${baseId}-panel-${value}`}\n aria-selected={isActive}\n aria-disabled={disabled}\n disabled={disabled}\n onClick={() => !disabled && onChange(value)}\n className={classNames('zendir-tab', isActive && 'zendir-tab--active', className)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: tokens.spacing.xs,\n padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: isActive ? tokens.typography.fontWeight.semibold : tokens.typography.fontWeight.normal,\n fontFamily: tokens.typography.fontFamily.primary,\n color: isActive ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.text.secondary,\n backgroundColor: 'transparent',\n border: 'none',\n borderBottom: `${tokens.borders.width.thick} solid ${isActive ? tokens.colors.accent.primary : 'transparent'}`,\n marginBottom: '-1px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n transition: `all ${tokens.animation.fast}`,\n outline: 'none',\n }}\n onMouseEnter={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.color = tokens.colors.text.primary;\n }\n }}\n onMouseLeave={(e) => {\n if (!disabled && !isActive) {\n e.currentTarget.style.color = tokens.colors.text.secondary;\n }\n }}\n onFocus={(e) => {\n e.currentTarget.style.boxShadow = tokens.borders.focusRing.subtle;\n }}\n onBlur={(e) => {\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {icon}\n {children}\n </button>\n );\n});\n\nconst TabsPanel = memo(function TabsPanel({\n value,\n children,\n className = '',\n}: TabsPanelProps): React.ReactElement | null {\n const { value: activeValue, baseId } = useTabsContext();\n \n if (value !== activeValue) return null;\n \n return (\n <div\n role=\"tabpanel\"\n id={`${baseId}-panel-${value}`}\n aria-labelledby={`${baseId}-tab-${value}`}\n className={classNames('zendir-tabs-panel', className)}\n >\n {children}\n </div>\n );\n});\n\nexport const Tabs = memo(function Tabs({\n value,\n onChange,\n children,\n className = '',\n}: TabsProps): React.ReactElement {\n const baseId = useId();\n return (\n <TabsContext.Provider value={{ value, onChange, baseId }}>\n <div className={classNames('zendir-tabs', className)}>\n {children}\n </div>\n </TabsContext.Provider>\n );\n}) as React.NamedExoticComponent<TabsProps> & {\n List: typeof TabsList;\n Tab: typeof Tab;\n Panel: typeof TabsPanel;\n};\n\n// Attach subcomponents\n(Tabs as any).List = TabsList;\n(Tabs as any).Tab = Tab;\n(Tabs as any).Panel = TabsPanel;\n\nexport default Tabs;\n"],"names":["TabsList","Tab","TabsPanel","Tabs"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Toast.js","sources":["../../../src/react/core/Toast.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Toast / Notification System\r\n * \r\n * Contextual toast notifications with queue management, auto-dismiss,\r\n * and Astro UX status colors. Essential for operator dashboards surfacing\r\n * connection events, command results, and system alerts.\r\n * \r\n * Astro UX Compliance:\r\n * - 6-level status colors (off, standby, normal, caution, serious, critical)\r\n * - Positioned top-right (operator dashboard convention)\r\n * - Reduced motion support\r\n * - ARIA live region for screen readers\r\n * \r\n * @example\r\n * ```tsx\r\n * // Wrap app in ToastProvider\r\n * <ToastProvider>\r\n * <App />\r\n * </ToastProvider>\r\n * \r\n * // Use in any component\r\n * const toast = useToast();\r\n * toast({ status: 'normal', title: 'Connected', message: 'MQTT link established' });\r\n * toast({ status: 'caution', title: 'SAFE Mode', message: 'Spacecraft entered SAFE mode', duration: 0 });\r\n * toast({ status: 'critical', title: 'Link Lost', message: 'Downlink signal below threshold' });\r\n * ```\r\n */\r\n\r\nimport React, { memo, createContext, useContext, useState, useCallback, useRef, useEffect } from 'react';\r\nimport { useTheme } from '../theme';\r\nimport { safeAccentText } from '../utils';\r\nimport { useBreakpoint } from './layout/useBreakpoint';\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport type ToastStatus = 'normal' | 'standby' | 'caution' | 'serious' | 'critical' | 'off';\r\nexport type ToastPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';\r\n\r\nexport interface ToastOptions {\r\n /** Status level (determines color) */\r\n status?: ToastStatus;\r\n /** Title text (bold) */\r\n title: string;\r\n /** Description text */\r\n message?: string;\r\n /** Auto-dismiss duration in ms (0 = persistent, default 5000) */\r\n duration?: number;\r\n /** Dismissible by user click (default true) */\r\n dismissible?: boolean;\r\n /** Icon override */\r\n icon?: React.ReactNode;\r\n /** Action button */\r\n action?: {\r\n label: string;\r\n onClick: () => void;\r\n };\r\n}\r\n\r\ninterface ToastInstance extends ToastOptions {\r\n id: string;\r\n createdAt: number;\r\n}\r\n\r\n// ─── Context ─────────────────────────────────────────────────────────────────\r\n\r\ntype ToastFn = (options: ToastOptions) => string;\r\n\r\ninterface ToastContextValue {\r\n toast: ToastFn;\r\n dismiss: (id: string) => void;\r\n dismissAll: () => void;\r\n}\r\n\r\nconst ToastContext = createContext<ToastContextValue | null>(null);\r\n\r\n// ─── Hook ────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Hook to show toast notifications.\r\n * Must be used within a `<ToastProvider>`.\r\n * \r\n * @example\r\n * ```tsx\r\n * const toast = useToast();\r\n * toast({ status: 'normal', title: 'Success', message: 'Command sent' });\r\n * ```\r\n */\r\nexport function useToast(): ToastFn {\r\n const ctx = useContext(ToastContext);\r\n if (!ctx) throw new Error('useToast must be used within <ToastProvider>');\r\n return ctx.toast;\r\n}\r\n\r\n/**\r\n * Hook to access full toast API (toast, dismiss, dismissAll).\r\n */\r\nexport function useToastManager(): ToastContextValue {\r\n const ctx = useContext(ToastContext);\r\n if (!ctx) throw new Error('useToastManager must be used within <ToastProvider>');\r\n return ctx;\r\n}\r\n\r\n// ─── Toast Item ──────────────────────────────────────────────────────────────\r\n\r\nconst ToastItem = memo(function ToastItem({\r\n toast: t,\r\n onDismiss,\r\n}: {\r\n toast: ToastInstance;\r\n onDismiss: (id: string) => void;\r\n}): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [exiting, setExiting] = useState(false);\r\n const dismissTimeoutRef = useRef<ReturnType<typeof setTimeout>>();\r\n \r\n // Cleanup dismiss timeout on unmount\r\n useEffect(() => {\r\n return () => { clearTimeout(dismissTimeoutRef.current); };\r\n }, []);\r\n \r\n const statusColors: Record<string, string> = {\r\n normal: tokens.colors.status.normal,\r\n standby: tokens.colors.status.standby,\r\n caution: tokens.colors.status.caution,\r\n serious: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n off: tokens.colors.status.off,\r\n };\r\n \r\n const color = statusColors[t.status || 'off'] || tokens.colors.status.off;\r\n \r\n const handleDismiss = useCallback(() => {\r\n setExiting(true);\r\n dismissTimeoutRef.current = setTimeout(() => onDismiss(t.id), 200);\r\n }, [t.id, onDismiss]);\r\n \r\n // Auto-dismiss\r\n useEffect(() => {\r\n const dur = t.duration ?? 5000;\r\n if (dur <= 0) return;\r\n const timer = setTimeout(handleDismiss, dur);\r\n return () => clearTimeout(timer);\r\n }, [t.duration, handleDismiss]);\r\n \r\n // Status icon — Astro UX dual-coded shapes\r\n const statusIcon = (() => {\r\n const s = 10;\r\n const h = s / 2;\r\n const status = t.status || 'off';\r\n const sharedStyle: React.CSSProperties = { flexShrink: 0, marginTop: '3px' };\r\n switch (status) {\r\n case 'critical':\r\n return (\r\n <svg width={s} height={s} viewBox={`0 0 ${s} ${s}`} style={sharedStyle}>\r\n <polygon points={`${h},${s} 0,0 ${s},0`} fill={color} />\r\n </svg>\r\n );\r\n case 'serious':\r\n return (\r\n <svg width={s} height={s} viewBox={`0 0 ${s} ${s}`} style={sharedStyle}>\r\n <polygon points={`${h},0 ${s},${h} ${h},${s} 0,${h}`} fill={color} />\r\n </svg>\r\n );\r\n case 'caution':\r\n return (\r\n <svg width={s} height={s} viewBox={`0 0 ${s} ${s}`} style={sharedStyle}>\r\n <rect width={s} height={s} fill={color} />\r\n </svg>\r\n );\r\n case 'standby':\r\n return (\r\n <svg width={s} height={s} viewBox=\"0 0 12 12\" style={sharedStyle}>\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 width={s} height={s} viewBox=\"0 0 12 12\" style={sharedStyle}>\r\n <circle cx=\"6\" cy=\"6\" r=\"3\" fill={color} />\r\n </svg>\r\n );\r\n default: // normal\r\n return (\r\n <svg width={s} height={s} viewBox={`0 0 ${s} ${s}`} style={sharedStyle}>\r\n <circle cx={h} cy={h} r={h} fill={color} />\r\n </svg>\r\n );\r\n }\r\n })();\r\n \r\n return (\r\n <div\r\n role=\"alert\"\r\n style={{\r\n display: 'flex',\r\n gap: tokens.spacing.sm,\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n border: `1px solid ${color}30`,\r\n borderLeft: `3px solid ${color}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: `${tokens.shadows.lg}, 0 0 20px ${color}15`,\r\n maxWidth: 'min(400px, calc(100vw - 32px))',\r\n minWidth: 'min(280px, calc(100vw - 32px))',\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n animation: exiting\r\n ? `zendir-toast-exit 200ms ${tokens.animation.easing.default} forwards`\r\n : `zendir-toast-enter 300ms ${tokens.animation.easing.default}`,\r\n transition: tokens.animation.normal,\r\n }}\r\n >\r\n {t.icon || statusIcon}\r\n \r\n <div style={{ flex: 1, minWidth: 0 }}>\r\n <div style={{\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n lineHeight: tokens.typography.lineHeight.tight,\r\n }}>\r\n {t.title}\r\n </div>\r\n {t.message && (\r\n <div style={{\r\n fontSize: tokens.typography.fontSize.xs,\r\n color: tokens.colors.text.secondary,\r\n marginTop: '2px',\r\n lineHeight: tokens.typography.lineHeight.normal,\r\n }}>\r\n {t.message}\r\n </div>\r\n )}\r\n {t.action && (\r\n <button\r\n onClick={t.action.onClick}\r\n style={{\r\n marginTop: tokens.spacing.xs,\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: safeAccentText(tokens.colors.accent.primary),\r\n background: 'none',\r\n border: 'none',\r\n padding: 0,\r\n cursor: 'pointer',\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n }}\r\n >\r\n {t.action.label}\r\n </button>\r\n )}\r\n </div>\r\n \r\n {(t.dismissible !== false) && (\r\n <button\r\n aria-label=\"Dismiss notification\"\r\n onClick={handleDismiss}\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 44,\r\n height: 44,\r\n border: 'none',\r\n background: 'none',\r\n color: tokens.colors.text.tertiary,\r\n cursor: 'pointer',\r\n borderRadius: tokens.borderRadius.sm,\r\n padding: tokens.spacing.xs,\r\n flexShrink: 0,\r\n transition: tokens.animation.fast,\r\n }}\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\r\n </svg>\r\n </button>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Provider ────────────────────────────────────────────────────────────────\r\n\r\nexport interface ToastProviderProps {\r\n /** Toast position (default: top-right) */\r\n position?: ToastPosition;\r\n /** Maximum visible toasts (default: 5) */\r\n maxVisible?: number;\r\n /** Default duration in ms (default: 5000) */\r\n defaultDuration?: number;\r\n /** Children */\r\n children: React.ReactNode;\r\n}\r\n\r\nexport const ToastProvider = memo(function ToastProvider({\r\n position = 'top-right',\r\n maxVisible = 5,\r\n defaultDuration = 5000,\r\n children,\r\n}: ToastProviderProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { isMobile } = useBreakpoint();\r\n const [toasts, setToasts] = useState<ToastInstance[]>([]);\r\n const idCounter = useRef(0);\r\n \r\n const toast = useCallback((options: ToastOptions): string => {\r\n const id = `toast-${++idCounter.current}-${Date.now()}`;\r\n const instance: ToastInstance = {\r\n ...options,\r\n id,\r\n createdAt: Date.now(),\r\n duration: options.duration ?? defaultDuration,\r\n };\r\n setToasts(prev => [...prev, instance].slice(-maxVisible * 2)); // Keep buffer\r\n return id;\r\n }, [defaultDuration, maxVisible]);\r\n \r\n const dismiss = useCallback((id: string) => {\r\n setToasts(prev => prev.filter(t => t.id !== id));\r\n }, []);\r\n \r\n const dismissAll = useCallback(() => {\r\n setToasts([]);\r\n }, []);\r\n \r\n // Position styles\r\n const inset = isMobile ? tokens.spacing.sm : tokens.spacing.md;\r\n const positionStyles: Record<ToastPosition, React.CSSProperties> = {\r\n 'top-right': { top: inset, right: inset },\r\n 'top-left': { top: inset, left: inset },\r\n 'bottom-right': { bottom: inset, right: inset },\r\n 'bottom-left': { bottom: inset, left: inset },\r\n 'top-center': { top: inset, left: '50%', transform: 'translateX(-50%)' },\r\n 'bottom-center': { bottom: inset, left: '50%', transform: 'translateX(-50%)' },\r\n };\r\n \r\n const isBottom = position.startsWith('bottom');\r\n const visibleToasts = toasts.slice(-maxVisible);\r\n \r\n return (\r\n <ToastContext.Provider value={{ toast, dismiss, dismissAll }}>\r\n {children}\r\n \r\n {/* Toast container */}\r\n {visibleToasts.length > 0 && (\r\n <div\r\n aria-live={visibleToasts.some(t => t.status === 'critical' || t.status === 'serious') ? 'assertive' : 'polite'}\r\n aria-atomic=\"false\"\r\n style={{\r\n position: 'fixed',\r\n zIndex: 9999,\r\n display: 'flex',\r\n flexDirection: isBottom ? 'column-reverse' : 'column',\r\n gap: tokens.spacing.sm,\r\n pointerEvents: 'none',\r\n ...positionStyles[position],\r\n }}\r\n >\r\n {visibleToasts.map(t => (\r\n <div key={t.id} style={{ pointerEvents: 'auto' }}>\r\n <ToastItem toast={t} onDismiss={dismiss} />\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n \r\n <style>{`\r\n @keyframes zendir-toast-enter {\r\n from { opacity: 0; transform: translateX(20px); }\r\n to { opacity: 1; transform: translateX(0); }\r\n }\r\n @keyframes zendir-toast-exit {\r\n from { opacity: 1; transform: translateX(0); }\r\n to { opacity: 0; transform: translateX(20px); }\r\n }\r\n @media (prefers-reduced-motion: reduce) {\r\n @keyframes zendir-toast-enter { from { opacity: 0; } to { opacity: 1; } }\r\n @keyframes zendir-toast-exit { from { opacity: 1; } to { opacity: 0; } }\r\n }\r\n `}</style>\r\n </ToastContext.Provider>\r\n );\r\n});\r\n\r\nexport default ToastProvider;\r\n"],"names":["ToastItem","ToastProvider"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Toggle.js","sources":["../../../src/react/core/Toggle.tsx"],"sourcesContent":["/**\n * @zendir/ui - Toggle/Switch Component\n * \n * Toggle switch following Astro UX Design System with Zendir purple accents.\n * \n * Astro UX Compliance:\n * - Binary on/off control\n * - Clear visual states\n * - Accessible keyboard support\n * \n * Zendir Enhancements:\n * - Purple accent when active\n * - Subtle glow effect\n * - Smooth spring animation\n * \n * @example\n * ```tsx\n * <Toggle checked={enabled} onChange={setEnabled} label=\"Enable telemetry\" />\n * ```\n */\n\nimport React, { memo, forwardRef, useId, useState } from 'react';\nimport { useTheme } from '../theme';\nimport { classNames } from '../utils';\n\nexport interface ToggleProps {\n /** Checked state */\n checked?: boolean;\n /** Change handler */\n onChange?: (checked: boolean) => void;\n /** Label text */\n label?: string;\n /** Label position */\n labelPosition?: 'left' | 'right';\n /** Disabled state */\n disabled?: boolean;\n /** Size variant */\n size?: 'small' | 'medium';\n /** Custom className */\n className?: string;\n /** Name attribute */\n name?: string;\n}\n\nexport const Toggle = memo(forwardRef<HTMLInputElement, ToggleProps>(function Toggle(\n {\n checked = false,\n onChange,\n label,\n labelPosition = 'right',\n disabled = false,\n size = 'medium',\n className = '',\n name,\n },\n ref\n): React.ReactElement {\n const { tokens, theme } = useTheme();\n const [focused, setFocused] = useState(false);\n const [hovered, setHovered] = useState(false);\n const id = useId();\n \n const sizeConfig = {\n small: { width: 34, height: 18, knobSize: 14 },\n medium: { width: 40, height: 22, knobSize: 16 },\n };\n const config = sizeConfig[size];\n // Touch target: 44px minimum via padding around the visual toggle\n const touchPadV = Math.max(0, (44 - config.height) / 2);\n const touchPadH = Math.max(0, (44 - config.width) / 2);\n \n const handleClick = () => {\n if (!disabled) {\n onChange?.(!checked);\n }\n };\n \n const isTransparentTheme = theme === 'transparent' || theme === 'transparent-bold' || theme === 'transparent-minimal';\n const transparentDefault = tokens.colors.interactive.transparentDefault;\n const transparentHover = tokens.colors.interactive.transparentHover;\n const bgColor = checked\n ? tokens.colors.accent.primary\n : isTransparentTheme && transparentDefault && transparentHover\n ? (hovered && !disabled ? transparentHover : transparentDefault)\n : hovered && !disabled\n ? `${tokens.colors.border.muted}`\n : tokens.colors.border.muted;\n \n const toggle = (\n <div\n role=\"switch\"\n aria-checked={checked}\n aria-disabled={disabled}\n aria-label={!label ? (name ?? 'Toggle') : undefined}\n tabIndex={disabled ? -1 : 0}\n onClick={handleClick}\n onKeyDown={(e) => (e.key === ' ' || e.key === 'Enter') && (e.preventDefault(), handleClick())}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n style={{\n padding: `${touchPadV}px ${touchPadH}px`,\n cursor: disabled ? 'not-allowed' : 'pointer',\n flexShrink: 0,\n outline: 'none',\n display: 'inline-flex',\n alignItems: 'center',\n }}\n >\n <div\n style={{\n width: config.width,\n height: config.height,\n backgroundColor: bgColor,\n ...(isTransparentTheme && !checked && { backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)' }),\n borderRadius: config.height / 2,\n opacity: disabled ? 0.5 : 1,\n position: 'relative',\n transition: `${tokens.animation.normal}, opacity ${tokens.animation.duration.normal}ms ${tokens.animation.easing.default}`,\n flexShrink: 0,\n boxShadow: checked \n ? `0 0 10px ${tokens.colors.accent.primary}25, inset 0 1px 1px rgba(255,255,255,0.08)` \n : focused \n ? `0 0 0 2px ${tokens.colors.accent.primary}25` \n : 'inset 0 1px 2px rgba(0,0,0,0.15)',\n }}\n >\n <div\n style={{\n position: 'absolute',\n top: (config.height - config.knobSize) / 2,\n left: checked ? config.width - config.knobSize - 3 : 3,\n width: config.knobSize,\n height: config.knobSize,\n backgroundColor: '#ffffff',\n borderRadius: '50%',\n boxShadow: checked \n ? '0 1px 4px rgba(0,0,0,0.25)' \n : '0 1px 3px rgba(0,0,0,0.2)',\n transition: tokens.animation.spring,\n transform: hovered && !disabled ? 'scale(1.05)' : 'scale(1)',\n }}\n />\n </div>\n <input\n ref={ref}\n type=\"checkbox\"\n id={id}\n name={name}\n checked={checked}\n disabled={disabled}\n onChange={() => {}}\n tabIndex={-1}\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n opacity: 0,\n width: 0,\n height: 0,\n pointerEvents: 'none',\n }}\n />\n </div>\n );\n \n if (!label) {\n return (\n <div className={classNames('zendir-toggle', className)}>\n {toggle}\n </div>\n );\n }\n \n return (\n <label\n htmlFor={id}\n className={classNames('zendir-toggle', className)}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: tokens.spacing.sm,\n cursor: disabled ? 'not-allowed' : 'pointer',\n flexDirection: labelPosition === 'left' ? 'row-reverse' : 'row',\n }}\n >\n {toggle}\n <span\n style={{\n fontSize: tokens.typography.fontSize.sm,\n color: disabled ? tokens.colors.text.muted : tokens.colors.text.primary,\n }}\n >\n {label}\n </span>\n </label>\n );\n}));\n\nexport default Toggle;\n"],"names":["Toggle"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","sources":["../../../src/react/core/Tooltip.tsx"],"sourcesContent":["/**\n * @zendir/ui - Tooltip Component\n * \n * Tooltip following Astro UX Design System.\n * Uses a portal to render in document.body, ensuring correct positioning\n * even when the trigger is inside containers with CSS transforms or overflow.\n * \n * @example\n * ```tsx\n * <Tooltip content=\"Click to enable telemetry\">\n * <Button>Enable</Button>\n * </Tooltip>\n * ```\n */\n\nimport React, { memo, useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';\nimport ReactDOM from 'react-dom';\nimport { useTheme } from '../theme';\nimport { classNames } from '../utils';\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface TooltipProps {\n /** Tooltip content */\n content: React.ReactNode;\n /** Placement */\n placement?: TooltipPlacement;\n /** Delay before showing (ms) */\n delay?: number;\n /** Disabled state */\n disabled?: boolean;\n /** Children element to trigger tooltip */\n children: React.ReactElement;\n /** Custom className */\n className?: string;\n}\n\nexport const Tooltip = memo(function Tooltip({\n content,\n placement = 'top',\n delay = 200,\n disabled = false,\n children,\n className = '',\n}: TooltipProps): React.ReactElement {\n const { tokens } = useTheme();\n const [visible, setVisible] = useState(false);\n const [positioned, setPositioned] = useState(false);\n const [position, setPosition] = useState({ top: 0, left: 0 });\n const triggerRef = useRef<HTMLDivElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<number>();\n const hideTimeoutRef = useRef<number>();\n \n const showTooltip = useCallback(() => {\n if (disabled) return;\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = undefined;\n }\n timeoutRef.current = window.setTimeout(() => {\n setVisible(true);\n setPositioned(false);\n }, delay);\n }, [disabled, delay]);\n \n const hideTooltip = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\n hideTimeoutRef.current = window.setTimeout(() => {\n setVisible(false);\n setPositioned(false);\n }, 100);\n }, []);\n \n const hideImmediately = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\n setVisible(false);\n setPositioned(false);\n }, []);\n \n const calculatePosition = useCallback(() => {\n if (!triggerRef.current || !tooltipRef.current) return;\n \n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n const gap = 8;\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n \n let top = 0;\n let left = 0;\n \n switch (placement) {\n case 'top':\n top = triggerRect.top - tooltipRect.height - gap;\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\n break;\n case 'bottom':\n top = triggerRect.bottom + gap;\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\n break;\n case 'left':\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\n left = triggerRect.left - tooltipRect.width - gap;\n break;\n case 'right':\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\n left = triggerRect.right + gap;\n break;\n }\n \n // Keep tooltip within viewport bounds\n if (left < gap) left = gap;\n if (left + tooltipRect.width > viewportWidth - gap) {\n left = viewportWidth - tooltipRect.width - gap;\n }\n if (top < gap) top = gap;\n if (top + tooltipRect.height > viewportHeight - gap) {\n top = viewportHeight - tooltipRect.height - gap;\n }\n \n setPosition({ top, left });\n setPositioned(true);\n }, [placement]);\n \n // Use useLayoutEffect for initial positioning - runs synchronously\n // before browser paint, preventing flash at wrong position\n useLayoutEffect(() => {\n if (visible && triggerRef.current && tooltipRef.current) {\n calculatePosition();\n }\n }, [visible, calculatePosition]);\n \n // Use useEffect for scroll/resize listeners\n useEffect(() => {\n if (!visible) return;\n \n const handleScrollOrResize = () => {\n calculatePosition();\n };\n \n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n \n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n };\n }, [visible, calculatePosition]);\n \n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\n useEffect(() => {\n if (!visible) return;\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n hideImmediately();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [visible, hideImmediately]);\n \n useEffect(() => {\n return () => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\n };\n }, []);\n \n return (\n <>\n <div\n ref={triggerRef}\n className={classNames('zendir-tooltip-trigger', className)}\n style={{ display: 'inline-flex', pointerEvents: 'auto', maxWidth: '100%', width: 'inherit' }}\n onMouseEnter={showTooltip}\n onMouseLeave={hideTooltip}\n onFocus={showTooltip}\n onBlur={hideTooltip}\n >\n {children}\n </div>\n \n {visible && ReactDOM.createPortal(\n <div\n ref={tooltipRef}\n role=\"tooltip\"\n onMouseEnter={showTooltip}\n onMouseLeave={hideTooltip}\n style={{\n position: 'fixed',\n top: positioned ? position.top : 0,\n left: positioned ? position.left : 0,\n width: 'max-content',\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\n backgroundColor: tokens.colors.background.elevated,\n color: tokens.colors.text.primary,\n fontSize: '0.75rem',\n fontWeight: 500,\n fontFamily: tokens.typography.fontFamily.primary,\n borderRadius: tokens.borderRadius.md,\n border: `1px solid ${tokens.colors.accent.primary}20`,\n boxShadow: positioned ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10` : 'none',\n zIndex: 2147483100,\n maxWidth: 'min(280px, calc(100vw - 24px))',\n whiteSpace: 'normal',\n pointerEvents: 'auto',\n visibility: positioned ? 'visible' : 'hidden',\n opacity: positioned ? 1 : 0,\n transition: positioned ? undefined : 'none',\n animation: positioned ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both` : 'none',\n backdropFilter: 'blur(8px)',\n }}\n >\n {content}\n <style>\n {`@keyframes zendir-tooltip { from { opacity: 0; transform: scale(0.95) translateY(${placement === 'bottom' ? '-4px' : placement === 'top' ? '4px' : '0'}); } to { opacity: 1; transform: scale(1) translateY(0); } }\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\n </style>\n </div>,\n document.body\n )}\n </>\n );\n});\n\nexport default Tooltip;\n"],"names":["Tooltip"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Typography.js","sources":["../../../src/react/core/Typography.tsx"],"sourcesContent":["/**\n * @zendir/ui - Typography Component\n * \n * AstroUXDS-compliant typography component providing consistent text styling\n * across the design system. All sizing uses rem units per Astro specifications.\n * \n * Typography Scale (Astro UX Design System):\n * - Display: Large hero text (display1, display2)\n * - Heading: Section headers (h1-h6)\n * - Body: Content text (body1, body2, body3)\n * - Control: UI element labels\n * - Mono: Tabular/code data\n * \n * @see https://www.astrouxds.com/foundations/typography/\n */\n\nimport React from 'react';\nimport { useTheme } from '../theme/ThemeProvider';\n\n// ============================================================================\n// Typography Variants (AstroUXDS Compliant)\n// ============================================================================\n\n/**\n * Typography variant following Astro UX Design System\n * Maps directly to official Astro typography tokens\n */\nexport type TypographyVariant =\n // Display - Large hero text\n | 'display1'\n | 'display2'\n // Headings\n | 'h1'\n | 'h1Bold'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n // Body text\n | 'body1'\n | 'body1Bold'\n | 'body2'\n | 'body2Bold'\n | 'body3'\n | 'body3Bold'\n // Control (UI labels)\n | 'control1'\n | 'control1Bold'\n // Compact variants for dense UIs (cards, badges)\n | 'compact' // 10px - for tight spaces\n | 'compactBold'\n | 'micro' // 9px - for very tight spaces\n | 'microBold'\n // Monospace for data/code\n | 'mono'\n | 'monoSmall'\n | 'monoCompact';\n\n/**\n * Semantic element to render\n */\nexport type TypographyElement =\n | 'h1'\n | 'h2'\n | 'h3'\n | 'h4'\n | 'h5'\n | 'h6'\n | 'p'\n | 'span'\n | 'div'\n | 'label'\n | 'code'\n | 'pre';\n\n/**\n * Text color variants\n */\nexport type TypographyColor =\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'muted'\n | 'inverse'\n | 'inherit'\n // Status colors\n | 'normal'\n | 'standby'\n | 'caution'\n | 'serious'\n | 'critical'\n | 'off';\n\nexport interface TypographyProps {\n /** Typography variant (required) */\n variant: TypographyVariant;\n /** HTML element to render */\n as?: TypographyElement;\n /** Text color */\n color?: TypographyColor;\n /** Enable tabular figures for numeric alignment */\n tabular?: boolean;\n /** Truncate with ellipsis */\n truncate?: boolean;\n /** Text alignment */\n align?: 'left' | 'center' | 'right';\n /** Disable text wrapping */\n noWrap?: boolean;\n /** Transform text case */\n transform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize';\n /** Additional CSS styles */\n style?: React.CSSProperties;\n /** Additional CSS class */\n className?: string;\n /** Children */\n children?: React.ReactNode;\n /** Test ID */\n 'data-testid'?: string;\n}\n\n// ============================================================================\n// AstroUXDS Font Stack (Standardized)\n// ============================================================================\n\n/**\n * Standard font family fallback stack per AstroUXDS\n * Roboto is the primary font for all Astro applications\n */\nexport const FONT_FAMILY_PRIMARY = 'var(--font-family-primary, \"Public Sans\", \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif)';\n\n/**\n * Monospace font stack for tabular data and code\n * Roboto Mono is the primary monospace font for Astro\n */\nexport const FONT_FAMILY_MONO = '\"Roboto Mono\", \"SF Mono\", \"Consolas\", \"Liberation Mono\", monospace';\n\n// ============================================================================\n// AstroUXDS Font Weights\n// Official weights: 300 (Light), 400 (Regular), 500 (Medium), 700 (Bold)\n// Note: Astro does NOT use 600 (semibold) - use 500 or 700 instead\n// ============================================================================\n\nexport const FONT_WEIGHTS = {\n light: 300,\n regular: 400,\n medium: 500,\n bold: 700,\n} as const;\n\n// ============================================================================\n// Typography Style Definitions\n// ============================================================================\n\ninterface TypographyStyleDef {\n fontSize: string;\n fontWeight: number;\n lineHeight: string;\n letterSpacing: string;\n fontFamily?: string;\n}\n\n/**\n * Get typography styles for a variant\n * All sizes in rem for Astro compliance\n */\nfunction getTypographyStyles(variant: TypographyVariant): TypographyStyleDef {\n const styles: Record<TypographyVariant, TypographyStyleDef> = {\n // Display variants\n display1: { fontSize: '3.75rem', fontWeight: 300, lineHeight: '4.375rem', letterSpacing: '-0.5px', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n display2: { fontSize: '3rem', fontWeight: 400, lineHeight: '3.5rem', letterSpacing: '0', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n \n // Heading variants\n h1: { fontSize: '2.125rem', fontWeight: 400, lineHeight: '2.5rem', letterSpacing: '0.25px', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h1Bold: { fontSize: '2.125rem', fontWeight: 700, lineHeight: '2.5rem', letterSpacing: '0.25px', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h2: { fontSize: '1.5rem', fontWeight: 400, lineHeight: '1.75rem', letterSpacing: '0', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h3: { fontSize: '1.25rem', fontWeight: 500, lineHeight: '1.5rem', letterSpacing: '0.15px', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h4: { fontSize: '1.25rem', fontWeight: 300, lineHeight: '1.5rem', letterSpacing: '0.15px', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h5: { fontSize: '1.125rem', fontWeight: 400, lineHeight: '1.5rem', letterSpacing: '0', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n h6: { fontSize: '1.125rem', fontWeight: 300, lineHeight: '1.5rem', letterSpacing: '0', fontFamily: 'var(--font-family-heading, \"Sora\", \"Roboto\", sans-serif)' },\n \n // Body variants\n body1: { fontSize: '1rem', fontWeight: 400, lineHeight: '1.5rem', letterSpacing: '0.5px' },\n body1Bold: { fontSize: '1rem', fontWeight: 700, lineHeight: '1.5rem', letterSpacing: '0.5px' },\n body2: { fontSize: '0.875rem', fontWeight: 400, lineHeight: '1.25rem', letterSpacing: '0.5px' },\n body2Bold: { fontSize: '0.875rem', fontWeight: 700, lineHeight: '1.25rem', letterSpacing: '0.5px' },\n body3: { fontSize: '0.75rem', fontWeight: 400, lineHeight: '1rem', letterSpacing: '0' },\n body3Bold: { fontSize: '0.75rem', fontWeight: 700, lineHeight: '1rem', letterSpacing: '0' },\n \n // Control variants (UI labels)\n control1: { fontSize: '1rem', fontWeight: 400, lineHeight: '1.25rem', letterSpacing: '0.5px' },\n control1Bold: { fontSize: '1rem', fontWeight: 700, lineHeight: '1.25rem', letterSpacing: '0.5px' },\n \n // Compact variants for dense UIs (cards, badges, data labels)\n compact: { fontSize: '0.625rem', fontWeight: 400, lineHeight: '0.875rem', letterSpacing: '0' }, // 10px\n compactBold: { fontSize: '0.625rem', fontWeight: 500, lineHeight: '0.875rem', letterSpacing: '0' },\n micro: { fontSize: '0.5625rem', fontWeight: 400, lineHeight: '0.75rem', letterSpacing: '0' }, // 9px\n microBold: { fontSize: '0.5625rem', fontWeight: 500, lineHeight: '0.75rem', letterSpacing: '0' },\n \n // Monospace variants for data/code\n mono: { fontSize: '0.875rem', fontWeight: 400, lineHeight: '1.25rem', letterSpacing: '0', fontFamily: FONT_FAMILY_MONO },\n monoSmall: { fontSize: '0.75rem', fontWeight: 400, lineHeight: '1rem', letterSpacing: '0', fontFamily: FONT_FAMILY_MONO },\n monoCompact: { fontSize: '0.625rem', fontWeight: 400, lineHeight: '0.875rem', letterSpacing: '0', fontFamily: FONT_FAMILY_MONO },\n };\n \n return styles[variant];\n}\n\n/**\n * Get default HTML element for variant\n */\nfunction getDefaultElement(variant: TypographyVariant): TypographyElement {\n if (variant.startsWith('display')) return 'h1';\n if (variant.startsWith('h1')) return 'h1';\n if (variant.startsWith('h2')) return 'h2';\n if (variant.startsWith('h3')) return 'h3';\n if (variant.startsWith('h4')) return 'h4';\n if (variant.startsWith('h5')) return 'h5';\n if (variant.startsWith('h6')) return 'h6';\n if (variant.startsWith('mono')) return 'code';\n if (variant.startsWith('control')) return 'label';\n return 'span';\n}\n\n// ============================================================================\n// Typography Component\n// ============================================================================\n\n/**\n * Typography component providing AstroUXDS-compliant text styling.\n * \n * @example\n * ```tsx\n * // Heading\n * <Typography variant=\"h3\">Section Title</Typography>\n * \n * // Body text\n * <Typography variant=\"body1\" color=\"secondary\">Description text</Typography>\n * \n * // Monospace data with tabular figures\n * <Typography variant=\"mono\" tabular>123.456</Typography>\n * \n * // Compact label for cards\n * <Typography variant=\"compact\" color=\"tertiary\">LABEL</Typography>\n * ```\n */\nexport const Typography: React.FC<TypographyProps> = ({\n variant,\n as,\n color = 'primary',\n tabular = false,\n truncate = false,\n align,\n noWrap = false,\n transform,\n style,\n className,\n children,\n 'data-testid': testId,\n}) => {\n const { tokens } = useTheme();\n \n const typographyStyles = getTypographyStyles(variant);\n const Element = as || getDefaultElement(variant);\n \n // Resolve color\n const getColor = (): string | undefined => {\n if (color === 'inherit') return 'inherit';\n if (color === 'primary') return tokens.colors.text.primary;\n if (color === 'secondary') return tokens.colors.text.secondary;\n if (color === 'tertiary') return tokens.colors.text.tertiary;\n if (color === 'muted') return tokens.colors.text.muted || tokens.colors.text.tertiary;\n if (color === 'inverse') return tokens.colors.text.inverse;\n // Status colors\n if (color === 'normal') return tokens.colors.status.normal;\n if (color === 'standby') return tokens.colors.status.standby;\n if (color === 'caution') return tokens.colors.status.caution;\n if (color === 'serious') return tokens.colors.status.serious;\n if (color === 'critical') return tokens.colors.status.critical;\n if (color === 'off') return tokens.colors.status.off;\n return undefined;\n };\n \n const combinedStyle: React.CSSProperties = {\n fontFamily: typographyStyles.fontFamily || FONT_FAMILY_PRIMARY,\n fontSize: typographyStyles.fontSize,\n fontWeight: typographyStyles.fontWeight,\n lineHeight: typographyStyles.lineHeight,\n letterSpacing: typographyStyles.letterSpacing,\n color: getColor(),\n margin: 0,\n padding: 0,\n // Tabular figures for numeric alignment\n ...(tabular && {\n fontVariantNumeric: 'tabular-nums',\n fontFeatureSettings: '\"tnum\"',\n }),\n // Truncation\n ...(truncate && {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }),\n // No wrap\n ...(noWrap && !truncate && {\n whiteSpace: 'nowrap',\n }),\n // Text alignment\n ...(align && { textAlign: align }),\n // Text transform\n ...(transform && { textTransform: transform }),\n // Custom styles (can override defaults)\n ...style,\n };\n \n return (\n <Element\n className={className}\n style={combinedStyle}\n data-testid={testId}\n >\n {children}\n </Element>\n );\n};\n\n// ============================================================================\n// Convenience Components\n// ============================================================================\n\n/** Display 1 - Largest display text */\nexport const Display1: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"display1\" {...props} />\n);\n\n/** Display 2 - Large display text */\nexport const Display2: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"display2\" {...props} />\n);\n\n/** Heading 1 */\nexport const H1: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'h1Bold' : 'h1'} {...props} />\n);\n\n/** Heading 2 */\nexport const H2: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"h2\" {...props} />\n);\n\n/** Heading 3 */\nexport const H3: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"h3\" {...props} />\n);\n\n/** Heading 4 */\nexport const H4: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"h4\" {...props} />\n);\n\n/** Heading 5 */\nexport const H5: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"h5\" {...props} />\n);\n\n/** Heading 6 */\nexport const H6: React.FC<Omit<TypographyProps, 'variant'>> = (props) => (\n <Typography variant=\"h6\" {...props} />\n);\n\n/** Body text level 1 (16px) */\nexport const Body1: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'body1Bold' : 'body1'} {...props} />\n);\n\n/** Body text level 2 (14px) */\nexport const Body2: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'body2Bold' : 'body2'} {...props} />\n);\n\n/** Body text level 3 (12px) */\nexport const Body3: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'body3Bold' : 'body3'} {...props} />\n);\n\n/** Compact text for dense UIs (10px) */\nexport const Compact: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'compactBold' : 'compact'} {...props} />\n);\n\n/** Micro text for very tight spaces (9px) */\nexport const Micro: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'microBold' : 'micro'} {...props} />\n);\n\n/** Monospace text for data/code */\nexport const Mono: React.FC<Omit<TypographyProps, 'variant'> & { size?: 'normal' | 'small' | 'compact' }> = ({ \n size = 'normal', \n tabular = true, // Default to tabular for mono\n ...props \n}) => {\n const variant = size === 'compact' ? 'monoCompact' : size === 'small' ? 'monoSmall' : 'mono';\n return <Typography variant={variant} tabular={tabular} {...props} />;\n};\n\n/** Data value text - monospace with tabular figures (for numeric displays) */\nexport const DataText: React.FC<Omit<TypographyProps, 'variant' | 'tabular'> & { \n size?: 'large' | 'normal' | 'small' | 'compact';\n}> = ({ size = 'normal', ...props }) => {\n const variantMap: Record<string, TypographyVariant> = {\n large: 'mono',\n normal: 'mono',\n small: 'monoSmall',\n compact: 'monoCompact',\n };\n return <Typography variant={variantMap[size]} tabular {...props} />;\n};\n\n/** Label text for form controls and card headers */\nexport const Label: React.FC<Omit<TypographyProps, 'variant'> & { bold?: boolean }> = ({ bold, ...props }) => (\n <Typography variant={bold ? 'control1Bold' : 'control1'} as=\"label\" {...props} />\n);\n\nexport default Typography;\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"propertyConfig.js","sources":["../../../src/react/core/propertyConfig.ts"],"sourcesContent":["/**\n * @zendir/ui - Property Configuration System\n * \n * Enterprise-grade property-to-icon/status/unit mapping system.\n * Provides automatic icon, status, and formatting for common telemetry properties.\n * \n * AstroUXDS Compliance:\n * - Uses official Astro icon names\n * - Status colors per AstroUXDS status system\n * - Dual-coding (color + shape) for accessibility\n * \n * Features:\n * - Auto-derive status from value + thresholds\n * - Contextual icons for space operations properties\n * - Unit formatting with SI prefixes\n * - Extensible configuration\n * \n * @example\n * ```tsx\n * import { getPropertyConfig, deriveStatus, PROPERTY_PRESETS } from '@zendir/ui';\n * \n * // Get config for a known property\n * const tempConfig = getPropertyConfig('temperature');\n * // { icon: 'thermal', unit: '°C', thresholds: { caution: 60, critical: 80 }, ... }\n * \n * // Derive status from value\n * const status = deriveStatus(75, tempConfig.thresholds);\n * // 'caution'\n * ```\n */\n\nimport type { StatusLevel } from '../utils';\nimport type { AstroIconName } from './AstroIcon';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Threshold configuration for automatic status derivation\n * Values are compared: value >= threshold triggers that status (high thresholds)\n * or value <= threshold triggers that status (low thresholds)\n */\nexport interface StatusThresholds {\n /** Value at or above which status becomes 'critical' (high is bad) */\n critical?: number;\n /** Value at or above which status becomes 'serious' (high is bad) */\n serious?: number;\n /** Value at or above which status becomes 'caution' (high is bad) */\n caution?: number;\n /** Value at or below which status becomes 'caution' (low is bad) */\n cautionLow?: number;\n /** Value at or below which status becomes 'serious' (low is bad) */\n seriousLow?: number;\n /** Value at or below which status becomes 'critical' (low is bad) */\n criticalLow?: number;\n}\n\n/**\n * Complete property configuration\n */\nexport interface PropertyConfig {\n /** Display name (sentence case per AstroUXDS) */\n label: string;\n /** Astro icon name */\n icon: AstroIconName;\n /** Unit of measurement */\n unit?: string;\n /** Precision for number formatting */\n precision?: number;\n /** Status thresholds for auto-derivation */\n thresholds?: StatusThresholds;\n /** Category for grouping */\n category?: PropertyCategory;\n /** Description for tooltips */\n description?: string;\n /** Format function override */\n format?: (value: number) => string;\n}\n\n/**\n * Property categories for organization\n */\nexport type PropertyCategory = \n | 'power'\n | 'thermal'\n | 'attitude'\n | 'communications'\n | 'propulsion'\n | 'navigation'\n | 'payload'\n | 'health'\n | 'orbital'\n | 'general';\n\n/**\n * Property key - either a preset name or custom string\n */\nexport type PropertyKey = keyof typeof PROPERTY_PRESETS | (string & {});\n\n// =============================================================================\n// Property Presets\n// =============================================================================\n\n/**\n * Predefined property configurations for common space operations telemetry\n * \n * These provide sensible defaults that can be overridden per-use.\n */\nexport const PROPERTY_PRESETS = {\n // === Power System ===\n battery: {\n label: 'Battery',\n icon: 'battery-full' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'power' as PropertyCategory,\n // Low values are bad for battery (use *Low thresholds)\n thresholds: { criticalLow: 10, seriousLow: 20, cautionLow: 30 },\n description: 'Battery state of charge',\n },\n voltage: {\n label: 'Voltage',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'V',\n precision: 2,\n category: 'power' as PropertyCategory,\n thresholds: { criticalLow: 24, cautionLow: 26 },\n description: 'Bus voltage',\n },\n current: {\n label: 'Current',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'A',\n precision: 2,\n category: 'power' as PropertyCategory,\n description: 'Current draw',\n },\n power: {\n label: 'Power',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'W',\n precision: 1,\n category: 'power' as PropertyCategory,\n description: 'Power consumption or generation',\n },\n solarPower: {\n label: 'Solar',\n icon: 'solar' as AstroIconName,\n unit: 'W',\n precision: 1,\n category: 'power' as PropertyCategory,\n description: 'Solar array output',\n },\n load: {\n label: 'Load',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'W',\n precision: 1,\n category: 'power' as PropertyCategory,\n description: 'Power load',\n },\n\n // === Thermal ===\n temperature: {\n label: 'Temperature',\n icon: 'thermal' as AstroIconName,\n unit: '°C',\n precision: 1,\n category: 'thermal' as PropertyCategory,\n thresholds: { critical: 80, serious: 70, caution: 60, cautionLow: -20, criticalLow: -40 },\n description: 'Component temperature',\n },\n heater: {\n label: 'Heater',\n icon: 'thermal' as AstroIconName,\n unit: '',\n category: 'thermal' as PropertyCategory,\n description: 'Heater status',\n },\n\n // === Attitude ===\n roll: {\n label: 'Roll',\n icon: 'explore' as AstroIconName,\n unit: '°',\n precision: 2,\n category: 'attitude' as PropertyCategory,\n thresholds: { critical: 45, caution: 30 },\n description: 'Roll angle',\n },\n pitch: {\n label: 'Pitch',\n icon: 'explore' as AstroIconName,\n unit: '°',\n precision: 2,\n category: 'attitude' as PropertyCategory,\n thresholds: { critical: 45, caution: 30 },\n description: 'Pitch angle',\n },\n yaw: {\n label: 'Yaw',\n icon: 'explore' as AstroIconName,\n unit: '°',\n precision: 2,\n category: 'attitude' as PropertyCategory,\n thresholds: { critical: 45, caution: 30 },\n description: 'Yaw angle',\n },\n pointingError: {\n label: 'Pointing error',\n icon: 'explore' as AstroIconName,\n unit: '°',\n precision: 3,\n category: 'attitude' as PropertyCategory,\n thresholds: { critical: 1.0, serious: 0.5, caution: 0.1 },\n description: 'Attitude pointing error',\n },\n angularVelocity: {\n label: 'Angular velocity',\n icon: 'explore' as AstroIconName,\n unit: '°/s',\n precision: 3,\n category: 'attitude' as PropertyCategory,\n thresholds: { critical: 5.0, caution: 2.0 },\n description: 'Rotation rate',\n },\n\n // === Communications ===\n signalStrength: {\n label: 'Signal',\n icon: 'antenna-receive' as AstroIconName,\n unit: 'dBm',\n precision: 1,\n category: 'communications' as PropertyCategory,\n thresholds: { criticalLow: -100, cautionLow: -85 },\n description: 'Received signal strength',\n },\n dataRate: {\n label: 'Data rate',\n icon: 'antenna-transmit' as AstroIconName,\n unit: 'Mbps',\n precision: 2,\n category: 'communications' as PropertyCategory,\n description: 'Communication data rate',\n },\n packetLoss: {\n label: 'Packet loss',\n icon: 'netcom' as AstroIconName,\n unit: '%',\n precision: 2,\n category: 'communications' as PropertyCategory,\n thresholds: { critical: 10, serious: 5, caution: 1 },\n description: 'Communication packet loss rate',\n },\n snr: {\n label: 'SNR',\n icon: 'antenna' as AstroIconName,\n unit: 'dB',\n precision: 1,\n category: 'communications' as PropertyCategory,\n thresholds: { criticalLow: 5, cautionLow: 10 },\n description: 'Signal-to-noise ratio',\n },\n queue: {\n label: 'Queue',\n icon: 'netcom' as AstroIconName,\n unit: 'pkts',\n precision: 0,\n category: 'communications' as PropertyCategory,\n thresholds: { critical: 1000, caution: 500 },\n description: 'Packet queue depth',\n },\n\n // === Propulsion ===\n fuelLevel: {\n label: 'Fuel',\n icon: 'propulsion-power' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'propulsion' as PropertyCategory,\n thresholds: { critical: 5, serious: 10, caution: 20 },\n description: 'Propellant remaining',\n },\n deltaV: {\n label: 'Delta-V',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'm/s',\n precision: 1,\n category: 'propulsion' as PropertyCategory,\n description: 'Available delta-V budget',\n },\n thrustLevel: {\n label: 'Thrust',\n icon: 'propulsion-power' as AstroIconName,\n unit: 'N',\n precision: 2,\n category: 'propulsion' as PropertyCategory,\n description: 'Current thrust output',\n },\n\n // === Navigation ===\n altitude: {\n label: 'Altitude',\n icon: 'altitude' as AstroIconName,\n unit: 'km',\n precision: 1,\n category: 'orbital' as PropertyCategory,\n thresholds: { criticalLow: 200, cautionLow: 250 },\n description: 'Orbital altitude',\n },\n velocity: {\n label: 'Velocity',\n icon: 'altitude' as AstroIconName,\n unit: 'km/s',\n precision: 3,\n category: 'orbital' as PropertyCategory,\n description: 'Orbital velocity',\n },\n inclination: {\n label: 'Inclination',\n icon: 'altitude' as AstroIconName,\n unit: '°',\n precision: 2,\n category: 'orbital' as PropertyCategory,\n description: 'Orbital inclination',\n },\n eccentricity: {\n label: 'Eccentricity',\n icon: 'altitude' as AstroIconName,\n unit: '',\n precision: 6,\n category: 'orbital' as PropertyCategory,\n description: 'Orbital eccentricity',\n },\n\n // === Payload ===\n payloadStatus: {\n label: 'Payload',\n icon: 'payload' as AstroIconName,\n unit: '',\n category: 'payload' as PropertyCategory,\n description: 'Payload operational status',\n },\n storageUsed: {\n label: 'Storage',\n icon: 'hardware' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'payload' as PropertyCategory,\n thresholds: { critical: 95, serious: 90, caution: 80 },\n description: 'Onboard storage usage',\n },\n\n // === Health ===\n cpuUsage: {\n label: 'CPU',\n icon: 'processor' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'health' as PropertyCategory,\n thresholds: { critical: 95, serious: 85, caution: 75 },\n description: 'Processor utilization',\n },\n memoryUsage: {\n label: 'Memory',\n icon: 'processor' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'health' as PropertyCategory,\n thresholds: { critical: 95, serious: 85, caution: 75 },\n description: 'Memory utilization',\n },\n uptime: {\n label: 'Uptime',\n icon: 'mission' as AstroIconName,\n unit: 's',\n precision: 0,\n category: 'health' as PropertyCategory,\n description: 'System uptime',\n },\n\n // === General ===\n count: {\n label: 'Count',\n icon: 'list' as AstroIconName,\n unit: '',\n precision: 0,\n category: 'general' as PropertyCategory,\n description: 'Item count',\n },\n percentage: {\n label: 'Percentage',\n icon: 'info' as AstroIconName,\n unit: '%',\n precision: 1,\n category: 'general' as PropertyCategory,\n description: 'Percentage value',\n },\n duration: {\n label: 'Duration',\n icon: 'schedule' as AstroIconName,\n unit: 's',\n precision: 0,\n category: 'general' as PropertyCategory,\n description: 'Time duration',\n },\n} as const satisfies Record<string, PropertyConfig>;\n\n// =============================================================================\n// Category Icons (for section headers)\n// =============================================================================\n\nexport const CATEGORY_ICONS: Record<PropertyCategory, AstroIconName> = {\n power: 'propulsion-power',\n thermal: 'thermal',\n attitude: 'explore',\n communications: 'antenna',\n propulsion: 'propulsion-power',\n navigation: 'gps-fixed',\n payload: 'payload',\n health: 'favorite',\n orbital: 'altitude',\n general: 'info',\n};\n\nexport const CATEGORY_LABELS: Record<PropertyCategory, string> = {\n power: 'Power',\n thermal: 'Thermal',\n attitude: 'Attitude',\n communications: 'Communications',\n propulsion: 'Propulsion',\n navigation: 'Navigation',\n payload: 'Payload',\n health: 'Health',\n orbital: 'Orbital',\n general: 'General',\n};\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Get property configuration by key\n * Returns preset config or a default config for unknown properties\n */\nexport function getPropertyConfig(key: PropertyKey): PropertyConfig {\n const preset = PROPERTY_PRESETS[key as keyof typeof PROPERTY_PRESETS];\n if (preset) {\n return preset;\n }\n\n // Return default config for unknown properties\n return {\n label: formatPropertyLabel(key),\n icon: 'info',\n unit: '',\n precision: 2,\n category: 'general',\n description: key,\n };\n}\n\n/**\n * Format a property key into a display label (sentence case)\n */\nexport function formatPropertyLabel(key: string): string {\n // Handle camelCase → Sentence case\n const words = key\n .replace(/([A-Z])/g, ' $1')\n .replace(/[_-]/g, ' ')\n .trim()\n .toLowerCase();\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\n/**\n * Derive status level from a value and thresholds\n * \n * @param value - Current value\n * @param thresholds - Status thresholds\n * @returns StatusLevel\n * \n * @example\n * ```tsx\n * deriveStatus(75, { caution: 60, critical: 80 }) // 'caution'\n * deriveStatus(25, { criticalLow: 10, cautionLow: 30 }) // 'caution'\n * deriveStatus(50, { caution: 60, critical: 80 }) // 'normal'\n * ```\n */\nexport function deriveStatus(\n value: number | undefined | null,\n thresholds?: StatusThresholds\n): StatusLevel {\n if (value === undefined || value === null || !Number.isFinite(value)) {\n return 'off';\n }\n\n if (!thresholds) {\n return 'normal';\n }\n\n // Check high thresholds (value >= threshold is bad)\n if (thresholds.critical !== undefined && value >= thresholds.critical) {\n return 'critical';\n }\n if (thresholds.serious !== undefined && value >= thresholds.serious) {\n return 'serious';\n }\n if (thresholds.caution !== undefined && value >= thresholds.caution) {\n return 'caution';\n }\n\n // Check low thresholds (value <= threshold is bad)\n if (thresholds.criticalLow !== undefined && value <= thresholds.criticalLow) {\n return 'critical';\n }\n if (thresholds.seriousLow !== undefined && value <= thresholds.seriousLow) {\n return 'serious';\n }\n if (thresholds.cautionLow !== undefined && value <= thresholds.cautionLow) {\n return 'caution';\n }\n\n return 'normal';\n}\n\n/**\n * Derive status for battery specifically (low is bad)\n */\nexport function deriveBatteryStatus(level: number | undefined): StatusLevel {\n if (level === undefined || level === null) return 'off';\n if (level <= 10) return 'critical';\n if (level <= 20) return 'serious';\n if (level <= 30) return 'caution';\n return 'normal';\n}\n\n/**\n * Format a value with its property config\n */\nexport function formatPropertyValue(\n value: number | undefined | null,\n config: PropertyConfig\n): string {\n if (value === undefined || value === null || !Number.isFinite(value)) {\n return '--';\n }\n\n if (config.format) {\n return config.format(value);\n }\n\n const precision = config.precision ?? 2;\n const formatted = value.toFixed(precision);\n const unit = config.unit ?? '';\n \n return unit ? `${formatted}${unit.startsWith('°') || unit.startsWith('%') ? '' : ' '}${unit}` : formatted;\n}\n\n/**\n * Create a custom property config by merging with a preset\n */\nexport function createPropertyConfig(\n baseKey: PropertyKey,\n overrides: Partial<PropertyConfig>\n): PropertyConfig {\n const base = getPropertyConfig(baseKey);\n return { ...base, ...overrides };\n}\n\n/**\n * Get all properties in a category\n */\nexport function getPropertiesByCategory(category: PropertyCategory): Array<{ key: string; config: PropertyConfig }> {\n return Object.entries(PROPERTY_PRESETS)\n .filter(([_, config]) => config.category === category)\n .map(([key, config]) => ({ key, config }));\n}\n"],"names":[],"mappings":"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"}
|