Accès rapide
Composant permettant de générer un menu.
Attributs du composant
Modificateur de taille (boutons et icones)
sm: Smallmd: Mediumlg: Largexl: Extra-large
Modificateur d'alignement
left: Le popover est aligné sur la gauche du bouton de dropdownright: Le popover est aligné sur la droite du bouton de dropdown
Size
Alignement
Left
Right
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="kuik" uri="kuik" %>
<%--@elvariable id="quickAccessViewModel" type="fr.kosmos.web.kore.attributes.DefaultQuickAccess"--%>
<kuik:heading size="lg" skin="a" title="Size" level="3" />
<kuik:quick-access quickaccess="${quickAccessViewModel}" size="sm"/><br>
<kuik:quick-access quickaccess="${quickAccessViewModel}" size="md"/><br>
<kuik:quick-access quickaccess="${quickAccessViewModel}" size="lg"/><br>
<kuik:quick-access quickaccess="${quickAccessViewModel}" size="xl"/><br>
<kuik:heading size="lg" skin="a" title="Alignement" level="3" />
<kuik:heading size="lg" title="Left" level="4" />
<kuik:quick-access quickaccess="${quickAccessViewModel}"/><br>
<hr>
<kuik:heading size="lg" title="Right" level="4" />
<kuik:quick-access quickaccess="${quickAccessViewModel}" placement="right"/><br>
| Name | Type | Required | Description |
|---|---|---|---|
| status | java.lang.String | false | |
| priority | java.lang.String | false | |
| quickaccess | fr.kosmos.web.kore.attributes.interfaces.QuickAccess | true | |
| placement | java.lang.String | false | Placement du popover par rapport au dropdown (défaut : start) |
| size | java.lang.String | false | Taille (défaut : sm) |
| iconOnly | java.lang.Boolean | false | N'affiche que l'icône dans le bouton |
.quick-access {
--radius: var(--radius-none);
--focus-outline-color: var(--links-focus-radius-color);
align-items: center;
display: inline-flex;
flex-wrap: wrap;
list-style: none;
margin: var(--space-0);
padding: var(--space-0);
}
.quick-access__item {
max-inline-size: 100%;
.button {
--button-radius: var(--size-0);
}
.button:hover {
--button-fill: var(--neutral-20);
}
}
.quick-access__link,
.quick-access__btn {
--padding-h: var(--space-3);
--padding-v: var(--space-2);
align-items: center;
border-radius: var(--radius);
color: var(--neutral-70);
display: inline-flex;
font-style: normal;
font-weight: var(--font-weight-400);
gap: var(--space-2);
line-height: normal;
padding: var(--padding-v) var(--padding-h);
text-align: start;
text-decoration: none;
}
.quick-access__btn {
background-color: var(--neutral-20);
border: var(--space-0);
cursor: pointer;
font-size: var(--font-size-10);
position: relative;
}
.quick-access__link:hover,
.quick-access__btn:hover {
background-color: var(--neutral-20);
text-decoration: none;
}
.quick-access__link:focus,
.quick-access__btn:focus {
background-color: var(--neutral-20);
border-radius: var(--radius);
outline: 2px solid var(--focus-outline-color);
outline-offset: 1px;
}
.quick-access__label {
align-items: center;
display: inline-flex;
gap: var(--space-2);
}
.header__top-header {
.quick-access__item {
font-size: var(--font-size-10);
}
}
/**
* Initialise les événements des boutons des menus d'accès rapide.
* @param {(HTMLButtonElement|HTMLAnchorElement)[]} quickAccessMenus les éléments du menu.
*/
function initEventsButton(quickAccessMenus) {
for (const currentMenu of quickAccessMenus) {
currentMenu.addEventListener('keydown', handleKeyDownMenu(quickAccessMenus), true);
if (currentMenu.popoverTargetElement) {
const subMenu = currentMenu.popoverTargetElement;
const subMenuItems = Array.from(subMenu.querySelectorAll('.js-quick-access__link'));
subMenu.addEventListener('keydown', handleKeyDownItems(subMenuItems, quickAccessMenus), true);
}
}
}
/**
* Fabrique de gestionnaire d'événement clavier pour les menus.
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
* @return {EventListener} le gestionnaire
*/
function handleKeyDownMenu(menuButtons) {
/**
* Gestionnaire d'événement clavier pour les menus
*/
return function handleKeyDownEventHandler(event) {
const currentButton = event.currentTarget;
const currentButtonIndex = menuButtons.findIndex((b) => b === currentButton);
const keysAction = {
ArrowDown: focusFirstItemSubMenu,
ArrowLeft: openPreviousMenu,
ArrowRight: openNextMenu,
Home: openFirstMenu,
End: openLastMenu
};
if (Object.hasOwn(keysAction, event.key)) {
keysAction[event.key]({currentButtonIndex, menuButtons});
event.stopPropagation();
event.preventDefault();
}
};
}
/**
* Ouverture du menu précédent.
* @param {Number} currentButtonIndex index du menu courant
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
*/
function openPreviousMenu({currentButtonIndex, menuButtons}) {
if (currentButtonIndex > 0) {
menuButtons[currentButtonIndex].popoverTargetElement?.hidePopover();
const menuEntry = menuButtons[currentButtonIndex - 1];
menuEntry.focus();
if (menuEntry.popoverTargetElement) {
menuEntry.popoverTargetElement.showPopover();
focusFirstItemSubMenu({currentButtonIndex: currentButtonIndex - 1, menuButtons});
}
}
}
/**
* Ouverture du menu suivant.
* @param {Number} currentButtonIndex index du menu courant
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
*/
function openNextMenu({currentButtonIndex, menuButtons}) {
if (currentButtonIndex < menuButtons.length - 1) {
menuButtons[currentButtonIndex].popoverTargetElement?.hidePopover();
const menuEntry = menuButtons[currentButtonIndex + 1];
menuEntry.focus();
if (menuEntry.popoverTargetElement) {
menuEntry.popoverTargetElement.showPopover();
focusFirstItemSubMenu({currentButtonIndex: currentButtonIndex + 1, menuButtons});
}
}
}
/**
* Ouverture du premier menu.
* @param {Number} currentButtonIndex index du menu courant
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
*/
function openFirstMenu({currentButtonIndex, menuButtons}) {
if (currentButtonIndex !== 0) {
menuButtons[currentButtonIndex].popoverTargetElement?.hidePopover();
const menuEntry = menuButtons[0];
menuEntry.focus();
if (menuEntry.popoverTargetElement) {
menuEntry.popoverTargetElement.showPopover();
focusFirstItemSubMenu({currentButtonIndex: 0, menuButtons});
}
}
}
/**
* Ouverture du dernier menu.
* @param {Number} currentButtonIndex index du menu courant
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
*/
function openLastMenu({currentButtonIndex, menuButtons}) {
if (currentButtonIndex !== menuButtons.length - 1) {
menuButtons[currentButtonIndex].popoverTargetElement?.hidePopover();
const menuEntry = menuButtons[menuButtons.length - 1];
menuEntry.focus();
if (menuEntry.popoverTargetElement) {
menuEntry.popoverTargetElement.showPopover();
focusFirstItemSubMenu({currentButtonIndex: menuButtons.length - 1, menuButtons});
}
}
}
/**
* Place le focus sur le premier item du menu.
* @param {Number} currentButtonIndex index du menu courant
* @param {HTMLButtonElement|HTMLAnchorElement[]} menuButtons boutons du menu
*/
function focusFirstItemSubMenu({currentButtonIndex, menuButtons}) {
const menuEntry = menuButtons[currentButtonIndex].popoverTargetElement;
menuEntry.showPopover();
if (menuEntry) {
menuEntry.querySelector('.js-quick-access__link').focus();
}
}
/**
* Fabrique de gestionnaire d'événement clavier pour les sous-menus.
* @param {HTMLElement[]} subMenuItems items du menu
* @param {HTMLButtonElement|HTMLAnchorElement[]} parentMenus boutons du menu parent
* @return {EventListener} le gestionnaire
*/
function handleKeyDownItems(subMenuItems, parentMenus) {
/**
* Gestionnaire d'événement clavier pour les sous-menus.
*/
return function handleKeyDownItemsEventHandler(event) {
const currentItem = event.target;
const currentItemIndex = subMenuItems.findIndex((b) => b === currentItem);
const keysAction = {
ArrowLeft: openPreviousParentMenu,
ArrowRight: openNextParentMenu,
ArrowUp: focusPreviousItemMenu,
ArrowDown: focusNextItemMenu,
Home: focusFirstItemMenu,
End: focusLastItemMenu
};
if (Object.hasOwn(keysAction, event.key)) {
keysAction[event.key]({currentItemIndex, subMenuItems, currentItem, parentMenus});
event.stopPropagation();
event.preventDefault();
}
};
}
/**
* Place le focus sur l'item précédent dans le menu.
* @param {Number} currentItemIndex index de l'item courant
* @param {HTMLElement[]} subMenuItems Item du sous menu
*/
function focusPreviousItemMenu({currentItemIndex, subMenuItems}) {
if (currentItemIndex > 0) {
const item = subMenuItems[currentItemIndex - 1];
item.focus();
}
}
/**
* Place le focus sur l'item suivant dans le menu.
* @param {Number} currentItemIndex index de l'item courant
* @param {HTMLElement[]} subMenuItems Item du sous menu
*/
function focusNextItemMenu({currentItemIndex, subMenuItems}) {
if (currentItemIndex < subMenuItems.length - 1) {
const item = subMenuItems[currentItemIndex + 1];
item.focus();
}
}
/**
* Place le focus sur le premier item dans le menu.
* @param {HTMLElement[]} subMenuItems Item du sous menu
*/
function focusFirstItemMenu({subMenuItems}) {
subMenuItems[0]?.focus();
}
/**
* Place le focus sur le dernier item dans le menu.
* @param {HTMLElement[]} subMenuItems Item du sous menu
*/
function focusLastItemMenu({subMenuItems}) {
subMenuItems[subMenuItems.length - 1]?.focus();
}
/**
* Ouvre le menu parent précédent.
* @param {HTMLElement} currentItem item sélectionné dans le sous menu
* @param {HTMLButtonElement|HTMLAnchorElement[]} parentMenus menus parents
*/
function openPreviousParentMenu({currentItem, parentMenus}) {
const currentParentMenuButton = currentItem.closest('.js-quick-access__item')?.querySelector('.js-quick-access__entry');
const currentParentMenuButtonIndex = parentMenus.findIndex((b) => b === currentParentMenuButton);
openPreviousMenu({currentButtonIndex: currentParentMenuButtonIndex, menuButtons: parentMenus});
}
/**
* Ouvre le menu parent suivant.
* @param {HTMLElement} currentItem item sélectionné dans le sous menu
* @param {HTMLButtonElement|HTMLAnchorElement[]} parentMenus menus parents
*/
function openNextParentMenu({currentItem, parentMenus}) {
const currentParentMenuButton = currentItem.closest('.js-quick-access__item')?.querySelector('.js-quick-access__entry');
const currentParentMenuButtonIndex = parentMenus.findIndex((b) => b === currentParentMenuButton);
openNextMenu({currentButtonIndex: currentParentMenuButtonIndex, menuButtons: parentMenus});
}
/**
* Initialisation des accès rapides.
*/
document.querySelectorAll('.js-quick-access').forEach((quickAccessMenu) => {
const quickAccessButtons = Array.from(quickAccessMenu.querySelectorAll(':scope > [role="menuitem"] .js-quick-access__entry'));
initEventsButton(quickAccessButtons);
});
<%@ 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="status" required="false" type="java.lang.String" %>
<%@ attribute name="priority" required="false" type="java.lang.String" %>
<%@ attribute name="quickaccess" required="true" type="fr.kosmos.web.kore.attributes.interfaces.QuickAccess" %>
<%@ attribute name="placement" required="false" type="java.lang.String" description="Placement du popover par rapport au dropdown (défaut : start)"%>
<%@ attribute name="size" required="false" type="java.lang.String" description="Taille (défaut : sm)" %>
<%@ attribute name="iconOnly" required="false" type="java.lang.Boolean" description="N'affiche que l'icône dans le bouton" %>
<c:if test="${empty pageScope.priority}">
<c:set var="priority" value="tertiary" />
</c:if>
<c:if test="${empty pageScope.size}">
<c:set var="size" value="sm" />
</c:if>
<c:if test="${empty pageScope.status}">
<c:set var="status" value="neutral" />
</c:if>
<c:if test="${empty pageScope.placement}">
<c:set var="placement" value="start" />
</c:if>
<c:if test="${not empty pageScope.quickaccess && not empty pageScope.quickaccess.menus}">
<c:set value="quick-access js-quick-access" var="classes"/>
<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>
<ul class="${pageScope.classes}" role="menubar" ${pageScope.attributes}>
<c:forEach var="menu" items="${pageScope.quickaccess.menus}">
<%--@elvariable id="menu" type="fr.kosmos.web.kore.attributes.interfaces.QuickAccessMenu"--%>
<li role="menuitem" class="quick-access__item js-quick-access__item">
<c:choose>
<c:when test="${empty menu.items}">
<c:set var="itemClasses">
quick-access__link js-quick-access__entry button button--${status} button--${priority} <c:if test="${empty menu.title}"> quick-access__link--icon-only</c:if>
</c:set>
<kuik:link link="${menu.link}" class="${itemClasses}">
<span class="quick-access__label">
<c:if test="${not empty menu.description}">
<span class="visually-hidden">${menu.description}</span>
</c:if>
<c:if test="${not empty menu.icon}">
<kuik:icon title="${menu.description}" source="${menu.icon}" size="${pageScope.size}"/>
</c:if>
${menu.title}
</span>
</kuik:link>
</c:when>
<c:otherwise>
<kuik:dropdown
status="${pageScope.status}"
priority="${pageScope.priority}"
title="${menu.title}"
description="${menu.description}"
icon="${menu.icon}"
size="${pageScope.size}"
class="quick-access__btn js-quick-access__entry"
iconOnly="${pageScope.iconOnly}"
popoverClasses="u-popover__content--no-padding u-popover__content--${pageScope.placement}"
>
<ul class="pop-list">
<c:forEach var="item" items="${menu.items}">
<li class="pop-list__item" role="menuitem">
<c:set var="view" value="${not empty item.view ? item.view : '/static/kuik/quick-access/quick-access-default-view.jsp'}" />
<c:set var="viewItem" value="${item}" scope="request" />
<jsp:include page="${pageScope.view}" />
</li>
</c:forEach>
</ul>
</kuik:dropdown>
</c:otherwise>
</c:choose>
</li>
</c:forEach>
</ul>
</c:if>
<resources:addScript path="/static/kuik/quick-access/quick-access.js" type="module"/>