| 1 | import { useEffect, useState, type MouseEvent } from "react"; |
| 2 | |
| 3 | /** Arms one control at a time, including controls rendered in a menu portal. */ |
| 4 | export function useInlineActionConfirm(scopeKey: string) { |
| 5 | const [armed, setArmed] = useState<{ |
| 6 | scopeKey: string; |
| 7 | action: string; |
| 8 | element: HTMLElement; |
| 9 | } | null>(null); |
| 10 | const active = armed?.scopeKey === scopeKey ? armed : null; |
| 11 | |
| 12 | useEffect(() => { |
| 13 | if (!armed) return; |
| 14 | const disarm = () => setArmed(null); |
| 15 | if (armed.scopeKey !== scopeKey) { |
| 16 | disarm(); |
| 17 | return; |
| 18 | } |
| 19 | const onPointerDown = (event: PointerEvent) => { |
| 20 | if (event.target instanceof Node && !armed.element.contains(event.target)) disarm(); |
| 21 | }; |
| 22 | const onKeyDown = (event: KeyboardEvent) => { |
| 23 | if (event.key === "Escape") { |
| 24 | event.preventDefault(); |
| 25 | event.stopPropagation(); |
| 26 | disarm(); |
| 27 | } |
| 28 | }; |
| 29 | const timeout = window.setTimeout(disarm, 3000); |
| 30 | document.addEventListener("pointerdown", onPointerDown, true); |
| 31 | window.addEventListener("keydown", onKeyDown, true); |
| 32 | window.addEventListener("blur", disarm); |
| 33 | return () => { |
| 34 | window.clearTimeout(timeout); |
| 35 | document.removeEventListener("pointerdown", onPointerDown, true); |
| 36 | window.removeEventListener("keydown", onKeyDown, true); |
| 37 | window.removeEventListener("blur", disarm); |
| 38 | }; |
| 39 | }, [armed, scopeKey]); |
| 40 | |
| 41 | return { |
| 42 | isArmed: (action: string) => active?.action === action, |
| 43 | props: (action: string, onConfirm: () => void) => ({ |
| 44 | onClick: (event: MouseEvent<HTMLElement>) => { |
| 45 | if (active?.action === action && active.element === event.currentTarget) { |
| 46 | setArmed(null); |
| 47 | onConfirm(); |
| 48 | } else { |
| 49 | setArmed({ scopeKey, action, element: event.currentTarget }); |
| 50 | } |
| 51 | }, |
| 52 | onBlur: () => setArmed(null), |
| 53 | }), |
| 54 | }; |
| 55 | } |