import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; interface ConfirmPopoverProps { // Where the click was (clientX/clientY); the popover opens right there, kept inside the window x: number; y: number; message: React.ReactNode; confirmLabel: string; danger?: boolean; onConfirm: () => void; onCancel: () => void; } const MARGIN = 8; // Small confirmation right at the mouse (Doku/ui-patterns.md): Enter or the button confirms, Escape, a // click outside or "Abbrechen" closes it. export const ConfirmPopover: React.FC = ({ x, y, message, confirmLabel, danger, onConfirm, onCancel }) => { const ref = useRef(null); const confirmRef = useRef(null); const [pos, setPos] = useState({ left: x, top: y, ready: false }); // Below and right of the pointer; flipped above or to the left when it would leave the window useLayoutEffect(() => { const el = ref.current; if (!el) return; const { width, height } = el.getBoundingClientRect(); let left = x - 12; let top = y + MARGIN; if (left + width > window.innerWidth - MARGIN) left = window.innerWidth - width - MARGIN; if (top + height > window.innerHeight - MARGIN) top = y - height - MARGIN; setPos({ left: Math.max(MARGIN, left), top: Math.max(MARGIN, top), ready: true }); confirmRef.current?.focus(); }, [x, y]); useEffect(() => { const onDown = (e: Event) => { if (ref.current && !ref.current.contains(e.target as Node)) onCancel(); }; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onCancel(); }; document.addEventListener('pointerdown', onDown); document.addEventListener('keydown', onKey); return () => { document.removeEventListener('pointerdown', onDown); document.removeEventListener('keydown', onKey); }; }, [onCancel]); return createPortal(
{message}
, document.body ); };