app/components/admin/media-drawer.tsx
74 lines and 11 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| focusTile | 9 to 17 | 5 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| useInspectorDialog | 24 to 80 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:33> | 33 to 35 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:37> | 37 to 49 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:46> | 46 to 48 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:47> | 47 to 47 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:51> | 51 to 77 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| close | 54 to 54 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| onCancel | 56 to 59 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| onClick | 61 to 70 | 6 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:73> | 73 to 76 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports no other module of this repo. Imported by app/components/admin/media-inspector.tsx.
Source
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router";
/**
* Where focus goes when the inspector closes: the tile it was opened for, found by `data-tile`
* rather than a stored node, because opening is a navigation and a drawer opened from the URL has
* no trigger at all. The grid's tab stop is the thumbnail and the list's is the name.
*/
function focusTile(key: string) {
if (document.activeElement && document.activeElement !== document.body) return;
const tile = document.querySelector(`[data-tile="${CSS.escape(key)}"]`);
const grid = tile?.closest("[data-view]")?.getAttribute("data-view") === "grid";
const target =
tile?.querySelector<HTMLElement>(grid ? "a.cap-media-open" : "a.cap-table-open") ??
document.getElementById("main");
target?.focus({ preventScroll: false });
}
/**
* Drives the inspector's native `<dialog>`: modal once hydrated, so the page behind is inert and
* the browser owns the focus trap. Returns whether it has hydrated; until then the server render
* shows the dialog inline through `data-inline`, and its own links open and close it without script.
*/
export function useInspectorDialog(
ref: React.RefObject<HTMLDialogElement | null>,
activeKey: string,
closeHref: string,
) {
const navigate = useNavigate();
const [hydrated, setHydrated] = useState(false);
const keyRef = useRef(activeKey);
useEffect(() => {
keyRef.current = activeKey;
}, [activeKey]);
useEffect(() => {
setHydrated(true);
const el = ref.current;
if (!el) return;
if (!el.open) el.showModal();
// The panel, not Close: focusing Close makes a screen reader announce "Close" as the whole event.
el.focus({ preventScroll: true });
// Closing unmounts the dialog and drops focus on the body. Deferred: a tile inside a scroller is
// not focusable until it has a box.
return () => {
window.setTimeout(() => focusTile(keyRef.current), 0);
};
}, [ref]);
useEffect(() => {
const el = ref.current;
if (!el) return;
const close = () => navigate(closeHref, { preventScrollReset: true });
// Escape leaves by the URL, as the Close link does: a natively closed dialog would leave ?key= behind.
const onCancel = async (event: Event) => {
event.preventDefault();
await close();
};
// A click outside the panel's box landed on the backdrop, which stands in for the scrim link.
const onClick = async (event: MouseEvent) => {
if (event.target !== el) return;
const r = el.getBoundingClientRect();
const inside =
event.clientX >= r.left &&
event.clientX <= r.right &&
event.clientY >= r.top &&
event.clientY <= r.bottom;
if (!inside) await close();
};
el.addEventListener("cancel", onCancel);
el.addEventListener("click", onClick);
return () => {
el.removeEventListener("cancel", onCancel);
el.removeEventListener("click", onClick);
};
}, [ref, closeHref, navigate]);
return hydrated;
}