fix(dsfr): Modal avec overlay visible et fermeture fiable
L'ancienne implémentation (<dialog> plein écran transparent) rendait la page inutilisable au clic sur éditer/supprimer : overlay invisible captant tous les clics, sans fond assombri ni fermeture au clic extérieur. Remplacé par un overlay React maîtrisé (fond assombri, clic extérieur/Échap/bouton ferment). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,11 +12,10 @@ interface ModalProps {
|
||||
size?: ModalSize;
|
||||
}
|
||||
|
||||
// Largeur de la colonne DSFR selon la taille demandée.
|
||||
const sizeColClasses: Record<ModalSize, string> = {
|
||||
sm: "fr-col-12 fr-col-md-4",
|
||||
md: "fr-col-12 fr-col-md-6",
|
||||
lg: "fr-col-12 fr-col-md-8",
|
||||
const maxWidthBySize: Record<ModalSize, string> = {
|
||||
sm: "480px",
|
||||
md: "672px",
|
||||
lg: "896px",
|
||||
};
|
||||
|
||||
export default function Modal({
|
||||
@@ -34,10 +33,9 @@ export default function Modal({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
if (!isOpen) return;
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
document.body.style.overflow = "";
|
||||
@@ -46,49 +44,52 @@ export default function Modal({
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
// L'ouverture/fermeture est pilotée par React (isOpen/onClose), on applique
|
||||
// donc la structure et les classes visuelles DSFR sans le JS d'ouverture DSFR.
|
||||
// Overlay React maîtrisé (pas le JS DSFR) : fond assombri visible, fermeture
|
||||
// au clic extérieur / Échap / bouton. Look DSFR via les classes fr-modal__*.
|
||||
return (
|
||||
<dialog
|
||||
className="fr-modal fr-modal--opened"
|
||||
aria-modal="true"
|
||||
<div
|
||||
role="dialog"
|
||||
open
|
||||
aria-modal="true"
|
||||
onClick={onClose}
|
||||
style={{
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
zIndex: 1000,
|
||||
display: "block",
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
maxWidth: "none",
|
||||
maxHeight: "none",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "center",
|
||||
overflowY: "auto",
|
||||
padding: "2rem 1rem",
|
||||
background: "rgba(22, 22, 22, 0.64)",
|
||||
}}
|
||||
>
|
||||
<div className="fr-container fr-container--fluid fr-container-md">
|
||||
<div className="fr-grid-row fr-grid-row--center">
|
||||
<div className={sizeColClasses[size]}>
|
||||
<div className="fr-modal__body">
|
||||
<div className="fr-modal__header">
|
||||
<button
|
||||
type="button"
|
||||
className="fr-btn--close fr-btn"
|
||||
aria-label="Fermer"
|
||||
onClick={onClose}
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
<div className="fr-modal__content">
|
||||
{title && <h1 className="fr-modal__title">{title}</h1>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="fr-modal__body"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: maxWidthBySize[size],
|
||||
background: "var(--background-default-grey, #fff)",
|
||||
borderRadius: "0.25rem",
|
||||
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.32)",
|
||||
padding: "1.5rem",
|
||||
}}
|
||||
>
|
||||
<div className="fr-modal__header">
|
||||
<button
|
||||
type="button"
|
||||
className="fr-btn--close fr-btn"
|
||||
aria-label="Fermer"
|
||||
onClick={onClose}
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
<div className="fr-modal__content">
|
||||
{title && <h1 className="fr-modal__title">{title}</h1>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user