@zendir/ui 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +419 -406
  2. package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
  3. package/dist/react/3d/ZenSpace3D.js.map +1 -1
  4. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
  5. package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
  6. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
  7. package/dist/react/3d/capturePngAnalysis.js.map +1 -1
  8. package/dist/react/3d/threeLoader.js.map +1 -1
  9. package/dist/react/astro/ClassificationBanner.js.map +1 -1
  10. package/dist/react/astro/GlobalStatusBar.js.map +1 -1
  11. package/dist/react/astro/MissionClock.js.map +1 -1
  12. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  13. package/dist/react/astro/Notification.js.map +1 -1
  14. package/dist/react/astro/Progress.js.map +1 -1
  15. package/dist/react/astro/SimulationControls.js.map +1 -1
  16. package/dist/react/astro/StatusIndicator.js.map +1 -1
  17. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  18. package/dist/react/cards/AccessCard.js.map +1 -1
  19. package/dist/react/cards/OrbitCard.js.map +1 -1
  20. package/dist/react/cards/SpacecraftCard.js.map +1 -1
  21. package/dist/react/cards/TelemetryCard.js.map +1 -1
  22. package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
  23. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  24. package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
  25. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
  26. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
  27. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  28. package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
  29. package/dist/react/charts/unified/domain.js.map +1 -1
  30. package/dist/react/charts/unified/generators.js.map +1 -1
  31. package/dist/react/charts/unified/presets.js.map +1 -1
  32. package/dist/react/charts/unified/sync.js.map +1 -1
  33. package/dist/react/charts/unified/theme.js.map +1 -1
  34. package/dist/react/charts/unified/useChartStream.js.map +1 -1
  35. package/dist/react/chatgpt/index.js.map +1 -1
  36. package/dist/react/context/CategoryContext.js.map +1 -1
  37. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  38. package/dist/react/context/SpatialSelectionContext.js.map +1 -1
  39. package/dist/react/core/data/DataTable.js.map +1 -1
  40. package/dist/react/core/data/DataValue.js.map +1 -1
  41. package/dist/react/core/data/propertyConfig.js.map +1 -1
  42. package/dist/react/core/display/AstroIcon.js.map +1 -1
  43. package/dist/react/core/display/Badge.js.map +1 -1
  44. package/dist/react/core/display/CardHeader.js.map +1 -1
  45. package/dist/react/core/display/Container.js.map +1 -1
  46. package/dist/react/core/display/CopyButton.js.map +1 -1
  47. package/dist/react/core/display/GlassCard.js.map +1 -1
  48. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
  49. package/dist/react/core/display/Icon.js.map +1 -1
  50. package/dist/react/core/display/Typography.js.map +1 -1
  51. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
  52. package/dist/react/core/feedback/Dialog.js.map +1 -1
  53. package/dist/react/core/feedback/Toast.js.map +1 -1
  54. package/dist/react/core/inputs/Button.js.map +1 -1
  55. package/dist/react/core/inputs/Checkbox.js.map +1 -1
  56. package/dist/react/core/inputs/Input.js.map +1 -1
  57. package/dist/react/core/inputs/LimitsBar.js.map +1 -1
  58. package/dist/react/core/inputs/NumberInput.js.map +1 -1
  59. package/dist/react/core/inputs/PinInput.js.map +1 -1
  60. package/dist/react/core/inputs/Select.js.map +1 -1
  61. package/dist/react/core/inputs/Toggle.js.map +1 -1
  62. package/dist/react/core/layout/Box.js.map +1 -1
  63. package/dist/react/core/layout/Center.js.map +1 -1
  64. package/dist/react/core/layout/Divider.js.map +1 -1
  65. package/dist/react/core/layout/Flex.js.map +1 -1
  66. package/dist/react/core/layout/Grid.js.map +1 -1
  67. package/dist/react/core/layout/Spacer.js.map +1 -1
  68. package/dist/react/core/layout/Stack.js.map +1 -1
  69. package/dist/react/core/layout/responsive.js.map +1 -1
  70. package/dist/react/core/layout/useBreakpoint.js.map +1 -1
  71. package/dist/react/core/navigation/AppBar.js.map +1 -1
  72. package/dist/react/core/navigation/Pagination.js.map +1 -1
  73. package/dist/react/core/navigation/SideNav.js.map +1 -1
  74. package/dist/react/core/navigation/Tabs.js.map +1 -1
  75. package/dist/react/core/overlays/Popover.js.map +1 -1
  76. package/dist/react/core/overlays/SidePanel.js.map +1 -1
  77. package/dist/react/core/overlays/Tooltip.js.map +1 -1
  78. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
  79. package/dist/react/core/widgets/Capture.js.map +1 -1
  80. package/dist/react/core/widgets/ChatPanel.js.map +1 -1
  81. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
  82. package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
  83. package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
  84. package/dist/react/core/widgets/FileExplorer.js.map +1 -1
  85. package/dist/react/core/widgets/HexViewer.js.map +1 -1
  86. package/dist/react/core/widgets/ImageFrame.js.map +1 -1
  87. package/dist/react/core/widgets/ImageGallery.js.map +1 -1
  88. package/dist/react/core/widgets/ImagePanel.js.map +1 -1
  89. package/dist/react/core/widgets/LogViewer.js.map +1 -1
  90. package/dist/react/core/widgets/MessageStream.js.map +1 -1
  91. package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
  92. package/dist/react/core/widgets/PacketViewer.js.map +1 -1
  93. package/dist/react/hooks/useCompactMode.js.map +1 -1
  94. package/dist/react/hooks/useSimulationScene.js.map +1 -1
  95. package/dist/react/panels/LayerControlPanel.js.map +1 -1
  96. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
  97. package/dist/react/shared/ErrorBoundary.js.map +1 -1
  98. package/dist/react/shared/Skeleton.js.map +1 -1
  99. package/dist/react/theme/ThemeProvider.js +2 -1
  100. package/dist/react/theme/ThemeProvider.js.map +1 -1
  101. package/dist/react/theme/cardAccent.js.map +1 -1
  102. package/dist/react/theme/config.js.map +1 -1
  103. package/dist/react/theme/types.js.map +1 -1
  104. package/dist/react/types.js.map +1 -1
  105. package/dist/react/utils/categoryPalette.js.map +1 -1
  106. package/dist/react/utils/index.js.map +1 -1
  107. package/dist/style.css +216 -216
  108. package/dist/tokens/core.js.map +1 -1
  109. package/dist/tokens/index.js.map +1 -1
  110. package/dist/tokens/palettes/astro.js.map +1 -1
  111. package/dist/tokens/palettes/index.js.map +1 -1
  112. package/dist/tokens/palettes/tailwind.js.map +1 -1
  113. package/dist/tokens/palettes/zendir.js.map +1 -1
  114. package/dist/tokens/themes.js.map +1 -1
  115. package/dist/tokens/tokens.css +533 -533
  116. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SidePanel.js","names":[],"sources":["../../../../src/react/core/overlays/SidePanel.tsx"],"sourcesContent":["/**\n * @zendir/ui - SidePanel Component\n *\n * A sliding panel that pushes content instead of overlaying it.\n * Designed to be placed as a flex sibling so the parent layout\n * naturally adjusts when the panel opens/closes.\n *\n * Features:\n * - Push-content behavior (flex sibling, content adjusts automatically)\n * - Smooth slide-in/out animation\n * - Configurable width, position (left/right)\n * - Optional overlay backdrop (for modal-like behavior)\n * - Theme-integrated via useTheme()\n * - Accessible (focus trap, keyboard support)\n * - Close on escape key\n *\n * @example\n * ```tsx\n * // Push-content pattern: SidePanel as flex sibling\n * <div style={{ display: 'flex', height: '100vh' }}>\n * <main style={{ flex: 1, transition: 'all 0.3s ease' }}>\n * {children}\n * </main>\n * <SidePanel\n * open={isChatOpen}\n * onClose={() => setIsChatOpen(false)}\n * title=\"Chat Assistant\"\n * width={380}\n * >\n * <ChatPanel messages={messages} onSend={handleSend} />\n * </SidePanel>\n * </div>\n * ```\n */\n\nimport React, { memo, useEffect, useRef, useCallback, useState } from \"react\";\nimport { useTheme } from \"../../theme\";\nimport { classNames } from \"../../utils\";\nimport { useBreakpoint } from \"../layout/useBreakpoint\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type SidePanelPosition = \"left\" | \"right\";\n\nexport interface SidePanelProps {\n /** Whether the panel is open */\n open: boolean;\n /** Called when the panel should close */\n onClose: () => void;\n /** Panel title displayed in the header */\n title?: string;\n /** Panel width in px or CSS string (default: 380) */\n width?: number | string;\n /** Which side the panel slides in from (default: 'right') */\n position?: SidePanelPosition;\n /** Show a close button in the header (default: true) */\n showCloseButton?: boolean;\n /** Close when pressing Escape (default: true) */\n closeOnEscape?: boolean;\n /** Show a semi-transparent overlay behind the panel (default: false) */\n overlay?: boolean;\n /** Close when clicking the overlay (default: true, only applies when overlay=true) */\n closeOnOverlayClick?: boolean;\n /** Panel content */\n children: React.ReactNode;\n /** Optional header actions (rendered in header, right of title) */\n headerActions?: React.ReactNode;\n /** Optional leading element (rendered in header, left of title — useful for collapse icons) */\n headerLeading?: React.ReactNode;\n /** Called when the header bar is clicked (useful for collapse-on-click) */\n onHeaderClick?: () => void;\n /** Custom style overrides for the header bar */\n headerStyle?: React.CSSProperties;\n /** Custom className */\n className?: string;\n /** Custom style overrides for the panel container */\n style?: React.CSSProperties;\n}\n\n// ============================================================================\n// Unique animation ID\n// ============================================================================\n\nlet sidePanelInstanceCount = 0;\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const SidePanel = memo(function SidePanel({\n open,\n onClose,\n title,\n width = 380,\n position = \"right\",\n showCloseButton = true,\n closeOnEscape = true,\n overlay = false,\n closeOnOverlayClick = true,\n children,\n headerActions,\n headerLeading,\n onHeaderClick,\n headerStyle: headerStyleProp,\n className = \"\",\n style: styleProp,\n}: SidePanelProps): React.ReactElement | null {\n const { tokens, theme } = useTheme();\n const isTransparentTheme =\n theme === \"transparent\" ||\n theme === \"transparent-bold\" ||\n theme === \"transparent-minimal\";\n const panelRef = useRef<HTMLDivElement>(null);\n const [animState, setAnimState] = useState<\n \"closed\" | \"opening\" | \"open\" | \"closing\"\n >(\"closed\");\n const _instanceId = useRef(++sidePanelInstanceCount);\n\n const { isMobile } = useBreakpoint();\n const resolvedWidth = isMobile\n ? \"100%\"\n : typeof width === \"number\"\n ? `${width}px`\n : width;\n\n // Animate open/close\n useEffect(() => {\n if (open) {\n setAnimState(\"opening\");\n const timer = setTimeout(() => setAnimState(\"open\"), 30);\n return () => clearTimeout(timer);\n } else {\n if (animState === \"open\" || animState === \"opening\") {\n setAnimState(\"closing\");\n const timer = setTimeout(() => setAnimState(\"closed\"), 550);\n return () => clearTimeout(timer);\n }\n }\n }, [open]);\n\n // Escape key handler\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (closeOnEscape && e.key === \"Escape\") {\n onClose();\n }\n },\n [closeOnEscape, onClose],\n );\n\n useEffect(() => {\n if (open) {\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }\n }, [open, handleKeyDown]);\n\n // Don't render at all when fully closed\n if (animState === \"closed\") return null;\n\n const isVisible = animState === \"open\";\n const slideFrom =\n position === \"right\" ? \"translateX(100%)\" : \"translateX(-100%)\";\n\n return (\n <>\n {/* Optional overlay */}\n {overlay && (\n <div\n className=\"zendir-sidepanel-overlay\"\n onClick={closeOnOverlayClick ? onClose : undefined}\n style={{\n position: \"fixed\",\n inset: 0,\n backgroundColor: isTransparentTheme\n ? `${tokens.colors.background.base}4D`\n : `${tokens.colors.background.base}66`,\n backdropFilter: isTransparentTheme ? \"blur(2px)\" : undefined,\n WebkitBackdropFilter: isTransparentTheme ? \"blur(2px)\" : undefined,\n zIndex: 999,\n opacity: isVisible ? 1 : 0,\n transition: \"opacity 0.3s ease\",\n pointerEvents: isVisible ? \"auto\" : \"none\",\n }}\n />\n )}\n\n {/* Panel */}\n <div\n ref={panelRef}\n className={classNames(\"zendir-sidepanel\", className)}\n style={{\n width: isVisible ? resolvedWidth : \"0px\",\n minWidth: isVisible ? resolvedWidth : \"0px\",\n maxWidth: resolvedWidth,\n height: \"100%\",\n overflow: \"hidden\",\n transition:\n \"width 0.6s cubic-bezier(0.2, 0.8, 0.2, 1), min-width 0.6s cubic-bezier(0.2, 0.8, 0.2, 1)\",\n flexShrink: 0,\n position: \"relative\",\n zIndex: overlay ? 1000 : \"auto\",\n ...(overlay && { position: \"fixed\", top: 0, [position]: 0 }),\n ...styleProp,\n }}\n >\n <div\n style={{\n width: resolvedWidth,\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n backgroundColor: isTransparentTheme\n ? \"rgba(15, 15, 35, 0.85)\"\n : tokens.colors.background.surface,\n ...(isTransparentTheme && {\n backdropFilter: \"blur(16px)\",\n WebkitBackdropFilter: \"blur(16px)\",\n }),\n borderLeft:\n position === \"right\" ? tokens.borders.divider : undefined,\n borderRight:\n position === \"left\" ? tokens.borders.divider : undefined,\n transform: isVisible ? \"translateX(0)\" : slideFrom,\n transition: \"transform 0.6s cubic-bezier(0.2, 0.8, 0.2, 1)\",\n overflow: \"hidden\",\n }}\n >\n {/* Header */}\n {(title || showCloseButton || headerActions || headerLeading) && (\n <div\n onClick={onHeaderClick}\n role={onHeaderClick ? \"button\" : undefined}\n tabIndex={onHeaderClick ? 0 : undefined}\n onKeyDown={\n onHeaderClick\n ? (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onHeaderClick();\n }\n }\n : undefined\n }\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: `${tokens.spacing.md} ${tokens.spacing.md}`,\n borderBottom: tokens.borders.divider,\n flexShrink: 0,\n gap: tokens.spacing.sm,\n ...(onHeaderClick && { cursor: \"pointer\" }),\n ...headerStyleProp,\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n flex: 1,\n minWidth: 0,\n }}\n >\n {headerLeading}\n {title && (\n <h3\n style={{\n margin: 0,\n fontSize: tokens.typography.fontSize.base,\n fontWeight: tokens.typography.fontWeight.semibold,\n color: tokens.colors.text.primary,\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n }}\n >\n {title}\n </h3>\n )}\n </div>\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.xs,\n flexShrink: 0,\n }}\n >\n {headerActions}\n {showCloseButton && (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close panel\"\n style={{\n padding: tokens.spacing.sm,\n minWidth: 44,\n minHeight: 44,\n backgroundColor: \"transparent\",\n border: \"none\",\n borderRadius: tokens.borderRadius.sm,\n cursor: \"pointer\",\n color:\n tokens.colors.text.muted ?? tokens.colors.text.tertiary,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: `color 0.15s ease, background-color 0.15s ease`,\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = tokens.colors.text.primary;\n e.currentTarget.style.backgroundColor =\n tokens.colors.background.base;\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color =\n tokens.colors.text.muted ?? tokens.colors.text.tertiary;\n e.currentTarget.style.backgroundColor = \"transparent\";\n }}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" />\n </svg>\n </button>\n )}\n </div>\n </div>\n )}\n\n {/* Content */}\n <div\n style={{\n flex: 1,\n overflow: \"auto\",\n minHeight: 0,\n }}\n >\n {children}\n </div>\n </div>\n </div>\n </>\n );\n});\n\nexport default SidePanel;\n"],"mappings":"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"}
