Hooks
useDismiss
Closing a layer on outside click and Escape — with an eye on dialogs.
Closing a popup layer on an outside click and on Escape. A small hook — but with one caveat that is the whole reason it exists.
Example
import { useRef, useState } from "react";
import { useDismiss } from "@toimetdev/pathlogs-hooks";
function Popover() {
const [open, setOpen] = useState(false);
const box = useRef<HTMLDivElement>(null);
useDismiss(box, { enabled: open, onDismiss: () => setOpen(false) });
return (
<div ref={box} className="relative">
<button onClick={() => setOpen((v) => !v)}>Open</button>
{open && <div className="absolute">…</div>}
</div>
);
}Options
| Prop | Type | Default | Description |
|---|---|---|---|
enabled* | boolean | — | While false, no listeners are attached at all. |
onDismiss* | () => void | — | What to do. Read at event time. |
escape | boolean | true | Close on Escape. |
outsideClick | boolean | true | Close on an outside click. |
blockedBy | string | "[data-pl-overlay]" | A selector meaning «something else is open above me». While such an element is in the document, closing is skipped. |
enabled*booleanWhile false, no listeners are attached at all.
onDismiss*() => voidWhat to do. Read at event time.
escapebooleandefaults to: true
Close on Escape.
outsideClickbooleandefaults to: true
Close on an outside click.
blockedBystringdefaults to: "[data-pl-overlay]"
A selector meaning «something else is open above me». While such an element is in the document, closing is skipped.
A dialog on top of a menu
This is what blockedBy is for. A typical piece of markup: the dialog's trigger sits inside a dropdown menu, while the dialog itself is portalled into body.
<Menu>
<MenuItem onClick={() => setConfirmOpen(true)}>Delete project</MenuItem>
<ConfirmDialog open={confirmOpen} … /> {/* portalled into body */}
</Menu>Clicking the menu item opens the dialog. If the menu closed on that same click, it would unmount the trigger — and with it the dialog declared inside. The window would never appear: the user would simply see that «the button does not work».
If you have your own modals, give their selector: blockedBy: ".my-modal-backdrop". An empty string turns the check off.