'use client'; import * as PopoverPrimitive from '@radix-ui/react-popover'; import { useEffect, useRef } from 'react'; import { cn } from '../../../utils/cn'; import { ClockHistoryIcon } from '../../icons-v2-generated/date-and-time/clock-history-icon'; import { type AppLayoutDrawerHandle, useAppLayoutDrawerContainer, useAppLayoutDrawerCoordination, } from '../../navigation/app-layout-context'; import { HeaderButton } from '../../navigation/header-button'; import { OVERLAY_BACKDROP_CLASS } from '../../ui/drawer'; import { useOptionalTimeTracker } from './time-tracker-context'; import { TimeTrackerPanel } from './time-tracker-panel'; import { useTrackerClock } from './use-tracker-clock'; export interface TimeTrackerHeaderButtonProps { /** Dimming/disabled passthrough from the header. */ className?: string; disabled?: boolean; } /** * Header affordance for the time tracker. Renders nothing unless wrapped in a * ``. The trigger is the standard `HeaderButton`; when a * session is active it also shows the live elapsed time. The popup is a Radix * Popover anchored under the button (not a modal/drawer). Inside AppLayout the * dim backdrop covers only the main content area (header and sidebar stay * interactive) and the popup joins the layout's panel coordination — opening * it closes any open in-layout drawer and vice versa. Outside AppLayout it * falls back to a viewport-wide backdrop. */ export function TimeTrackerHeaderButton({ className, disabled }: TimeTrackerHeaderButtonProps) { const ctx = useOptionalTimeTracker(); const elapsedLabel = useTrackerClock({ status: ctx?.status ?? 'ready', runningSince: ctx?.runningSince, accumulatedMs: ctx?.accumulatedMs, }); const layoutContainer = useAppLayoutDrawerContainer(); const coordination = useAppLayoutDrawerCoordination(); const isOpen = ctx?.isOpen ?? false; const isOpenRef = useRef(isOpen); isOpenRef.current = isOpen; const closeRef = useRef(ctx?.close); closeRef.current = ctx?.close; // Stable handle: the same object must be registered AND passed as `self` // to notifyDrawerDidOpen so the coordinator can skip it when closing the // other panels. const selfHandleRef = useRef(null); if (!selfHandleRef.current) { selfHandleRef.current = { close: () => { if (isOpenRef.current) closeRef.current?.(); }, }; } useEffect(() => { if (isOpen) coordination?.notifyDrawerDidOpen(selfHandleRef.current ?? undefined); }, [isOpen, coordination]); useEffect(() => { if (!selfHandleRef.current) return; return coordination?.registerDrawer(selfHandleRef.current); }, [coordination]); if (!ctx) return null; const { open, close, status } = ctx; const isPaused = status === 'paused'; const isActive = status === 'tracking' || isPaused; return ( (next ? open() : close())}> {isActive && ( {elapsedLabel} )} } /> {/* Popovers have no built-in overlay — portal a dim backdrop behind the panel. Clicks land on it (outside Content), so Radix still closes the popover. Own Portal: Radix's Portal slots a single child. Inside AppLayout the backdrop portals into the main-area container (absolute, AppLayoutDrawer-overlay tier) so header/sidebar stay undimmed and interactive; standalone it dims the whole viewport. */}