BjModal
Modale React rendue via createPortal sur document.body (toujours en portail, pas de prop pour le désactiver). Fermeture : overlay, bouton croix, touche Escape ; classe bj-modal-open sur le body quand open est true.
Utilisation
import { useState } from 'react'
import { BjModal } from '@flrxnt/dsbj/react'
export default function App() {
const [open, setOpen] = useState(false)
return (
<>
<button type="button" onClick={() => setOpen(true)}>Ouvrir</button>
<BjModal
open={open}
onClose={() => setOpen(false)}
title="Titre de la modale"
>
<p>Contenu de la modale.</p>
</BjModal>
</>
)
}Aperçu
Titre de la modale
Contenu de la modale.
Portail et cycle de vie
Le nœud de la modale est toujours monté dans document.body. Prévoir un état open côté parent et synchroniser avec votre routeur ou votre store si besoin.
onClose — patterns TypeScript
import { useCallback, useState } from 'react'
import { BjModal } from '@flrxnt/dsbj/react'
export function ModalWithStableClose() {
const [open, setOpen] = useState(false)
const handleClose = useCallback(() => {
setOpen(false)
}, [])
return (
<BjModal open={open} onClose={handleClose} title="Exemple">
<p>Fermer met à jour l’état parent.</p>
</BjModal>
)
}onClose — déclencheurs (aperçu structurel)
Le clic overlay, le bouton croix (si title) et la touche Escape appellent tous onClose ; gardez une seule fonction stable (useCallback) si vous la passez à des effets.
Titre
Croix · overlay · Escape → onClose
size default
bj-modal (default)
size="default" — pas de modificateur
<BjModal open={open} onClose={() => setOpen(false)} size="default" title="Taille par défaut">
<p>Corps</p>
</BjModal>size sm
bj-modal--sm
size="sm"
<BjModal open={open} onClose={() => setOpen(false)} size="sm" title="Petite">
<p>Modale étroite</p>
</BjModal>size lg
bj-modal--lg
size="lg"
<BjModal open={open} onClose={() => setOpen(false)} size="lg" title="Large">
<p>Modale large</p>
</BjModal>size full
bj-modal--full
size="full"
<BjModal open={open} onClose={() => setOpen(false)} size="full" title="Plein écran">
<p>Occupe la vue</p>
</BjModal>Sans title (pas d’en-tête ni bouton intégré)
Pas de header : fermeture overlay / Escape uniquement.
import { useState } from 'react'
import { BjModal } from '@flrxnt/dsbj/react'
export function ModalSansTitre() {
const [open, setOpen] = useState(false)
return (
<BjModal open={open} onClose={() => setOpen(false)}>
<p>Pas de barre de titre : fermer via overlay ou Escape.</p>
</BjModal>
)
}footer en ReactNode
Confirmer
Contenu principal.
import { useState } from 'react'
import { BjModal } from '@flrxnt/dsbj/react'
export function ModalAvecFooter() {
const [open, setOpen] = useState(false)
return (
<BjModal
open={open}
onClose={() => setOpen(false)}
title="Confirmer"
footer={
<>
<button type="button" className="bj-btn bj-btn--secondary" onClick={() => setOpen(false)}>
Annuler
</button>
<button type="button" className="bj-btn" onClick={() => setOpen(false)}>
Valider
</button>
</>
}
>
<p>Contenu principal.</p>
</BjModal>
)
}Combinaison : title, footer, size
Réglages
className my-modal + size lg + footer
import { useState } from 'react'
import { BjModal } from '@flrxnt/dsbj/react'
export function ModalCombo() {
const [open, setOpen] = useState(false)
return (
<BjModal
open={open}
onClose={() => setOpen(false)}
size="lg"
title="Réglages"
className="my-modal"
footer={<button type="button" className="bj-btn" onClick={() => setOpen(false)}>OK</button>}
>
<p>size lg + title + footer + className</p>
</BjModal>
)
}Props
| Prop | Description |
|---|---|
open | Contrôle la visibilité ; si false, le composant ne rend rien. |
onClose | Callback sans argument, appelé à la fermeture (overlay, croix, Escape). |
size | sm |
title | Titre optionnel ; si absent, pas de header ni libellé aria-labelledby. |
children | Contenu du corps (.bj-modal__body). |
footer | ReactNode optionnel rendu dans le footer sous le corps. |
className | Classe CSS additionnelle sur la racine .bj-modal. |