Panel
Le composant `panel` permet de créer un panneau.
Il prend en paramètres les éléments suivants :
id: l'identifiant du panneauposition: la position du panneaumodal: détermine si le panneau est modalbackdrop: modificateur pour le backdrop
Modificateurs de position
left: Le volet est à gaucheright: Le volet est à droitetop: Le volet est en hautbottom: Le volet est en bas
Modificateurs destinés au backdrop
-
panel--modal: Indique que le volet a un comportement modal -
dark: Fonce l'arrière-plan du volet -
light: Éclaircit l'arrière-plan du volet -
blur: Floute l'arrière-plan du volet -
Exemples
<kuik:panel id="panel" position="right" modal="true" backdrop="light blur">
</kuik:panel>
Panneau à gauche
Panel Body
Panel Footer
Panneau à droite
Panel Body
Panel Footer
Panneau en bas
Panel Body
Panel Footer
Panneau en haut
Panel Body
Panel Footer
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="kuik" uri="kuik" %>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel">Afficher le panel à gauche</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-right">Afficher le panel à droite</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-top">Afficher le panel en haut</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-bottom">Afficher le panel en bas</kuik:button>
</p>
<hr>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-modal">Panneau modal</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-dark">Panneau modal, page foncée</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-light">Panneau modal, page éclaircie</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-blur">Panneau modal, page floue</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-dark-blur">Panneau modal, page foncée et floue</kuik:button>
</p>
<p>
<kuik:button type="primary" size="lg" popoverTarget="panel-light-blur">Panneau modal, page éclaircie et floue</kuik:button>
</p>
<kuik:panel id="panel">
<kuik:panel-header popoverTarget="panel" title="Panneau à gauche"/>
<kuik:panel-body>
<p>Panel Body</p>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-right" position="right">
<kuik:panel-header popoverTarget="panel-right" title="Panneau à droite"/>
<kuik:panel-body>
<p>Panel Body</p>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-bottom" position="bottom">
<kuik:panel-header popoverTarget="panel-bottom" title="Panneau en bas"/>
<kuik:panel-body>
<p>Panel Body</p>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-top" position="top">
<kuik:panel-header popoverTarget="panel-top" title="Panneau en haut"/>
<kuik:panel-body>
<p>Panel Body</p>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-modal" modal="true" >
<kuik:panel-header popoverTarget="panel-modal" title="Panneau modal" closeIcon="icon://ui/arrow-left"/>
<kuik:panel-body>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-dark" modal="true" backdrop="dark" >
<kuik:panel-header popoverTarget="panel-dark" title="Panneau modal, page foncée" displayCloseIcon="false"/>
<kuik:panel-body>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-light" modal="true" backdrop="light" >
<kuik:panel-header popoverTarget="panel-light" title="Panneau modal, page éclaircie"/>
<kuik:panel-body>
<div>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</div>
<div class="form-field form-field--md">
<label class="form-field__label" for="test-modal-focus">test-modal-focus</label>
<div class="form-field__control">
<input class="form-field__input" id="test-modal-focus" type="text" />
</div>
</div>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-blur" modal="true" backdrop="blur" >
<kuik:panel-header popoverTarget="panel-blur" title="Panneau modal, page floue"/>
<kuik:panel-body>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-dark-blur" modal="true" backdrop="dark blur" >
<kuik:panel-header popoverTarget="panel-dark-blur" title="Panneau modal, page foncée et floue"/>
<kuik:panel-body>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
<kuik:panel id="panel-light-blur" modal="true" backdrop="light blur" >
<kuik:panel-header popoverTarget="panel-light-blur" title="Panneau modal, page éclaircie et floue"/>
<kuik:panel-body>
<p>Panel Body</p>
<a href="https://www.kosmos-education.com">kosmos</a>
<kuik:button status="accent">Un bouton</kuik:button>
</kuik:panel-body>
<kuik:panel-footer>
<p>Panel Footer</p>
</kuik:panel-footer>
</kuik:panel>
| Name | Type | Required | Description |
|---|---|---|---|
| id | java.lang.String | true | Id du panel |
| position | java.lang.String | false | Position du panel (défaut : left) |
| modal | java.lang.Boolean | false | Définit si le panel agit commme une modal (défaut : false) |
| backdrop | java.lang.String | false | Modification du backdrop (si modal activée) [blur|dark|light] (défaut : aucun) |
.panel {
--panel-bottom: auto;
--panel-fill: var(--body-fill);
--panel-content: var(--body-content);
--panel-color-scheme: light dark;
--panel-height: 100dvh;
--panel-left: auto;
--panel-top: auto;
--panel-max-width: initial;
--panel-min-height: 0;
--panel-right: auto;
--panel-width: 100%;
background-color: var(--panel-fill);
border: none;
bottom: var(--panel-bottom);
box-shadow: var(--shadow-5);
color: var(--panel-content);
color-scheme: var(--color-scheme);
display: none;
flex-direction: column;
height: var(--panel-height);
left: var(--panel-left);
max-width: var(--panel-max-width);
min-height: var(--panel-min-height);
overscroll-behavior: contain;
padding: var(--space-0);
position: fixed;
right: var(--panel-right);
top: var(--panel-top);
transition:
var(--duration-normal) translate var(--transition-function),
var(--duration-normal) display allow-discrete
;
width: var(--panel-width);
}
.panel.panel--open,
.panel:popover-open {
display: flex;
translate: 0 0;
}
:root:has(.panel:popover-open) {
overflow: hidden;
}
.panel::backdrop {
backdrop-filter: blur(var(--size-0));
background-color: transparent;
position: fixed;
}
@starting-style {
.panel::backdrop {
backdrop-filter: blur(var(--size-0));
background-color: transparent;
}
}
.panel--backdrop-dark.panel--open::backdrop,
.panel--backdrop-dark:popover-open::backdrop {
background-color: rgb(0 0 0 / 40%);
}
.panel--backdrop-light.panel--open::backdrop,
.panel--backdrop-light:popover-open::backdrop {
background-color: rgb(255 255 255 / 40%);
}
.panel--backdrop-blur.panel--open::backdrop,
.panel--backdrop-blur:popover-open::backdrop {
backdrop-filter: blur(var(--size-1));
}
.panel__header {
align-items: center;
background-color: var(--panel-fill);
border-block-end: var(--line-thin) var(--neutral-30);
display: flex;
gap: var(--space-2);
justify-content: space-between;
padding: var(--space-1) var(--space-2);
position: sticky;
top: 0;
width: 100%;
z-index: 1;
}
.panel__header--vertical {
align-items: start;
flex-direction: column;
gap: var(--space-4);
}
.panel__title {
font-size: var(--font-size-50);
font-weight: var(--font-weight-700);
margin: var(--space-0) auto;
}
.panel__body {
flex-grow: 1;
overflow-x: hidden;
position: relative;
scroll-snap-type: x mandatory;
}
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
.panel__body--smooth-scroll {
scroll-behavior: smooth;
}
}
.panel__body--padding {
padding: var(--space-4);
}
.panel__footer {
background-color: var(--neutral-10);
border-block-start: var(--line-thin) var(--neutral-30);
bottom: 0;
padding: var(--space-1) var(--space-2);
position: sticky;
width: 100%;
}
.panel--left {
--panel-left: 0;
--panel-max-width: var(--size-120);
--panel-min-height: 100%;
--panel-top: 0;
translate: -100% 0;
}
.panel--right {
--panel-right: 0;
--panel-max-width: var(--size-120);
--panel-min-height: 100%;
--panel-top: 0;
translate: 100% 0;
}
.panel--top {
--panel-height: auto;
--panel-left: 0;
--panel-right: 0;
--panel-top: 0;
translate: 0 -100%;
}
.panel--bottom {
--panel-bottom: 0;
--panel-height: auto;
--panel-left: 0;
--panel-right: 0;
translate: 0 100%;
}
.panel--left.panel--open,
.panel--left:popover-open {
@starting-style {
translate: -100% 0;
}
}
.panel--right.panel--open,
.panel--right:popover-open {
@starting-style {
translate: 100% 0;
}
}
.panel--bottom.panel--open,
.panel--bottom:popover-open {
@starting-style {
translate: 0 100%;
}
}
.panel--top.panel--open,
.panel--top:popover-open {
@starting-style {
translate: 0 -100%;
}
}
.panel--no-border {
.panel__header {
border-block-end: none;
}
.panel__footer {
background-color: transparent;
border-block-start: none;
}
}
.panel--no-sticky {
.panel__header,
.panel__footer {
position: static;
}
.panel__body {
flex-shrink: 0;
overflow-y: hidden;
}
}
.panel--skin-a {
--panel-fill: var(--color1-20);
--panel-content: var(--color1-60);
--panel-color-scheme: light dark;
--heading-title-color: var(--color1-50);
--heading-subtitle-color: var(--color1-70);
--links: var(--color1-60);
--links-visited: var(--color1-60);
.panel__header {
padding: var(--space-8);
}
.panel__footer {
padding: var(--space-6) var(--space-8);
}
}
.panel-backdrop {
height: 100dvh;
inset: 0;
position: fixed;
width: 100dvw;
z-index: calc(infinity);
}
import {jailFocus, unJailFocus} from '/static/js/jailfocus.js';
// CONSTANTS
const TOGGLE_EVENT = 'toggle';
const DIV_TAG = 'div';
const BACKDROP_CLASS = 'panel-backdrop';
const POPOVER_OPEN_CLASS = 'panel--open';
const PANEL_MODAL_SELECTOR = '.panel--modal';
const BACKDROP_SELECTOR = `.${BACKDROP_CLASS}`;
const OPENED_PANELS_SELECTOR = `${PANEL_MODAL_SELECTOR}.${POPOVER_OPEN_CLASS}`;
const POPOVER_STATE_OPEN = 'open';
/**
* Événement exécuté à l'ouverture ou la fermeture du panneau.
*/
function panelToggleHandler(event) {
const {target} = event;
target.classList.toggle(POPOVER_OPEN_CLASS, event.newState === POPOVER_STATE_OPEN);
const openedPanels = document.querySelectorAll(OPENED_PANELS_SELECTOR);
const currentBackdrop = document.querySelector(BACKDROP_SELECTOR);
if (openedPanels.length && !currentBackdrop) {
const backdrop = document.createElement(DIV_TAG);
backdrop.classList.add(BACKDROP_CLASS);
document.body.appendChild(backdrop);
} else if (!(openedPanels.length) && currentBackdrop) {
currentBackdrop.remove();
}
if (event.newState === POPOVER_STATE_OPEN) {
jailFocus(target);
} else {
unJailFocus(target);
}
}
/**
* Ajoute les contrôles d'événements nécessaires au fonctionnement modal du panneau.
* @param {HTMLElement} panel Panneau concerné
*/
function bindModalPanelEvents(panel) {
panel.addEventListener(TOGGLE_EVENT, panelToggleHandler);
}
/**
* Retire les contrôles d'événement nécessaires au fonctionnement modal du panneau.
* @param {HTMLElement} panel Panneau concerné
*/
function unbindModalPanelEvents(panel) {
panel.removeEventListener(TOGGLE_EVENT, panelToggleHandler);
}
/**
* Initialise un panneau modal.
* @param {HTMLElement} panel Panneau à initialiser
*/
function initModalPanel(panel) {
bindModalPanelEvents(panel);
}
/**
* Détruit la gestion associée à un panneau modal.
* @param {HTMLElement} panel Panneau concerné
*/
function destroyModalPanel(panel) {
unbindModalPanelEvents(panel);
}
/**
* Initialise tous les panneaux modaux trouvés sur la page.
*/
function initAllModalPanel() {
document.querySelectorAll(PANEL_MODAL_SELECTOR).forEach(initModalPanel);
}
/**
* Détruit la gestion associée à tous les panneaux modaux trouvés sur la page.
*/
function destroyAllModalPanel() {
document.querySelectorAll(PANEL_MODAL_SELECTOR).forEach(destroyModalPanel);
}
export { initAllModalPanel, initModalPanel, destroyAllModalPanel, destroyModalPanel };
window.addEventListener('load', initAllModalPanel);
const body = document.querySelector('body');
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.target.querySelectorAll(PANEL_MODAL_SELECTOR).forEach((panel) => {
destroyModalPanel(panel);
initModalPanel(panel);
});
});
});
mutationObserver.observe(body, {childList: true, subtree: true, attributes: false});
<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true" dynamic-attributes="dynattrs" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="resources" uri="resources" %>
<%@ attribute name="id" required="true" type="java.lang.String" description="Id du panel" %>
<%@ attribute name="position" required="false" type="java.lang.String" description="Position du panel (défaut : left)" %>
<%@ attribute name="modal" required="false" type="java.lang.Boolean" description="Définit si le panel agit commme une modal (défaut : false)" %>
<%@ attribute name="backdrop" required="false" type="java.lang.String" description="Modification du backdrop (si modal activée) [blur|dark|light] (défaut : aucun)" %>
<c:if test="${empty pageScope.position}">
<c:set var="position" value="left"/>
</c:if>
<c:if test="${pageScope.modal}">
<c:set var="modalClass" value="panel--modal"/>
<c:if test="${not empty pageScope.backdrop}">
<c:forEach var="backdropPart" items="${pageScope.backdrop.split(' ')}">
<c:set var="modalClass" value="${modalClass} panel--backdrop-${backdropPart}"/>
</c:forEach>
</c:if>
</c:if>
<c:set var="classes" value="panel panel--${pageScope.position} ${pageScope.modalClass}"/>
<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>
<div id="${pageScope.id}" class="${pageScope.classes}" popover ${pageScope.attributes}>
<jsp:doBody/>
</div>
<resources:addScript path="/static/kuik/panel/panel.js" type="module"/>