1
+ {"version":3,"file":"SidePanel.js","names":[],"sources":["../../../../src/react/core/overlays/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 =\r\n theme === \"transparent\" ||\r\n theme === \"transparent-bold\" ||\r\n theme === \"transparent-minimal\";\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const [animState, setAnimState] = useState<\r\n \"closed\" | \"opening\" | \"open\" | \"closing\"\r\n >(\"closed\");\r\n const _instanceId = useRef(++sidePanelInstanceCount);\r\n\r\n const { isMobile } = useBreakpoint();\r\n const resolvedWidth = isMobile\r\n ? \"100%\"\r\n : typeof width === \"number\"\r\n ? `${width}px`\r\n : 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(\r\n (e: KeyboardEvent) => {\r\n if (closeOnEscape && e.key === \"Escape\") {\r\n onClose();\r\n }\r\n },\r\n [closeOnEscape, onClose],\r\n );\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 =\r\n 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\r\n ? `${tokens.colors.background.base}4D`\r\n : `${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 ? resolvedWidth : \"0px\",\r\n minWidth: isVisible ? resolvedWidth : \"0px\",\r\n maxWidth: resolvedWidth,\r\n height: \"100%\",\r\n overflow: \"hidden\",\r\n transition:\r\n \"width 0.6s cubic-bezier(0.2, 0.8, 0.2, 1), min-width 0.6s cubic-bezier(0.2, 0.8, 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:\r\n position === \"right\" ? tokens.borders.divider : undefined,\r\n borderRight:\r\n position === \"left\" ? tokens.borders.divider : undefined,\r\n transform: isVisible ? \"translateX(0)\" : slideFrom,\r\n transition: \"transform 0.6s cubic-bezier(0.2, 0.8, 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={\r\n onHeaderClick\r\n ? (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n onHeaderClick();\r\n }\r\n }\r\n : undefined\r\n }\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\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n flex: 1,\r\n minWidth: 0,\r\n }}\r\n >\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\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n flexShrink: 0,\r\n }}\r\n >\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:\r\n 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 =\r\n tokens.colors.background.base;\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.color =\r\n tokens.colors.text.muted ?? tokens.colors.text.tertiary;\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n }}\r\n >\r\n <svg\r\n width=\"20\"\r\n height=\"20\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n >\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"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/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, {\n memo,\n useState,\n useRef,\n useEffect,\n useLayoutEffect,\n useCallback,\n} 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={{\n display: \"inline-flex\",\n pointerEvents: \"auto\",\n maxWidth: \"100%\",\n width: \"inherit\",\n }}\n onMouseEnter={showTooltip}\n onMouseLeave={hideTooltip}\n onFocus={showTooltip}\n onBlur={hideTooltip}\n >\n {children}\n </div>\n\n {visible &&\n 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\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\n : \"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\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\n : \"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"],"mappings":"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"}
1
+ {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/Tooltip.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Tooltip Component\r\n *\r\n * Tooltip following Astro UX Design System.\r\n * Uses a portal to render in document.body, ensuring correct positioning\r\n * even when the trigger is inside containers with CSS transforms or overflow.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Tooltip content=\"Click to enable telemetry\">\r\n * <Button>Enable</Button>\r\n * </Tooltip>\r\n * ```\r\n */\r\n\r\nimport React, {\r\n memo,\r\n useState,\r\n useRef,\r\n useEffect,\r\n useLayoutEffect,\r\n useCallback,\r\n} from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { classNames } from \"../../utils\";\r\n\r\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\r\n\r\nexport interface TooltipProps {\r\n /** Tooltip content */\r\n content: React.ReactNode;\r\n /** Placement */\r\n placement?: TooltipPlacement;\r\n /** Delay before showing (ms) */\r\n delay?: number;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Children element to trigger tooltip */\r\n children: React.ReactElement;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\nexport const Tooltip = memo(function Tooltip({\r\n content,\r\n placement = \"top\",\r\n delay = 200,\r\n disabled = false,\r\n children,\r\n className = \"\",\r\n}: TooltipProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [visible, setVisible] = useState(false);\r\n const [positioned, setPositioned] = useState(false);\r\n const [position, setPosition] = useState({ top: 0, left: 0 });\r\n const triggerRef = useRef<HTMLDivElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<number>();\r\n const hideTimeoutRef = useRef<number>();\r\n\r\n const showTooltip = useCallback(() => {\r\n if (disabled) return;\r\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\r\n if (hideTimeoutRef.current) {\r\n clearTimeout(hideTimeoutRef.current);\r\n hideTimeoutRef.current = undefined;\r\n }\r\n timeoutRef.current = window.setTimeout(() => {\r\n setVisible(true);\r\n setPositioned(false);\r\n }, delay);\r\n }, [disabled, delay]);\r\n\r\n const hideTooltip = useCallback(() => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\r\n hideTimeoutRef.current = window.setTimeout(() => {\r\n setVisible(false);\r\n setPositioned(false);\r\n }, 100);\r\n }, []);\r\n\r\n const hideImmediately = useCallback(() => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n setVisible(false);\r\n setPositioned(false);\r\n }, []);\r\n\r\n const calculatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const gap = 8;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (placement) {\r\n case \"top\":\r\n top = triggerRect.top - tooltipRect.height - gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"bottom\":\r\n top = triggerRect.bottom + gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"left\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left - tooltipRect.width - gap;\r\n break;\r\n case \"right\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport bounds\r\n if (left < gap) left = gap;\r\n if (left + tooltipRect.width > viewportWidth - gap) {\r\n left = viewportWidth - tooltipRect.width - gap;\r\n }\r\n if (top < gap) top = gap;\r\n if (top + tooltipRect.height > viewportHeight - gap) {\r\n top = viewportHeight - tooltipRect.height - gap;\r\n }\r\n\r\n setPosition({ top, left });\r\n setPositioned(true);\r\n }, [placement]);\r\n\r\n // Use useLayoutEffect for initial positioning - runs synchronously\r\n // before browser paint, preventing flash at wrong position\r\n useLayoutEffect(() => {\r\n if (visible && triggerRef.current && tooltipRef.current) {\r\n calculatePosition();\r\n }\r\n }, [visible, calculatePosition]);\r\n\r\n // Use useEffect for scroll/resize listeners\r\n useEffect(() => {\r\n if (!visible) return;\r\n\r\n const handleScrollOrResize = () => {\r\n calculatePosition();\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.addEventListener(\"resize\", handleScrollOrResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.removeEventListener(\"resize\", handleScrollOrResize);\r\n };\r\n }, [visible, calculatePosition]);\r\n\r\n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\r\n useEffect(() => {\r\n if (!visible) return;\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n hideImmediately();\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleEscape);\r\n return () => document.removeEventListener(\"keydown\", handleEscape);\r\n }, [visible, hideImmediately]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n };\r\n }, []);\r\n\r\n return (\r\n <>\r\n <div\r\n ref={triggerRef}\r\n className={classNames(\"zendir-tooltip-trigger\", className)}\r\n style={{\r\n display: \"inline-flex\",\r\n pointerEvents: \"auto\",\r\n maxWidth: \"100%\",\r\n width: \"inherit\",\r\n }}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n >\r\n {children}\r\n </div>\r\n\r\n {visible &&\r\n ReactDOM.createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n style={{\r\n position: \"fixed\",\r\n top: positioned ? position.top : 0,\r\n left: positioned ? position.left : 0,\r\n width: \"max-content\",\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n color: tokens.colors.text.primary,\r\n fontSize: \"0.75rem\",\r\n fontWeight: 500,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n borderRadius: tokens.borderRadius.md,\r\n border: `1px solid ${tokens.colors.accent.primary}20`,\r\n boxShadow: positioned\r\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\r\n : \"none\",\r\n zIndex: 2147483100,\r\n maxWidth: \"min(280px, calc(100vw - 24px))\",\r\n whiteSpace: \"normal\",\r\n pointerEvents: \"auto\",\r\n visibility: positioned ? \"visible\" : \"hidden\",\r\n opacity: positioned ? 1 : 0,\r\n transition: positioned ? undefined : \"none\",\r\n animation: positioned\r\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\r\n : \"none\",\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n {content}\r\n <style>\r\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); } }\r\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\r\n </style>\r\n </div>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n});\r\n\r\nexport default Tooltip;\r\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityPlanner.js","names":[],"sources":["../../../../src/react/core/widgets/ActivityPlanner.tsx"],"sourcesContent":["/**\n * @zendir/ui - ActivityPlanner Component\n *\n * Activity scheduling and management panel. Creates, edits, and monitors\n * scheduled activities across timelines. Integrates with MissionCalendar\n * and Timeline components.\n *\n * Space Mission Features:\n * - Create/edit activities with command or script execution\n * - Drag-and-drop reorder within timeline\n * - Hazardous activity warnings (Astro UX)\n * - Activity lifecycle tracking (created → scheduled → started → completed)\n * - Overlap detection within a timeline\n * - Metadata recording (operator, environment, notes)\n * - Contact window awareness\n * - Batch scheduling\n *\n * @example\n * ```tsx\n * <ActivityPlanner\n * activities={activities}\n * timelines={timelines}\n * onActivityCreate={(a) => console.log('Create:', a)}\n * onActivityUpdate={(a) => console.log('Update:', a)}\n * />\n * ```\n */\n\nimport React, { useState, useMemo, useCallback, memo, useRef } from \"react\";\nimport { useTheme } from \"../../theme\";\nimport type {\n CalendarEvent,\n CalendarTimeline,\n ActivityStatus,\n ActivityType,\n} from \"./MissionCalendar\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ActivityFormData {\n title: string;\n type: ActivityType;\n start: string; // ISO string\n end: string; // ISO string\n timeline: string;\n description: string;\n command: string;\n script: string;\n hazardous: boolean;\n metadata: Record<string, string>;\n}\n\nexport interface ActivityPlannerProps {\n /** Activities to display */\n activities: CalendarEvent[];\n /** Available timelines */\n timelines: CalendarTimeline[];\n /** Currently selected date for scheduling */\n selectedDate?: Date;\n /** Height (default: 500) */\n height?: number | string;\n /** Title */\n title?: string;\n /** Read-only mode */\n readOnly?: boolean;\n /** Show overlap warnings */\n showOverlapWarning?: boolean;\n /** Called on create */\n onActivityCreate?: (activity: Partial<CalendarEvent>) => void;\n /** Called on update */\n onActivityUpdate?: (id: string, updates: Partial<CalendarEvent>) => void;\n /** Called on delete */\n onActivityDelete?: (id: string) => void;\n /** Called when requesting to view activity in calendar */\n onViewInCalendar?: (activity: CalendarEvent) => void;\n /** Called when requesting to view in timeline */\n onViewInTimeline?: (activity: CalendarEvent) => void;\n /** CSS class */\n className?: string;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Map activity status → Astro UX status color using tokens.\n * created/scheduled → standby (cyan)\n * started/completed → normal (green)\n * paused/stopped/disabled → caution (yellow)\n * error/failed/crashed → critical (red)\n */\nfunction getStatusColor(\n status?: ActivityStatus,\n tokens?: ReturnType<typeof useTheme>[\"tokens\"],\n): string {\n if (!status || !tokens) return tokens?.colors.text.muted ?? \"currentColor\";\n switch (status) {\n case \"created\":\n case \"scheduled\":\n return tokens.colors.status.standby;\n case \"started\":\n case \"completed\":\n return tokens.colors.status.normal;\n case \"paused\":\n case \"stopped\":\n case \"disabled\":\n return tokens.colors.status.caution;\n case \"error\":\n case \"failed\":\n case \"crashed\":\n return tokens.colors.status.critical;\n }\n}\n\n/**\n * Return Astro UX status shape + contextual icon.\n * The leading character is the Astro status shape for the mapped level:\n * standby → ◯ (ring), normal → ● (filled circle),\n * caution → ■ (square), critical → ▼ (triangle down)\n * Followed by a contextual glyph for the specific activity state.\n */\nfunction getStatusIcon(status?: ActivityStatus): string {\n if (!status) return \"◯\";\n switch (status) {\n case \"created\":\n return \"◯\"; // standby ring\n case \"scheduled\":\n return \"◯ ◉\"; // standby ring + scheduled\n case \"started\":\n return \"● ▶\"; // normal circle + play\n case \"completed\":\n return \"● ✓\"; // normal circle + check\n case \"paused\":\n return \"■ ⏸\"; // caution square + pause\n case \"stopped\":\n return \"■ ⏹\"; // caution square + stop\n case \"disabled\":\n return \"■ ⊘\"; // caution square + disabled\n case \"error\":\n case \"failed\":\n case \"crashed\":\n return \"▼\"; // critical triangle down\n }\n}\n\nfunction formatDate(d: Date): string {\n return d.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n });\n}\n\nfunction formatTime(d: Date): string {\n return d.toLocaleTimeString(\"en-US\", {\n hour12: false,\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n}\n\nfunction checkOverlap(a: CalendarEvent, b: CalendarEvent): boolean {\n if (!a.end || !b.end) return false;\n if (a.timeline !== b.timeline) return false;\n return a.start < b.end && b.start < a.end;\n}\n\n// =============================================================================\n// Form Component\n// =============================================================================\n\nconst ActivityForm = memo(function ActivityForm({\n timelines,\n selectedDate,\n tokens,\n onSubmit,\n onCancel,\n}: {\n timelines: CalendarTimeline[];\n selectedDate?: Date;\n tokens: ReturnType<typeof useTheme>[\"tokens\"];\n onSubmit: (data: ActivityFormData) => void;\n onCancel: () => void;\n}) {\n const now = selectedDate || new Date();\n const endDefault = new Date(now.getTime() + 3600000);\n\n const [form, setForm] = useState<ActivityFormData>({\n title: \"\",\n type: \"command\",\n start: now.toISOString().slice(0, 16),\n end: endDefault.toISOString().slice(0, 16),\n timeline: timelines[0]?.id || \"\",\n description: \"\",\n command: \"\",\n script: \"\",\n hazardous: false,\n metadata: {},\n });\n const [metaKey, setMetaKey] = useState(\"\");\n const [metaValue, setMetaValue] = useState(\"\");\n\n const addMetadata = useCallback(() => {\n if (metaKey.trim()) {\n setForm((prev) => ({\n ...prev,\n metadata: { ...prev.metadata, [metaKey.trim()]: metaValue },\n }));\n setMetaKey(\"\");\n setMetaValue(\"\");\n }\n }, [metaKey, metaValue]);\n\n const inputStyle: React.CSSProperties = {\n background: tokens.colors.background.base,\n color: tokens.colors.text.primary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs}px ${tokens.spacing.sm}px`,\n fontSize: tokens.typography.fontSize.xs,\n width: \"100%\",\n minHeight: 40,\n fontFamily: tokens.typography.fontFamily.primary,\n transition: \"all 150ms ease\",\n };\n\n const labelStyle: React.CSSProperties = {\n fontSize: tokens.typography.fontSize.xxs,\n fontWeight: 500,\n color: tokens.colors.text.muted,\n textTransform: \"uppercase\",\n marginBottom: 3,\n display: \"block\",\n };\n\n return (\n <div\n style={{ padding: 12, display: \"flex\", flexDirection: \"column\", gap: 8 }}\n >\n <div\n style={{ display: \"grid\", gridTemplateColumns: \"1fr 120px\", gap: 8 }}\n >\n <div>\n <label htmlFor=\"activity-title\" style={labelStyle}>\n Title\n </label>\n <input\n id=\"activity-title\"\n aria-label=\"Activity title\"\n style={inputStyle}\n value={form.title}\n onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))}\n placeholder=\"Activity name\"\n />\n </div>\n <div>\n <label htmlFor=\"activity-type\" style={labelStyle}>\n Type\n </label>\n <select\n id=\"activity-type\"\n aria-label=\"Activity type\"\n style={inputStyle}\n value={form.type}\n onChange={(e) =>\n setForm((p) => ({ ...p, type: e.target.value as ActivityType }))\n }\n >\n <option value=\"command\">Command</option>\n <option value=\"script\">Script</option>\n <option value=\"reserve\">Reserve</option>\n <option value=\"contact\">Contact</option>\n <option value=\"note\">Note</option>\n <option value=\"metadata\">Metadata</option>\n </select>\n </div>\n </div>\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 8 }}>\n <div>\n <label htmlFor=\"activity-start-time\" style={labelStyle}>\n Start Time\n </label>\n <input\n id=\"activity-start-time\"\n aria-label=\"Start time\"\n type=\"datetime-local\"\n style={{\n ...inputStyle,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n value={form.start}\n onChange={(e) => setForm((p) => ({ ...p, start: e.target.value }))}\n />\n </div>\n <div>\n <label htmlFor=\"activity-end-time\" style={labelStyle}>\n End Time\n </label>\n <input\n id=\"activity-end-time\"\n aria-label=\"End time\"\n type=\"datetime-local\"\n style={{\n ...inputStyle,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n value={form.end}\n onChange={(e) => setForm((p) => ({ ...p, end: e.target.value }))}\n />\n </div>\n </div>\n {timelines.length > 0 && (\n <div>\n <label htmlFor=\"activity-timeline\" style={labelStyle}>\n Timeline\n </label>\n <select\n id=\"activity-timeline\"\n aria-label=\"Timeline\"\n style={inputStyle}\n value={form.timeline}\n onChange={(e) =>\n setForm((p) => ({ ...p, timeline: e.target.value }))\n }\n >\n {timelines.map((t) => (\n <option key={t.id} value={t.id}>\n {t.name}\n </option>\n ))}\n </select>\n </div>\n )}\n {form.type === \"command\" && (\n <div>\n <label htmlFor=\"activity-command\" style={labelStyle}>\n Command\n </label>\n <input\n id=\"activity-command\"\n aria-label=\"Command\"\n style={{\n ...inputStyle,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n value={form.command}\n onChange={(e) =>\n setForm((p) => ({ ...p, command: e.target.value }))\n }\n placeholder='cmd(\"TARGET COMMAND\")'\n />\n </div>\n )}\n {form.type === \"script\" && (\n <div>\n <label htmlFor=\"activity-script\" style={labelStyle}>\n Script Path\n </label>\n <input\n id=\"activity-script\"\n aria-label=\"Script path\"\n style={{\n ...inputStyle,\n fontFamily: tokens.typography.fontFamily.mono,\n }}\n value={form.script}\n onChange={(e) => setForm((p) => ({ ...p, script: e.target.value }))}\n placeholder=\"/scripts/ops/collect.rb\"\n />\n </div>\n )}\n <div>\n <label htmlFor=\"activity-description\" style={labelStyle}>\n Description\n </label>\n <textarea\n id=\"activity-description\"\n aria-label=\"Description\"\n style={{ ...inputStyle, minHeight: 48, resize: \"vertical\" }}\n value={form.description}\n onChange={(e) =>\n setForm((p) => ({ ...p, description: e.target.value }))\n }\n placeholder=\"Notes...\"\n />\n </div>\n\n {/* Metadata KV */}\n <div>\n <div style={labelStyle}>Metadata</div>\n <div style={{ display: \"flex\", gap: 4, marginBottom: 4 }}>\n <input\n aria-label=\"Metadata key\"\n style={{ ...inputStyle, flex: 1 }}\n value={metaKey}\n onChange={(e) => setMetaKey(e.target.value)}\n placeholder=\"Key\"\n />\n <input\n aria-label=\"Metadata value\"\n style={{ ...inputStyle, flex: 1 }}\n value={metaValue}\n onChange={(e) => setMetaValue(e.target.value)}\n placeholder=\"Value\"\n />\n <button\n onClick={addMetadata}\n style={{\n background: tokens.colors.interactive.default,\n color: tokens.colors.text.inverse,\n border: \"none\",\n borderRadius: tokens.borderRadius.md,\n padding: `0 ${tokens.spacing.sm}px`,\n fontSize: tokens.typography.fontSize.xs,\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n }}\n >\n +\n </button>\n </div>\n {Object.keys(form.metadata).length > 0 && (\n <div style={{ display: \"flex\", gap: 4, flexWrap: \"wrap\" }}>\n {Object.entries(form.metadata).map(([k, v]) => (\n <span\n key={k}\n style={{\n fontSize: tokens.typography.fontSize.micro,\n background: tokens.colors.background.surface,\n padding: `2px 6px`,\n borderRadius: tokens.borderRadius.sm,\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.xs,\n }}\n >\n {k}: {v}\n <button\n type=\"button\"\n aria-label={`Remove metadata ${k}`}\n onClick={() =>\n setForm((p) => {\n const m = { ...p.metadata };\n delete m[k];\n return { ...p, metadata: m };\n })\n }\n style={{\n cursor: \"pointer\",\n color: tokens.colors.status.critical,\n fontWeight: 500,\n border: \"none\",\n background: \"transparent\",\n padding: 0,\n lineHeight: 1,\n }}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n )}\n </div>\n\n {/* Hazardous toggle */}\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n fontSize: tokens.typography.fontSize.xs,\n cursor: \"pointer\",\n }}\n >\n <input\n aria-label=\"Hazardous activity\"\n type=\"checkbox\"\n checked={form.hazardous}\n onChange={(e) =>\n setForm((p) => ({ ...p, hazardous: e.target.checked }))\n }\n />\n <span\n style={{\n color: form.hazardous\n ? tokens.colors.status.critical\n : tokens.colors.text.secondary,\n }}\n >\n ⚠ Hazardous Activity\n </span>\n </label>\n\n {/* Buttons */}\n <div\n style={{\n display: \"flex\",\n gap: 6,\n justifyContent: \"flex-end\",\n marginTop: 4,\n }}\n >\n <button\n onClick={onCancel}\n style={{\n background: \"transparent\",\n color: tokens.colors.text.muted,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs}px 14px`,\n fontSize: tokens.typography.fontSize.xs,\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n }}\n >\n Cancel\n </button>\n <button\n onClick={() => onSubmit(form)}\n disabled={!form.title.trim()}\n style={{\n background: tokens.colors.interactive.default,\n color: tokens.colors.text.inverse,\n border: \"none\",\n borderRadius: tokens.borderRadius.md,\n padding: `${tokens.spacing.xs}px 14px`,\n fontSize: tokens.typography.fontSize.xs,\n cursor: form.title.trim() ? \"pointer\" : \"not-allowed\",\n fontWeight: 500,\n opacity: form.title.trim() ? 1 : 0.5,\n transition: \"all 150ms ease\",\n }}\n >\n Create Activity\n </button>\n </div>\n </div>\n );\n});\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const ActivityPlanner = memo(function ActivityPlanner({\n activities,\n timelines,\n selectedDate,\n height = 500,\n title = \"Activity Planner\",\n readOnly = false,\n showOverlapWarning = true,\n onActivityCreate,\n onActivityUpdate: _onActivityUpdate,\n onActivityDelete,\n onViewInCalendar,\n onViewInTimeline,\n className = \"\",\n}: ActivityPlannerProps): React.ReactElement {\n const { tokens, theme } = useTheme();\n const isTransparentTheme =\n theme === \"transparent\" ||\n theme === \"transparent-bold\" ||\n theme === \"transparent-minimal\";\n const [showForm, setShowForm] = useState(false);\n const [filter, setFilter] = useState(\"\");\n const [sortBy, setSortBy] = useState<\"time\" | \"status\" | \"timeline\">(\"time\");\n const listRef = useRef<HTMLDivElement>(null);\n\n // Overlap detection\n const overlaps = useMemo(() => {\n const pairs: [string, string][] = [];\n for (let i = 0; i < activities.length; i++) {\n for (let j = i + 1; j < activities.length; j++) {\n if (checkOverlap(activities[i], activities[j])) {\n pairs.push([activities[i].id, activities[j].id]);\n }\n }\n }\n return new Set(pairs.flat());\n }, [activities]);\n\n // Sort and filter\n const displayActivities = useMemo(() => {\n let list = [...activities];\n if (filter) {\n const lf = filter.toLowerCase();\n list = list.filter(\n (a) =>\n a.title.toLowerCase().includes(lf) ||\n a.description?.toLowerCase().includes(lf) ||\n a.timeline?.toLowerCase().includes(lf) ||\n a.command?.toLowerCase().includes(lf),\n );\n }\n switch (sortBy) {\n case \"time\":\n list.sort((a, b) => a.start.getTime() - b.start.getTime());\n break;\n case \"status\":\n list.sort((a, b) => (a.status || \"\").localeCompare(b.status || \"\"));\n break;\n case \"timeline\":\n list.sort((a, b) => (a.timeline || \"\").localeCompare(b.timeline || \"\"));\n break;\n }\n return list;\n }, [activities, filter, sortBy]);\n\n // Stats\n const stats = useMemo(\n () => ({\n total: activities.length,\n scheduled: activities.filter(\n (a) => a.status === \"scheduled\" || a.status === \"created\",\n ).length,\n running: activities.filter((a) => a.status === \"started\").length,\n completed: activities.filter((a) => a.status === \"completed\").length,\n errors: activities.filter(\n (a) =>\n a.status === \"error\" ||\n a.status === \"failed\" ||\n a.status === \"crashed\",\n ).length,\n }),\n [activities],\n );\n\n const handleCreate = useCallback(\n (data: ActivityFormData) => {\n onActivityCreate?.({\n title: data.title,\n type: data.type,\n start: new Date(data.start),\n end: new Date(data.end),\n timeline: data.timeline,\n description: data.description,\n command: data.command || undefined,\n script: data.script || undefined,\n metadata:\n Object.keys(data.metadata).length > 0 ? data.metadata : undefined,\n status: \"created\",\n });\n setShowForm(false);\n },\n [onActivityCreate],\n );\n\n const panelPadX = tokens.spacing.md;\n const panelPadY = tokens.spacing.sm;\n\n return (\n <div\n className={`activity-planner ${className}`}\n style={{\n height: typeof height === \"number\" ? height : undefined,\n background: tokens.colors.background.base,\n ...(tokens.colors.border.cardStyle ?? {\n border: `1px solid ${tokens.colors.border.muted}`,\n }),\n borderRadius: tokens.borderRadius.lg,\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n fontFamily: tokens.typography.fontFamily.primary,\n color: tokens.colors.text.primary,\n backdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\n WebkitBackdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: `${panelPadY}px ${panelPadX}px`,\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n backdropFilter: \"blur(8px)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n }}\n >\n <span\n style={{ fontWeight: 500, fontSize: tokens.typography.fontSize.sm }}\n >\n {title}\n </span>\n <div\n style={{\n display: \"flex\",\n gap: 10,\n fontSize: tokens.typography.fontSize.xxs,\n color: tokens.colors.text.muted,\n }}\n >\n <span>Scheduled {stats.scheduled}</span>\n <span style={{ color: tokens.colors.text.secondary }}>\n Running {stats.running}\n </span>\n <span style={{ color: tokens.colors.text.secondary }}>\n Completed {stats.completed}\n </span>\n {stats.errors > 0 && (\n <span style={{ color: tokens.colors.status.critical }}>\n Errors {stats.errors}\n </span>\n )}\n </div>\n </div>\n {!readOnly && (\n <button\n onClick={() => setShowForm(!showForm)}\n style={{\n background: showForm\n ? tokens.colors.status.critical + \"22\"\n : tokens.colors.interactive.default,\n color: showForm\n ? tokens.colors.status.critical\n : tokens.colors.text.inverse,\n border: \"none\",\n borderRadius: tokens.borderRadius.md,\n padding: \"3px 10px\",\n fontSize: tokens.typography.fontSize.xs,\n fontWeight: 500,\n cursor: \"pointer\",\n transition: \"all 150ms ease\",\n }}\n >\n {showForm ? \"✕ Cancel\" : \"+ New Activity\"}\n </button>\n )}\n </div>\n\n {/* Create form */}\n {showForm && !readOnly && (\n <div\n style={{\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface + \"88\",\n }}\n >\n <ActivityForm\n timelines={timelines}\n selectedDate={selectedDate}\n tokens={tokens}\n onSubmit={handleCreate}\n onCancel={() => setShowForm(false)}\n />\n </div>\n )}\n\n {/* Search / Sort */}\n <div\n style={{\n padding: `${tokens.spacing.xs}px ${panelPadX}px`,\n display: \"flex\",\n gap: 6,\n alignItems: \"center\",\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\n }}\n >\n <input\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n aria-label=\"Search activities\"\n placeholder=\"Search activities...\"\n style={{\n flex: 1,\n background: tokens.colors.background.surface,\n color: tokens.colors.text.primary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: \"3px 8px\",\n fontSize: tokens.typography.fontSize.xxs,\n fontFamily: tokens.typography.fontFamily.primary,\n transition: \"all 150ms ease\",\n }}\n />\n <select\n value={sortBy}\n onChange={(e) =>\n setSortBy(e.target.value as \"time\" | \"status\" | \"timeline\")\n }\n aria-label=\"Sort activities\"\n style={{\n background: tokens.colors.background.surface,\n color: tokens.colors.text.secondary,\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.md,\n padding: \"3px 6px\",\n fontSize: tokens.typography.fontSize.xxs,\n transition: \"all 150ms ease\",\n }}\n >\n <option value=\"time\">Sort: Time</option>\n <option value=\"status\">Sort: Status</option>\n <option value=\"timeline\">Sort: Timeline</option>\n </select>\n </div>\n\n {/* Activity list */}\n <div ref={listRef} style={{ flex: 1, overflow: \"auto\", padding: 8 }}>\n {displayActivities.length === 0 ? (\n <div\n style={{\n textAlign: \"center\",\n color: tokens.colors.text.muted,\n padding: 40,\n fontSize: tokens.typography.fontSize.xs,\n }}\n >\n {filter ? \"No matching activities\" : \"No activities scheduled\"}\n </div>\n ) : (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n {displayActivities.map((a) => {\n const color = a.color || getStatusColor(a.status, tokens);\n const hasOverlap = overlaps.has(a.id);\n return (\n <div\n key={a.id}\n style={{\n display: \"grid\",\n gridTemplateColumns: \"24px 1fr auto\",\n alignItems: \"center\",\n gap: tokens.spacing.sm,\n padding: \"6px 10px\",\n background: hasOverlap\n ? `${tokens.colors.status.caution}10`\n : `${color}08`,\n borderLeft: `3px solid ${color}`,\n borderRadius: tokens.borderRadius.md,\n fontSize: tokens.typography.fontSize.xs,\n cursor: \"pointer\",\n border:\n hasOverlap && showOverlapWarning\n ? `1px solid ${tokens.colors.status.caution}44`\n : undefined,\n transition: \"all 150ms ease\",\n }}\n >\n {/* Status icon */}\n <div style={{ textAlign: \"center\" }}>\n <span\n style={{\n color,\n fontSize: tokens.typography.fontSize.sm,\n fontWeight: 500,\n }}\n >\n {getStatusIcon(a.status)}\n </span>\n </div>\n {/* Info */}\n <div style={{ overflow: \"hidden\" }}>\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n marginBottom: 2,\n }}\n >\n <span\n style={{\n fontWeight: 500,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {a.title}\n </span>\n {a.timeline && (\n <span\n style={{\n fontSize: tokens.typography.fontSize.micro,\n background: tokens.colors.background.surface,\n padding: `0 ${tokens.spacing.xs}px`,\n borderRadius: tokens.borderRadius.sm,\n color: tokens.colors.text.muted,\n }}\n >\n {a.timeline}\n </span>\n )}\n {hasOverlap && showOverlapWarning && (\n <span\n style={{\n fontSize: tokens.typography.fontSize.micro,\n color: tokens.colors.status.caution,\n fontWeight: 500,\n }}\n >\n ⚠ OVERLAP\n </span>\n )}\n </div>\n <div\n style={{\n fontSize: tokens.typography.fontSize.micro,\n fontFamily: tokens.typography.fontFamily.mono,\n color: tokens.colors.text.muted,\n }}\n >\n {formatDate(a.start)} {formatTime(a.start)}\n {a.end ? ` → ${formatTime(a.end)}` : \"\"}\n </div>\n </div>\n {/* Actions */}\n <div style={{ display: \"flex\", gap: 4 }}>\n {onViewInCalendar && (\n <button\n aria-label={`View ${a.title} in calendar`}\n onClick={() => onViewInCalendar(a)}\n style={actionBtnStyle(tokens)}\n title=\"View in Calendar\"\n >\n Calendar\n </button>\n )}\n {onViewInTimeline && (\n <button\n aria-label={`View ${a.title} in timeline`}\n onClick={() => onViewInTimeline(a)}\n style={actionBtnStyle(tokens)}\n title=\"View in Timeline\"\n >\n Timeline\n </button>\n )}\n {!readOnly && onActivityDelete && (\n <button\n aria-label={`Delete ${a.title}`}\n onClick={() => onActivityDelete(a.id)}\n style={{\n ...actionBtnStyle(tokens),\n color: tokens.colors.status.critical,\n }}\n title=\"Delete\"\n >\n Delete\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div\n style={{\n padding: `${tokens.spacing.xs}px ${panelPadX}px`,\n borderTop: `1px solid ${tokens.colors.border.muted}`,\n background: tokens.colors.background.surface,\n fontSize: tokens.typography.fontSize.micro,\n color: tokens.colors.text.muted,\n display: \"flex\",\n justifyContent: \"space-between\",\n }}\n >\n <span>\n {displayActivities.length} / {activities.length} activities\n </span>\n {overlaps.size > 0 && showOverlapWarning && (\n <span style={{ color: tokens.colors.status.caution }}>\n ⚠ {overlaps.size} overlapping activities\n </span>\n )}\n </div>\n </div>\n );\n});\n\nfunction actionBtnStyle(\n tokens: ReturnType<typeof useTheme>[\"tokens\"],\n): React.CSSProperties {\n return {\n background: \"transparent\",\n border: `1px solid ${tokens.colors.border.muted}`,\n borderRadius: tokens.borderRadius.sm,\n padding: `2px ${tokens.spacing.xs}px`,\n fontSize: tokens.typography.fontSize.xxs,\n cursor: \"pointer\",\n lineHeight: 1,\n transition: \"all 150ms ease\",\n };\n}\n\nexport default ActivityPlanner;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActivityPlanner.js","names":[],"sources":["../../../../src/react/core/widgets/ActivityPlanner.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - ActivityPlanner Component\r\n *\r\n * Activity scheduling and management panel. Creates, edits, and monitors\r\n * scheduled activities across timelines. Integrates with MissionCalendar\r\n * and Timeline components.\r\n *\r\n * Space Mission Features:\r\n * - Create/edit activities with command or script execution\r\n * - Drag-and-drop reorder within timeline\r\n * - Hazardous activity warnings (Astro UX)\r\n * - Activity lifecycle tracking (created → scheduled → started → completed)\r\n * - Overlap detection within a timeline\r\n * - Metadata recording (operator, environment, notes)\r\n * - Contact window awareness\r\n * - Batch scheduling\r\n *\r\n * @example\r\n * ```tsx\r\n * <ActivityPlanner\r\n * activities={activities}\r\n * timelines={timelines}\r\n * onActivityCreate={(a) => console.log('Create:', a)}\r\n * onActivityUpdate={(a) => console.log('Update:', a)}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { useState, useMemo, useCallback, memo, useRef } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport type {\r\n CalendarEvent,\r\n CalendarTimeline,\r\n ActivityStatus,\r\n ActivityType,\r\n} from \"./MissionCalendar\";\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface ActivityFormData {\r\n title: string;\r\n type: ActivityType;\r\n start: string; // ISO string\r\n end: string; // ISO string\r\n timeline: string;\r\n description: string;\r\n command: string;\r\n script: string;\r\n hazardous: boolean;\r\n metadata: Record<string, string>;\r\n}\r\n\r\nexport interface ActivityPlannerProps {\r\n /** Activities to display */\r\n activities: CalendarEvent[];\r\n /** Available timelines */\r\n timelines: CalendarTimeline[];\r\n /** Currently selected date for scheduling */\r\n selectedDate?: Date;\r\n /** Height (default: 500) */\r\n height?: number | string;\r\n /** Title */\r\n title?: string;\r\n /** Read-only mode */\r\n readOnly?: boolean;\r\n /** Show overlap warnings */\r\n showOverlapWarning?: boolean;\r\n /** Called on create */\r\n onActivityCreate?: (activity: Partial<CalendarEvent>) => void;\r\n /** Called on update */\r\n onActivityUpdate?: (id: string, updates: Partial<CalendarEvent>) => void;\r\n /** Called on delete */\r\n onActivityDelete?: (id: string) => void;\r\n /** Called when requesting to view activity in calendar */\r\n onViewInCalendar?: (activity: CalendarEvent) => void;\r\n /** Called when requesting to view in timeline */\r\n onViewInTimeline?: (activity: CalendarEvent) => void;\r\n /** CSS class */\r\n className?: string;\r\n}\r\n\r\n// =============================================================================\r\n// Helpers\r\n// =============================================================================\r\n\r\n/**\r\n * Map activity status → Astro UX status color using tokens.\r\n * created/scheduled → standby (cyan)\r\n * started/completed → normal (green)\r\n * paused/stopped/disabled → caution (yellow)\r\n * error/failed/crashed → critical (red)\r\n */\r\nfunction getStatusColor(\r\n status?: ActivityStatus,\r\n tokens?: ReturnType<typeof useTheme>[\"tokens\"],\r\n): string {\r\n if (!status || !tokens) return tokens?.colors.text.muted ?? \"currentColor\";\r\n switch (status) {\r\n case \"created\":\r\n case \"scheduled\":\r\n return tokens.colors.status.standby;\r\n case \"started\":\r\n case \"completed\":\r\n return tokens.colors.status.normal;\r\n case \"paused\":\r\n case \"stopped\":\r\n case \"disabled\":\r\n return tokens.colors.status.caution;\r\n case \"error\":\r\n case \"failed\":\r\n case \"crashed\":\r\n return tokens.colors.status.critical;\r\n }\r\n}\r\n\r\n/**\r\n * Return Astro UX status shape + contextual icon.\r\n * The leading character is the Astro status shape for the mapped level:\r\n * standby → ◯ (ring), normal → ● (filled circle),\r\n * caution → ■ (square), critical → ▼ (triangle down)\r\n * Followed by a contextual glyph for the specific activity state.\r\n */\r\nfunction getStatusIcon(status?: ActivityStatus): string {\r\n if (!status) return \"◯\";\r\n switch (status) {\r\n case \"created\":\r\n return \"◯\"; // standby ring\r\n case \"scheduled\":\r\n return \"◯ ◉\"; // standby ring + scheduled\r\n case \"started\":\r\n return \"● ▶\"; // normal circle + play\r\n case \"completed\":\r\n return \"● ✓\"; // normal circle + check\r\n case \"paused\":\r\n return \"■ ⏸\"; // caution square + pause\r\n case \"stopped\":\r\n return \"■ ⏹\"; // caution square + stop\r\n case \"disabled\":\r\n return \"■ ⊘\"; // caution square + disabled\r\n case \"error\":\r\n case \"failed\":\r\n case \"crashed\":\r\n return \"▼\"; // critical triangle down\r\n }\r\n}\r\n\r\nfunction formatDate(d: Date): string {\r\n return d.toLocaleDateString(\"en-US\", {\r\n month: \"short\",\r\n day: \"numeric\",\r\n year: \"numeric\",\r\n });\r\n}\r\n\r\nfunction formatTime(d: Date): string {\r\n return d.toLocaleTimeString(\"en-US\", {\r\n hour12: false,\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n });\r\n}\r\n\r\nfunction checkOverlap(a: CalendarEvent, b: CalendarEvent): boolean {\r\n if (!a.end || !b.end) return false;\r\n if (a.timeline !== b.timeline) return false;\r\n return a.start < b.end && b.start < a.end;\r\n}\r\n\r\n// =============================================================================\r\n// Form Component\r\n// =============================================================================\r\n\r\nconst ActivityForm = memo(function ActivityForm({\r\n timelines,\r\n selectedDate,\r\n tokens,\r\n onSubmit,\r\n onCancel,\r\n}: {\r\n timelines: CalendarTimeline[];\r\n selectedDate?: Date;\r\n tokens: ReturnType<typeof useTheme>[\"tokens\"];\r\n onSubmit: (data: ActivityFormData) => void;\r\n onCancel: () => void;\r\n}) {\r\n const now = selectedDate || new Date();\r\n const endDefault = new Date(now.getTime() + 3600000);\r\n\r\n const [form, setForm] = useState<ActivityFormData>({\r\n title: \"\",\r\n type: \"command\",\r\n start: now.toISOString().slice(0, 16),\r\n end: endDefault.toISOString().slice(0, 16),\r\n timeline: timelines[0]?.id || \"\",\r\n description: \"\",\r\n command: \"\",\r\n script: \"\",\r\n hazardous: false,\r\n metadata: {},\r\n });\r\n const [metaKey, setMetaKey] = useState(\"\");\r\n const [metaValue, setMetaValue] = useState(\"\");\r\n\r\n const addMetadata = useCallback(() => {\r\n if (metaKey.trim()) {\r\n setForm((prev) => ({\r\n ...prev,\r\n metadata: { ...prev.metadata, [metaKey.trim()]: metaValue },\r\n }));\r\n setMetaKey(\"\");\r\n setMetaValue(\"\");\r\n }\r\n }, [metaKey, metaValue]);\r\n\r\n const inputStyle: React.CSSProperties = {\r\n background: tokens.colors.background.base,\r\n color: tokens.colors.text.primary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs}px ${tokens.spacing.sm}px`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n width: \"100%\",\r\n minHeight: 40,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n transition: \"all 150ms ease\",\r\n };\r\n\r\n const labelStyle: React.CSSProperties = {\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: 500,\r\n color: tokens.colors.text.muted,\r\n textTransform: \"uppercase\",\r\n marginBottom: 3,\r\n display: \"block\",\r\n };\r\n\r\n return (\r\n <div\r\n style={{ padding: 12, display: \"flex\", flexDirection: \"column\", gap: 8 }}\r\n >\r\n <div\r\n style={{ display: \"grid\", gridTemplateColumns: \"1fr 120px\", gap: 8 }}\r\n >\r\n <div>\r\n <label htmlFor=\"activity-title\" style={labelStyle}>\r\n Title\r\n </label>\r\n <input\r\n id=\"activity-title\"\r\n aria-label=\"Activity title\"\r\n style={inputStyle}\r\n value={form.title}\r\n onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))}\r\n placeholder=\"Activity name\"\r\n />\r\n </div>\r\n <div>\r\n <label htmlFor=\"activity-type\" style={labelStyle}>\r\n Type\r\n </label>\r\n <select\r\n id=\"activity-type\"\r\n aria-label=\"Activity type\"\r\n style={inputStyle}\r\n value={form.type}\r\n onChange={(e) =>\r\n setForm((p) => ({ ...p, type: e.target.value as ActivityType }))\r\n }\r\n >\r\n <option value=\"command\">Command</option>\r\n <option value=\"script\">Script</option>\r\n <option value=\"reserve\">Reserve</option>\r\n <option value=\"contact\">Contact</option>\r\n <option value=\"note\">Note</option>\r\n <option value=\"metadata\">Metadata</option>\r\n </select>\r\n </div>\r\n </div>\r\n <div style={{ display: \"grid\", gridTemplateColumns: \"1fr 1fr\", gap: 8 }}>\r\n <div>\r\n <label htmlFor=\"activity-start-time\" style={labelStyle}>\r\n Start Time\r\n </label>\r\n <input\r\n id=\"activity-start-time\"\r\n aria-label=\"Start time\"\r\n type=\"datetime-local\"\r\n style={{\r\n ...inputStyle,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n value={form.start}\r\n onChange={(e) => setForm((p) => ({ ...p, start: e.target.value }))}\r\n />\r\n </div>\r\n <div>\r\n <label htmlFor=\"activity-end-time\" style={labelStyle}>\r\n End Time\r\n </label>\r\n <input\r\n id=\"activity-end-time\"\r\n aria-label=\"End time\"\r\n type=\"datetime-local\"\r\n style={{\r\n ...inputStyle,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n value={form.end}\r\n onChange={(e) => setForm((p) => ({ ...p, end: e.target.value }))}\r\n />\r\n </div>\r\n </div>\r\n {timelines.length > 0 && (\r\n <div>\r\n <label htmlFor=\"activity-timeline\" style={labelStyle}>\r\n Timeline\r\n </label>\r\n <select\r\n id=\"activity-timeline\"\r\n aria-label=\"Timeline\"\r\n style={inputStyle}\r\n value={form.timeline}\r\n onChange={(e) =>\r\n setForm((p) => ({ ...p, timeline: e.target.value }))\r\n }\r\n >\r\n {timelines.map((t) => (\r\n <option key={t.id} value={t.id}>\r\n {t.name}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )}\r\n {form.type === \"command\" && (\r\n <div>\r\n <label htmlFor=\"activity-command\" style={labelStyle}>\r\n Command\r\n </label>\r\n <input\r\n id=\"activity-command\"\r\n aria-label=\"Command\"\r\n style={{\r\n ...inputStyle,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n value={form.command}\r\n onChange={(e) =>\r\n setForm((p) => ({ ...p, command: e.target.value }))\r\n }\r\n placeholder='cmd(\"TARGET COMMAND\")'\r\n />\r\n </div>\r\n )}\r\n {form.type === \"script\" && (\r\n <div>\r\n <label htmlFor=\"activity-script\" style={labelStyle}>\r\n Script Path\r\n </label>\r\n <input\r\n id=\"activity-script\"\r\n aria-label=\"Script path\"\r\n style={{\r\n ...inputStyle,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n }}\r\n value={form.script}\r\n onChange={(e) => setForm((p) => ({ ...p, script: e.target.value }))}\r\n placeholder=\"/scripts/ops/collect.rb\"\r\n />\r\n </div>\r\n )}\r\n <div>\r\n <label htmlFor=\"activity-description\" style={labelStyle}>\r\n Description\r\n </label>\r\n <textarea\r\n id=\"activity-description\"\r\n aria-label=\"Description\"\r\n style={{ ...inputStyle, minHeight: 48, resize: \"vertical\" }}\r\n value={form.description}\r\n onChange={(e) =>\r\n setForm((p) => ({ ...p, description: e.target.value }))\r\n }\r\n placeholder=\"Notes...\"\r\n />\r\n </div>\r\n\r\n {/* Metadata KV */}\r\n <div>\r\n <div style={labelStyle}>Metadata</div>\r\n <div style={{ display: \"flex\", gap: 4, marginBottom: 4 }}>\r\n <input\r\n aria-label=\"Metadata key\"\r\n style={{ ...inputStyle, flex: 1 }}\r\n value={metaKey}\r\n onChange={(e) => setMetaKey(e.target.value)}\r\n placeholder=\"Key\"\r\n />\r\n <input\r\n aria-label=\"Metadata value\"\r\n style={{ ...inputStyle, flex: 1 }}\r\n value={metaValue}\r\n onChange={(e) => setMetaValue(e.target.value)}\r\n placeholder=\"Value\"\r\n />\r\n <button\r\n onClick={addMetadata}\r\n style={{\r\n background: tokens.colors.interactive.default,\r\n color: tokens.colors.text.inverse,\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `0 ${tokens.spacing.sm}px`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n cursor: \"pointer\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n +\r\n </button>\r\n </div>\r\n {Object.keys(form.metadata).length > 0 && (\r\n <div style={{ display: \"flex\", gap: 4, flexWrap: \"wrap\" }}>\r\n {Object.entries(form.metadata).map(([k, v]) => (\r\n <span\r\n key={k}\r\n style={{\r\n fontSize: tokens.typography.fontSize.micro,\r\n background: tokens.colors.background.surface,\r\n padding: `2px 6px`,\r\n borderRadius: tokens.borderRadius.sm,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n }}\r\n >\r\n {k}: {v}\r\n <button\r\n type=\"button\"\r\n aria-label={`Remove metadata ${k}`}\r\n onClick={() =>\r\n setForm((p) => {\r\n const m = { ...p.metadata };\r\n delete m[k];\r\n return { ...p, metadata: m };\r\n })\r\n }\r\n style={{\r\n cursor: \"pointer\",\r\n color: tokens.colors.status.critical,\r\n fontWeight: 500,\r\n border: \"none\",\r\n background: \"transparent\",\r\n padding: 0,\r\n lineHeight: 1,\r\n }}\r\n >\r\n ×\r\n </button>\r\n </span>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Hazardous toggle */}\r\n <label\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: 6,\r\n fontSize: tokens.typography.fontSize.xs,\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n <input\r\n aria-label=\"Hazardous activity\"\r\n type=\"checkbox\"\r\n checked={form.hazardous}\r\n onChange={(e) =>\r\n setForm((p) => ({ ...p, hazardous: e.target.checked }))\r\n }\r\n />\r\n <span\r\n style={{\r\n color: form.hazardous\r\n ? tokens.colors.status.critical\r\n : tokens.colors.text.secondary,\r\n }}\r\n >\r\n ⚠ Hazardous Activity\r\n </span>\r\n </label>\r\n\r\n {/* Buttons */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n gap: 6,\r\n justifyContent: \"flex-end\",\r\n marginTop: 4,\r\n }}\r\n >\r\n <button\r\n onClick={onCancel}\r\n style={{\r\n background: \"transparent\",\r\n color: tokens.colors.text.muted,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs}px 14px`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n cursor: \"pointer\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n Cancel\r\n </button>\r\n <button\r\n onClick={() => onSubmit(form)}\r\n disabled={!form.title.trim()}\r\n style={{\r\n background: tokens.colors.interactive.default,\r\n color: tokens.colors.text.inverse,\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.md,\r\n padding: `${tokens.spacing.xs}px 14px`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n cursor: form.title.trim() ? \"pointer\" : \"not-allowed\",\r\n fontWeight: 500,\r\n opacity: form.title.trim() ? 1 : 0.5,\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n Create Activity\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n});\r\n\r\n// =============================================================================\r\n// Main Component\r\n// =============================================================================\r\n\r\nexport const ActivityPlanner = memo(function ActivityPlanner({\r\n activities,\r\n timelines,\r\n selectedDate,\r\n height = 500,\r\n title = \"Activity Planner\",\r\n readOnly = false,\r\n showOverlapWarning = true,\r\n onActivityCreate,\r\n onActivityUpdate: _onActivityUpdate,\r\n onActivityDelete,\r\n onViewInCalendar,\r\n onViewInTimeline,\r\n className = \"\",\r\n}: ActivityPlannerProps): React.ReactElement {\r\n const { tokens, theme } = useTheme();\r\n const isTransparentTheme =\r\n theme === \"transparent\" ||\r\n theme === \"transparent-bold\" ||\r\n theme === \"transparent-minimal\";\r\n const [showForm, setShowForm] = useState(false);\r\n const [filter, setFilter] = useState(\"\");\r\n const [sortBy, setSortBy] = useState<\"time\" | \"status\" | \"timeline\">(\"time\");\r\n const listRef = useRef<HTMLDivElement>(null);\r\n\r\n // Overlap detection\r\n const overlaps = useMemo(() => {\r\n const pairs: [string, string][] = [];\r\n for (let i = 0; i < activities.length; i++) {\r\n for (let j = i + 1; j < activities.length; j++) {\r\n if (checkOverlap(activities[i], activities[j])) {\r\n pairs.push([activities[i].id, activities[j].id]);\r\n }\r\n }\r\n }\r\n return new Set(pairs.flat());\r\n }, [activities]);\r\n\r\n // Sort and filter\r\n const displayActivities = useMemo(() => {\r\n let list = [...activities];\r\n if (filter) {\r\n const lf = filter.toLowerCase();\r\n list = list.filter(\r\n (a) =>\r\n a.title.toLowerCase().includes(lf) ||\r\n a.description?.toLowerCase().includes(lf) ||\r\n a.timeline?.toLowerCase().includes(lf) ||\r\n a.command?.toLowerCase().includes(lf),\r\n );\r\n }\r\n switch (sortBy) {\r\n case \"time\":\r\n list.sort((a, b) => a.start.getTime() - b.start.getTime());\r\n break;\r\n case \"status\":\r\n list.sort((a, b) => (a.status || \"\").localeCompare(b.status || \"\"));\r\n break;\r\n case \"timeline\":\r\n list.sort((a, b) => (a.timeline || \"\").localeCompare(b.timeline || \"\"));\r\n break;\r\n }\r\n return list;\r\n }, [activities, filter, sortBy]);\r\n\r\n // Stats\r\n const stats = useMemo(\r\n () => ({\r\n total: activities.length,\r\n scheduled: activities.filter(\r\n (a) => a.status === \"scheduled\" || a.status === \"created\",\r\n ).length,\r\n running: activities.filter((a) => a.status === \"started\").length,\r\n completed: activities.filter((a) => a.status === \"completed\").length,\r\n errors: activities.filter(\r\n (a) =>\r\n a.status === \"error\" ||\r\n a.status === \"failed\" ||\r\n a.status === \"crashed\",\r\n ).length,\r\n }),\r\n [activities],\r\n );\r\n\r\n const handleCreate = useCallback(\r\n (data: ActivityFormData) => {\r\n onActivityCreate?.({\r\n title: data.title,\r\n type: data.type,\r\n start: new Date(data.start),\r\n end: new Date(data.end),\r\n timeline: data.timeline,\r\n description: data.description,\r\n command: data.command || undefined,\r\n script: data.script || undefined,\r\n metadata:\r\n Object.keys(data.metadata).length > 0 ? data.metadata : undefined,\r\n status: \"created\",\r\n });\r\n setShowForm(false);\r\n },\r\n [onActivityCreate],\r\n );\r\n\r\n const panelPadX = tokens.spacing.md;\r\n const panelPadY = tokens.spacing.sm;\r\n\r\n return (\r\n <div\r\n className={`activity-planner ${className}`}\r\n style={{\r\n height: typeof height === \"number\" ? height : undefined,\r\n background: tokens.colors.background.base,\r\n ...(tokens.colors.border.cardStyle ?? {\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n }),\r\n borderRadius: tokens.borderRadius.lg,\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n color: tokens.colors.text.primary,\r\n backdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n WebkitBackdropFilter: isTransparentTheme ? \"blur(10px)\" : undefined,\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n padding: `${panelPadY}px ${panelPadX}px`,\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n backdropFilter: \"blur(8px)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n }}\r\n >\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n }}\r\n >\r\n <span\r\n style={{ fontWeight: 500, fontSize: tokens.typography.fontSize.sm }}\r\n >\r\n {title}\r\n </span>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n gap: 10,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.muted,\r\n }}\r\n >\r\n <span>Scheduled {stats.scheduled}</span>\r\n <span style={{ color: tokens.colors.text.secondary }}>\r\n Running {stats.running}\r\n </span>\r\n <span style={{ color: tokens.colors.text.secondary }}>\r\n Completed {stats.completed}\r\n </span>\r\n {stats.errors > 0 && (\r\n <span style={{ color: tokens.colors.status.critical }}>\r\n Errors {stats.errors}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n {!readOnly && (\r\n <button\r\n onClick={() => setShowForm(!showForm)}\r\n style={{\r\n background: showForm\r\n ? tokens.colors.status.critical + \"22\"\r\n : tokens.colors.interactive.default,\r\n color: showForm\r\n ? tokens.colors.status.critical\r\n : tokens.colors.text.inverse,\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.md,\r\n padding: \"3px 10px\",\r\n fontSize: tokens.typography.fontSize.xs,\r\n fontWeight: 500,\r\n cursor: \"pointer\",\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n {showForm ? \"✕ Cancel\" : \"+ New Activity\"}\r\n </button>\r\n )}\r\n </div>\r\n\r\n {/* Create form */}\r\n {showForm && !readOnly && (\r\n <div\r\n style={{\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface + \"88\",\r\n }}\r\n >\r\n <ActivityForm\r\n timelines={timelines}\r\n selectedDate={selectedDate}\r\n tokens={tokens}\r\n onSubmit={handleCreate}\r\n onCancel={() => setShowForm(false)}\r\n />\r\n </div>\r\n )}\r\n\r\n {/* Search / Sort */}\r\n <div\r\n style={{\r\n padding: `${tokens.spacing.xs}px ${panelPadX}px`,\r\n display: \"flex\",\r\n gap: 6,\r\n alignItems: \"center\",\r\n borderBottom: `1px solid ${tokens.colors.border.muted}`,\r\n }}\r\n >\r\n <input\r\n value={filter}\r\n onChange={(e) => setFilter(e.target.value)}\r\n aria-label=\"Search activities\"\r\n placeholder=\"Search activities...\"\r\n style={{\r\n flex: 1,\r\n background: tokens.colors.background.surface,\r\n color: tokens.colors.text.primary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: \"3px 8px\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n transition: \"all 150ms ease\",\r\n }}\r\n />\r\n <select\r\n value={sortBy}\r\n onChange={(e) =>\r\n setSortBy(e.target.value as \"time\" | \"status\" | \"timeline\")\r\n }\r\n aria-label=\"Sort activities\"\r\n style={{\r\n background: tokens.colors.background.surface,\r\n color: tokens.colors.text.secondary,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.md,\r\n padding: \"3px 6px\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n <option value=\"time\">Sort: Time</option>\r\n <option value=\"status\">Sort: Status</option>\r\n <option value=\"timeline\">Sort: Timeline</option>\r\n </select>\r\n </div>\r\n\r\n {/* Activity list */}\r\n <div ref={listRef} style={{ flex: 1, overflow: \"auto\", padding: 8 }}>\r\n {displayActivities.length === 0 ? (\r\n <div\r\n style={{\r\n textAlign: \"center\",\r\n color: tokens.colors.text.muted,\r\n padding: 40,\r\n fontSize: tokens.typography.fontSize.xs,\r\n }}\r\n >\r\n {filter ? \"No matching activities\" : \"No activities scheduled\"}\r\n </div>\r\n ) : (\r\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\r\n {displayActivities.map((a) => {\r\n const color = a.color || getStatusColor(a.status, tokens);\r\n const hasOverlap = overlaps.has(a.id);\r\n return (\r\n <div\r\n key={a.id}\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: \"24px 1fr auto\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n padding: \"6px 10px\",\r\n background: hasOverlap\r\n ? `${tokens.colors.status.caution}10`\r\n : `${color}08`,\r\n borderLeft: `3px solid ${color}`,\r\n borderRadius: tokens.borderRadius.md,\r\n fontSize: tokens.typography.fontSize.xs,\r\n cursor: \"pointer\",\r\n border:\r\n hasOverlap && showOverlapWarning\r\n ? `1px solid ${tokens.colors.status.caution}44`\r\n : undefined,\r\n transition: \"all 150ms ease\",\r\n }}\r\n >\r\n {/* Status icon */}\r\n <div style={{ textAlign: \"center\" }}>\r\n <span\r\n style={{\r\n color,\r\n fontSize: tokens.typography.fontSize.sm,\r\n fontWeight: 500,\r\n }}\r\n >\r\n {getStatusIcon(a.status)}\r\n </span>\r\n </div>\r\n {/* Info */}\r\n <div style={{ overflow: \"hidden\" }}>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: 6,\r\n marginBottom: 2,\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontWeight: 500,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {a.title}\r\n </span>\r\n {a.timeline && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.micro,\r\n background: tokens.colors.background.surface,\r\n padding: `0 ${tokens.spacing.xs}px`,\r\n borderRadius: tokens.borderRadius.sm,\r\n color: tokens.colors.text.muted,\r\n }}\r\n >\r\n {a.timeline}\r\n </span>\r\n )}\r\n {hasOverlap && showOverlapWarning && (\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.micro,\r\n color: tokens.colors.status.caution,\r\n fontWeight: 500,\r\n }}\r\n >\r\n ⚠ OVERLAP\r\n </span>\r\n )}\r\n </div>\r\n <div\r\n style={{\r\n fontSize: tokens.typography.fontSize.micro,\r\n fontFamily: tokens.typography.fontFamily.mono,\r\n color: tokens.colors.text.muted,\r\n }}\r\n >\r\n {formatDate(a.start)} {formatTime(a.start)}\r\n {a.end ? ` → ${formatTime(a.end)}` : \"\"}\r\n </div>\r\n </div>\r\n {/* Actions */}\r\n <div style={{ display: \"flex\", gap: 4 }}>\r\n {onViewInCalendar && (\r\n <button\r\n aria-label={`View ${a.title} in calendar`}\r\n onClick={() => onViewInCalendar(a)}\r\n style={actionBtnStyle(tokens)}\r\n title=\"View in Calendar\"\r\n >\r\n Calendar\r\n </button>\r\n )}\r\n {onViewInTimeline && (\r\n <button\r\n aria-label={`View ${a.title} in timeline`}\r\n onClick={() => onViewInTimeline(a)}\r\n style={actionBtnStyle(tokens)}\r\n title=\"View in Timeline\"\r\n >\r\n Timeline\r\n </button>\r\n )}\r\n {!readOnly && onActivityDelete && (\r\n <button\r\n aria-label={`Delete ${a.title}`}\r\n onClick={() => onActivityDelete(a.id)}\r\n style={{\r\n ...actionBtnStyle(tokens),\r\n color: tokens.colors.status.critical,\r\n }}\r\n title=\"Delete\"\r\n >\r\n Delete\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Footer */}\r\n <div\r\n style={{\r\n padding: `${tokens.spacing.xs}px ${panelPadX}px`,\r\n borderTop: `1px solid ${tokens.colors.border.muted}`,\r\n background: tokens.colors.background.surface,\r\n fontSize: tokens.typography.fontSize.micro,\r\n color: tokens.colors.text.muted,\r\n display: \"flex\",\r\n justifyContent: \"space-between\",\r\n }}\r\n >\r\n <span>\r\n {displayActivities.length} / {activities.length} activities\r\n </span>\r\n {overlaps.size > 0 && showOverlapWarning && (\r\n <span style={{ color: tokens.colors.status.caution }}>\r\n ⚠ {overlaps.size} overlapping activities\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n});\r\n\r\nfunction actionBtnStyle(\r\n tokens: ReturnType<typeof useTheme>[\"tokens\"],\r\n): React.CSSProperties {\r\n return {\r\n background: \"transparent\",\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n borderRadius: tokens.borderRadius.sm,\r\n padding: `2px ${tokens.spacing.xs}px`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n cursor: \"pointer\",\r\n lineHeight: 1,\r\n transition: \"all 150ms ease\",\r\n };\r\n}\r\n\r\nexport default ActivityPlanner;\r\n"],"mappings":"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"}