import { useEffect, useRef, useState, type ReactNode } from "react"; import { IconButton } from "./Button.tsx"; import { IconMore } from "./icons.tsx"; // выпадающий список: кнопка открывает, щелчок вне или Escape закрывает (строки 897–904) export function Dropdown({ button, className = "", children, onOpen }: { button: (p: { open: boolean; toggle(): void }) => ReactNode; className?: string; children: (close: () => void) => ReactNode; onOpen?: () => void; }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { if (!open) return; const away = (e: PointerEvent) => { if (!ref.current?.contains(e.target as Node)) setOpen(false); }; document.addEventListener("pointerdown", away); return () => document.removeEventListener("pointerdown", away); }, [open]); const toggle = () => { if (!open) onOpen?.(); setOpen(!open); }; // T12.1: побочное действие вне updater return (
{ if (e.key === "Escape") setOpen(false); }}> {button({ open, toggle })} {open &&
{children(() => setOpen(false))}
}
); } // меню «Ещё»: Dropdown с кнопкой-иконкой; пункты — кнопки (aria-pressed для переключателей) export function Menu({ label, children, align = "end" }: { label: string; children: (close: () => void) => ReactNode; align?: "start" | "end"; }) { return ( } aria-haspopup="menu" aria-expanded={open} onClick={toggle} />}> {children} ); } export function MenuItem({ onSelect, pressed, danger, icon, children }: { onSelect(): void; pressed?: boolean; danger?: boolean; icon?: ReactNode; children: ReactNode; }) { return ( ); } export function MenuSeparator() { return
; }