v3: code quality pass

This commit is contained in:
khannurien
2026-03-24 18:47:05 +00:00
parent cd4076343b
commit c293f3e706
39 changed files with 1464 additions and 1555 deletions

56
src/components/Modal.tsx Normal file
View File

@@ -0,0 +1,56 @@
import { type ReactNode, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
interface ModalProps {
title: string;
onClose: () => void;
children: ReactNode;
wide?: boolean;
}
export function Modal({ title, onClose, children, wide = false }: ModalProps) {
const backdropRef = useRef<HTMLDivElement>(null);
useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "";
};
}, []);
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape" && !e.defaultPrevented) onClose();
};
document.addEventListener("keydown", handler);
return () => document.removeEventListener("keydown", handler);
}, [onClose]);
return createPortal(
<div
className="modal-backdrop"
ref={backdropRef}
onClick={(e) => {
if (e.target === backdropRef.current) onClose();
}}
>
<div className={`modal-card${wide ? " modal-card--wide" : ""}`}>
<div className="modal-header">
<span className="modal-title">{title}</span>
<button
type="button"
className="modal-close-btn"
onClick={onClose}
aria-label="Close"
>
</button>
</div>
<div className="modal-body">
{children}
</div>
</div>
</div>,
document.body,
);
}