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;
|
size?: ModalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Largeur de la colonne DSFR selon la taille demandée.
|
const maxWidthBySize: Record<ModalSize, string> = {
|
||||||
const sizeColClasses: Record<ModalSize, string> = {
|
sm: "480px",
|
||||||
sm: "fr-col-12 fr-col-md-4",
|
md: "672px",
|
||||||
md: "fr-col-12 fr-col-md-6",
|
lg: "896px",
|
||||||
lg: "fr-col-12 fr-col-md-8",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Modal({
|
export default function Modal({
|
||||||
@@ -34,10 +33,9 @@ export default function Modal({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (!isOpen) return;
|
||||||
document.addEventListener("keydown", handleKeyDown);
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
document.body.style.overflow = "hidden";
|
document.body.style.overflow = "hidden";
|
||||||
}
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("keydown", handleKeyDown);
|
document.removeEventListener("keydown", handleKeyDown);
|
||||||
document.body.style.overflow = "";
|
document.body.style.overflow = "";
|
||||||
@@ -46,31 +44,37 @@ export default function Modal({
|
|||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
// L'ouverture/fermeture est pilotée par React (isOpen/onClose), on applique
|
// Overlay React maîtrisé (pas le JS DSFR) : fond assombri visible, fermeture
|
||||||
// donc la structure et les classes visuelles DSFR sans le JS d'ouverture DSFR.
|
// au clic extérieur / Échap / bouton. Look DSFR via les classes fr-modal__*.
|
||||||
return (
|
return (
|
||||||
<dialog
|
<div
|
||||||
className="fr-modal fr-modal--opened"
|
|
||||||
aria-modal="true"
|
|
||||||
role="dialog"
|
role="dialog"
|
||||||
open
|
aria-modal="true"
|
||||||
|
onClick={onClose}
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
display: "block",
|
display: "flex",
|
||||||
background: "transparent",
|
alignItems: "flex-start",
|
||||||
border: "none",
|
justifyContent: "center",
|
||||||
maxWidth: "none",
|
overflowY: "auto",
|
||||||
maxHeight: "none",
|
padding: "2rem 1rem",
|
||||||
width: "100%",
|
background: "rgba(22, 22, 22, 0.64)",
|
||||||
height: "100%",
|
}}
|
||||||
|
>
|
||||||
|
<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-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">
|
<div className="fr-modal__header">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -87,8 +91,5 @@ export default function Modal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</dialog>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user