fix: react-doctor dialog/a11y/labels → 68/100 with 973 warnings

This commit is contained in:
Nora
2026-06-26 03:43:04 -06:00
parent d0bfec9d36
commit e97eb33bf1
34 changed files with 611 additions and 580 deletions
+47 -32
View File
@@ -37,6 +37,33 @@ const Icons = {
),
};
async function resizeImage(file: File, maxWidth: number): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.onload = () => {
let { width, height } = img;
if (width > maxWidth) {
height = Math.round(height * (maxWidth / width));
width = maxWidth;
}
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d")!;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error("Failed to resize image"));
return;
}
blob.arrayBuffer().then((buf) => resolve(buf));
}, "image/jpeg", 0.85);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
export default function ProductsClient({
products,
brandId,
@@ -75,33 +102,6 @@ export default function ProductsClient({
const activeCount = products.filter((p) => p.active).length;
const inactiveCount = products.filter((p) => !p.active).length;
async function resizeImage(file: File, maxWidth: number): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const img = document.createElement("img");
img.onload = () => {
let { width, height } = img;
if (width > maxWidth) {
height = Math.round(height * (maxWidth / width));
width = maxWidth;
}
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d")!;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error("Failed to resize image"));
return;
}
blob.arrayBuffer().then((buf) => resolve(buf));
}, "image/jpeg", 0.85);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
const handleUploadImage = useCallback(
async (file: File): Promise<{ success: boolean; imageUrl?: string; error?: string }> => {
const validTypes = ["image/png", "image/jpeg", "image/webp"];
@@ -381,6 +381,7 @@ function TableView({
const [menuPos, setMenuPos] = useState<{ top: number; left: number; width: number } | null>(null);
const [mounted, setMounted] = useState(false);
const buttonRefs = useRef<Record<string, HTMLButtonElement | null>>({});
const popupDialogRef = useRef<HTMLDialogElement>(null);
useEffect(() => {
const init = async () => {
@@ -389,6 +390,21 @@ function TableView({
init();
}, []);
useEffect(() => {
if (!deleteConfirm) return;
const dialog = popupDialogRef.current;
if (!dialog) return;
if (!dialog.open) dialog.showModal();
const onCancel = (e: Event) => {
e.preventDefault();
onDeleteCancel();
};
dialog.addEventListener("cancel", onCancel);
return () => {
dialog.removeEventListener("cancel", onCancel);
};
}, [deleteConfirm, onDeleteCancel]);
// Track the previous deleteConfirm value so we can adjust menuPos
// inline during render when the prop changes — avoids a stale frame
// between the prop change and the effect running. Use a lazy
@@ -540,12 +556,11 @@ function TableView({
<>
<div
className="fixed inset-0 z-[60]"
onClick={onDeleteCancel}
/>
<div
role="dialog"
<dialog
ref={popupDialogRef}
aria-label="Confirm delete"
className="fixed z-[70] w-72 rounded-xl bg-[var(--admin-card-bg)] border border-[var(--admin-border)] shadow-xl p-4"
className="fixed z-[70] m-0 p-4 max-w-none max-h-none w-72 rounded-xl bg-[var(--admin-card-bg)] border border-[var(--admin-border)] shadow-xl"
style={{ top: menuPos.top, left: menuPos.left, transform: "translateX(-100%)" }}
>
<p className="text-sm font-semibold text-[var(--admin-text-primary)]">
@@ -571,7 +586,7 @@ function TableView({
{deletingId === openProduct.id ? "..." : "Delete"}
</AdminButton>
</div>
</div>
</dialog>
</>,
document.body
)}