import { X } from "lucide-react" import { AnimatePresence, motion } from "motion/react" import type { ReactNode } from "react" import { useEffect } from "react" import { Button } from "@/components/ui/button" import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet" import { useIsMobile } from "@/hooks/useMobile" import { duration, easeEmphasized, easeStandard } from "@/lib/motion" import { cn } from "@/lib/utils" /** Same grammar as the shared `slideUp`, on the axis this panel travels. */ const panelSlide = { hidden: { opacity: 0, x: 16 }, visible: { opacity: 1, x: 0, transition: { duration: duration.base, ease: easeEmphasized }, }, exit: { opacity: 0, x: 16, transition: { duration: duration.fast, ease: easeStandard }, }, } export const PANEL_SECTION = "text-xs font-medium uppercase tracking-[0.5px] text-muted-foreground" /** * The editor's settings panel: floating over the canvas so the graph stays * visible and running behind it, a full-screen sheet where there is no room * for that. * * Node settings and flow settings share it, so the two read as one surface. */ export function SidePanel({ open, label, testId, bodyKey, expanded = false, header, footer, children, onClose, }: { open: boolean /** Names the panel for screen readers. */ label: string testId: string /** Remounts the contents when the thing being edited changes. */ bodyKey: string /** Fill the content area instead of floating beside the canvas. */ expanded?: boolean header: ReactNode footer?: ReactNode children: ReactNode onClose: () => void }) { const isMobile = useIsMobile() useEffect(() => { if (!open) return const onKey = (event: KeyboardEvent) => { if (event.key === "Escape") onClose() } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) }, [open, onClose]) const contents = ( <>