Lots F1-F4 : runtime de chargement/registre plugin, extension des menus existants, panneau de gestion des plugins, types de layout custom (sélecteur, fallback, cellule dédiée) branchés sur le port plugin. Suite npm typecheck/test verte (947/947). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
/**
|
|
* A small modal confirmation, local to the plugins feature (ticket #43, F2).
|
|
* Mirrors `features/devices/ConfirmDialog` (not shared — that dialog is itself
|
|
* feature-local by design, see its header comment); duplicated rather than
|
|
* cross-imported to keep each feature's public surface to its own `index.ts`.
|
|
*/
|
|
|
|
import { useEffect, useRef } from "react";
|
|
|
|
import { Button, zIndex } from "@/shared";
|
|
|
|
interface PluginConfirmDialogProps {
|
|
title: string;
|
|
body: string;
|
|
confirmLabel: string;
|
|
danger?: boolean;
|
|
busy?: boolean;
|
|
onConfirm: () => void | Promise<void>;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
export function PluginConfirmDialog({
|
|
title,
|
|
body,
|
|
confirmLabel,
|
|
danger = false,
|
|
busy = false,
|
|
onConfirm,
|
|
onCancel,
|
|
}: PluginConfirmDialogProps) {
|
|
const cancelRef = useRef<HTMLButtonElement>(null);
|
|
|
|
useEffect(() => {
|
|
cancelRef.current?.focus();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
if (e.key === "Escape") onCancel();
|
|
}
|
|
window.addEventListener("keydown", onKeyDown);
|
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
}, [onCancel]);
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 flex items-center justify-center bg-black/50 p-4"
|
|
style={{ zIndex: zIndex.floatingWindow }}
|
|
onClick={onCancel}
|
|
>
|
|
<div
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={title}
|
|
onClick={(e) => e.stopPropagation()}
|
|
className="flex w-full max-w-md flex-col gap-3 rounded-lg border border-border bg-raised p-4 shadow-xl"
|
|
>
|
|
<h3 className="text-sm font-semibold text-content">{title}</h3>
|
|
<p className="whitespace-pre-line text-sm text-muted">{body}</p>
|
|
<div className="flex justify-end gap-2">
|
|
<Button ref={cancelRef} size="sm" variant="ghost" onClick={onCancel}>
|
|
Annuler
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant={danger ? "danger" : "primary"}
|
|
loading={busy}
|
|
onClick={() => void onConfirm()}
|
|
>
|
|
{confirmLabel}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|