Modale
Ce composant présente une fenêtre modale
Classes utilisables
Modificateurs de taille
.modal--sm: Small.modal--md: Medium.modal--lg: Large.modal--xl: Extra-large
Modificateurs d'apparence
.modal--sticky-header: rend le header de la modale "sticky".modal--sticky-footer: rend le footer de la modale "sticky".modal--backdrop-dark: Fonce l'arrière-plan du volet.modal--backdrop-light: Éclaircit l'arrière-plan du volet.modal--backdrop-blur: Floute l'arrière-plan du volet
Taille
sm
md
lg
xl
Exemples utilisant Javascript
confirm
prompt
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="kuik" uri="kuik" %>
<%@ taglib prefix="resources" uri="resources" %>
<kuik:heading size="lg" skin="a" title="Taille" level="3" />
<kuik:heading size="lg" title="sm" level="4" />
<kuik:button command="show-modal" commandfor="modal-example-sm" size="sm">
Afficher Modale sm
</kuik:button>
<kuik:modal
size="sm"
modalTitle="Modal sm"
closeIconTitle="Fermer"
id="modal-example-sm"
class="modal--backdrop-light"
>
<kuik:modal-body>
<p class="u-pi-sm">
Diam eu maximus fusce <strong>sollicitudin</strong> placerat lorem <i>molestie</i> leo gravida interdum nunc <u>cursus condimentum</u> felis facilisis nisi rutrum condimentum sollicitudin purus purus ipsum suspendisse nisl.
</p>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex">
<div class="u-hspacer-auto"></div>
<kuik:button command="close" commandfor="modal-example-sm" size="sm">
Fermer
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<kuik:heading size="lg" title="md" level="4" />
<kuik:button command="show-modal" commandfor="modal-example-md" size="md">
Afficher Modale md
</kuik:button>
<kuik:modal
size="md"
modalTitle="Modal md"
closeIconTitle="Fermer"
id="modal-example-md"
class="modal--backdrop-dark"
>
<kuik:modal-body>
<p class="u-pi-sm">
Diam eu maximus fusce <strong>sollicitudin</strong> placerat lorem <i>molestie</i> leo gravida interdum nunc <u>cursus condimentum</u> felis facilisis nisi rutrum condimentum sollicitudin purus purus ipsum suspendisse nisl.
</p>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex">
<div class="u-hspacer-auto"></div>
<kuik:button command="close" commandfor="modal-example-md" size="md">
Fermer
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<kuik:heading size="lg" title="lg" level="4" />
<kuik:button command="show-modal" commandfor="modal-example-lg" size="lg">
Afficher Modale lg
</kuik:button>
<kuik:modal
size="lg"
modalTitle="Modal lg"
closeIconTitle="Fermer"
id="modal-example-lg"
class="modal--backdrop-light modal--backdrop-blur"
>
<kuik:modal-body>
<p class="u-pi-sm">
Diam eu maximus fusce <strong>sollicitudin</strong> placerat lorem <i>molestie</i> leo gravida interdum nunc <u>cursus condimentum</u> felis facilisis nisi rutrum condimentum sollicitudin purus purus ipsum suspendisse nisl.
</p>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex">
<div class="u-hspacer-auto"></div>
<kuik:button command="close" commandfor="modal-example-lg" size="lg">
Fermer
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<kuik:heading size="lg" title="xl" level="4" />
<kuik:button command="show-modal" commandfor="modal-example-xl" size="xl">
Afficher Modale xl
</kuik:button>
<kuik:modal
size="xl"
modalTitle="Modal xl"
closeIconTitle="Fermer"
id="modal-example-xl"
class="modal--backdrop-dark modal--backdrop-blur"
>
<kuik:modal-body>
<p class="u-pi-sm">
Diam eu maximus fusce <strong>sollicitudin</strong> placerat lorem <i>molestie</i> leo gravida interdum nunc <u>cursus condimentum</u> felis facilisis nisi rutrum condimentum sollicitudin purus purus ipsum suspendisse nisl.
</p>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex">
<div class="u-hspacer-auto"></div>
<kuik:button command="close" commandfor="modal-example-xl" size="xl">
Fermer
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<kuik:heading size="lg" skin="a" title="Exemples utilisant Javascript" level="3" />
<kuik:heading size="lg" title="confirm" level="4" />
<kuik:button id="for-modal-example-confirm" size="md">
Afficher Modale Confirm
</kuik:button>
<kuik:modal
size="md"
modalTitle="Modal Confirm"
closeIconTitle="Fermer"
id="modal-example-confirm"
class="modal--backdrop-dark modal--backdrop-blur"
>
<kuik:modal-body>
<p class="u-pi-sm">
Voulez-vous continuer ?
</p>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex" style="--column-gap:var(--space-2)">
<div class="u-hspacer-auto"></div>
<kuik:button priority="secondary" size="md" value="no" type="submit">
Non
</kuik:button>
<kuik:button size="md" value="yes" type="submit">
Oui
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<kuik:heading size="lg" title="prompt" level="4" />
<kuik:button id="for-modal-example-prompt" size="md">
Afficher Modale Prompt
</kuik:button>
<kuik:modal
size="md"
modalTitle="Modal Confirm"
closeIconTitle="Fermer"
id="modal-example-prompt"
class="modal--backdrop-dark modal--backdrop-blur"
>
<kuik:modal-body>
<div class="u-p-sm">
<kuik:form-field-input id="name" label="Votre nom" />
</div>
</kuik:modal-body>
<kuik:modal-footer>
<div class="l-flex" style="--column-gap:var(--space-2)">
<div class="u-hspacer-auto"></div>
<kuik:button priority="secondary" size="md" command="close" commandfor="modal-example-prompt">
Annuler
</kuik:button>
<kuik:button size="md" value="send" type="submit">
Envoyer
</kuik:button>
</div>
</kuik:modal-footer>
</kuik:modal>
<script type="module" defer>
import { declareCustomFormModal, requestModal } from "/static/kuik/modal/modal-form.js";
const buttonConfirm = document.getElementById('for-modal-example-confirm');
buttonConfirm.addEventListener('click', async () => {
const returnValue = await requestModal('modal-example-confirm');
alert(`Return value = \${returnValue}`);
});
const confirmModal = declareCustomFormModal('modal-example-prompt', (event, modal, formData) => {
if (modal.returnValue === 'send') {
return formData.get('name');
}
});
const buttonPrompt = document.getElementById('for-modal-example-prompt');
buttonPrompt.addEventListener('click', async () => {
const returnValue = await requestModal(confirmModal);
alert(`Return value = \${returnValue}`);
});
</script>
| Name | Type | Required | Description |
|---|---|---|---|
| id | java.lang.String | true | Identifiant unique de la modale |
| size | java.lang.String | false | Taille (défaut : sm) |
| modalTitle | java.lang.String | true | Titre de la modale |
| closeIconTitle | java.lang.String | true | Titre du bouton de fermeture |
.modal {
--modal-fill: var(--white);
--modal-min-margin: var(--space-8);
background-color: var(--modal-fill);
border: var(--line-thin) var(--neutral-30);
border-radius: var(--radius-md);
max-width: min(100vw - var(--modal-min-margin) * 2, var(--modal-max-width));
opacity: 0;
overflow: hidden;
padding: var(--space-0);
transition: var(--duration-normal) all allow-discrete;
translate: 0 calc(-1 * var(--space-4));
}
.modal[open] {
opacity: 1;
translate: 0 0;
}
.modal__header {
align-items: center;
background-color: var(--modal-fill);
border-block-end: var(--line-thin) var(--neutral-30);
display: flex;
gap: var(--space-2);
justify-content: start;
padding: var(--space-1) var(--space-2);
width: 100%;
}
.modal__title {
font-size: var(--font-size-50);
font-weight: var(--font-weight-700);
margin-block: var(--space-0);
}
.modal__close-label {
@extend %visually-hidden;
}
.modal__footer {
background-color: var(--neutral-10);
border-top: var(--line-thin) var(--neutral-30);
padding: var(--space-1) var(--space-2);
width: 100%;
}
.modal--sticky-header {
.modal__header {
position: sticky;
top: 0;
}
}
.modal--sticky-footer {
.modal__footer {
bottom: 0;
position: sticky;
}
}
.modal--sm {
--modal-max-width: var(--maxwidth-sm);
}
.modal--md {
--modal-max-width: var(--maxwidth-md);
}
.modal--lg {
--modal-max-width: var(--maxwidth-lg);
}
.modal--xl {
--modal-max-width: var(--maxwidth-xl);
}
.modal::backdrop {
backdrop-filter: blur(var(--size-0));
background-color: transparent;
opacity: 0;
position: fixed;
transition: var(--duration-normal) all allow-discrete;
}
.modal[open]::backdrop {
opacity: 1;
}
.modal--backdrop-dark[open]::backdrop {
background-color: rgb(0 0 0 / 40%);
}
.modal--backdrop-light[open]::backdrop {
background-color: rgb(255 255 255 / 40%);
}
.modal--backdrop-blur[open]::backdrop {
backdrop-filter: blur(var(--size-1));
}
:root:has(.modal) {
scrollbar-gutter: stable;
}
:root:has(.modal[open]) {
overflow: hidden;
}
@starting-style {
.modal[open] {
opacity: 0;
translate: 0 calc(-1 * var(--space-4));
}
.modal::backdrop {
backdrop-filter: blur(var(--size-0));
background-color: transparent;
opacity: 0;
}
}
/**
* Vérifie si l'élément fourni en paramètre est une modale et la renvoie si c'est le cas.
* @param modal {String|HTMLDialogElement} Identifiant unique ou élément HTML à vérifier.
* @returns {HTMLDialogElement} Modale comme élément HTML.
* @throws {Error} Erreur si l'élément n'est pas trouvé ou si ce n'est pas une modale.
*/
export function retrieveModal(modal) {
const originalModal = modal;
if (typeof modal === typeof '') {
modal = document.getElementById(modal);
}
if (!(modal && modal instanceof HTMLDialogElement)) throw new Error(`#${originalModal} is not a dialog.`);
return modal;
}
/**
* Permet d'invoquer une fenêtre de dialogue modale pour interroger un utilisateur.
* @param modal {String|HTMLDialogElement} Fenêtre de dialogue modale. Peut être un identifiant unique ou un élément HTML
* @returns {Promise} Valeur de retour
*/
export function requestModal(modal) {
return new Promise((resolve, reject) => {
try {
modal = retrieveModal(modal);
modal.addEventListener('close', event => {
resolve(event.target.returnValue);
}, { once: true });
modal.showModal();
} catch (e) {
reject(e);
}
});
}
/**
* Permet à une fenêtre de dialogue modale contenant un formulaire de renvoyer une valeur de retour personnalisée.
* @param modal {String|HTMLDialogElement} Fenêtre de dialogue modale. Peut être un identifiant unique ou un élément HTML
* @param closeHookCallback {Function} Fonction à appeler avant la fermeture de la modale.
* Cette fonction reçoit l'événement de fermeture (event), la modale (modal) et le formulaire (form) en paramètre.
* Elle peut envoyer une valeur de retour qui sera prise en charge par le handler de l'événement de fermeture.
* @returns {HTMLDialogElement} Renvoie la modale comme élément HTML.
* @throws {Error} Erreur si la modale demandée n'est pas trouvée ou n'est pas une fenêtre de dialogue.
*/
export function declareCustomFormModal(modal, closeHookCallback) {
modal = retrieveModal(modal);
modal.addEventListener('close', async event => {
event.preventDefault();
const [ form ] = modal.getElementsByTagName('form');
const formData = new FormData(form);
const returnValue = await closeHookCallback(event, modal, formData);
modal.returnValue = returnValue;
modal.close(returnValue);
}, { capture: true });
return modal;
}
<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true" dynamic-attributes="dynattrs" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="kuik" uri="kuik" %>
<%@ taglib prefix="resources" uri="resources" %>
<%@ attribute name="id" required="true" type="java.lang.String" description="Identifiant unique de la modale" %>
<%@ attribute name="size" required="false" type="java.lang.String" description="Taille (défaut : sm)" %>
<%@ attribute name="modalTitle" required="true" type="java.lang.String" description="Titre de la modale" %>
<%@ attribute name="closeIconTitle" required="true" type="java.lang.String" description="Titre du bouton de fermeture" %>
<c:if test="${empty pageScope.size}">
<c:set var="size" value="sm" />
</c:if>
<c:set var="classesSize" value="modal--${pageScope.size}"/>
<c:set var="idAttr">id="${pageScope.id}"</c:set>
<c:set var="classes" value="modal ${classesSize}" />
<c:set var="attributes">
<c:forEach items="${dynattrs}" var="a">
<c:choose>
<c:when test="${a.key == 'class'}">
<c:set value="${classes} ${a.value}" var="classes"/>
</c:when>
<c:otherwise>${a.key}="${a.value}" </c:otherwise>
</c:choose>
</c:forEach>
</c:set>
<c:set var="__modalStep" value="modalBody" scope="request" />
<jsp:doBody var="modalBody" />
<c:set var="__modalStep" value="modalFooter" scope="request" />
<jsp:doBody var="modalFooter" />
<dialog class="${pageScope.classes}" ${idAttr} ${pageScope.attributes}>
<form method="dialog">
<header class="modal__header">
<kuik:button
priority="tertiary"
size="md"
icon="true"
command="close"
commandfor="${pageScope.id}"
>
<kuik:icon source="icon://ui/close" title="${pageScope.closeIconTitle}" size="md" />
</kuik:button>
<h2 class="modal__title">
<c:out value="${pageScope.modalTitle}" escapeXml="false" />
</h2>
</header>
<c:out value="${modalBody}" escapeXml="false" />
<c:if test="${not empty modalFooter}">
<c:out value="${modalFooter}" escapeXml="false" />
</c:if>
</form>
</dialog>