"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { Button } from "@/components/ui/button"; import { Tag } from "@/components/icons"; import { TagPicker } from "./tag-picker"; const TAG_PICKER_WIDTH = 224; /** * The tag entry of the selection toolbar: opens the shared tag picker for * every selected message at once. (#1077) * * The picker is portalled to the body because the toolbar clips its overflow * to animate open and shut. */ export function BatchTagButton({ title, selectedIds, partialIds, onToggle, active, disabled, }: { title: string; /** Tags every selected message carries. */ selectedIds: string[]; /** Tags only some of them carry. */ partialIds: string[]; onToggle: (tagId: string) => void; /** Whether there is a selection to tag; the picker shuts without one. */ active: boolean; disabled?: boolean; }) { const [pos, setPos] = useState<{ top: number; left: number } | null>(null); const pickerRef = useRef(null); const buttonRef = useRef(null); const close = useCallback(() => setPos(null), []); const isOpen = pos !== null && active; useEffect(() => { if (!pos) return; const onPointerDown = (event: PointerEvent) => { const target = event.target as Node; // The anchor is left to its own click handler, which toggles the picker // shut - closing here first would let that click reopen it. if (buttonRef.current?.contains(target)) return; if (!pickerRef.current?.contains(target)) close(); }; const onKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") close(); }; document.addEventListener("pointerdown", onPointerDown, true); document.addEventListener("keydown", onKeyDown); window.addEventListener("resize", close); return () => { document.removeEventListener("pointerdown", onPointerDown, true); document.removeEventListener("keydown", onKeyDown); window.removeEventListener("resize", close); }; }, [pos, close]); return ( <> {isOpen && createPortal(
, document.body, )} ); }