refactor(ui): useDismiss hook for outside-click/Escape close

lib/useDismiss.ts replaces the identical mousedown-outside + Escape effect that
DataTable (filter popover), Channels (tag picker) and AddToPlaylist each hand-rolled.
Positioning stays with each caller (it genuinely varies); AddToPlaylist keeps its
own resize/scroll reposition effect.
This commit is contained in:
npeter83 2026-06-29 00:11:44 +02:00
parent f6b9ac2dd1
commit e68e0d3f7a
4 changed files with 40 additions and 43 deletions

View file

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useRef, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
@ -14,6 +14,7 @@ import {
UserMinus,
} from "lucide-react";
import { api, HttpError, type ManagedChannel, type Tag } from "../lib/api";
import { useDismiss } from "../lib/useDismiss";
import { formatEta, formatViews, relativeTime } from "../lib/format";
import { notify } from "../lib/notifications";
import Tooltip from "./Tooltip";
@ -616,19 +617,7 @@ function TagsCell({
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!open) return;
const onDown = (e: MouseEvent) => {
if (!ref.current?.contains(e.target as Node)) setOpen(false);
};
const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
document.addEventListener("mousedown", onDown);
document.addEventListener("keydown", onKey);
return () => {
document.removeEventListener("mousedown", onDown);
document.removeEventListener("keydown", onKey);
};
}, [open]);
useDismiss(open, () => setOpen(false), ref);
if (userTags.length === 0) return null;
// Show only the tags actually attached to this channel; the “+” opens a per-channel
// picker to attach/detach (kept the convenient "kirakás" without listing every tag