/** * BottomSheet — mobile-first slide-up drawer used by the player. * * UX notes: * - Anchored to the bottom; content scrolls independently of * the page, capped at ~70vh so the video stage stays visible. * - Backdrop click + ESC close the sheet (mirrors Modal). * - Focus is not trapped — the player wants the input to keep * focus when the sheet opens briefly for a quick tap. If you * need a trap for a future long-form sheet, wrap it in a * without changing this primitive. * - Styled against the same token palette as the editor * (#1a1a1a surface, #3f3f3f borders, #3ea6ff accent). */ import React, { useEffect } from "react"; import { X } from "lucide-react"; export interface BottomSheetProps { open: boolean; onClose: () => void; title: React.ReactNode; subtitle?: React.ReactNode; children: React.ReactNode; /** Extra actions rendered in the header (right-aligned). */ actions?: React.ReactNode; /** Max height as a CSS value; default `70vh`. */ maxHeight?: string; } export function BottomSheet({ open, onClose, title, subtitle, children, actions, maxHeight = "70vh", }: BottomSheetProps) { useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; return (
{title}
{subtitle &&
{subtitle}
}
{actions}
{children}
); }