@edugouvfr/ngx-dsfr
Version:
NgxDsfr est un portage Angular des éléments d'interface du Système de Design de l'État Français (DSFR).
13,395 lines • 1.15 MB
JavaScript
import * as i1 from '@angular/common';
import { CommonModule, isPlatformBrowser, KeyValuePipe } from '@angular/common';
import * as i0 from '@angular/core';
import { Input, Component, input, computed, inject, EventEmitter, booleanAttribute, Output, ViewEncapsulation, NgModule, HostBinding, Directive, SecurityContext, InjectionToken, Optional, Inject, Injectable, ChangeDetectorRef, Pipe, Renderer2, ViewChild, viewChild, ElementRef, signal, CUSTOM_ELEMENTS_SCHEMA, PLATFORM_ID, contentChildren, afterRenderEffect, effect, ContentChild, TemplateRef, ContentChildren, forwardRef, DOCUMENT, ChangeDetectionStrategy, DestroyRef, afterNextRender, ViewContainerRef, EnvironmentInjector, inputBinding, outputBinding, HostListener } from '@angular/core';
import { v4 } from 'uuid';
import { BehaviorSubject, Observable, EMPTY, merge, Subject, map, debounceTime, distinctUntilChanged, takeUntil, combineLatest } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';
import * as i2 from '@angular/router';
import { RouterModule, RouterLink, Router, ActivatedRoute } from '@angular/router';
import { fromFetch } from 'rxjs/internal/observable/dom/fetch';
import * as i2$1 from '@angular/forms';
import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
/**
* Définit les types de bouton supportés par le composant `dsfr-button`.
*/
var DsfrButtonTypeConst;
(function (DsfrButtonTypeConst) {
/**
* A utiliser pour un bouton de soumission de formulaire.
*/
DsfrButtonTypeConst["SUBMIT"] = "submit";
/**
* A utiliser pour un bouton de réinitialisation de formulaire.
*/
DsfrButtonTypeConst["RESET"] = "reset";
/**
* A utiliser pour un bouton d'action sur la page courante.
*/
DsfrButtonTypeConst["BUTTON"] = "button";
})(DsfrButtonTypeConst || (DsfrButtonTypeConst = {}));
/**
* Définit les variantes supportées par le composant `dsfr-button`.
*/
var DsfrButtonVariantConst;
(function (DsfrButtonVariantConst) {
/**
* A utiliser pour un bouton primaire.
*/
DsfrButtonVariantConst["PRIMARY"] = "primary";
/**
* A utiliser pour un bouton secondaire.
*/
DsfrButtonVariantConst["SECONDARY"] = "secondary";
/**
* A utiliser pour un bouton tertiaire.
*/
DsfrButtonVariantConst["TERTIARY"] = "tertiary";
/**
* A utiliser pour un bouton tertiaire sans bordure.
*/
DsfrButtonVariantConst["TERTIARY_NO_OUTLINE"] = "tertiary-no-outline";
})(DsfrButtonVariantConst || (DsfrButtonVariantConst = {}));
/**
* Définit les niveaux de titre supportés par le DSFR du H2 à H6.
*/
var DsfrHeadingLevelConst;
(function (DsfrHeadingLevelConst) {
/**
* @deprecated (since 1.8) use `undefined` instead
*/
DsfrHeadingLevelConst["NONE"] = "none";
/**
* Niveau de titre H1.
*/
DsfrHeadingLevelConst["H1"] = "H1";
/**
* Niveau de titre H2.
*/
DsfrHeadingLevelConst["H2"] = "H2";
/**
* Niveau de titre H3.
*/
DsfrHeadingLevelConst["H3"] = "H3";
/**
* Niveau de titre H4.
*/
DsfrHeadingLevelConst["H4"] = "H4";
/**
* Niveau de titre H5.
*/
DsfrHeadingLevelConst["H5"] = "H5";
/**
* Niveau de titre H6.
*/
DsfrHeadingLevelConst["H6"] = "H6";
})(DsfrHeadingLevelConst || (DsfrHeadingLevelConst = {}));
/**
* Définit les valeurs possibles pour l'attribut `target` d'un lien html.
*/
var DsfrLinkTargetConst;
(function (DsfrLinkTargetConst) {
DsfrLinkTargetConst["BLANK"] = "_blank";
DsfrLinkTargetConst["SELF"] = "_self";
DsfrLinkTargetConst["PARENT"] = "_parent";
DsfrLinkTargetConst["TOP"] = "_top";
})(DsfrLinkTargetConst || (DsfrLinkTargetConst = {}));
/** @deprecated (since 1.4) use {@link DsfrLinkTargetConst} instead */
var DsfrTargetLinkConst;
(function (DsfrTargetLinkConst) {
DsfrTargetLinkConst["BLANK"] = "_blank";
DsfrTargetLinkConst["SELF"] = "_self";
DsfrTargetLinkConst["PARENT"] = "_parent";
DsfrTargetLinkConst["TOP"] = "_top";
})(DsfrTargetLinkConst || (DsfrTargetLinkConst = {}));
/**
* Définit les différentes positions supportées par le DSFR.
*/
var DsfrPositionConst;
(function (DsfrPositionConst) {
/**
* Positionne à gauche.
*/
DsfrPositionConst["LEFT"] = "left";
/**
* Positione à droite.
*/
DsfrPositionConst["RIGHT"] = "right";
})(DsfrPositionConst || (DsfrPositionConst = {}));
/**
* Définit les différents niveaux de sévérité supportés par le DSFR.
*/
var DsfrSeverityConst;
(function (DsfrSeverityConst) {
/**
* Spécifie un niveau "information".
*/
DsfrSeverityConst["INFO"] = "info";
/**
* Spécifie un niveau "erreur".
*/
DsfrSeverityConst["ERROR"] = "error";
/**
* Spécifie un niveau "information" et/ou un état "valide".
*/
DsfrSeverityConst["VALID"] = "valid";
/**
* Spécifie un niveau "avertissement".
*/
DsfrSeverityConst["WARNING"] = "warning";
/**
* Spécifie un niveau "succès".
*
* @deprecated (since 1.13.0) Utiliser {@link VALID} à la place.
*/
DsfrSeverityConst["SUCCESS"] = "success";
})(DsfrSeverityConst || (DsfrSeverityConst = {}));
/**
* Définit les différentes tailles supportées par le DSFR.
*/
var DsfrSizeConst;
(function (DsfrSizeConst) {
/**
* Spécifie une taille "réduite".
*/
DsfrSizeConst["SM"] = "SM";
/**
* Spécifie une taille "moyenne".
*/
DsfrSizeConst["MD"] = "MD";
/**
* Spécifie une taille "large".
*/
DsfrSizeConst["LG"] = "LG";
})(DsfrSizeConst || (DsfrSizeConst = {}));
/**
* Définit les tailles de texte supportées par le DSFR.
*/
var DsfrTextSizeConst;
(function (DsfrTextSizeConst) {
/**
* Texte très petit.
*/
DsfrTextSizeConst["XS"] = "XS";
/**
* Texte petit.
*/
DsfrTextSizeConst["SM"] = "SM";
/**
* Texte moyen.
*/
DsfrTextSizeConst["MD"] = "MD";
/**
* Texte large.
*/
DsfrTextSizeConst["LG"] = "LG";
/**
* Texte très grand.
*/
DsfrTextSizeConst["XL"] = "XL";
})(DsfrTextSizeConst || (DsfrTextSizeConst = {}));
/**
* @since 0.7
*/
class DateUtils {
constructor() { }
/**
* Transforme un string représentant une date au format `'dd/mm/yyyy'` en objet `Date`.
* Le siècle peut-être sur 2 digits, dans ce cas, cela correspond à `'19yy'`.
* @returns Date ou undefined
*/
static parseDateFr(dateStr) {
const regexp = /^(?<day>3[01]|0?[1-9]|[12][0-9])\/(?<month>1[0-2]|0?[1-9])\/(?<year>[0-9]{4})$/;
return DateUtils.parseDate(dateStr, regexp);
}
/**
* Transforme un string représentant une date au format ISO 8601 en objet `Date`.
* @returns Date ou undefined
*/
static parseDateIso(dateStr, withHours = false) {
// https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html
const regexp = /^(?<year>-?(?:[1-9][0-9]*)?[0-9]{4})-(?<month>1[0-2]|0[1-9])-(?<day>3[01]|0[1-9]|[12][0-9])(T(?<hour>2[0-4]|[01][0-9]):(?<minute>[0-5][0-9]):(?<second>[0-5][0-9])(?<ms>\.[0-9]+)?(?<timezone>Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?)?$/;
return DateUtils.parseDate(dateStr, regexp, withHours);
}
/**
* Retourne une Date UTC, sans heure, minute seconde, à partir d'une date
* @param date
*/
static date2Utc(date, withHours = false) {
if (withHours) {
return this.createDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
}
else {
return this.createDate(date.getFullYear(), date.getMonth(), date.getDate());
}
}
/**
* Retourne une Date UTC, sans heure, minute seconde, selon plusieurs formats en entrée
* @param value
*/
static dateUtcOf(value) {
let date = undefined;
if (typeof value === 'string')
date = DateUtils.parseDateIso(value);
else if (typeof value === 'number')
date = DateUtils.numberToDateUtc(value);
else if (value)
date = DateUtils.date2Utc(value);
return date;
}
/**
* Retourne une Date UTC, sans minute et seconde, selon plusieurs formats en entrée
* @param value
*/
static dateUtcOfWithHours(value) {
let date = undefined;
if (typeof value === 'string')
date = DateUtils.parseDateIso(value, true);
else if (typeof value === 'number')
date = DateUtils.numberToDateUtc(value, true);
else if (value)
date = DateUtils.date2Utc(value, true);
return date;
}
static isTwoDigitsYear(year) {
return year >= 0 && year < 100;
}
/**
* Permet de créer une date avec un année contenant seulement un ou deux digits (i.e: année 27)
*
* @param year Année, possiblement négative.
* @param month Mois
* @param day Jour
* @param hour Heure
* @param needsOffsetting Défini si la date créée à besoin d'être basculée à minuit, heure locale
* @returns La date correspondant aux paramètres d'entrée
*/
static createDate(year, month, day, hour = 0, needsOffsetting = true) {
const date = needsOffsetting ? new Date(Date.UTC(year, month, day, hour)) : new Date(year, month, day, hour);
if (this.isTwoDigitsYear(year)) {
date.setFullYear(year);
}
return date;
}
static parseDate(dateStr, regexp, withHours = false) {
if (!dateStr)
return undefined;
const execArr = regexp.exec(dateStr);
const groups = execArr?.groups;
const valid = !!groups && !!groups['year'] && !!groups['month'] && !!groups['day'];
if (!valid) {
return undefined;
}
else if (withHours) {
const offset = groups['timezone'] === 'Z' ? 0 : Number(groups['timezone'].split(':')[0]);
return this.createDate(Number(groups['year']), Number(groups['month']) - 1, Number(groups['day']), Number(groups['hour']) + offset);
}
else {
return this.createDate(Number(groups['year']), Number(groups['month']) - 1, Number(groups['day']));
}
}
static numberToDateUtc(n, withHours = false) {
const d = new Date(n);
if (withHours) {
return this.createDate(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours());
}
else {
return this.createDate(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
}
}
}
class DomUtils {
constructor() { }
static surroundElementsWithLi(container, elements) {
DomUtils.removeEmptyLi(container);
for (const elem of elements) {
if (elem.nativeElement?.parentElement?.tagName !== 'LI') {
DomUtils.insertNodeInLi(elem.nativeElement);
}
}
}
static surroundNativeElementsWithLi(container, elements) {
DomUtils.removeEmptyLi(container);
for (const elem of elements) {
if (elem?.parentElement?.tagName !== 'LI' && elem?.tagName !== 'LI') {
DomUtils.insertNodeInLi(elem);
}
}
}
static removeEmptyLi({ nativeElement }) {
const nonProgrammaticLiElements = nativeElement.querySelectorAll('li[data-ngx-dsfr-li]');
nonProgrammaticLiElements.forEach((li) => {
if (li.childElementCount === 0) {
li.remove();
}
});
}
static insertNodeInLi(child) {
if (!document)
return; // mode SSR
const li = document.createElement('li');
li.setAttribute('data-ngx-dsfr-li', 'true');
child.replaceWith(li); // Ne remplace pas l'élément en lui même mais modifie la Node parent de l'élément: https://developer.mozilla.org/fr/docs/Web/API/Element/replaceWith
li.appendChild(child);
}
}
/**
* Les types MIME de fichiers supportés par le composant.
*
* @see https://developer.mozilla.org/fr/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
*/
var DsfrMimeTypeConst;
(function (DsfrMimeTypeConst) {
// TEXT
DsfrMimeTypeConst["TEXT_CSS"] = "text/css";
DsfrMimeTypeConst["TEXT_CSV"] = "text/csv";
DsfrMimeTypeConst["TEXT_HTML"] = "text/html";
DsfrMimeTypeConst["TEXT_CALENDAR"] = "text/calendar";
DsfrMimeTypeConst["TEXT_PLAIN"] = "text/plain";
DsfrMimeTypeConst["TEXT_XML"] = "text/xml";
// FONT
DsfrMimeTypeConst["FONT_OTF"] = "font/otf";
DsfrMimeTypeConst["FONT_TTF"] = "font/ttf";
DsfrMimeTypeConst["FONT_WOFF"] = "font/woff";
DsfrMimeTypeConst["FONT_WOFF2"] = "font/woff2";
// IMAGE
DsfrMimeTypeConst["IMAGE_BMP"] = "image/bmp";
DsfrMimeTypeConst["IMAGE_GIF"] = "image/gif";
DsfrMimeTypeConst["IMAGE_ICON"] = "image/x-icon";
DsfrMimeTypeConst["IMAGE_JPEG"] = "image/jpeg";
DsfrMimeTypeConst["IMAGE_PNG"] = "image/png";
DsfrMimeTypeConst["IMAGE_SVG"] = "image/svg+xml";
DsfrMimeTypeConst["IMAGE_TIFF"] = "image/tiff";
DsfrMimeTypeConst["IMAGE_WEBP"] = "image/webp";
// AUDIO
DsfrMimeTypeConst["AUDIO_3GPP"] = "audio/3gpp";
DsfrMimeTypeConst["AUDIO_3GPP2"] = "audio/3gpp2";
DsfrMimeTypeConst["AUDIO_ACC"] = "audio/aac";
DsfrMimeTypeConst["AUDIO_OGG"] = "audio/ogg";
DsfrMimeTypeConst["AUDIO_MIDI"] = "audio/midi";
DsfrMimeTypeConst["AUDIO_WAV"] = "audio/x-wav";
DsfrMimeTypeConst["AUDIO_WEBM"] = "audio/webm";
// VIDEO
DsfrMimeTypeConst["VIDEO_3GPP"] = "video/3gpp";
DsfrMimeTypeConst["VIDEO_3GPP2"] = "video/3gpp2";
DsfrMimeTypeConst["VIDEO_MPEG"] = "video/mpeg";
DsfrMimeTypeConst["VIDEO_OGG"] = "video/ogg";
DsfrMimeTypeConst["VIDEO_WEBM"] = "video/webm";
// APPLICATION
DsfrMimeTypeConst["APPLICATION_ABIWORD"] = "application/x-abiword";
DsfrMimeTypeConst["APPLICATION_CSH"] = "application/x-csh";
DsfrMimeTypeConst["APPLICATION_EPUB"] = "application/epub+zip";
DsfrMimeTypeConst["APPLICATION_FLASH"] = "application/x-shockwave-flash";
DsfrMimeTypeConst["APPLICATION_FORM_URLENCODED"] = "application/x-www-form-urlencoded";
DsfrMimeTypeConst["APPLICATION_JAVASCRIPT"] = "application/javascript";
DsfrMimeTypeConst["APPLICATION_JSON"] = "application/json";
DsfrMimeTypeConst["APPLICATION_OCTET_STREAM"] = "application/octet-stream";
DsfrMimeTypeConst["APPLICATION_OGG"] = "application/ogg";
DsfrMimeTypeConst["APPLICATION_PDF"] = "application/pdf";
DsfrMimeTypeConst["APPLICATION_RTF"] = "application/rtf";
DsfrMimeTypeConst["APPLICATION_TYPESCRIPT"] = "application/typescript";
DsfrMimeTypeConst["APPLICATION_XML"] = "application/xml";
DsfrMimeTypeConst["APPLICATION_XML_ATOM"] = "application/atom+xml";
DsfrMimeTypeConst["APPLICATION_XML_SVG"] = "application/svg+xml";
DsfrMimeTypeConst["APPLICATION_XML_XHTML"] = "application/xhtml+xml";
DsfrMimeTypeConst["APPLICATION_XML_XUL"] = "application/vnd.mozilla.xul+xml";
// ARCHIVE
DsfrMimeTypeConst["ARCHIVE_7Z_COMPRESSED"] = "application/x-7z-compressed";
DsfrMimeTypeConst["ARCHIVE_BZIP"] = "application/x-bzip";
DsfrMimeTypeConst["ARCHIVE_BZIP2"] = "application/x-bzip2";
DsfrMimeTypeConst["ARCHIVE_JAVA"] = "application/java-archive";
DsfrMimeTypeConst["ARCHIVE_GZIP"] = "application/gzip";
DsfrMimeTypeConst["ARCHIVE_RAR_COMPRESSED"] = "application/x-rar-compressed";
DsfrMimeTypeConst["ARCHIVE_TAR"] = "application/x-tar";
DsfrMimeTypeConst["ARCHIVE_RAR"] = "application/vnd.rar";
DsfrMimeTypeConst["ARCHIVE_ZIP"] = "application/zip";
DsfrMimeTypeConst["ARCHIVE_ZIP_COMPRESSED"] = "application/x-zip-compressed";
// MICROSOFT
DsfrMimeTypeConst["MS_FONT_OBJECT"] = "application/vnd.ms-fontobject";
DsfrMimeTypeConst["MS_EXCEL"] = "application/vnd.ms-excel";
DsfrMimeTypeConst["MS_EXCEL_XML"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
DsfrMimeTypeConst["MS_POWERPOINT"] = "application/vnd.ms-powerpoint";
DsfrMimeTypeConst["MS_POWERPOINT_XML"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
DsfrMimeTypeConst["MS_VIDEO"] = "video/x-msvideo";
DsfrMimeTypeConst["MS_VISIO"] = "application/vnd.visio";
DsfrMimeTypeConst["MS_WORD"] = "application/msword";
DsfrMimeTypeConst["MS_WORD_XML"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
// Autres
DsfrMimeTypeConst["MULTIPART_FORM_DATA"] = "multipart/form-data";
DsfrMimeTypeConst["SCRIPT_SHELL"] = "application/x-sh";
DsfrMimeTypeConst["OPEN_PRESENTATION"] = "application/vnd.oasis.opendocument.presentation";
DsfrMimeTypeConst["OPEN_SPREADSHEET"] = "application/vnd.oasis.opendocument.spreadsheet";
DsfrMimeTypeConst["OPEN_TEXT"] = "application/vnd.oasis.opendocument.text";
})(DsfrMimeTypeConst || (DsfrMimeTypeConst = {}));
/**
* Fonction utilitaire
*
* @ignore
*/
function convertMimeType2FileFormat(mineType) {
const element = mimeType2FileFormat.find((e) => e[0] === mineType);
return element ? element[1] : undefined;
}
/**
* @ignore
*/
var FileFormat;
(function (FileFormat) {
FileFormat["XLS"] = "XLS";
FileFormat["XLSX"] = "XLSX";
FileFormat["PPT"] = "PPT";
FileFormat["PPTX"] = "PPTX";
FileFormat["DOC"] = "DOC";
FileFormat["DOCX"] = "DOCX";
FileFormat["ODS"] = "ODS";
FileFormat["ODP"] = "ODP";
FileFormat["ODT"] = "ODT";
FileFormat["XML"] = "XML";
FileFormat["JPG"] = "JPG";
FileFormat["PNG"] = "PNG";
FileFormat["PDF"] = "PDF";
FileFormat["CSV"] = "CSV";
FileFormat["HTML"] = "HTML";
FileFormat["SVG"] = "SVG";
FileFormat["TXT"] = "TXT";
FileFormat["ZIP"] = "ZIP";
FileFormat["SEVEN_ZIP"] = "7Z";
FileFormat["RAR"] = "RAR";
FileFormat["XHTML"] = "XHTML";
FileFormat["BMP"] = "BMP";
FileFormat["GIF"] = "GIF";
FileFormat["TIF"] = "TIF";
FileFormat["RTF"] = "RTF";
FileFormat["GZIP"] = "GZIP";
FileFormat["TAR"] = "TAR";
})(FileFormat || (FileFormat = {}));
/**
* @ignore
*/
const mimeType2FileFormat = [
[DsfrMimeTypeConst.APPLICATION_PDF, FileFormat.PDF],
[DsfrMimeTypeConst.APPLICATION_RTF, FileFormat.RTF],
[DsfrMimeTypeConst.APPLICATION_XML, FileFormat.XML],
[DsfrMimeTypeConst.APPLICATION_XML_ATOM, FileFormat.XML],
[DsfrMimeTypeConst.APPLICATION_XML_XHTML, FileFormat.XHTML],
[DsfrMimeTypeConst.ARCHIVE_7Z_COMPRESSED, FileFormat.SEVEN_ZIP],
[DsfrMimeTypeConst.ARCHIVE_GZIP, FileFormat.GZIP],
[DsfrMimeTypeConst.ARCHIVE_RAR, FileFormat.RAR],
[DsfrMimeTypeConst.ARCHIVE_TAR, FileFormat.TAR],
[DsfrMimeTypeConst.ARCHIVE_ZIP, FileFormat.ZIP],
[DsfrMimeTypeConst.ARCHIVE_ZIP_COMPRESSED, FileFormat.ZIP],
[DsfrMimeTypeConst.IMAGE_BMP, FileFormat.BMP],
[DsfrMimeTypeConst.IMAGE_GIF, FileFormat.GIF],
[DsfrMimeTypeConst.IMAGE_JPEG, FileFormat.JPG],
[DsfrMimeTypeConst.IMAGE_PNG, FileFormat.PNG],
[DsfrMimeTypeConst.IMAGE_SVG, FileFormat.SVG],
[DsfrMimeTypeConst.IMAGE_TIFF, FileFormat.TIF],
[DsfrMimeTypeConst.MS_EXCEL, FileFormat.XLS],
[DsfrMimeTypeConst.MS_EXCEL_XML, FileFormat.XLSX],
[DsfrMimeTypeConst.MS_POWERPOINT, FileFormat.PPT],
[DsfrMimeTypeConst.MS_POWERPOINT_XML, FileFormat.PPTX],
[DsfrMimeTypeConst.MS_WORD, FileFormat.DOC],
[DsfrMimeTypeConst.MS_WORD_XML, FileFormat.DOCX],
[DsfrMimeTypeConst.OPEN_PRESENTATION, FileFormat.ODP],
[DsfrMimeTypeConst.OPEN_SPREADSHEET, FileFormat.ODS],
[DsfrMimeTypeConst.OPEN_TEXT, FileFormat.ODT],
[DsfrMimeTypeConst.TEXT_CSV, FileFormat.CSV],
[DsfrMimeTypeConst.TEXT_HTML, FileFormat.HTML],
[DsfrMimeTypeConst.TEXT_PLAIN, FileFormat.TXT],
[DsfrMimeTypeConst.TEXT_XML, FileFormat.XML],
];
const KILO_OCTETS = 1024;
const MEGA_OCTETS = KILO_OCTETS * 1024;
const GIGA_OCTETS = MEGA_OCTETS * 1024;
/**
* Construit le libellé de détail dans un composant de téléchargement lorsque ce libellé n'est pas fourni par le Dsfr
* @param mimeType Type mime du fichier
* @param sizeBytes Nombre d'octets
* @param sizeUnit Unité utilisée dans la restitution
* @return par exemple 'PDF - 10 octets'
*/
function downloadDetail(mimeType, sizeBytes, sizeUnit) {
return !sizeBytes
? convertMimeType2FileFormat(mimeType) || ''
: convertMimeType2FileFormat(mimeType) + ' - ' + fileSizeToString(sizeBytes, sizeUnit);
}
/**
* Retourne la taille sous forme de string sous forme de 'nombre unité'
* @param bytes Taille à transformer exprimée en octets
* @param sizeUnit Si true, l'unité sera en bytes (KB, MB, ... ) sinon en octets (ko, mo, ...)
*/
function fileSizeToString(bytes, sizeUnit) {
if (!bytes)
return '';
const bytesUnit = sizeUnit === 'bytes';
let fileSize;
let fileSizeUnit;
if (bytes < KILO_OCTETS) {
fileSize = bytes;
fileSizeUnit = bytesUnit ? FileSizeUnit.BYTES : FileSizeUnit.OCTETS;
}
else if (bytes < MEGA_OCTETS) {
fileSize = bytes / 1024;
fileSizeUnit = bytesUnit ? FileSizeUnit.KB : FileSizeUnit.KO;
}
else if (bytes < GIGA_OCTETS) {
fileSize = bytes / 1048576;
fileSizeUnit = bytesUnit ? FileSizeUnit.MB : FileSizeUnit.MO;
}
else {
fileSize = bytes / 1073741824;
fileSizeUnit = bytesUnit ? FileSizeUnit.GB : FileSizeUnit.GO;
}
fileSize = Math.round(fileSize * 100) / 100;
// since 1.8, les options régionales sont prise en compte pour les décimales
//FIXME: ne devrait-ton pas transmettre la locale depuis l'extérieur de manière à pouvoir la transmettre depuis i18nService ?
const localSize = fileSize.toLocaleString();
return localSize + ' ' + fileSizeUnit;
}
// -- Constantes d'unités de taille de fichier -------------------------------------------------------------------------
var FileSizeUnit;
(function (FileSizeUnit) {
FileSizeUnit["OCTETS"] = "octets";
FileSizeUnit["KO"] = "ko";
FileSizeUnit["MO"] = "Mo";
FileSizeUnit["GO"] = "Go";
FileSizeUnit["BYTES"] = "bytes";
FileSizeUnit["KB"] = "KB";
FileSizeUnit["MB"] = "MB";
FileSizeUnit["GB"] = "GB";
})(FileSizeUnit || (FileSizeUnit = {}));
var DsfrFileSizeUnitConst;
(function (DsfrFileSizeUnitConst) {
DsfrFileSizeUnitConst["BYTES"] = "bytes";
DsfrFileSizeUnitConst["OCTETS"] = "octets";
})(DsfrFileSizeUnitConst || (DsfrFileSizeUnitConst = {}));
/**
* https://www.w3resource.com/JSON/JSONPath-with-JavaScript.php
* @param obj : object|array This parameter represents the Object representing the JSON structure.
* @param path : string This parameter represents JSONPath expression string.
* @return une valeur extrait de l'objet Json ou undefined si le path est erroné
*/
function jsonPath2Value(obj, path) {
const keys = path.split('.');
// value est soit un objet JSON, soit une valeur terminale
let value = obj;
const breakException = {};
try {
keys.forEach((key) => {
if (!value)
throw breakException;
// key, ex: 'labels[20]'
const matchArr = key.match(/([^\[]*)\[(\d*)\]/);
const k = matchArr ? matchArr[1] : undefined; // clé sans l'indice, ex : 'labels'
const i = matchArr ? matchArr[2] : undefined; // indice, ex : 20
value = k && i ? value[k][i] : value[key];
});
}
catch (e) {
// console.error(`Le chemin '${path}' ne correspond pas à une valeur`);
}
return value;
}
/**
* La fonction permet de savoir si le code en cours s'exécute côté serveur ou côté browser.
*/
function isOnBrowser() {
return typeof window !== 'undefined';
}
/**
* Les méthodes statiques de cette classe permettent de gérer le local storage uniquement si le code s'exécute côté browser.
*/
class LocalStorage {
/**
* The setItem() method of the Storage interface, when passed a key name and value, will add that key to the given
* Storage object, or update that key's value if it already exists.
*/
static set(key, value) {
if (isOnBrowser())
window.localStorage?.setItem(key, value);
}
/**
* The getItem() method of the Storage interface, when passed a key name, will return that key's value, or null if
* he key does not exist, in the given Storage object.
* @return Signature identique à l'interface Storage.getItem(key: string): string | null
*/
static get(key) {
return isOnBrowser() ? window.localStorage?.getItem(key) : null;
}
/**
* The removeItem() method of the Storage interface, when passed a key name, will remove that key from the given
* Storage object if it exists. The Storage interface of the Web Storage API provides access to a
* particular domain's session or local storage.
*/
static remove(key) {
if (isOnBrowser())
window.localStorage?.removeItem(key);
}
}
var StorageEnum;
(function (StorageEnum) {
StorageEnum["SCHEME"] = "scheme";
StorageEnum["REMEMBER_ME"] = "rememberMe";
StorageEnum["LOGIN"] = "login";
// 'lang' pour la langue (dans _commons)
})(StorageEnum || (StorageEnum = {}));
/**
* Retourne vrai si la string 's' est null, undefined ou vide.
* @param s la chaïne à considére
*/
function isStringEmptyOrNull(s) {
return !s || s.trim() === '';
}
function toArray(value) {
if (Array.isArray(value)) {
return value;
}
else {
return value ? [value] : [];
}
}
/**
* Fournit un identifiant unique de type string basé, actuellement, sur uuidv4.
*/
function newUniqueId() {
return v4();
}
/**
* Base class for all ControlValueAccessor classes defined in Forms package. Contains common logic and utility functions.
* Le type de `value` est : T | undefined, undefined par défaut. Par exemple si on manipule un input de type number, celui-ci
* sera initialisé à undefined par défaut.
*/
// Même si on ne précise pas undefined, à l'exécution, c'est quand même le cas.
// Même si on indique un type number, à l'exécution, si l'utilisateur saisie "1a", value sera de type string
class DefaultValueAccessorComponent {
constructor() {
/**
* Permet de désactiver le champ.
*/
this.disabled = false;
/** @internal */
this.fnOnChange = (_) => { };
/** @internal */
this.fnOnTouched = () => { };
}
get value() {
return this._value;
}
/**
* La valeur gérée par le champ de formulaire.
*/
set value(value) {
this._value = value;
this.fnOnChange(this.value);
}
/**
* Writes a new value to the element.
* This method is called by the forms API to write to the view when programmatic changes from model to view are requested.
*
* @internal
*/
writeValue(value) {
this._value = value;
}
/**
* Registers a callback function that is called when the control's value changes in the UI.
* When the value changes in the UI, call the registered function to allow the forms API to update itself:
* host: {
* '(change)': '_onChange($event.target.value)'
* }
* @internal
*/
registerOnChange(fn) {
this.fnOnChange = fn;
}
/**
* Registers a callback function that is called by the forms API on initialization to update the form model on blur.
* On blur (or equivalent), your class should call the registered function to allow the forms API to update itself:
* host: {
* '(blur)': '_onTouched()'
* }
* @internal
*/
registerOnTouched(fn) {
this.fnOnTouched = fn;
}
/** @internal */
onBlur() {
this.fnOnTouched();
}
/**
* Function that is called by the forms API when the control status changes to or from 'DISABLED'.
* Depending on the status, it enables or disables the appropriate DOM element.
*
* @internal
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultValueAccessorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DefaultValueAccessorComponent, isStandalone: true, selector: "ng-component", inputs: { disabled: "disabled", value: "value" }, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultValueAccessorComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { disabled: [{
type: Input
}], value: [{
type: Input
}] } });
/**
* Ce composant est le contrôle abstrait des contrôles Dsfr possédant un id, un name et un label.
*/
// Renamed from AbstractControlComponent
class DefaultControlComponent extends DefaultValueAccessorComponent {
constructor() {
super(...arguments);
/**
* Message d'erreur, quand il est présent les couleurs du contrôle changent.
* @deprecated (since 1.12) utiliser message/messageSeverity à la place
*/
this.error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : []));
/**
* Message de validation, quand il est présent les couleurs du contrôle changent.
* @deprecated (since 1.12) utiliser message/messageSeverity à la place
*/
this.valid = input(...(ngDevMode ? [undefined, { debugName: "valid" }] : []));
/**
* Message d'information lié au composant.
*/
this.message = input(...(ngDevMode ? [undefined, { debugName: "message" }] : []));
/**
* Représente la sévérité du message.
*/
this.messageSeverity = input(...(ngDevMode ? [undefined, { debugName: "messageSeverity" }] : []));
/** Attribut aria-describedby référençant une description supplémentaire optionnelle */
this.ariaDescribedBy = input(...(ngDevMode ? [undefined, { debugName: "ariaDescribedBy" }] : []));
/** @internal */
this.messageViewModel = computed(() => {
if (this.error())
return this.error();
if (this.valid())
return this.valid();
return this.message();
}, ...(ngDevMode ? [{ debugName: "messageViewModel" }] : []));
/** @internal */
this.severityViewModel = computed(() => {
const message = this.messageViewModel();
if (!message?.length)
return undefined;
if (this.error())
return DsfrSeverityConst.ERROR;
if (this.valid())
return DsfrSeverityConst.VALID;
if (this.messageSeverity() === DsfrSeverityConst.SUCCESS)
return DsfrSeverityConst.VALID;
return this.messageSeverity();
}, ...(ngDevMode ? [{ debugName: "severityViewModel" }] : []));
/** @internal */
this.combinedAriaDescribedBy = computed(() => {
const ariaDescribedBy = this.ariaDescribedBy();
const ids = [];
if (ariaDescribedBy)
ids.push(ariaDescribedBy);
ids.push(this.messagesGroupId); // toujours présent meme si aucun message
return ids.length ? ids.join(' ') : null;
}, ...(ngDevMode ? [{ debugName: "combinedAriaDescribedBy" }] : []));
/** @internal */
this.messagesGroupId = '';
this._id = '';
}
/**
* Cet attribut doit être utilisé en tant que propriété et non en attribut, ex. `[id]="'monid'"`.
*
* @deprecated since 1.5, utiliser `inputId` à la place.
*/
set id(value) {
if (value) {
this._id = value;
this.inputId ??= this._id;
}
}
/** @internal */
ngOnInit() {
this.inputId = this.inputId || newUniqueId(); // même si this.inputId = '', l'id sera valorisé (contrairement à '')
this.messagesGroupId = newUniqueId();
this.labelId = `${this.inputId}-label`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: DefaultControlComponent, isStandalone: true, selector: "ng-component", inputs: { ariaControls: { classPropertyName: "ariaControls", publicName: "ariaControls", isSignal: false, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: false, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: false, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, valid: { classPropertyName: "valid", publicName: "valid", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, messageSeverity: { classPropertyName: "messageSeverity", publicName: "messageSeverity", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: false, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultControlComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { ariaControls: [{
type: Input
}], inputId: [{
type: Input
}], hint: [{
type: Input
}], label: [{
type: Input
}], name: [{
type: Input
}], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], valid: [{ type: i0.Input, args: [{ isSignal: true, alias: "valid", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], messageSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageSeverity", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], id: [{
type: Input
}] } });
// -- Background -------------------------------------------------------------------------------------------------------
/**
* Définit les différentes couleurs d'arrière-plan supportées par le DSFR.
*/
var DsfrPanelBackgroundConst;
(function (DsfrPanelBackgroundConst) {
DsfrPanelBackgroundConst["DEFAULT"] = "default";
DsfrPanelBackgroundConst["GREY"] = "grey";
DsfrPanelBackgroundConst["TRANSPARENT"] = "transparent";
})(DsfrPanelBackgroundConst || (DsfrPanelBackgroundConst = {}));
// -- Border -----------------------------------------------------------------------------------------------------------
/**
* Définit les différentes couleurs de bordure supportées par le DSFR.
*/
var DsfrPanelBorderConst;
(function (DsfrPanelBorderConst) {
DsfrPanelBorderConst["DEFAULT"] = "default";
DsfrPanelBorderConst["NO_BORDER"] = "no-border";
DsfrPanelBorderConst["SHADOW"] = "shadow";
})(DsfrPanelBorderConst || (DsfrPanelBorderConst = {}));
/**
* Définit les différents balisages HTML supportés par la description du panel.
*/
var DsfrPanelDescMarkupConst;
(function (DsfrPanelDescMarkupConst) {
/**
* Pour obtenir un conteneur `<DIV>`.
*/
DsfrPanelDescMarkupConst["DIV"] = "div";
/**
* Pour obtenir un conteneur `<P>` par défault.
*/
DsfrPanelDescMarkupConst["P"] = "p";
})(DsfrPanelDescMarkupConst || (DsfrPanelDescMarkupConst = {}));
// Ne peut pas être déplacé dans 'shared' à cause de la dépendance sur DsfrBadge et DsfrTag
const DEFAULT_HEADING_LEVEL = 'H3';
class BasePanelComponent {
constructor() {
/**
* Permet de positionner des badges (jusqu'à 4 badges maxi) en haut du composant, optionnel.
*
* @see DsfrBadge
*/
this.badges = [];
/**
* Permet d'avoir un fond gris ou transparent.
*/
this.customBackground = 'default';
/**
* Permet d'afficher une ombre ou ne pas mettre de bordure.
*/
this.customBorder = 'default';
/**
* Type de balise HTML pour le conteneur de la description. <p> par défaut.
*/
this.descriptionMarkup = DsfrPanelDescMarkupConst.P;
/**
* Permet de désactiver le composant.
*/
this.disabled = false;
/**
* Equivalent à l'attribut html natif 'download'.
* - true : la fenêtre de téléchargement s'affiche directement,
* - false : le fichier est ouvert avant d'être téléchargé,
* - string : nouveau nom de fichier proposé au téléchargement.
*/
this.downloadDirect = true;
/**
* Si 'true' (défaut) : le détail du téléchargement est automatiquement renseigné par le script DSFR.
*/
this.downloadAssessFile = true;
/**
* Permet de désactiver le lien étendu de la carte.
*/
this.enlargeLink = true;
/**
* Le niveau de titre devant être utilisé (tag `h4` par défaut).
* Cette balise ne produit pas de style, mais de la structure.
*/
this.headingLevel = DEFAULT_HEADING_LEVEL;
/**
* Passe le composant en mode horizontal.
*/
this.horizontal = false;
/**
* Dimension du composant, 'MD' par défaut ('SM, 'MD' uniquement pour la tuile).
*/
this.panelSize = 'MD';
/**
* Permet d'avoir un bouton à la place d'un lien.
*
* @since 1.12
*/
this.enlargeButton = false;
this._download = false;
this._tags = [];
this._detailBottom = '';
this.langService = inject(LangService);
}
get downloadSizeUnit() {
return this._downloadSizeUnit || this.langService.lang ? 'octets' : 'bytes';
}
get tags() {
return this._tags;
}
get itemLink() {
const label = this.heading ? this.heading : '';
const itemLink = { label: label, ariaLabel: this.ariaLabel }; // ariaLabel (since 1.7)
if (this.link) {
itemLink.link = this.link;
}
else if (this.route) {
itemLink.route = this.route;
}
return itemLink;
}
get download() {
return this._download;
}
get detailBottom() {
return !this.download ? this._detailBottom : this.downloadDetail;
}
/**
* Dimension du composant, 'MD' par défaut ('SM, 'MD' uniquement pour la tuile).
*
* @deprecated since(1.15.0) utiliser 'panelSize' à la place
*/
get size() {
return this.panelSize;
}
/**
* Dans le cas d'un téléchargement sans l'option 'downloadAssessFile',
* on crée le détail à l'aide des propriétés 'downloadSizeBytes', 'downloadMimeType'.
* @internal
*/
get downloadDetail() {
return downloadDetail(this.downloadMimeType, this.downloadSizeBytes, this.downloadSizeUnit);
}
/**
* Dimension du composant, 'MD' par défaut ('SM, 'MD' uniquement pour la tuile).
*
* @deprecated since(1.15.0) utiliser 'panelSize' à la place
*/
set size(value) {
this.panelSize = value;
}
/**
* Transforme le composant classique en composant de téléchargement.
*/
set download(download) {
this._download = download;
if (this._download)
this.enlargeLink = true;
}
/** Zone de détail se trouvant visuellement sous la description et à côté de l’icône, optionnel.
* Pour la tuile, on peut passer un slot de sélecteur "detail".
* En cas de téléchargement le détail est remplacé par les informations du fichier calculées selon downloadAssessFile.
*/
set detailBottom(value) {
this._detailBottom = value;
}
/**
* Force l'unité par défaut qui dépend de la langue du site, en octets (ko, Mo, ...) pour 'fr', en 'bytes' (KB, MB...) pour les autres langues.
*/
set downloadSizeUnit(value) {
this._downloadSizeUnit = value;
}
/**
* Permet de positionner des tags en haut de la carte (cliquables ou non), optionnel.
* voir DsfrTag
*/
set tags(tags) {
this._tags = tags;
// Vérifie si un tag est cliquable pour désactiver enlargeLink.
if (tags?.find((tag) => tag.link))
this.enlargeLink = false;
}
/**
* Activation du router Angular.
* Provoque un bug accessibilité sur la navigation au clavier (ajout d'un tabindex=0)
*
* @deprecated (since 1.11.5) utiliser `routePath` à la place.
*/
set routerLink(value) {
if (value)
this.routePath = value;
}
/** @internal */
hasLink() {
return !!(this.link || this.route || this.routePath);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BasePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: BasePanelComponent, isStandalone: true, selector: "ng-component", inputs: { ariaLabel: "ariaLabel", badges: "badges", customBackground: "customBackground", customBorder: "customBorder", description: "description", descriptionMarkup: "descriptionMarkup", disabled: "disabled", downloadDirect: "downloadDirect", downloadAssessFile: "downloadAssessFile", downloadLangCode: "downloadLangCode", downloadMimeType: "downloadMimeType", downloadSizeBytes: "downloadSizeBytes", enlargeLink: "enlargeLink", heading: "heading", headingLevel: "headingLevel", horizontal: "horizontal", link: "link", route: "route", routePath: "routePath", routerLinkActive: "routerLinkActive", routerLinkExtras: "routerLinkExtras", panelSize: "panelSize", enlargeButton: "enlargeButton", linkTarget: "linkTarget", size: "size", download: "download", detailBottom: "detailBottom", downloadSizeUnit: "downloadSizeUnit", tags: "tags", routerLink: "routerLink" }, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BasePanelComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{
type: Input
}], badges: [{
type: Input
}], customBackground: [{
type: Input
}], customBorder: [{
type: Input
}], description: [{
type: Input
}], descriptionMarkup: [{
type: Input
}], disabled: [{
type: Input
}], downloadDirect: [{
type: Input
}], downloadAssessFile: [{
type: Input
}], downloadLangCode: [{
type: Input
}], downloadMimeType: [{
type: Input
}], downloadSizeBytes: [{
type: Input
}], enlargeLink: [{
type: Input
}], heading: [{
type: Input
}], headingLevel: [{
type: Input
}], horizontal: [{
type: Input
}], link: [{
type: Input
}], route: [{
type: Input
}], routePath: [{
type: Input
}], routerLinkActive: [{
type: Input
}], routerLinkExtras: [{
type: Input
}], panelSize: [{
type: Input
}], enlargeButton: [{
type: Input
}], linkTarget: [{
type: Input
}], size: [{
type: Input
}], download: [{
type: Input
}], detailBottom: [{
type: Input
}], downloadSizeUnit: [{
type: Input
}], tags: [{
type: Input
}], routerLink: [{
type: Input
}] } });
/**
* Composant de checkbox sans ControlValueAccessor
* Pour utilisation d'un composant "light" répété x fois, ex. Table
*/
class EduCheckboxLightComponent {
constructor() {
/* Evenement émis lors du clic sur la checkbox */
this.checkboxClick = new EventEmitter();
/* Identifiant de la checkbox (obligatoire) */
this.id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
/* Label associé à la checkbox */
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
/* Surcharge de la propriété tabIndex (optionnel) */
this.tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : []));
/* Checkbox désactivée, faux par défaut */
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/* Icone affichée à gauche du label (optionnel) */
this.icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : []));
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = input(false, ...(ngDevMode ? [{ debugName: "labelSrOnly" }] : []));
/* Etat de la checkbox */
this.selected = input(false, ...(ngDevMode ? [{ debugName: "selected" }] : []));
}
/**@internal */
onClick(event) {
this.checkboxClick.emit(event);
}
/**@internal */
isIndeterminate() {
return this.selected() === undefined;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduCheckboxLightComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: EduCheckboxLightComponent, isStandalone: true, selector: "edu-checkbox-light, dsfr-ext-checkbox", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, labelSrOnly: { classPropertyName: "labelSrOnly", publicName: "labelSrOnly", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checkboxClick: "checkboxClick" }, ngImport: i0, template: "<span class=\"fr-checkbox-group fr-checkbox-group--sm\">\n <input\n [attr.tabindex]=\"tabIndex() || null\"\n [checked]=\"selected()\"\n (click)=\"onClick($event)\"\n [disabled]=\"disabled()\"\n class=\"edu-table-checkbox\"\n [indeterminate]=\"isIndeterminate()\"\n [id]=\"id()\"\n type=\"checkbox\" />\n <label class=\"fr-label\" [for]=\"id()\" [class.fr-label--disabled]=\"disabled()\">\n @if (labelSrOnly() && label()) {\n <span class=\"fr-sr-only\">{{ label() }} </span>\n } @else if (label()) {\n <span class=\"text-node\">{{ label() }}</span>\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (icon()) {\n <span [class]=\"icon() + ' fr-icon--sm'\"> </span>\n }\n </label>\n</span>\n", styles: [".fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:before{background-color:var(--background-active-blue-france);background-image:none}.fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.7rem;left:-1.65rem;width:.75rem;height:.1rem;top:initial;background:var(--background-contrast-blue-france)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.675rem;left:-1.25rem;width:.5rem;height:.125rem;top:initial;background:var(--background-contrast-blue-france)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduCheckboxLightComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-checkbox-light, dsfr-ext-checkbox', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<span class=\"fr-checkbox-group fr-checkbox-group--sm\">\n <input\n [attr.tabindex]=\"tabIndex() || null\"\n [checked]=\"selected()\"\n (click)=\"onClick($event)\"\n [disabled]=\"disabled()\"\n class=\"edu-table-checkbox\"\n [indeterminate]=\"isIndeterminate()\"\n [id]=\"id()\"\n type=\"checkbox\" />\n <label class=\"fr-label\" [for]=\"id()\" [class.fr-label--disabled]=\"disabled()\">\n @if (labelSrOnly() && label()) {\n <span class=\"fr-sr-only\">{{ label() }} </span>\n } @else if (label()) {\n <span class=\"text-node\">{{ label() }}</span>\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (icon()) {\n <span [class]=\"icon() + ' fr-icon--sm'\"> </span>\n }\n </label>\n</span>\n", styles: [".fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:before{background-color:var(--background-active-blue-france);background-image:none}.fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.7rem;left:-1.65rem;width:.75rem;height:.1rem;top:initial;background:var(--background-contrast-blue-france)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.675rem;left:-1.25rem;width:.5rem;height:.125rem;top:initial;background:var(--background-contrast-blue-france)}\n"] }]
}], propDecorators: { checkboxClick: [{
type: Output
}], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], labelSrOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelSrOnly", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }] } });
/**
* Permet de définir le titre de composant d'un niveau de titre de `<h2>` à `<h6>`, avec un niveau par défaut de `<h2>` à `<h6>` ou `<p>`.
* Ne permets pas la projection du titre.
*/
class HeadingComponent {
constructor() {
/** Autoriser l'affichage d'un titre de niveau H1 */
this.allowFirstLevel = false;
}
getLevel() {
return this.level && this.level !== DsfrHeadingLevelConst.NONE ? this.level : this.defaultLevel;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: HeadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: HeadingComponent, isStandalone: true, selector: "edu-heading", inputs: { customClass: "customClass", allowFirstLevel: "allowFirstLevel", headingId: "headingId", level: "level", defaultLevel: "defaultLevel" }, ngImport: i0, template: "@switch (getLevel()) {\n @case ('H1') {\n @if (allowFirstLevel) {\n <h1 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h1>\n } @else {\n <h2 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n }\n @case ('H2') {\n <h2 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @case ('H3') {\n <h3 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n @case ('H4') {\n <h4 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n @case ('P') {\n <p [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </p>\n }\n @default {\n <p [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </p>\n }\n}\n\n<ng-template #headingTemplate>\n <ng-content></ng-content>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: HeadingComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-heading', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "@switch (getLevel()) {\n @case ('H1') {\n @if (allowFirstLevel) {\n <h1 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h1>\n } @else {\n <h2 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n }\n @case ('H2') {\n <h2 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @case ('H3') {\n <h3 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n @case ('H4') {\n <h4 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n @case ('P') {\n <p [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </p>\n }\n @default {\n <p [attr.id]=\"headingId || null\" [class]=\"customClass\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </p>\n }\n}\n\n<ng-template #headingTemplate>\n <ng-content></ng-content>\n</ng-template>\n" }]
}], propDecorators: { customClass: [{
type: Input
}], allowFirstLevel: [{
type: Input
}], headingId: [{
type: Input
}], level: [{
type: Input
}], defaultLevel: [{
type: Input,
args: [{ required: true }]
}] } });
class HeadingModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: HeadingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: HeadingModule, imports: [CommonModule, HeadingComponent], exports: [HeadingComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: HeadingModule, imports: [CommonModule, HeadingComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: HeadingModule, decorators: [{
type: NgModule,
args: [{
exports: [HeadingComponent],
imports: [CommonModule, HeadingComponent],
}]
}] });
/**
*/
class EduMessageSeverityDirective {
constructor() {
this._severity = null;
}
get className() {
switch (this._severity) {
case DsfrSeverityConst.ERROR:
return 'fr-message--error';
case DsfrSeverityConst.SUCCESS:
case DsfrSeverityConst.VALID:
return 'fr-message--valid';
case DsfrSeverityConst.INFO:
return 'fr-message--info';
case DsfrSeverityConst.WARNING:
return 'fr-message--warning';
default:
return '';
}
}
set severity(value) {
this._severity = value;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduMessageSeverityDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: EduMessageSeverityDirective, isStandalone: true, selector: "[eduMessageSeverity]", inputs: { severity: ["eduMessageSeverity", "severity"] }, host: { properties: { "class": "this.className" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduMessageSeverityDirective, decorators: [{
type: Directive,
args: [{ selector: '[eduMessageSeverity]', standalone: true }]
}], propDecorators: { className: [{
type: HostBinding,
args: ['class']
}], severity: [{
type: Input,
args: ['eduMessageSeverity']
}] } });
/**
* InputGroupComponent contient la gestion des messages de l'input
* @since 1.11.0
*/
class InputGroupComponent {
constructor() {
/**
* Permet de désactiver le champ.
*/
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/**
* Id de la div affichant les messages d'erreur ou de validation.
*/
this.messagesGroupId = input.required(...(ngDevMode ? [{ debugName: "messagesGroupId" }] : []));
/**
* Message(s) d'information(s) lié au composant fourni par l'utilisateur du composant
*/
this.message = input(...(ngDevMode ? [undefined, { debugName: "message" }] : []));
/**
* Représente la sévérité du ou des messages
*/
this.messageSeverity = input(...(ngDevMode ? [undefined, { debugName: "messageSeverity" }] : []));
/**
* Message(s) d'information(s) lié au composant correspondant à son système de validation interne
* @internal
*/
this.internalError = input(...(ngDevMode ? [undefined, { debugName: "internalError" }] : []));
this.DsfrSeverity = DsfrSeverityConst;
/**
* Gestion de l'affichage de la combinaison des messages internes et externes du composant.
*/
this.newMessages = computed(() => {
const internalError = this.internalError();
const message = this.message();
return this.handleMessagesConcat(internalError, message);
}, ...(ngDevMode ? [{ debugName: "newMessages" }] : []));
/**
* Gestion de l'affichage dans le cas où le composant serait dans un autre statut qu'en erreur,
* et qu'une erreur interne se manifeste. Dans ce cas, seules sont affichées les erreurs internes, et le statut
* affiché du composant est forcé en erreur.
*/
this.displayedMessageStatus = computed(() => {
const newSeverity = this.messageSeverity();
const internalError = toArray(this.internalError());
// priorité erreur interne
if (newSeverity !== DsfrSeverityConst.ERROR && internalError.length > 0) {
return {
message: internalError,
messageSeverity: DsfrSeverityConst.ERROR,
};
}
return {
message: this.newMessages(),
messageSeverity: newSeverity,
};
}, ...(ngDevMode ? [{ debugName: "displayedMessageStatus" }] : []));
/** Gestion des classes CSS ajoutées selon la sévérité */
this.inputGroupSeverityClasses = computed(() => {
const status = this.displayedMessageStatus();
if (!status?.message || !status?.messageSeverity)
return null;
const severityToClass = {
[this.DsfrSeverity.ERROR]: 'fr-input-group--error',
[this.DsfrSeverity.SUCCESS]: 'fr-input-group--valid',
[this.DsfrSeverity.VALID]: 'fr-input-group--valid',
[this.DsfrSeverity.INFO]: '',
[this.DsfrSeverity.WARNING]: '',
};
return severityToClass[status.messageSeverity];
}, ...(ngDevMode ? [{ debugName: "inputGroupSeverityClasses" }] : []));
}
handleMessagesConcat(internalError, message) {
const internal = this.toArray(internalError);
const external = this.toArray(message);
const messages = [...internal, ...external];
return messages;
}
toArray(value) {
if (!value)
return [];
return Array.isArray(value) ? value : [value];
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: InputGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: InputGroupComponent, isStandalone: true, selector: "edu-input-group", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, messagesGroupId: { classPropertyName: "messagesGroupId", publicName: "messagesGroupId", isSignal: true, isRequired: true, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, messageSeverity: { classPropertyName: "messageSeverity", publicName: "messageSeverity", isSignal: true, isRequired: false, transformFunction: null }, internalError: { classPropertyName: "internalError", publicName: "internalError", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"fr-input-group\" [class.fr-input-group--disabled]=\"disabled()\" [ngClass]=\"inputGroupSeverityClasses()\">\n <ng-content />\n\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId()\" aria-live=\"polite\">\n @let messageStatus = displayedMessageStatus();\n\n @if (messageStatus.messageSeverity && messageStatus.message?.length) {\n @for (individualMessage of messageStatus.message; track individualMessage) {\n <!-- prettier-ignore -->\n <p class=\"fr-message\" [eduMessageSeverity]=\"messageStatus.messageSeverity\">{{ individualMessage }}</p>\n }\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: InputGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-input-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule, EduMessageSeverityDirective], template: "<div class=\"fr-input-group\" [class.fr-input-group--disabled]=\"disabled()\" [ngClass]=\"inputGroupSeverityClasses()\">\n <ng-content />\n\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId()\" aria-live=\"polite\">\n @let messageStatus = displayedMessageStatus();\n\n @if (messageStatus.messageSeverity && messageStatus.message?.length) {\n @for (individualMessage of messageStatus.message; track individualMessage) {\n <!-- prettier-ignore -->\n <p class=\"fr-message\" [eduMessageSeverity]=\"messageStatus.messageSeverity\">{{ individualMessage }}</p>\n }\n }\n </div>\n</div>\n" }]
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], messagesGroupId: [{ type: i0.Input, args: [{ isSignal: true, alias: "messagesGroupId", required: true }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], messageSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageSeverity", required: false }] }], internalError: [{ type: i0.Input, args: [{ isSignal: true, alias: "internalError", required: false }] }] } });
/** Ce composant correspond au lien de téléchargement utilisé par card, tile et download */
class LinkDownloadComponent {
constructor() {
/**
* Option de détail de téléchargement renseigné automatiquement.
* Si la valeur est 'bytes', l'unité sera en Bytes
*/
this.downloadAssessFile = false;
/**
* Langue courante. hreflang ne sera indiquée que différent de la langue courante.
*/
this.langCode = 'fr';
/** Remplacer le lien par un markup de bouton */
this.isButton = false;
/**
* Propage la valeur de 'route' ou de 'link' (selon le cas) lors du click sur le lien.
*/
this.linkSelect = new EventEmitter();
/**
* Active la version download avec l'attribut download.
* Permet l'utilisation de `downloadAssessFile` et `langCode`
*/
this.downloadDirect = input(false, ...(ngDevMode ? [{ debugName: "downloadDirect" }] : []));
/** Création du fileName en cas de download direct */
this.newFileName = computed(() => {
if (typeof this.downloadDirect() === 'string') {
return this._sanitizer.sanitize(SecurityContext.URL, this.downloadDirect());
}
return '';
}, ...(ngDevMode ? [{ debugName: "newFileName" }] : []));
this._sanitizer = inject(DomSanitizer);
}
get disabled() {
return !(this.item.route || this.item.routerLink || this.item.link);
}
get item() {
return this._item;
}
/** Item représentant un lien `<a>` avec ou sans `routerLink`. */
set item(item) {
if (item.label) {
this._item = { ...item, label: item.label };
}
else {
this._item = item;
}
if (this._item.link) {
this._sanitizedLink = this._sanitizer.sanitize(SecurityContext.URL, this._item.link);
}
if (this.item)
this.item.customClass = this.getLinkClasses();
}
/** @internal */
getHref() {
if (this.item.route)
return this.item.route;
if (this.item.link)
return this._sanitizedLink ?? undefined;
return undefined;
}
/** @internal */
onLinkSelect(event) {
if (this.disabled)
return;
if (this.item.route && !this.item.routerLink) {
event.preventDefault();
this.linkSelect.emit(this.item.route);
}
else if (this.item.link) {
this.linkSelect.emit(this.item.link);
}
else {
this.linkSelect.emit(this.item.linkId ?? this.item.label);
}
}
/** @internal */
getLinkTarget(item) {
return item.target ?? item.linkTarget;
}
/** @internal */
getLinkClasses() {
const classes = [];
classes.push(!this.customClass ? 'fr-link' : this.customClass);
if (this._item.icon) {
classes.push(this._item.icon);
// Dans le header, les liens utilisent la classe fr-btn, pour être cohérent, on va utiliser fr-btn--icon-left et right
const likeButton = this.customClass && this.customClass?.search('fr-btn') >= 0;
const prefixPos = likeButton ? 'fr-btn--icon-' : 'fr-link--icon-';
classes.push(this._item.iconPosition === DsfrPositionConst.RIGHT ? prefixPos + 'right' : prefixPos + 'left');
}
return classes.join(' ');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LinkDownloadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: LinkDownloadComponent, isStandalone: true, selector: "edu-link-download", inputs: { customClass: { classPropertyName: "customClass", publicName: "customClass", isSignal: false, isRequired: false, transformFunction: null }, downloadAssessFile: { classPropertyName: "downloadAssessFile", publicName: "downloadAssessFile", isSignal: false, isRequired: false, transformFunction: null }, langCode: { classPropertyName: "langCode", publicName: "langCode", isSignal: false, isRequired: false, transformFunction: null }, linkTarget: { classPropertyName: "linkTarget", publicName: "linkTarget", isSignal: false, isRequired: false, transformFunction: null }, isButton: { classPropertyName: "isButton", publicName: "isButton", isSignal: false, isRequired: false, transformFunction: null }, downloadDirect: { classPropertyName: "downloadDirect", publicName: "downloadDirect", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { linkSelect: "linkSelect" }, ngImport: i0, template: "<!-- Lien de t\u00E9l\u00E9chargement-->\n@if (!isButton) {\n <a\n [ngClass]=\"customClass\"\n [attr.aria-controls]=\"item.ariaControls || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"item.ariaLabel || null\"\n [attr.href]=\"getHref() || null\"\n [attr.download]=\"downloadDirect() ? newFileName() : null\"\n [attr.data-fr-assess-file]=\"downloadAssessFile || null\"\n [attr.hreflang]=\"langCode || null\"\n [attr.target]=\"linkTarget || null\"\n (click)=\"onLinkSelect($event)\"\n [innerHTML]=\"item.label\">\n </a>\n} @else {\n <button\n type=\"button\"\n (click)=\"onLinkSelect($event)\"\n [disabled]=\"disabled\"\n [ngClass]=\"customClass\"\n [innerHTML]=\"item.label\"></button>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: RouterModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LinkDownloadComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-link-download', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "<!-- Lien de t\u00E9l\u00E9chargement-->\n@if (!isButton) {\n <a\n [ngClass]=\"customClass\"\n [attr.aria-controls]=\"item.ariaControls || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"item.ariaLabel || null\"\n [attr.href]=\"getHref() || null\"\n [attr.download]=\"downloadDirect() ? newFileName() : null\"\n [attr.data-fr-assess-file]=\"downloadAssessFile || null\"\n [attr.hreflang]=\"langCode || null\"\n [attr.target]=\"linkTarget || null\"\n (click)=\"onLinkSelect($event)\"\n [innerHTML]=\"item.label\">\n </a>\n} @else {\n <button\n type=\"button\"\n (click)=\"onLinkSelect($event)\"\n [disabled]=\"disabled\"\n [ngClass]=\"customClass\"\n [innerHTML]=\"item.label\"></button>\n}\n" }]
}], propDecorators: { customClass: [{
type: Input
}], downloadAssessFile: [{
type: Input
}], langCode: [{
type: Input
}], linkTarget: [{
type: Input
}], isButton: [{
type: Input
}], linkSelect: [{
type: Output
}], downloadDirect: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadDirect", required: false }] }], item: [{
type: Input,
args: [{ required: true }]
}] } });
/** Ce composant permet de gérer facilement les pictogrammes issus du DSFR. */
class PictogramComponent {
constructor() {
/**
* Chemin vers le répertoire exposant les pictogrammes illustratifs DSFR.
*/
this.artworkDirPath = 'artwork';
/**
* Détermine l'affichage du pictogramme associé aux téléchargements.
*/
this.download = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: PictogramComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: PictogramComponent, isStandalone: true, selector: "edu-pictogram", inputs: { artworkDirPath: "artworkDirPath", artworkFilePath: "artworkFilePath", download: "download" }, ngImport: i0, template: "@if (artworkFilePath) {\n <svg aria-hidden=\"true\" class=\"fr-artwork\" viewBox=\"0 0 80 80\" width=\"80px\" height=\"80px\">\n <use class=\"fr-artwork-decorative\" [attr.href]=\"artworkFilePath + '#artwork-decorative'\"></use>\n <use class=\"fr-artwork-minor\" [attr.href]=\"artworkFilePath + '#artwork-minor'\"></use>\n <use class=\"fr-artwork-major\" [attr.href]=\"artworkFilePath + '#artwork-major'\"></use>\n </svg>\n} @else if (download && !artworkFilePath) {\n <!---DOWNLOAD-->\n <svg aria-hidden=\"true\" class=\"fr-artwork\" viewBox=\"0 0 80 80\" width=\"80px\" height=\"80px\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-major'\"></use>\n </svg>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: PictogramComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-pictogram', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "@if (artworkFilePath) {\n <svg aria-hidden=\"true\" class=\"fr-artwork\" viewBox=\"0 0 80 80\" width=\"80px\" height=\"80px\">\n <use class=\"fr-artwork-decorative\" [attr.href]=\"artworkFilePath + '#artwork-decorative'\"></use>\n <use class=\"fr-artwork-minor\" [attr.href]=\"artworkFilePath + '#artwork-minor'\"></use>\n <use class=\"fr-artwork-major\" [attr.href]=\"artworkFilePath + '#artwork-major'\"></use>\n </svg>\n} @else if (download && !artworkFilePath) {\n <!---DOWNLOAD-->\n <svg aria-hidden=\"true\" class=\"fr-artwork\" viewBox=\"0 0 80 80\" width=\"80px\" height=\"80px\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"artworkDirPath + '/pictograms/document/document-download.svg#artwork-major'\"></use>\n </svg>\n}\n" }]
}], propDecorators: { artworkDirPath: [{
type: Input
}], artworkFilePath: [{
type: Input
}], download: [{
type: Input
}] } });
class PictogramModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: PictogramModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: PictogramModule, imports: [CommonModule, RouterModule, PictogramComponent], exports: [PictogramComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: PictogramModule, imports: [CommonModule, RouterModule, PictogramComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: PictogramModule, decorators: [{
type: NgModule,
args: [{
exports: [PictogramComponent],
imports: [CommonModule, RouterModule, PictogramComponent],
}]
}] });
class SvgIconComponent {
constructor() {
this.role = 'img';
this.ariaHidden = true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SvgIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: SvgIconComponent, isStandalone: true, selector: "edu-svg-icon", inputs: { iconPath: "iconPath", role: "role", ariaHidden: "ariaHidden", alt: "alt", imageRatio: "imageRatio" }, ngImport: i0, template: `
<svg
class="fr-responsive-img"
[ngClass]="{
'fr-ratio-32x9': imageRatio === '16:9/2',
'fr-ratio-16x9': imageRatio === '16:9',
'fr-ratio-3x2': imageRatio === '3:2',
'fr-ratio-4x3': imageRatio === '4:3',
'fr-ratio-1x1': imageRatio === '1:1',
'fr-ratio-3x4': imageRatio === '3:4',
'fr-ratio-2x3': imageRatio === '2:3',
}"
[attr.aria-hidden]="ariaHidden"
[attr.role]="role"
[attr.alt]="alt">
<use attr.xlink:href="{{ iconPath }}"></use>
</svg>
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SvgIconComponent, decorators: [{
type: Component,
args: [{
selector: 'edu-svg-icon',
template: `
<svg
class="fr-responsive-img"
[ngClass]="{
'fr-ratio-32x9': imageRatio === '16:9/2',
'fr-ratio-16x9': imageRatio === '16:9',
'fr-ratio-3x2': imageRatio === '3:2',
'fr-ratio-4x3': imageRatio === '4:3',
'fr-ratio-1x1': imageRatio === '1:1',
'fr-ratio-3x4': imageRatio === '3:4',
'fr-ratio-2x3': imageRatio === '2:3',
}"
[attr.aria-hidden]="ariaHidden"
[attr.role]="role"
[attr.alt]="alt">
<use attr.xlink:href="{{ iconPath }}"></use>
</svg>
`,
imports: [CommonModule],
}]
}], propDecorators: { iconPath: [{
type: Input
}], role: [{
type: Input
}], ariaHidden: [{
type: Input
}], alt: [{
type: Input
}], imageRatio: [{
type: Input
}] } });
class SvgIconModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SvgIconModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: SvgIconModule, imports: [CommonModule, SvgIconComponent], exports: [SvgIconComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SvgIconModule, imports: [CommonModule, SvgIconComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SvgIconModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, SvgIconComponent],
exports: [SvgIconComponent],
}]
}] });
const DEFAULT_CONFIG = {
artworkDirPath: 'artwork',
};
/*
* Étant donné que ce jeton est à usage interne uniquement, nous n'exporterons pas ce fichier en dehors de
* la bibliothèque. La création du jeton DI dans un fichier séparé permet d'éviter dépendances circulaires.
*/
const DSFR_CONFIG_TOKEN = new InjectionToken('ngx-dsfr-config', {
providedIn: 'root',
factory: () => DEFAULT_CONFIG,
});
class DsfrConfigModule {
static forRoot(config) {
return {
ngModule: DsfrConfigModule,
providers: [{ provide: DSFR_CONFIG_TOKEN, useValue: config }],
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConfigModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrConfigModule, imports: [CommonModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConfigModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConfigModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
}]
}] });
/**
* Désactiver le lien du routerLink
* Cas particulier des entrées de menu : profiter de la directive routerLink mais ne pas effectuer la navigation
* Le preventDefault classique non fonctionnel sur les routerLink
*/
class DsfrDisableRouterLinkDirective {
constructor() {
this.disableNavigation = false;
const routerLink = inject(RouterLink, { optional: true });
// Replace method
if (routerLink) {
routerLink.onClick = () => {
return true;
};
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisableRouterLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "20.3.23", type: DsfrDisableRouterLinkDirective, isStandalone: true, selector: "button[routerLink][disableNavigation]", inputs: { disableNavigation: ["disableNavigation", "disableNavigation", booleanAttribute] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisableRouterLinkDirective, decorators: [{
type: Directive,
args: [{
selector: 'button[routerLink][disableNavigation]',
standalone: true,
}]
}], ctorParameters: () => [], propDecorators: { disableNavigation: [{
type: Input,
args: [{ transform: booleanAttribute }]
}] } });
const LABELS_BUNDLE_EN = {
commons: {
accept: 'Accept',
close: 'Close',
disabled: 'Disabled',
download: 'Download',
enlarge: 'Enlarge',
refuse: 'Refuse',
select: 'Select',
search: 'Search',
sort: 'Sort',
},
alert: {
close: 'Hide message',
},
backtotop: {
topOfPage: 'Top of page',
},
breadcrumb: {
viewBreadcrumb: 'View breadcrumb',
ariaLabel: 'you are here :',
},
consent: {
acceptAll: 'Accept All',
allowAllCookies: 'Allow all cookies',
refuseAll: 'Refuse all',
refuseAllCookies: 'Refuse all cookies',
customize: 'Customize',
customizeCookies: 'Customize cookies',
description: {
prelink: 'Welcome ! We use cookies to improve your experience and the services available on this site. To find out more, visit the ',
link: 'Personal data and cookies',
postlink: ' page. You can, at any time, have control over the cookies you wish to activate.',
},
manager: {
title: 'Cookies management panel',
preferences: 'Preferences for all services.',
personalData: 'Personal data and cookies',
confirmChoices: 'Confirm my choices',
finalityExempt: 'Enabled',
showDetails: 'Show more details',
},
},
display: {
heading: 'Display settings',
hint: 'Choose a theme to customize the look of the site.',
light: { label: 'Light theme' },
dark: { label: 'Dark theme' },
system: { label: 'System', hint: 'Use system settings.' },
},
email: {
label: 'Email address',
hint: 'Expected format: name@domain.fr',
error: 'The format of the email address entered is invalid. The expected format is: name@example.org',
},
follow: {
networks: { heading: 'Follow us<br/>on social networks' },
newsletter: {
heading: 'Subscribe to our newsletter',
button: { label: 'Subscribe', title: 'Subscribe to our newsletter' },
input: {
placeholder: 'Your email address (e.g. name@domain.fr)',
hint: `By entering your email address, you agree to receive our news by email. You can unsubscribe at any time using the unsubscribe links or by contacting us.`,
error: 'The format of the email address entered is invalid. The expected format is: name@example.org',
},
registration: { success: 'Your registration has been taken into account.' },
},
newWindow: 'New window',
},
footer: {
institutional: { externalLink: 'new windows' },
partners: { title: 'Our partners' },
display: 'Display settings',
},
franceConnect: {
title: 'Identify with',
link: { label: 'What is FranceConnect', tooltip: 'What is FranceConnect? - new window' },
},
header: {
menu: { label: 'Menu' },
mainMenu: 'Main menu',
},
link: {
newWindow: 'new window',
},
login: {
heading: 'Connecting to ',
franceConnect: {
heading: 'Connect with FranceConnect',
},
or: 'or',
account: {
heading: 'Log in with your account',
email: { label: 'Identifier' },
password: { label: 'Password' },
srOnly: { label: 'identifiers' },
rememberMe: { label: 'Remember me' },
button: { label: 'Log in' },
},
noAccount: { heading: 'You do not have an account ?', button: { label: 'Create an account' } },
},
modal: {
ariaLabel: 'Close the window',
},
notice: {
close: 'Hide message',
},
pagination: {
firstPage: 'First page',
previousPage: 'Previous page',
nextPage: 'Next page',
lastPage: 'Last page',
role: 'navigation',
rowsPerPageLabel: 'Number of rows per page',
row: 'row',
ariaLabel: 'Paging',
},
password: {
aria: { label: 'Show password' },
forgotPassword: 'Forgot your password ?',
label: 'Password',
message: 'Your password must contain:',
show: 'Show',
dataFrValid: 'valid',
dataFrError: 'in error',
invalidCriteria: 'criteria is invalid',
validCriteria: 'criteria is valid',
},
range: {
labelMin: 'Minimum value',
labelMax: 'Maximum value',
},
response: {
contactUs: 'Contact us',
},
select: {
placeholder: 'Select an option',
},
share: {
'heading': 'Share page',
'cookiesText': {
prelink: 'Please',
link: 'authorize the deposit of cookies',
postlink: 'to be shared on social networks.',
},
//
'copy': { title: 'Clipboard', label: 'Copy to clipboard' },
'facebook': { title: 'Facebook', label: 'Share on Facebook' },
'linkedin': { title: 'LinkedIn', label: 'Share on LinkedIn' },
'mail': { title: 'Email', label: 'Share by email' },
'mastodon': { title: 'Mastodon', label: 'Share on Mastodon' },
'twitter': { title: 'X', label: 'Share on X' },
'twitter-x': { title: 'X', label: 'Share on X' },
},
sidemenu: {
menuMobile: 'In this section',
},
stepper: {
step: 'Step',
on: 'on',
of: 'of',
nextStep: 'Next step:',
},
table: {
noData: 'No data',
selectRow: 'Select row',
selectAll: 'Select all',
unselectAll: 'Unselect all',
row: 'row',
rowsPerPageLabel: 'Number of rows per page',
noResult: 'No result',
unselectable: 'unselectable',
noSelectedRows: 'No selected rows',
oneSelectedRow: 'selected row',
selectedRows: 'selected rows',
sortExplanation: 'Column headers with buttons are sortable',
sortTableBy: 'Sort by',
},
tag: {
removeFilter: 'Remove the filter',
removable: 'Removable',
selectFilter: 'Select the filter',
selected: 'selected',
unselected: 'unselected',
},
tel: {
label: 'Phone number',
hint: 'Expected format: (+33) 1 23 45 67 89',
error: 'The phone number format entered is invalid.',
},
toggle: {
dataLabelChecked: 'Checked',
dataLabelUnchecked: 'Unchecked',
},
tooltip: {
button: { label: 'Contextual information' },
},
transcription: {
button: 'Transcription',
ariaLabel: 'Enlarge transcript',
},
translate: {
tooltip: 'Select a language',
},
upload: {
label: 'Add a file',
hint: '** Mandatory help text which specifies the constraints at the level of the expected file(s) **',
},
date: {
day: { label: 'Day', hint: 'Example: 14' },
month: { label: 'Month', hint: 'Example: 12' },
year: { label: 'Year', hint: 'Example: 1984' },
error: {
required: 'All fields are required.',
invalid: {
format_day: 'The day field must be in numeric format.',
format_year: 'The year field must be in numeric format.',
format_month: 'The month field must be in numeric format.',
day: 'The day is not valid.',
month: 'The month is not valid.',
date: 'The date is not valid.',
},
},
},
pageResponse: {
heading: 'Unexpected error',
description: 'Try refreshing the page or try again later.',
detail: 'Sorry, there is a problem with the service, we are working to resolve it as soon as possible.',
},
name: {
legend: 'Name',
country: {
label: 'Country',
placeholder: 'Select an option',
},
lastname: 'Name',
usualName: {
label: 'Username',
hint: 'Old name...',
},
firstname: 'First name',
addFirstName: 'Add a first name',
legendFirstName: 'Firstname(s)',
noFirstName: "I don't have a first name",
},
};
const LABELS_BUNDLE_FR = {
commons: {
accept: 'Accepter',
close: 'Fermer',
disabled: 'Désactivé',
download: 'Télécharger',
enlarge: 'Agrandir',
refuse: 'Refuser',
select: 'Sélectionner',
search: 'Rechercher',
sort: 'Trier',
},
alert: {
close: 'Masquer le message',
},
backtotop: {
topOfPage: 'Haut de page',
},
breadcrumb: {
viewBreadcrumb: 'Voir le fil d’Ariane',
ariaLabel: 'vous êtes ici :',
},
consent: {
acceptAll: 'Tout accepter',
allowAllCookies: 'Autoriser tous les cookies',
refuseAll: 'Tout refuser',
refuseAllCookies: 'Refuser tous les cookies',
customize: 'Personnaliser',
customizeCookies: 'Personnaliser les cookies',
description: {
prelink: 'Bienvenue ! Nous utilisons des cookies pour améliorer votre expérience et les services disponibles sur ce site. Pour en savoir plus, visitez la page',
link: 'Données personnelles et cookies',
postlink: '. Vous pouvez, à tout moment, avoir le contrôle sur les cookies que vous souhaitez activer.',
},
manager: {
title: 'Panneau de gestion des cookies',
preferences: 'Préférences pour tous les services.',
personalData: 'Données personnelles et cookies',
confirmChoices: 'Confirmer mes choix',
finalityExempt: 'Activé',
showDetails: 'Voir plus de détails',
},
},
display: {
heading: 'Paramètres d’affichage',
hint: 'Choisissez un thème pour personnaliser l’apparence du site.',
light: { label: 'Thème clair' },
dark: { label: 'Thème sombre' },
system: { label: 'Système', hint: 'Utilise les paramètres système.' },
},
email: {
label: 'Adresse électronique',
hint: 'Format attendu : nom@domaine.fr',
error: 'Le format de l’adresse électronique saisie n’est pas valide. Le format attendu est : nom@exemple.org',
},
follow: {
networks: { heading: 'Suivez-nous<br/>sur les réseaux sociaux' },
newsletter: {
heading: 'Abonnez-vous à notre lettre d’information',
button: { label: 'S’abonner', title: 'S’abonner à notre lettre d’information' },
input: {
placeholder: 'Votre adresse électronique (ex. : nom@domaine.fr)',
hint: `En renseignant votre adresse électronique, vous acceptez de recevoir nos actualités par courriel. Vous pouvez vous désinscrire à tout moment à l’aide des liens de désinscription ou en nous contactant.`,
error: 'Le format de l’adresse électronique saisie n’est pas valide. Le format attendu est : nom@exemple.org',
},
registration: { success: 'Votre inscription a bien été prise en compte.' },
},
newWindow: 'Nouvelle fenêtre',
},
footer: {
partners: { title: 'Nos partenaires' },
institutional: { externalLink: 'nouvelle fenêtre' },
display: "Paramètres d'affichage",
},
franceConnect: {
title: 'S’identifier avec',
link: { label: 'Qu’est-ce que FranceConnect', tooltip: 'Qu’est-ce que FranceConnect ? - nouvelle fenêtre' },
},
header: {
menu: { label: 'Menu' },
mainMenu: 'Menu principal',
},
link: {
newWindow: 'nouvelle fenêtre',
},
login: {
heading: 'Connexion à ',
franceConnect: {
heading: 'Se connecter avec FranceConnnect',
},
or: 'ou',
account: {
heading: 'Se connecter avec son compte',
email: { label: 'Identifiant' },
password: { label: 'Mot de passe' },
srOnly: { label: 'identifiants' },
rememberMe: { label: 'Se souvenir de moi' },
button: { label: 'Se connecter' },
},
noAccount: { heading: 'Vous n’avez pas de compte ?', button: { label: 'Créer un compte' } },
},
modal: {
ariaLabel: 'Fermer la fenêtre',
},
notice: {
close: 'Masquer le message',
},
pagination: {
firstPage: 'Première page',
previousPage: 'Page précédente',
nextPage: 'Page suivante',
lastPage: 'Dernière page',
role: 'navigation',
row: 'ligne',
ariaLabel: 'Pagination',
rowsPerPageLabel: 'Nombre de lignes par page',
},
password: {
aria: { label: 'Afficher le mot de passe' },
forgotPassword: 'Mot de passe oublié ?',
label: 'Mot de passe',
message: 'Votre mot de passe doit contenir :',
show: 'Afficher',
dataFrValid: 'validé',
dataFrError: 'en erreur',
invalidCriteria: 'critère non valide',
validCriteria: 'critère validé',
},
range: {
labelMin: 'Valeur minimale',
labelMax: 'Valeur maximale',
},
response: {
contactUs: 'Contactez-nous',
},
select: {
placeholder: 'Sélectionnez une option',
},
share: {
'heading': 'Partager la page',
'cookiesText': {
prelink: 'Veuillez',
link: 'autoriser le dépôt de cookies',
postlink: 'pour partager sur les réseaux sociaux.',
},
//
'copy': { title: 'Presse-papier', label: 'Copier dans le presse-papier' },
'facebook': { title: 'Facebook', label: 'Partager sur Facebook' },
'linkedin': { title: 'LinkedIn', label: 'Partager sur LinkedIn' },
'mail': { title: 'Email', label: 'Partager par email' },
'mastodon': { title: 'Mastodon', label: 'Partager sur Mastodon' },
'twitter': { title: 'X', label: 'Partager sur X' },
'twitter-x': { title: 'X', label: 'Partager sur X' },
},
sidemenu: {
menuMobile: 'Dans cette rubrique',
},
stepper: {
step: 'Étape',
on: 'sur',
of: 'sur',
nextStep: 'Étape suivante :',
},
table: {
noData: 'Aucune donnée',
selectRow: 'Sélectionner la ligne',
selectAll: 'Tout sélectionner',
unselectAll: 'Tout déselectionner',
row: 'ligne',
rowsPerPageLabel: 'Nombre de lignes par page',
noResult: 'Aucun résultat',
unselectable: 'Non sélectionnable',
noSelectedRows: 'Aucune ligne sélectionnée',
oneSelectedRow: 'ligne sélectionnée',
selectedRows: 'lignes sélectionnées',
sortExplanation: 'Les en-têtes de colonnes avec boutons sont triables',
sortTableBy: 'Trier par',
},
tag: {
removeFilter: 'Retirer le filtre',
removable: 'Supprimable',
selectFilter: 'Sélectionner le filtre',
selected: 'Sélectionné',
unselected: 'Non sélectionné',
},
tel: {
label: 'Numéro de téléphone',
hint: 'Format attendu : (+33) 1 23 45 67 89',
error: 'Le format de numéro de téléphone saisi n’est pas valide.',
},
toggle: {
dataLabelChecked: 'Activé',
dataLabelUnchecked: 'Désactivé',
},
tooltip: {
button: { label: 'Information contextuelle' },
},
transcription: {
button: 'Transcription',
ariaLabel: 'Agrandir la transcription',
},
translate: {
tooltip: 'Sélectionner une langue',
},
upload: {
label: 'Ajouter un fichier',
hint: "** Texte d'aide obligatoire qui précise les contraintes au niveau du ou des fichiers attendus **",
},
date: {
day: { label: 'Jour', hint: 'Exemple : 14' },
month: { label: 'Mois', hint: 'Exemple : 12' },
year: { label: 'Année', hint: 'Exemple : 1984' },
error: {
required: 'Tous les champs sont requis.',
invalid: {
format_day: 'Le champ jour doit être au format numérique.',
format_year: 'Le champ année doit être au format numérique.',
format_month: 'Le champ mois doit être au format numérique.',
day: 'Le jour est invalide.',
month: 'Le mois est invalide.',
date: 'La date est invalide.',
},
},
},
pageResponse: {
heading: 'Erreur inattendue',
description: 'Essayez de rafraichir la page ou bien ressayez plus tard.',
detail: 'Désolé, le service rencontre un problème, nous travaillons pour le résoudre le plus rapidement possible.',
},
name: {
legend: 'Nom',
country: {
label: 'Pays',
placeholder: 'Sélectionner une option',
},
lastname: 'Nom',
usualName: {
label: "Nom d'usage",
hint: 'Ancien nom...',
},
firstname: 'Prénom',
addFirstName: 'Ajouter un prénom',
legendFirstName: 'Prénom(s)',
noFirstName: "Je n'ai pas de prénom",
},
};
/**
* Fusionne récursivement deux objets sans perte de propriétés.
*
* ⚠️ Contrairement au spread operator `{ ...a, ...b }` :
* - Le spread réalise une fusion **superficielle** (shallow merge) :
* si une propriété est elle-même un objet, elle sera complètement écrasée
* par celle du second objet, entraînant la perte de ses sous-propriétés.
* - Cette fonction `deepMerge` effectue une fusion **profonde** (deep merge) :
* les sous-objets sont parcourus récursivement et leurs propriétés sont fusionnées,
* de sorte que les propriétés existantes sont conservées sauf si elles sont
* explicitement remplacées par `toMerge`.
*
* Exemple :
*
* ```ts
* const o1 = { a: { b: 'toto', c: 'tutu' } };
* const o2 = { a: { b: 'tata' } };
* ```
*
* si on procède avec le _spread operator_ :
*
* ```ts
* const o3 = { ...o1, ...o2 };
* ```
*
* alors `o3` contiendra en sortie `{ a: { b: 'tata' } }` la propriété `c` aura été supprimée
*
* en utilisant la méthode `deepMerge` :
*
* ```ts
* const o3 = deepMerge(o1, o2);
* ```
*
* alors `o3` contiendra en sortie `{ a: { b: 'tata', c: 'tutu' } }` la propriété `c` aura été préservée.
*
* @param reference l'objet de référence
* @param toMerge l'objet à fusionner
* @returns une nouvelle référence vers l'objet résultant de la fusion
*/
function i18nDeepMerge(reference, toMerge, options = {}) {
// on clone d'abord la référence en profondeur
const result = structuredClone(reference);
// on parcourt les clés de la structure à merger
for (const [key, toMergeValue] of Object.entries(toMerge)) {
const referenceValue = result[key];
result[key] = recursiveDeepMerge(key, referenceValue, toMergeValue, options);
}
return result;
}
function recursiveDeepMerge(property, reference, toMerge, options = {}) {
const { conflictStrategy = 'replace' } = options;
let result;
if (isStringValue(reference) && isStringValue(toMerge)) {
// cas 1 : deux strings ou tableaux de string : on écrase
result = toMerge;
}
else if (isStringArrayValue(reference) && isStringArrayValue(toMerge)) {
// cas 1 : deux strings ou tableaux de string : on écrase
result = [...toMerge];
}
else if (isRecordValue(reference) && isRecordValue(toMerge)) {
// cas 2 : deux objets : on fusionne récursivement
result = reference; // on peut faire ça directement car déja cloné à l'étape précédente
for (const key of Object.keys(toMerge)) {
result[key] = recursiveDeepMerge(key, reference[key], toMerge[key], options);
}
}
else {
// cas 3 : conflit : types différents
switch (conflictStrategy) {
case 'replace':
result = structuredClone(toMerge);
break;
case 'keep':
result = structuredClone(reference);
break;
case 'error':
throw new Error(`Types incompatibles sur la clé <${property}> : reference=${typeof reference}, toMerge=${typeof toMerge}`);
}
}
return result;
}
/** @internal */
function isStringValue(value) {
return typeof value === 'string';
}
/** @internal */
function isStringArrayValue(value) {
return Array.isArray(value) && value.every((item) => typeof item === 'string');
}
/** @internal */
function isRecordValue(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
class ConfigService {
/**
* Puisqu'il est possible que les utilisateurs de la bibliothèque oublient d'appeler forRoot(), nous marquons
* le jeton DI comme optionnel à l'aide du décorateur intégré (pour éviter une erreur "Aucun fournisseur pour ...").
* Dans ces cas, le paramètre constructeur sera nul, mais nous fournirons des valeurs par défaut.
*/
constructor(
// eslint-disable-next-line @angular-eslint/prefer-inject
params) {
this.params = params;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConfigService, deps: [{ token: DSFR_CONFIG_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConfigService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConfigService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [DSFR_CONFIG_TOKEN]
}] }] });
/** Levels used for identifying the severity of an event. Levels are organized from most specific to least. */
var DsfrLogLevel;
(function (DsfrLogLevel) {
DsfrLogLevel[DsfrLogLevel["OFF"] = 0] = "OFF";
DsfrLogLevel[DsfrLogLevel["ERROR"] = 2] = "ERROR";
DsfrLogLevel[DsfrLogLevel["WARN"] = 3] = "WARN";
DsfrLogLevel[DsfrLogLevel["INFO"] = 4] = "INFO";
DsfrLogLevel[DsfrLogLevel["DEBUG"] = 5] = "DEBUG";
DsfrLogLevel[DsfrLogLevel["ALL"] = 7] = "ALL";
})(DsfrLogLevel || (DsfrLogLevel = {}));
/** Les opérations de journalisation de la librairie sont effectuées via ce service. */
class LoggerService {
constructor() {
/** Indique à partir de quel niveau on effectue la journalisation, WARN par défaut. */
this._level = DsfrLogLevel.WARN;
}
set level(value) {
if (!value)
return;
this._level = value;
this.info(`Configuration du niveau de log : '${this.levelAsString()}'`);
}
error(message) {
this.log(DsfrLogLevel.ERROR, message);
}
warn(message) {
this.log(DsfrLogLevel.WARN, message);
}
info(message) {
this.log(DsfrLogLevel.INFO, message);
}
debug(message) {
this.log(DsfrLogLevel.DEBUG, message);
}
log(level, message) {
if (!isOnBrowser())
return;
if (level <= this._level) {
switch (level) {
case DsfrLogLevel.ERROR:
console.error(message);
break;
case DsfrLogLevel.WARN:
console.warn(message);
break;
case DsfrLogLevel.INFO:
console.info(message);
break;
default:
console.log(message);
break;
}
}
}
levelAsString() {
switch (this._level) {
case DsfrLogLevel.OFF:
return 'OFF';
case DsfrLogLevel.ERROR:
return 'ERROR';
case DsfrLogLevel.WARN:
return 'WARN';
case DsfrLogLevel.INFO:
return 'INFO';
case DsfrLogLevel.DEBUG:
return 'DEBUG';
case DsfrLogLevel.ALL:
return 'ALL';
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LoggerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LoggerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LoggerService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
/**
* Le service est chargé de maintenir le code langue de l'application et de signaler le changement de langue aux observateurs.
*/
class LangService {
static { this.DEF_LANG = 'fr'; }
// eslint-disable-next-line @angular-eslint/prefer-inject
constructor(logger) {
this.logger = logger;
/** Par défaut, le code de la langue est stocké dans le localStorage. */
this.hasLocalStorage = true;
/** Nom par défaut du local storage. */
this.localStorageName = 'lang';
this.langChange = new BehaviorSubject(LangService.DEF_LANG);
this.langChange$ = this.langChange.asObservable();
if (this.hasLocalStorage) {
const lang = LocalStorage.get(this.localStorageName);
if (lang)
this.lang = LocalStorage.get(this.localStorageName);
}
}
get lang() {
return this.langChange.value;
}
set lang(code) {
if (!code || code === this.lang)
return;
this.logger.info(`Changement de langue courante: '${code}'`);
if (this.hasLocalStorage)
LocalStorage.set(this.localStorageName, code);
this.langChange.next(code);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LangService, deps: [{ token: LoggerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LangService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: LangService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: LoggerService }] });
var Language;
(function (Language) {
Language["FR"] = "fr";
Language["EN"] = "en";
})(Language || (Language = {}));
/* eslint-disable @angular-eslint/prefer-inject */
/**
* Maintient le code langue de l'application et signale le changement de langue.
*/
class DsfrI18nService {
constructor(config, logger, langService) {
this.logger = logger;
this.langService = langService;
/**
* L'ensemble des bundles de libellés gérés par le service I18N.
*/
this.bundles = new Map();
this.completeLangChange = new BehaviorSubject(this.langService.lang);
this.completeLangChange$ = this.completeLangChange.asObservable();
this.bundles.set(Language.FR, LABELS_BUNDLE_FR);
this.bundles.set(Language.EN, LABELS_BUNDLE_EN);
this.processI18nConfig(config).subscribe({
// langChange$ est un BehaviorSubject, cela garantit ainsi qu'à la souscription, on aura une exécution de la
// méthode onLangChange avec la dernière valeur émise par langChange$, ceci même si la souscription intervient
// après la dernière émission de langChange$
complete: () => this.langService.langChange$.subscribe((code) => this.onLangChange(code)),
});
}
/**
* Retourne la valeur de traduction dans `messages` partir d'un path json.
* @param jsonPath le chemin dans le fichier de messages json de la langue courante
* @return le message et si non trouvé le path json
*/
t(jsonPath) {
return jsonPath2Value(this.currentBundle, jsonPath) ?? this.messageNotFound(this.langService.lang, jsonPath);
}
/**
* Permet d'étendre les ressources d'internationalisation sur un périmètre de langue défini.
*
* @param lang Le code langue sur deux caractères en minuscules.
* @param extension Le bundle contenant l'ensemble des libellés traduits dans la langue indiquée.
*/
extendsLabelsBundle(lang, extension) {
const locale = lang.toLowerCase();
if (locale.length !== 2) {
this.logger.warn(`Bundle <${locale}> ignoré : le code langue n'est pas indiqué sur deux caractères`);
return;
}
const existing = this.bundles.get(lang);
if (existing) {
const extendedBundle = i18nDeepMerge(existing, extension, { conflictStrategy: 'replace' });
this.bundles.set(lang, extendedBundle);
// si on modifie le bundle de la langue courante, il faut mettre à jour le bundle courant et notifier
if (this.langService.lang === lang) {
this.currentBundle = extendedBundle;
this.completeLangChange.next(lang);
}
}
else {
this.logger.warn(`Aucun bundle trouvé pour le code langue <${lang}>`);
}
}
/**
* Charge le bon fichier de messages selon le code langue, messages en anglais si non trouvé.
*/
onLangChange(code) {
this.currentBundle = this.bundles?.get(code) ?? this.bundles?.get(Language.EN) ?? LABELS_BUNDLE_EN;
this.completeLangChange.next(code);
}
/**
* Si message non trouvé, retourne le message en anglais pour les langues autres que les langues par défaut.
*/
messageNotFound(codeLang, jsonPath) {
this.logger.warn(`Aucune traduction '${codeLang}' pour '${jsonPath}'`);
// try fallback to english
const englishFallback = codeLang !== Language.EN ? jsonPath2Value(this.bundles.get(Language.EN), jsonPath) : null;
return englishFallback ?? jsonPath;
}
processI18nConfig(config) {
// on traite la nouvelle propriété en premier pour lui donner la priorité
if (config?.i18n?.labelsBundles) {
return new Observable((subscriber) => {
for (const [lang, bundle] of Object.entries(config.i18n.labelsBundles)) {
this.bundles.set(lang, bundle);
}
subscriber.complete();
});
}
// traitement de la propriété dépréciée
if (config?.i18n?.messagesFiles) {
return this.readMessagesFiles(config.i18n.messagesFiles);
}
return EMPTY;
}
/**
* Lit tous les fichiers de la configuration
* @param i18nconfig Configuration i18n
* @return événement complet sur l'observable quand tous les fichiers ont été lus, erreur ou non
*/
readMessagesFiles(messagesFiles) {
const all = [];
messagesFiles.forEach((mf) => {
const observable = fetchFile(mf.filePath);
observable.subscribe({
next: (data) => {
console.info(`Lecture du fichier ${mf.filePath}`);
this.bundles.set(mf.code, data);
},
error: (err) => {
console.error(`Erreur ${err} dans la lecture du fichier ${mf.filePath}`);
},
});
all.push(observable);
});
return new Observable((subscriber) => {
merge(...all).subscribe({
complete: () => subscriber.complete(),
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nService, deps: [{ token: DSFR_CONFIG_TOKEN }, { token: LoggerService }, { token: LangService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [DSFR_CONFIG_TOKEN]
}] }, { type: LoggerService }, { type: LangService }] });
/**
* Lecture d'un fichier sur le serveur
* @param filePath Chemin partiel du fichier
* @param contentType text par défaut
* @return Observable
*/
function fetchFile(filePath, contentType = 'text/plain') {
const myHeaders = new Headers();
myHeaders.append('Content-Type', contentType);
const myConfig = { headers: myHeaders };
return new Observable((subscriber) => {
const fetchObs = fromFetch(filePath, myConfig).subscribe({
next: (response) => {
if (response.ok) {
response.json().then((data) => {
subscriber.next(data);
subscriber.complete();
});
}
else {
subscriber.error(response.status);
subscriber.complete();
}
},
error: (err) => subscriber.error(err),
});
});
}
class DsfrI18nPipe {
constructor() {
this.value = '';
this.lastKey = null;
this.langChangeSub$ = null;
this.i18nService = inject(DsfrI18nService);
this.cdr = inject(ChangeDetectorRef);
}
transform(jsonPath) {
if (!jsonPath || jsonPath.length === 0) {
return jsonPath;
}
// Utiliser le cache si pas de changement de langue
if (this.lastKey === jsonPath) {
return this.value;
}
this.lastKey = jsonPath; // Maj du cache
this.dispose(); // necessaire dans le cas de pipe impur
this.langChangeSub$ = this.i18nService.completeLangChange$.subscribe(() => {
if (this.lastKey) {
this.value = this.i18nService.t(this.lastKey);
this.cdr.markForCheck(); // pour composants OnPush
}
});
return this.value;
}
ngOnDestroy() {
this.dispose();
}
dispose() {
this.langChangeSub$?.unsubscribe();
// Note : Il est important de nettoyer la référence fournit dans langChangeSub$
// RxJS garde une référence au callback dans sa gestion interne, ce qui peut empêcher le nettoyage en mémoire.
this.langChangeSub$ = null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nPipe, isStandalone: true, name: "dsfrI18n", pure: false }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrI18nPipe, decorators: [{
type: Pipe,
args: [{
name: 'dsfrI18n',
standalone: true,
pure: false,
}]
}] });
class DsfrAccordionComponent {
constructor() {
/**
* Indique si l'accordéon doit être ouvert initialement.
*/
this.expanded = false;
/**
* Le niveau du titre dans la structure, ne change pas l'apparence, `<h3>` par défaut.
*/
this.headingLevel = DsfrHeadingLevelConst.H3;
/**
* Événement présentant le nouvel état de l'accordéon.
*/
this.expandedChange = new EventEmitter();
this._unlisten = [];
this.renderer2 = inject(Renderer2);
}
ngOnInit() {
this.accordionId = newUniqueId();
}
/**
* Ecouter les events scripts dsfr conceal/disclose sur l'accordéon
*/
ngAfterViewInit() {
this._unlisten.push(this.renderer2.listen(this.accordion.nativeElement, 'dsfr.conceal', (e) => {
// requestAnimationFrame > permet d'améliorer la cohabitation avec l'instrumentation DSFR (le cas échéant)
// cf. https://github.com/GouvernementFR/dsfr/issues/1099
window.requestAnimationFrame(() => {
if (this.expanded && e.target === this.accordion.nativeElement) {
this.expanded = false;
this.expandedChange.emit(this.expanded);
}
});
}), this.renderer2.listen(this.accordion.nativeElement, 'dsfr.disclose', (e) => {
window.requestAnimationFrame(() => {
if (!this.expanded && e.target === this.accordion.nativeElement) {
this.expanded = true;
this.expandedChange.emit(this.expanded);
}
});
}));
}
ngOnDestroy() {
this._unlisten.forEach((unlistenFunc) => unlistenFunc());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrAccordionComponent, isStandalone: true, selector: "dsfr-accordion", inputs: { content: "content", expanded: "expanded", heading: "heading", headingLevel: "headingLevel", index: "index", headingAriaLabel: "headingAriaLabel" }, outputs: { expandedChange: "expandedChange" }, viewQueries: [{ propertyName: "accordion", first: true, predicate: ["dsfrAccordion"], descendants: true }], ngImport: i0, template: "<section class=\"fr-accordion\">\n <!-- Titre obligatoire -->\n <edu-heading [level]=\"headingLevel\" defaultLevel=\"H3\" customClass=\"fr-accordion__title\">\n <ng-container *ngTemplateOutlet=\"accordionButton\"></ng-container>\n </edu-heading>\n\n <div #dsfrAccordion class=\"fr-collapse\" [id]=\"accordionId\">\n <!-- La propri\u00E9t\u00E9 content est prioritaire sur le slot -->\n @if (content) {\n {{ content }}\n } @else {\n <ng-content></ng-content>\n <!-- TODO @deprecated \u00E0 supprimer en v2 au profit du slot par d\u00E9faut -->\n <ng-content select=\"[content]\"></ng-content>\n <!-- TODO @deprecated \u00E0 supprimer en v2 au profit du slot par d\u00E9faut -->\n <ng-content select=\"[body]\"></ng-content>\n }\n </div>\n</section>\n\n<ng-template #accordionButton>\n <button\n type=\"button\"\n class=\"fr-accordion__btn\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"accordionId\"\n [attr.aria-label]=\"headingAriaLabel\">\n <!-- La propri\u00E9t\u00E9 heading est prioritaire sur le slot de m\u00EAme nom -->\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-content select=\"[heading]\"></ng-content>\n }\n </button>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-accordion', encapsulation: ViewEncapsulation.None, imports: [CommonModule, HeadingComponent], template: "<section class=\"fr-accordion\">\n <!-- Titre obligatoire -->\n <edu-heading [level]=\"headingLevel\" defaultLevel=\"H3\" customClass=\"fr-accordion__title\">\n <ng-container *ngTemplateOutlet=\"accordionButton\"></ng-container>\n </edu-heading>\n\n <div #dsfrAccordion class=\"fr-collapse\" [id]=\"accordionId\">\n <!-- La propri\u00E9t\u00E9 content est prioritaire sur le slot -->\n @if (content) {\n {{ content }}\n } @else {\n <ng-content></ng-content>\n <!-- TODO @deprecated \u00E0 supprimer en v2 au profit du slot par d\u00E9faut -->\n <ng-content select=\"[content]\"></ng-content>\n <!-- TODO @deprecated \u00E0 supprimer en v2 au profit du slot par d\u00E9faut -->\n <ng-content select=\"[body]\"></ng-content>\n }\n </div>\n</section>\n\n<ng-template #accordionButton>\n <button\n type=\"button\"\n class=\"fr-accordion__btn\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"accordionId\"\n [attr.aria-label]=\"headingAriaLabel\">\n <!-- La propri\u00E9t\u00E9 heading est prioritaire sur le slot de m\u00EAme nom -->\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-content select=\"[heading]\"></ng-content>\n }\n </button>\n</ng-template>\n" }]
}], propDecorators: { content: [{
type: Input
}], expanded: [{
type: Input
}], heading: [{
type: Input
}], headingLevel: [{
type: Input
}], index: [{
type: Input
}], headingAriaLabel: [{
type: Input
}], expandedChange: [{
type: Output
}], accordion: [{
type: ViewChild,
args: ['dsfrAccordion']
}] } });
class DsfrAccordionModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionModule, imports: [CommonModule, HeadingComponent, DsfrAccordionComponent], exports: [DsfrAccordionComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionModule, imports: [CommonModule, HeadingComponent, DsfrAccordionComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrAccordionComponent],
imports: [CommonModule, HeadingComponent, DsfrAccordionComponent],
}]
}] });
class DsfrAccordionsGroupComponent {
constructor() {
/** Dissocie le groupe d'accordéon pour en avoir plusieurs d'ouvert à la fois.*/
this.ungroup = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrAccordionsGroupComponent, isStandalone: true, selector: "dsfr-accordions-group", inputs: { ungroup: "ungroup" }, ngImport: i0, template: "<div [attr.data-fr-group]=\"ungroup ? false : null\" class=\"fr-accordions-group\">\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-accordions-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<div [attr.data-fr-group]=\"ungroup ? false : null\" class=\"fr-accordions-group\">\n <ng-content></ng-content>\n</div>\n" }]
}], propDecorators: { ungroup: [{
type: Input
}] } });
class DsfrAccordionsGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupModule, imports: [CommonModule, DsfrAccordionsGroupComponent], exports: [DsfrAccordionsGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupModule, imports: [CommonModule, DsfrAccordionsGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAccordionsGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrAccordionsGroupComponent],
imports: [CommonModule, DsfrAccordionsGroupComponent],
}]
}] });
/**
* Définit les tailles d'alerte supportées par le DSFR.
*/
var DsfrAlertSizeConst;
(function (DsfrAlertSizeConst) {
/**
* A utiliser pour une alerte de taille réduite.
*/
DsfrAlertSizeConst["SM"] = "SM";
/**
* A utiliser pour une alerte de taille standard (par défaut).
*/
DsfrAlertSizeConst["MD"] = "MD";
})(DsfrAlertSizeConst || (DsfrAlertSizeConst = {}));
class DsfrAlertComponent {
constructor() {
/** La valeur à utiliser pour le rôle ARIA. 'alert' par défaut. */
this.ariaRoleValue = 'alert';
/** Doit être vrai si l'alerte apparait dynamiquement en cours de navigation. (ajout de role=alert pour l'accessibilité),
* false par défaut */
// https://www.systeme-de-design.gouv.fr/version-courante/fr/composants/alerte
// Pour les développeurs.
// Nous avons retiré l’attribut role="alert" des exemples de code pour les alertes présentes au chargement de la page.
// En effet, l'élément avec un role="alert" sont les premières choses lues par les technologies d'assistance.
// De ce fait, le role="alert" doit être réservé aux alertes ajoutées (/injectées) dynamiquement au cours de la navigation.
// Ex : alertes de notification suite à une action utilisateur ou mise à jour d’un statut.
this.hasAriaRole = false;
/** L'alerte peut être masquée */
this.closeable = false;
/** Signale la fermeture de l'alerte. */
this.conceal = new EventEmitter();
this.containerSlotHeading = viewChild('containerSlotHeading', ...(ngDevMode ? [{ debugName: "containerSlotHeading", read: ElementRef }] : [{ read: ElementRef }]));
this.isNgContentEmpty = signal(false, ...(ngDevMode ? [{ debugName: "isNgContentEmpty" }] : []));
this.el = inject(ElementRef);
this.renderer = inject(Renderer2);
this.severity = DsfrSeverityConst.INFO;
this.alertSize = DsfrAlertSizeConst.MD;
}
/**
* La taille de l'alerte. 'MD' par défaut.
*
* @deprecated since(1.15.0) utiliser 'alertSize' à la place
*/
get size() {
return this.alertSize;
}
/** @deprecated (since 1.7.0) use 'closeable' instead (avec un 'e') */
get closable() {
return this.closeable;
}
/**
* La taille de l'alerte. 'MD' par défaut.
*
* @deprecated since(1.15.0) utiliser 'alertSize' à la place
*/
set size(value) {
this.alertSize = value;
}
/** @deprecated (since 1.7.0) utiliser `closeable` à la place (avec un 'e') */
set closable(value) {
this.closeable = value;
}
/**
* Supprimer le container <p> du titre si le contenu projeté heading est vide
* ok a l'initialisation la balise sera bien supprimé.
* mais si du contenu est ajouté dynamiquement dans le slot du heading alors qu'il est vide a l'init, le heading ne
* sera pas projeté. Solution temporaire pour ce cas précis : initialiser avec une valeur vide type <span></span>
* */
ngAfterViewInit() {
if (this.isSmall() && !this.heading) {
const hasContent = this.hasContent(this.containerSlotHeading()?.nativeElement);
this.isNgContentEmpty.set(!hasContent);
}
}
getClasses() {
return {
'fr-alert': true,
'fr-alert--error': this.severity === DsfrSeverityConst.ERROR,
'fr-alert--success': this.severity === DsfrSeverityConst.VALID || this.severity === DsfrSeverityConst.SUCCESS,
'fr-alert--info': this.severity === DsfrSeverityConst.INFO,
'fr-alert--warning': this.severity === DsfrSeverityConst.WARNING,
'fr-alert--sm': this.alertSize === DsfrSizeConst.SM,
};
}
isSmall() {
return this.alertSize == DsfrSizeConst.SM;
}
onClose(event) {
const parentNode = this.el.nativeElement?.parentNode;
if (parentNode) {
this.renderer.removeChild(parentNode, this.el.nativeElement, true);
}
this.conceal.emit();
}
/**
* Test si le container doit être affiché (contenu visible)
* @returns true si le container possède au moins un noeuf enfant ou du contenu de texte (exclure les commentaires et chaine vide)
*/
hasContent(container) {
return !!(container?.children?.length > 0 || (container?.textContent && container.textContent !== ' '));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrAlertComponent, isStandalone: true, selector: "dsfr-alert", inputs: { heading: "heading", headingLevel: "headingLevel", message: "message", severity: "severity", ariaRoleValue: "ariaRoleValue", alertSize: "alertSize", closeControlLabel: "closeControlLabel", hasAriaRole: "hasAriaRole", closeable: "closeable", size: "size", closable: "closable" }, outputs: { conceal: "conceal" }, viewQueries: [{ propertyName: "containerSlotHeading", first: true, predicate: ["containerSlotHeading"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div [attr.role]=\"hasAriaRole ? ariaRoleValue : null\" [class]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n <!-- La propri\u00E9t\u00E9 message est prioritaire sur le slot -->\n @if (message) {\n <p>{{ message }}</p>\n } @else {\n <ng-content select=\"[message]\"></ng-content>\n }\n @if (closeable) {\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [title]=\"closeControlLabel ?? ('alert.close' | dsfrI18n)\"\n (click)=\"onClose($event)\">\n {{ closeControlLabel ?? ('alert.close' | dsfrI18n) }}\n </button>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n<ng-template #headingTemplate>\n <!--FIXME v2 : supprimer les innerHTML -->\n @switch (headingLevel) {\n @case ('H2') {\n @if (heading) {\n <h2 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h2>\n } @else {\n <h2 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h2>\n }\n }\n @case ('H3') {\n @if (heading) {\n <h3 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h3>\n } @else {\n <h3 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h3>\n }\n }\n @case ('H4') {\n @if (heading) {\n <h4 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h4>\n } @else {\n <h4 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h4>\n }\n }\n @case ('H5') {\n @if (heading) {\n <h5 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h5>\n } @else {\n <h5 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h5>\n }\n }\n @case ('H6') {\n @if (heading) {\n <h6 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h6>\n } @else {\n <h6 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h6>\n }\n }\n @default {\n @if (heading) {\n <p class=\"fr-alert__title\" [innerHTML]=\"heading\"></p>\n } @else {\n <p #containerSlotHeading *ngIf=\"!isNgContentEmpty()\" class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </p>\n }\n }\n }\n</ng-template>\n<ng-template #headingPropTemplate>\n <ng-content></ng-content>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-alert', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrI18nPipe], template: "<div [attr.role]=\"hasAriaRole ? ariaRoleValue : null\" [class]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n <!-- La propri\u00E9t\u00E9 message est prioritaire sur le slot -->\n @if (message) {\n <p>{{ message }}</p>\n } @else {\n <ng-content select=\"[message]\"></ng-content>\n }\n @if (closeable) {\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [title]=\"closeControlLabel ?? ('alert.close' | dsfrI18n)\"\n (click)=\"onClose($event)\">\n {{ closeControlLabel ?? ('alert.close' | dsfrI18n) }}\n </button>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n<ng-template #headingTemplate>\n <!--FIXME v2 : supprimer les innerHTML -->\n @switch (headingLevel) {\n @case ('H2') {\n @if (heading) {\n <h2 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h2>\n } @else {\n <h2 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h2>\n }\n }\n @case ('H3') {\n @if (heading) {\n <h3 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h3>\n } @else {\n <h3 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h3>\n }\n }\n @case ('H4') {\n @if (heading) {\n <h4 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h4>\n } @else {\n <h4 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h4>\n }\n }\n @case ('H5') {\n @if (heading) {\n <h5 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h5>\n } @else {\n <h5 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h5>\n }\n }\n @case ('H6') {\n @if (heading) {\n <h6 class=\"fr-alert__title\" [innerHTML]=\"heading\"></h6>\n } @else {\n <h6 class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </h6>\n }\n }\n @default {\n @if (heading) {\n <p class=\"fr-alert__title\" [innerHTML]=\"heading\"></p>\n } @else {\n <p #containerSlotHeading *ngIf=\"!isNgContentEmpty()\" class=\"fr-alert__title\">\n <ng-container *ngTemplateOutlet=\"headingPropTemplate\"></ng-container>\n </p>\n }\n }\n }\n</ng-template>\n<ng-template #headingPropTemplate>\n <ng-content></ng-content>\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], message: [{
type: Input
}], severity: [{
type: Input
}], ariaRoleValue: [{
type: Input
}], alertSize: [{
type: Input
}], closeControlLabel: [{
type: Input
}], hasAriaRole: [{
type: Input
}], closeable: [{
type: Input
}], conceal: [{
type: Output
}], containerSlotHeading: [{ type: i0.ViewChild, args: ['containerSlotHeading', { ...{ read: ElementRef }, isSignal: true }] }], size: [{
type: Input
}], closable: [{
type: Input
}] } });
class DsfrAlertModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertModule, imports: [CommonModule, DsfrI18nPipe, DsfrAlertComponent], exports: [DsfrAlertComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertModule, imports: [CommonModule, DsfrAlertComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAlertModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrAlertComponent],
imports: [CommonModule, DsfrI18nPipe, DsfrAlertComponent],
}]
}] });
class DsfrButtonComponent {
constructor() {
/** Type du button, `button` par défaut. */
this.type = DsfrButtonTypeConst.BUTTON;
/** Style du bouton, `primary` par défaut. */
this.variant = DsfrButtonVariantConst.PRIMARY;
/**
* Taille du bouton, `MD` par défaut
*/
this.buttonSize = DsfrSizeConst.MD;
/** Permet de désactiver le bouton d'action, `false` par défaut. */
this.disabled = false;
/**
* Permet de passer le libellé du bouton en majuscules, 'false' par défaut.
*
* @deprecated (since 1.1.0)
*
* @internal
*/
this.uppercase = false;
/**
* Permet d'activer un visuel dénotant un "chargement" (busy), 'false' par défaut.
*
* @deprecated (since 1.1.0)
*
* @internal
*/
this.loader = false;
/** Permet d'inverser le contraste du marqueur de focus, `false` par défaut. */
this.invertedOutlineContrast = false;
/**
* Identifiant de l'élément `<button>`
*/
this.buttonId = newUniqueId();
/**
* Permet de définir le role donné à l'élément `button`
* @since 1.15.0
*/
this.buttonRole = 'button';
/**
* Permet de masquer visuellement le libellé en le laissant accessible aux dispositifs d'assistance.
*
* A utiliser pour les boutons avec icône seule.
*
* @since 1.15.0
*/
this.labelSrOnly = false;
this.logger = inject(LoggerService);
}
/**
* Taille du bouton, `MD` par défaut
*
* @deprecated since(1.15.0) utiliser 'buttonSize' à la place
*/
get size() {
return this.buttonSize;
}
/**
* Taille du bouton, `MD` par défaut
*
* @deprecated since(1.15.0) utiliser 'buttonSize' à la place
*/
set size(value) {
this.buttonSize = value;
}
ngOnInit() {
if (!this.label && !this.ariaLabel && !this.tooltipMessage) {
this.logger.warn('dsfr-button: le composant doit obligatoirement contenir un label, un aria-label ou un tooltipMessage');
}
}
buttonClasses() {
const hasIcon = !!this.icon;
const hasTextVisible = !!this.label && !this.labelSrOnly;
const classes = {
'fr-btn': true,
'fr-btn--secondary': this.variant === DsfrButtonVariantConst.SECONDARY,
'fr-btn--tertiary': this.variant === DsfrButtonVariantConst.TERTIARY,
'fr-btn--tertiary-no-outline': this.variant === DsfrButtonVariantConst.TERTIARY_NO_OUTLINE,
'fr-btn--icon-left': hasIcon && hasTextVisible && this.iconPosition !== DsfrPositionConst.RIGHT,
'fr-btn--icon-right': hasIcon && hasTextVisible && this.iconPosition === DsfrPositionConst.RIGHT,
'fr-btn--sm': this.buttonSize === DsfrSizeConst.SM,
'fr-btn--lg': this.buttonSize === DsfrSizeConst.LG,
// 'déprécié'
'uppercase': !!this.uppercase,
'inverted-outline-contrast': !!this.invertedOutlineContrast,
};
if (this.icon) {
classes[this.icon] = true;
}
return classes;
}
customClasses() {
return this.customClass ?? '';
}
/**
* Empêche que l'appuie sur la barre Espace soit considéré comme un clic si le rôle du button est 'link'
*/
handleSpaceKeyClick(evt) {
if (this.buttonRole === 'link') {
evt.preventDefault();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrButtonComponent, isStandalone: true, selector: "dsfr-button", inputs: { label: "label", type: "type", tooltipMessage: "tooltipMessage", variant: "variant", buttonSize: "buttonSize", icon: "icon", iconPosition: "iconPosition", disabled: "disabled", uppercase: "uppercase", loader: "loader", ariaLabel: "ariaLabel", invertedOutlineContrast: "invertedOutlineContrast", id: "id", buttonId: "buttonId", ariaControls: "ariaControls", ariaPressed: "ariaPressed", ariaHasPopup: "ariaHasPopup", ariaExpanded: ["ariaExpanded", "ariaExpanded", booleanAttribute], ariaHidden: ["ariaHidden", "ariaHidden", booleanAttribute], tabIndex: "tabIndex", customClass: "customClass", buttonRole: "buttonRole", labelSrOnly: ["labelSrOnly", "labelSrOnly", booleanAttribute], size: "size" }, ngImport: i0, template: "<button\n [attr.id]=\"buttonId\"\n [ngClass]=\"buttonClasses()\"\n [class]=\"customClasses()\"\n [attr.type]=\"type\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled || null\"\n [attr.role]=\"buttonRole === 'button' ? null : buttonRole\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-pressed]=\"ariaPressed === undefined ? null : ariaPressed\"\n [attr.aria-expanded]=\"ariaExpanded === undefined ? null : ariaExpanded\"\n [attr.aria-haspopup]=\"ariaHasPopup || null\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-hidden]=\"ariaHidden || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.tabindex]=\"tabIndex === undefined ? null : tabIndex\"\n (keydown.Space)=\"handleSpaceKeyClick($event)\">\n @if (label) {\n {{ label }}\n }\n</button>\n", styles: [".uppercase{text-transform:uppercase}button.inverted-outline-contrast:focus-visible{outline-color:var(--grey-950-100)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-button', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<button\n [attr.id]=\"buttonId\"\n [ngClass]=\"buttonClasses()\"\n [class]=\"customClasses()\"\n [attr.type]=\"type\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled || null\"\n [attr.role]=\"buttonRole === 'button' ? null : buttonRole\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-pressed]=\"ariaPressed === undefined ? null : ariaPressed\"\n [attr.aria-expanded]=\"ariaExpanded === undefined ? null : ariaExpanded\"\n [attr.aria-haspopup]=\"ariaHasPopup || null\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-hidden]=\"ariaHidden || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.tabindex]=\"tabIndex === undefined ? null : tabIndex\"\n (keydown.Space)=\"handleSpaceKeyClick($event)\">\n @if (label) {\n {{ label }}\n }\n</button>\n", styles: [".uppercase{text-transform:uppercase}button.inverted-outline-contrast:focus-visible{outline-color:var(--grey-950-100)}\n"] }]
}], propDecorators: { label: [{
type: Input
}], type: [{
type: Input
}], tooltipMessage: [{
type: Input
}], variant: [{
type: Input
}], buttonSize: [{
type: Input
}], icon: [{
type: Input
}], iconPosition: [{
type: Input
}], disabled: [{
type: Input
}], uppercase: [{
type: Input
}], loader: [{
type: Input
}], ariaLabel: [{
type: Input
}], invertedOutlineContrast: [{
type: Input
}], id: [{
type: Input
}], buttonId: [{
type: Input
}], ariaControls: [{
type: Input
}], ariaPressed: [{
type: Input
}], ariaHasPopup: [{
type: Input
}], ariaExpanded: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], ariaHidden: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], tabIndex: [{
type: Input
}], customClass: [{
type: Input
}], buttonRole: [{
type: Input
}], labelSrOnly: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], size: [{
type: Input
}] } });
class DsfrButtonModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonModule, imports: [CommonModule, FormsModule, DsfrButtonComponent], exports: [DsfrButtonComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonModule, imports: [CommonModule, FormsModule, DsfrButtonComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, FormsModule, DsfrButtonComponent],
exports: [DsfrButtonComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}]
}] });
class DsfrLinkComponent {
constructor() {
/**
* Permet de désactiver le composant.
*/
this.disabled = false;
/** Position de l'icône. À droite par défaut. */
this.iconPosition = DsfrPositionConst.RIGHT;
/** Texte du lien simple. Sinon utiliser un slot [label].*/
this.label = '';
/** Taille du lien. */
this.linkSize = DsfrSizeConst.MD;
/**
* Si l'input 'route' est renseigné, sa valeur sera émise lorsque le lien est sélectionné.
*/
this.linkSelect = new EventEmitter();
this.externalLink = false;
this.i18n = inject(DsfrI18nService);
this._sanitizer = inject(DomSanitizer);
}
/** Lien href externe, exclusif avec route et routePath. */
get link() {
return this._link;
}
/**
* Taille du lien.
*
* @deprecated since(1.15.0) utiliser 'linkSize' à la place
*/
get size() {
return this.linkSize;
}
/** @deprecated (since 1.5) utiliser `linkTarget` à la place. */
get targetLink() {
return this.linkTarget;
}
/** Lien href externe, exclusif avec route et routePath. */
set link(value) {
this._link = value;
if (this.link) {
this._sanitizedLink = this._sanitizer.sanitize(SecurityContext.URL, this.link);
}
}
/**
* Taille du lien.
*
* @deprecated since(1.15.0) utiliser 'linkSize' à la place
*/
set size(value) {
this.linkSize = value;
}
/** @deprecated (since 1.5) utiliser `linkTarget` à la place. */
set targetLink(value) {
this.linkTarget = value;
}
/**
* Activation du router Angular.
*
* L'usage de routerLink provoque un bug accessibilité sur la navigation au clavier (ajout d'un tabindex=0).
*
* @deprecated (since 1.11.5) utiliser `routePath` à la place.
*/
set routerLink(value) {
if (value)
this.routePath = value;
}
ngOnInit() {
if (this.linkTarget === DsfrLinkTargetConst.BLANK) {
this.externalLink = true;
this.tooltipMessage = this.tooltipMessage
? `${this.tooltipMessage} - ${this.i18n.t('link.newWindow')}`
: `${this.i18n.t('link.newWindow')}`;
// Ajout de l'information newWindow en sr-only en plus du title pour a11y (/!\ fix différent du DSFR)
if (this.label) {
this.label += `<span class="fr-sr-only">${this.i18n.t('link.newWindow')}</span>`;
}
}
}
/** @internal */
getHref() {
return this.disabled ? undefined : this._sanitizedLink || this.route;
}
/** @internal */
hasRouterLink() {
return !this.disabled && !!this.routePath;
}
/**
* DsfrLink est partagé par plusieurs composants, 'tag', 'card' et 'tile' à la place d'un simple lien.
* Cela permet de bénéficier des nombreuses caractéristiques du composant DsfrLink.
*
* @since 1.5 Cependant, la classe 'fr-link' ne doit pas cohabiter les classes de ces composants ('fr-tag', ...)
*
* @internal
*/
getClass() {
let classes = '';
if (!this.customClass && !this.disabled)
classes += 'fr-link';
if (this.icon && this.iconPosition === DsfrPositionConst.RIGHT)
classes += ' fr-link--icon-right';
if (this.icon && this.iconPosition === DsfrPositionConst.LEFT)
classes += ' fr-link--icon-left';
if (this.linkSize === DsfrSizeConst.SM)
classes += ' fr-link--sm';
if (this.linkSize === DsfrSizeConst.LG)
classes += ' fr-link--lg';
if (this.icon)
classes += ' ' + this.icon;
if (this.customClass)
classes += ' ' + this.customClass;
return classes;
}
/** @internal */
getButtonClasses() {
const classes = ['fr-btn', 'fr-btn--tertiary-no-outline'];
if (this.customClass)
classes.push(this.customClass);
if (this.linkSize === DsfrSizeConst.SM)
classes.push('fr-btn--sm');
if (this.linkSize === DsfrSizeConst.LG)
classes.push('fr-btn--lg');
if (this.icon) {
classes.push(this.icon);
classes.push(this.iconPosition === DsfrPositionConst.RIGHT ? 'fr-btn--icon-right' : 'fr-btn--icon-left');
}
return classes.join(' ');
}
/** @internal */
onLink(event) {
if (this.disabled)
return;
if (this.route && !this.routePath) {
event.preventDefault();
this.linkSelect.emit(this.route);
}
else if (this.link) {
this.linkSelect.emit(this.link);
}
else {
this.linkSelect.emit(this.linkId ?? this.label);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrLinkComponent, isStandalone: true, selector: "dsfr-link", inputs: { ariaCurrent: "ariaCurrent", ariaLabel: "ariaLabel", ariaControls: "ariaControls", customClass: "customClass", disabled: "disabled", icon: "icon", iconPosition: "iconPosition", linkId: "linkId", label: "label", linkTarget: "linkTarget", route: "route", routePath: "routePath", routerLinkActive: "routerLinkActive", routerLinkActiveOptions: "routerLinkActiveOptions", routerLinkExtras: "routerLinkExtras", linkSize: "linkSize", tooltipMessage: "tooltipMessage", mode: "mode", ariaLabelledBy: "ariaLabelledBy", ariaDescribedBy: "ariaDescribedBy", ariaCurrentWhenActive: "ariaCurrentWhenActive", link: "link", size: "size", targetLink: "targetLink", routerLink: "routerLink" }, outputs: { linkSelect: "linkSelect" }, ngImport: i0, template: "<!-- Lien externe ou g\u00E9r\u00E9 programmatiquement -->\n<!--FIXME : obligation de dupliquer ici pour g\u00E9rer a la fois innerHTML (/!\\ pas de outerHTML!) et la pr\u00E9sence du slot.\n voir pour fix en angular 19 d\u00E9tection d'un contenu dans le slot ou suppression de innerHTML en v2 -->\n@if (!hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Lien externe et label en innerHTML-->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Lien externe et slot pour le label -->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Routerlink et label en innerHTML-->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Routerlink et slot pour le label -->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (mode === 'button') {\n @if (label) {\n <!-- Bouton et label en innerHTML-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\"></button>\n } @else {\n <!-- Bouton et slot pour le label-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </button>\n }\n}\n\n<!-- D\u00E9clar\u00E9 dans un template : le ng-content ne doit \u00EAtre pr\u00E9sent qu'une seule fois -->\n<ng-template #slot>\n <ng-content></ng-content>\n @if (externalLink) {\n <span class=\"fr-sr-only\"> {{ i18n.t('link.newWindow') }} </span>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-link', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "<!-- Lien externe ou g\u00E9r\u00E9 programmatiquement -->\n<!--FIXME : obligation de dupliquer ici pour g\u00E9rer a la fois innerHTML (/!\\ pas de outerHTML!) et la pr\u00E9sence du slot.\n voir pour fix en angular 19 d\u00E9tection d'un contenu dans le slot ou suppression de innerHTML en v2 -->\n@if (!hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Lien externe et label en innerHTML-->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Lien externe et slot pour le label -->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Routerlink et label en innerHTML-->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Routerlink et slot pour le label -->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (mode === 'button') {\n @if (label) {\n <!-- Bouton et label en innerHTML-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\"></button>\n } @else {\n <!-- Bouton et slot pour le label-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </button>\n }\n}\n\n<!-- D\u00E9clar\u00E9 dans un template : le ng-content ne doit \u00EAtre pr\u00E9sent qu'une seule fois -->\n<ng-template #slot>\n <ng-content></ng-content>\n @if (externalLink) {\n <span class=\"fr-sr-only\"> {{ i18n.t('link.newWindow') }} </span>\n }\n</ng-template>\n" }]
}], propDecorators: { ariaCurrent: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaControls: [{
type: Input
}], customClass: [{
type: Input
}], disabled: [{
type: Input
}], icon: [{
type: Input
}], iconPosition: [{
type: Input
}], linkId: [{
type: Input
}], label: [{
type: Input
}], linkTarget: [{
type: Input
}], route: [{
type: Input
}], routePath: [{
type: Input
}], routerLinkActive: [{
type: Input
}], routerLinkActiveOptions: [{
type: Input
}], routerLinkExtras: [{
type: Input
}], linkSize: [{
type: Input
}], tooltipMessage: [{
type: Input
}], mode: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], ariaDescribedBy: [{
type: Input
}], ariaCurrentWhenActive: [{
type: Input
}], linkSelect: [{
type: Output
}], link: [{
type: Input
}], size: [{
type: Input
}], targetLink: [{
type: Input
}], routerLink: [{
type: Input
}] } });
class DsfrLinkModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkModule, imports: [CommonModule, RouterModule, DsfrLinkComponent], exports: [DsfrLinkComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkModule, imports: [CommonModule, RouterModule, DsfrLinkComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinkModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrLinkComponent],
imports: [CommonModule, RouterModule, DsfrLinkComponent],
}]
}] });
class DsfrBackToTopComponent {
constructor() {
this.buttonMode = input(false, ...(ngDevMode ? [{ debugName: "buttonMode", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/** Identifiant de l'élément cible en haut de page, par défaut "top" */
this.targetId = input('top', ...(ngDevMode ? [{ debugName: "targetId" }] : []));
this.scrollBehavior = 'auto';
this.platformId = inject(PLATFORM_ID);
if (isPlatformBrowser(this.platformId)) {
const prefersReduced = window?.matchMedia('(prefers-reduced-motion: reduce)').matches;
this.scrollBehavior = prefersReduced ? 'auto' : 'smooth';
}
}
scrollToTop() {
this.scrollToElement(this.targetId());
}
/**
* Scroll vers l'élément dont l'id correspond à targetId.
* Simule le même comportement que si on avait placé l'ancre #targetId dans l'url.
* Sans agir sur la navigation (pas de modification de l'historique)
* @param targetId Id de l'élément ciblé par le scroll.
*/
scrollToElement(targetId) {
const target = document.getElementById(targetId);
if (!target) {
return;
}
// simulation du comportement de scroll de l'ancre.
target.scrollIntoView({
behavior: this.scrollBehavior,
block: 'start',
inline: 'nearest',
});
// simulation du comportement de focus de l'ancre.
if (!target.hasAttribute('tabindex')) {
target.setAttribute('tabindex', '-1');
target.focus({ preventScroll: true });
requestAnimationFrame(() => target.removeAttribute('tabindex'));
}
else {
target.focus({ preventScroll: true });
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrBackToTopComponent, isStandalone: true, selector: "dsfr-backtotop", inputs: { buttonMode: { classPropertyName: "buttonMode", publicName: "buttonMode", isSignal: true, isRequired: false, transformFunction: null }, targetId: { classPropertyName: "targetId", publicName: "targetId", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (buttonMode()) {\n <dsfr-button\n [label]=\"'backtotop.topOfPage' | dsfrI18n\"\n [variant]=\"'tertiary-no-outline'\"\n [icon]=\"'fr-icon-arrow-up-fill'\"\n (click)=\"scrollToTop()\" />\n} @else {\n <dsfr-link\n class=\"fr-link fr-icon-arrow-up-fill fr-link--icon-left\"\n routePath=\"./\"\n [routerLinkExtras]=\"{ fragment: targetId() }\"\n >{{ 'backtotop.topOfPage' | dsfrI18n }}</dsfr-link\n >\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-backtotop', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe, DsfrButtonComponent], template: "@if (buttonMode()) {\n <dsfr-button\n [label]=\"'backtotop.topOfPage' | dsfrI18n\"\n [variant]=\"'tertiary-no-outline'\"\n [icon]=\"'fr-icon-arrow-up-fill'\"\n (click)=\"scrollToTop()\" />\n} @else {\n <dsfr-link\n class=\"fr-link fr-icon-arrow-up-fill fr-link--icon-left\"\n routePath=\"./\"\n [routerLinkExtras]=\"{ fragment: targetId() }\"\n >{{ 'backtotop.topOfPage' | dsfrI18n }}</dsfr-link\n >\n}\n" }]
}], ctorParameters: () => [], propDecorators: { buttonMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonMode", required: false }] }], targetId: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetId", required: false }] }] } });
class DsfrBackToTopModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopModule, imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe, DsfrBackToTopComponent], exports: [DsfrBackToTopComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopModule, imports: [CommonModule, DsfrLinkComponent, DsfrBackToTopComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBackToTopModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrBackToTopComponent],
imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe, DsfrBackToTopComponent],
}]
}] });
/**
* Définit les différent niveaux de sévérité supportés par le DSFR.
*/
var DsfrBadgeSeverityConst;
(function (DsfrBadgeSeverityConst) {
DsfrBadgeSeverityConst["NEW"] = "new";
DsfrBadgeSeverityConst["INFO"] = "info";
DsfrBadgeSeverityConst["SUCCESS"] = "success";
DsfrBadgeSeverityConst["WARNING"] = "warning";
DsfrBadgeSeverityConst["ERROR"] = "error";
})(DsfrBadgeSeverityConst || (DsfrBadgeSeverityConst = {}));
/**
* Définit les tailles de badge supportées par le DSFR.
*/
var DsfrBadgeSizeConst;
(function (DsfrBadgeSizeConst) {
/**
* Petite taille.
*/
DsfrBadgeSizeConst["SM"] = "SM";
/**
* Taille normale (par défaut).
*/
DsfrBadgeSizeConst["MD"] = "MD";
})(DsfrBadgeSizeConst || (DsfrBadgeSizeConst = {}));
class DsfrBadgeComponent {
constructor() {
/**
* L'icône de sévérité n'est pas affiché si `true` (`false` par défaut).
* Attention seuls les badges dits "système" peuvent avoir une icône, les badges "custom" ne le peuvent pas.
*/
this.noIcon = false;
/**
* Taille du badge, optionnel, `MD` par défaut.
*/
this.badgeSize = 'MD';
/** @internal */
this._severity = undefined;
}
/**
* Taille du badge, optionnel, `MD` par défaut.
*
* @deprecated since(1.15.0) utiliser 'badgeSize' à la place
*/
get size() {
return this.badgeSize;
}
get severity() {
return this._severity;
}
/**
* Taille du badge, optionnel, `MD` par défaut.
*
* @deprecated since(1.15.0) utiliser 'badgeSize' à la place
*/
set size(value) {
this.badgeSize = value;
}
/** Niveau d'alerte, optionnel. */
set severity(value) {
this._severity = !value ? undefined : value?.toLowerCase();
}
/** @internal */
getClasses() {
const classes = {
'fr-badge--sm': this.badgeSize === DsfrSizeConst.SM && !!this.label,
'fr-badge--no-icon': this.noIcon,
};
if (this.severity) {
classes['fr-badge--new'] = this.severity === DsfrBadgeSeverityConst.NEW;
classes['fr-badge--success'] = this.severity === DsfrBadgeSeverityConst.SUCCESS;
classes['fr-badge--info'] = this.severity == DsfrBadgeSeverityConst.INFO;
classes['fr-badge--warning'] = this.severity == DsfrBadgeSeverityConst.WARNING;
classes['fr-badge--error'] = this.severity === DsfrBadgeSeverityConst.ERROR;
}
return classes;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrBadgeComponent, isStandalone: true, selector: "dsfr-badge", inputs: { label: "label", noIcon: "noIcon", badgeSize: "badgeSize", customClass: "customClass", size: "size", severity: "severity" }, ngImport: i0, template: "<p class=\"fr-badge\" [class]=\"customClass ?? ''\" [ngClass]=\"getClasses()\">\n <span class=\"fr-ellipsis\">{{ label }}</span>\n</p>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-badge', encapsulation: ViewEncapsulation.None, imports: [CommonModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<p class=\"fr-badge\" [class]=\"customClass ?? ''\" [ngClass]=\"getClasses()\">\n <span class=\"fr-ellipsis\">{{ label }}</span>\n</p>\n" }]
}], propDecorators: { label: [{
type: Input,
args: [{ required: true }]
}], noIcon: [{
type: Input
}], badgeSize: [{
type: Input
}], customClass: [{
type: Input
}], size: [{
type: Input
}], severity: [{
type: Input
}] } });
class DsfrBadgeModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeModule, imports: [CommonModule, DsfrBadgeComponent], exports: [DsfrBadgeComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeModule, imports: [CommonModule, DsfrBadgeComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgeModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, DsfrBadgeComponent],
exports: [DsfrBadgeComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}]
}] });
class DsfrBadgesGroupComponent {
constructor() {
/**
* Les badges du groupe.
*/
this.badges = [];
/**
* Taille des badges.
*
* @since 1.9.0
*/
this.small = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrBadgesGroupComponent, isStandalone: true, selector: "dsfr-badges-group", inputs: { badges: "badges", small: "small" }, ngImport: i0, template: "<ul class=\"fr-badges-group\" [class.fr-badges-group--sm]=\"small\">\n @for (badge of badges; track badge.label) {\n <li>\n <dsfr-badge\n [label]=\"badge.label\"\n [noIcon]=\"badge.noIcon ?? false\"\n [severity]=\"badge.severity ?? undefined\"\n [customClass]=\"badge.customClass ?? ''\"></dsfr-badge>\n </li>\n }\n</ul>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrBadgeComponent, selector: "dsfr-badge", inputs: ["label", "noIcon", "badgeSize", "customClass", "size", "severity"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-badges-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrBadgeComponent], template: "<ul class=\"fr-badges-group\" [class.fr-badges-group--sm]=\"small\">\n @for (badge of badges; track badge.label) {\n <li>\n <dsfr-badge\n [label]=\"badge.label\"\n [noIcon]=\"badge.noIcon ?? false\"\n [severity]=\"badge.severity ?? undefined\"\n [customClass]=\"badge.customClass ?? ''\"></dsfr-badge>\n </li>\n }\n</ul>\n" }]
}], propDecorators: { badges: [{
type: Input
}], small: [{
type: Input
}] } });
class DsfrBadgesGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupModule, imports: [CommonModule, DsfrBadgeComponent, DsfrBadgesGroupComponent], exports: [DsfrBadgesGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupModule, imports: [CommonModule, DsfrBadgeComponent, DsfrBadgesGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBadgesGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrBadgesGroupComponent],
imports: [CommonModule, DsfrBadgeComponent, DsfrBadgesGroupComponent],
}]
}] });
class ItemLinkComponent extends DsfrLinkComponent {
constructor() {
super(...arguments);
// DsfrPositionConst.RIGHT pour être, malheureusement, compatible avec l'existant. undefined dans l'idéal.
this.defaultIconPosition = DsfrPositionConst.RIGHT;
}
/**@internal */
get item() {
return this._item;
}
/**
* Optionnel, utilisation avec un model DsfrLink (exclusif avec les autres propriétés).
*
* @since 1.7
*/
set item(value) {
this._item = value;
if (this._item) {
this.ariaControls = this._item.ariaControls;
this.ariaCurrent = this._item.active ? 'page' : undefined;
this.ariaLabel = this._item.ariaLabel;
this.routePath = this._item.routerLink;
this.route = this._item.route;
this.routerLinkActive = this._item.routerLinkActive;
this.routerLinkActiveOptions = this._item?.routerLinkActiveOptions;
this.routerLinkExtras = this._item.routerLinkExtras;
this.customClass = this.customClass || this._item.customClass;
this.disabled = this._item.disabled || false;
this.mode = this._item.mode;
this.icon = this._item.icon;
this.iconPosition = this._item.iconPosition || this.defaultIconPosition;
this.label = this._item.label;
this.link = this._item.link;
this.linkId = this._item.linkId;
this.linkTarget = this._item.linkTarget || this._item.target;
this.tooltipMessage = this._item.tooltipMessage;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ItemLinkComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: ItemLinkComponent, isStandalone: true, selector: "edu-item-link", inputs: { defaultIconPosition: "defaultIconPosition", item: "item" }, usesInheritance: true, ngImport: i0, template: "<!-- Lien externe ou g\u00E9r\u00E9 programmatiquement -->\n<!--FIXME : obligation de dupliquer ici pour g\u00E9rer a la fois innerHTML (/!\\ pas de outerHTML!) et la pr\u00E9sence du slot.\n voir pour fix en angular 19 d\u00E9tection d'un contenu dans le slot ou suppression de innerHTML en v2 -->\n@if (!hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Lien externe et label en innerHTML-->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Lien externe et slot pour le label -->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Routerlink et label en innerHTML-->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Routerlink et slot pour le label -->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (mode === 'button') {\n @if (label) {\n <!-- Bouton et label en innerHTML-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\"></button>\n } @else {\n <!-- Bouton et slot pour le label-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </button>\n }\n}\n\n<!-- D\u00E9clar\u00E9 dans un template : le ng-content ne doit \u00EAtre pr\u00E9sent qu'une seule fois -->\n<ng-template #slot>\n <ng-content></ng-content>\n @if (externalLink) {\n <span class=\"fr-sr-only\"> {{ i18n.t('link.newWindow') }} </span>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ItemLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-item-link', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "<!-- Lien externe ou g\u00E9r\u00E9 programmatiquement -->\n<!--FIXME : obligation de dupliquer ici pour g\u00E9rer a la fois innerHTML (/!\\ pas de outerHTML!) et la pr\u00E9sence du slot.\n voir pour fix en angular 19 d\u00E9tection d'un contenu dans le slot ou suppression de innerHTML en v2 -->\n@if (!hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Lien externe et label en innerHTML-->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Lien externe et slot pour le label -->\n <a\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.href]=\"getHref() || null\"\n [attr.id]=\"linkId || null\"\n [attr.rel]=\"linkTarget === '_blank' ? 'noopener external' : null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [ngClass]=\"getClass()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (hasRouterLink() && mode !== 'button') {\n @if (label) {\n <!-- Routerlink et label en innerHTML-->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\">\n </a>\n } @else {\n <!-- Routerlink et slot pour le label -->\n <a\n [ariaCurrentWhenActive]=\"ariaCurrentWhenActive ?? 'page'\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-current]=\"ariaCurrent || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.id]=\"linkId || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.title]=\"tooltipMessage || null\"\n [fragment]=\"routerLinkExtras?.fragment\"\n [ngClass]=\"getClass()\"\n [preserveFragment]=\"routerLinkExtras?.preserveFragment\"\n [queryParams]=\"routerLinkExtras?.queryParams\"\n [queryParamsHandling]=\"routerLinkExtras?.queryParamsHandling\"\n [replaceUrl]=\"routerLinkExtras?.replaceUrl\"\n [routerLink]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"routerLinkActiveOptions ? routerLinkActiveOptions : { exact: false }\"\n [skipLocationChange]=\"routerLinkExtras?.skipLocationChange\"\n [state]=\"routerLinkExtras?.state\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </a>\n }\n} @else if (mode === 'button') {\n @if (label) {\n <!-- Bouton et label en innerHTML-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\"\n [innerHTML]=\"label\"></button>\n } @else {\n <!-- Bouton et slot pour le label-->\n <button\n type=\"button\"\n [attr.id]=\"linkId\"\n [attr.aria-controls]=\"ariaControls || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"ariaLabelledBy || null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n [attr.data-fr-opened]=\"ariaControls ? false : null\"\n [attr.title]=\"tooltipMessage || null\"\n [disabled]=\"disabled\"\n [ngClass]=\"getButtonClasses()\"\n (click)=\"onLink($event)\">\n <ng-container [ngTemplateOutlet]=\"slot\"> </ng-container>\n </button>\n }\n}\n\n<!-- D\u00E9clar\u00E9 dans un template : le ng-content ne doit \u00EAtre pr\u00E9sent qu'une seule fois -->\n<ng-template #slot>\n <ng-content></ng-content>\n @if (externalLink) {\n <span class=\"fr-sr-only\"> {{ i18n.t('link.newWindow') }} </span>\n }\n</ng-template>\n" }]
}], propDecorators: { defaultIconPosition: [{
type: Input
}], item: [{
type: Input
}] } });
class DsfrBreadcrumbComponent {
/** @internal */
constructor() {
/** Tableau d'item du fil d'Ariane. */
this.items = [];
/** Confirmation du clic sur le bouton. */
this.linkSelect = new EventEmitter();
}
/** @internal */
onItem(item) {
this.linkSelect.emit(item);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrBreadcrumbComponent, isStandalone: true, selector: "dsfr-breadcrumb", inputs: { items: "items", ariaLabel: "ariaLabel" }, outputs: { linkSelect: "linkSelect" }, ngImport: i0, template: "<nav role=\"navigation\" class=\"fr-breadcrumb\" [attr.aria-label]=\"'breadcrumb.ariaLabel' | dsfrI18n\">\n <button type=\"button\" class=\"fr-breadcrumb__button\" aria-expanded=\"false\" aria-controls=\"breadcrumb-1\">\n {{ 'breadcrumb.viewBreadcrumb' | dsfrI18n }}\n </button>\n <div class=\"fr-collapse\" id=\"breadcrumb-1\">\n <ol class=\"fr-breadcrumb__list\">\n @for (item of items; track $index; let isFirst = $first; let isLast = $last) {\n <li>\n @if (isLast) {\n <!-- Formatage forc\u00E9 : suppression des espaces r\u00E9siduels li\u00E9s aux retours \u00E0 la ligne au sein d'un \u00E9l\u00E9ment inline -->\n <!-- prettier-ignore -->\n <a class=\"fr-breadcrumb__link\" aria-current=\"page\" [attr.aria-label]=\"item.ariaLabel || null\">{{\n item.label\n }}</a>\n } @else {\n <edu-item-link\n (linkSelect)=\"onItem(item)\"\n [customClass]=\"'fr-breadcrumb__link'\"\n [item]=\"item\"></edu-item-link>\n }\n </li>\n }\n </ol>\n </div>\n</nav>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-breadcrumb', encapsulation: ViewEncapsulation.None, imports: [CommonModule, ItemLinkComponent, DsfrI18nPipe], template: "<nav role=\"navigation\" class=\"fr-breadcrumb\" [attr.aria-label]=\"'breadcrumb.ariaLabel' | dsfrI18n\">\n <button type=\"button\" class=\"fr-breadcrumb__button\" aria-expanded=\"false\" aria-controls=\"breadcrumb-1\">\n {{ 'breadcrumb.viewBreadcrumb' | dsfrI18n }}\n </button>\n <div class=\"fr-collapse\" id=\"breadcrumb-1\">\n <ol class=\"fr-breadcrumb__list\">\n @for (item of items; track $index; let isFirst = $first; let isLast = $last) {\n <li>\n @if (isLast) {\n <!-- Formatage forc\u00E9 : suppression des espaces r\u00E9siduels li\u00E9s aux retours \u00E0 la ligne au sein d'un \u00E9l\u00E9ment inline -->\n <!-- prettier-ignore -->\n <a class=\"fr-breadcrumb__link\" aria-current=\"page\" [attr.aria-label]=\"item.ariaLabel || null\">{{\n item.label\n }}</a>\n } @else {\n <edu-item-link\n (linkSelect)=\"onItem(item)\"\n [customClass]=\"'fr-breadcrumb__link'\"\n [item]=\"item\"></edu-item-link>\n }\n </li>\n }\n </ol>\n </div>\n</nav>\n" }]
}], ctorParameters: () => [], propDecorators: { items: [{
type: Input
}], ariaLabel: [{
type: Input
}], linkSelect: [{
type: Output
}] } });
class DsfrBreadcrumbModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbModule, imports: [CommonModule, ItemLinkComponent, DsfrI18nPipe, DsfrBreadcrumbComponent], exports: [DsfrBreadcrumbComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbModule, imports: [CommonModule, ItemLinkComponent, DsfrBreadcrumbComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrBreadcrumbModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrBreadcrumbComponent],
imports: [CommonModule, ItemLinkComponent, DsfrI18nPipe, DsfrBreadcrumbComponent],
}]
}] });
// -- Alignement -------------------------------------------------------------------------------------------------------
/**
* Définit les modes d'alignement supportés par le composant `dsfr-buttons-group`.
*/
var DsfrAlignConst;
(function (DsfrAlignConst) {
/**
* Aligne les boutons sur la gauche.
*/
DsfrAlignConst["DEFAULT"] = "default";
/**
* Aligne les boutons sur la droite.
*/
DsfrAlignConst["RIGHT"] = "right";
/**
* Les boutons ont les mêmes espacements entre eux.
*/
DsfrAlignConst["BETWEEN"] = "between";
/**
* Aligne les boutons au centre du conteneur.
*/
DsfrAlignConst["CENTER"] = "center";
/**
* Combine un alignement à droite avec une inversion du sens des boutons de droite à gauche.
*/
DsfrAlignConst["REVERSE"] = "reverse";
})(DsfrAlignConst || (DsfrAlignConst = {}));
var DsfrInlineConst;
(function (DsfrInlineConst) {
/**
* Mode vertical (par défaut).
*/
DsfrInlineConst["NEVER"] = "never";
/**
* Toujours en mode horizontal.
*/
DsfrInlineConst["ALWAYS"] = "always";
/**
* Mode horizontal à partir du point de rupture SM (576px).
*/
DsfrInlineConst["FROM_SM"] = "SM";
/**
* Mode horizontal à partir du point de rupture MD 576px.
*/
DsfrInlineConst["FROM_MD"] = "MD";
/**
* Mode horizontal à partir du point de rupture LG ().
*/
DsfrInlineConst["FROM_LG"] = "LG";
})(DsfrInlineConst || (DsfrInlineConst = {}));
/**
* Définit lees différents balisages HTML supportés par le groupe de boutons.
*/
var DsfrButtonsGroupMarkupConst;
(function (DsfrButtonsGroupMarkupConst) {
/**
* Pour obtenir un conteneur `<DIV>`.
*/
DsfrButtonsGroupMarkupConst["DIV"] = "div";
/**
* Pour obtenir un conteneur `<UL>` (par défault).
*/
DsfrButtonsGroupMarkupConst["UL"] = "ul";
})(DsfrButtonsGroupMarkupConst || (DsfrButtonsGroupMarkupConst = {}));
class DsfrButtonsGroupComponent {
constructor() {
/**
* Permet de basculer la disposition des boutons en mode horizontal.
* En mode vertical, mode par défaut, les boutons prennent 100% de la largeur du conteneur.
*/
this.inline = DsfrInlineConst.NEVER;
/**
* Placement des boutons en mode `inline` :
* - `default` : aligne les boutons sur la gauche.
* - `right` : aligne les boutons sur la droite
* - `between` : les boutons ont les mêmes espacements ente eux.
* - `center` : aligne les boutons au centre du conteneur.
* - `reverse` : les boutons se lisent de droite à gauche (implique nécessairement un placement à droite).
*
* 📌 N'a aucun effet en mode vertical.
*/
this.alignment = DsfrAlignConst.DEFAULT;
/**
* Permet de mettre automatiquement tous les boutons d’un groupe à la même largeur.
* Cela permet une uniformité des boutons en mode horizontal.
* (en mode vertical, les boutons prennent 100% de la largeur du conteneur)
* Cette option peut être combinée avec la propriété d'alignement.
*/
this.equisized = false;
/**
* Taille des boutons du groupe.
*/
this.buttonGroupSize = DsfrSizeConst.MD;
/**
* Type de balise HTML pour la liste du groupe de boutons.
*
* 📌 Cette propriété ne doit pas être modifiée post-initialisation (i.e. dynamiquement).
*/
/*
* Note technique : Le fait de modifier le DOM pour encapsuler les boutons dans des balises <li> nous empêche
* aujourd'hui de supporter la modification dynamique du type de balisage. Passer d'un conteneur <ul> à <div> ne
* pose pas de problème mais l'inverse produit une structure HTML non valide - les <li> positionnés par un appel à
* la méthode `surroundButtonsWithLi` effectué au niveau du setter de `groupMarkup` se verrait effacés par la passe
* de rendu Angular qui remplace la <div> par un <ul>. Il serait possible d'invoquer la méthode `surroundButtonsWithLi`
* en utilisant un `requestAnimationFrame` mais cette approche nous semble être trop hasardeuse au regard du cas
* d'usage (changer dynamiquement la strucure HTML d'un groupe de boutons) qui ne semble pas pertinent.
*/
this.groupMarkup = DsfrButtonsGroupMarkupConst.UL;
this.DsfrButtonsGroupMarkup = DsfrButtonsGroupMarkupConst;
this.buttonsElements = contentChildren(DsfrButtonComponent, ...(ngDevMode ? [{ debugName: "buttonsElements", read: ElementRef }] : [{ read: ElementRef }]));
this.buttons = contentChildren(DsfrButtonComponent, ...(ngDevMode ? [{ debugName: "buttons" }] : []));
this._elementRef = inject(ElementRef);
afterRenderEffect(() => {
this.surroundButtonsWithLi();
});
}
/**
* Taille des boutons du groupe.
*
* @deprecated since(1.15.0) utiliser 'buttonGroupSize' à la place
*/
get size() {
return this.buttonGroupSize;
}
get iconPosition() {
return this._iconPosition;
}
/**
* Taille des boutons du groupe.
*
* @deprecated since(1.15.0) utiliser 'buttonGroupSize' à la place
*/
set size(value) {
this.buttonGroupSize = value;
}
/**
* Positionnement (gauche ou droite) au niveau du groupe des icônes dans les boutons qui
* contiennent à la fois une icône et un label.
*/
set iconPosition(value) {
this._iconPosition = value;
this.updateButtonsIconPosition();
}
/**
* Cette méthode calcule les styles du composant en évitant les magic strings.
*/
getClasses() {
let classes = [];
// Inline class
switch (this.inline) {
case 'always':
classes.push('fr-btns-group--inline');
break;
case 'LG':
classes.push('fr-btns-group--inline-lg');
break;
case 'MD':
classes.push('fr-btns-group--inline-md');
break;
case 'SM':
classes.push('fr-btns-group--inline-sm');
break;
default:
break;
}
// Alignment class
switch (this.alignment) {
case DsfrAlignConst.CENTER:
classes.push('fr-btns-group--center');
break;
case DsfrAlignConst.BETWEEN:
classes.push('fr-btns-group--between');
break;
case DsfrAlignConst.RIGHT:
classes.push('fr-btns-group--right');
break;
case DsfrAlignConst.REVERSE:
classes.push('fr-btns-group--inline-reverse', 'fr-btns-group--right');
break;
default:
break;
}
// Size class
if (this.buttonGroupSize === DsfrSizeConst.SM) {
classes.push('fr-btns-group--sm');
}
else if (this.buttonGroupSize === DsfrSizeConst.LG) {
classes.push('fr-btns-group--lg');
}
// Equisized class
if (this.equisized) {
classes.push('fr-btns-group--equisized');
}
// IconPosition class
if (this._iconPosition) {
classes.push(this._iconPosition === DsfrPositionConst.LEFT ? 'fr-btns-group--icon-left' : 'fr-btns-group--icon-right');
}
return classes;
}
/* Entoure chaque composant bouton par une balise <li> si nécessaire */
surroundButtonsWithLi() {
if (this.groupMarkup === DsfrButtonsGroupMarkupConst.UL) {
DomUtils.surroundElementsWithLi(this._elementRef, this.buttonsElements());
}
}
/** On force la position des icônes des boutons. */
updateButtonsIconPosition() {
this.buttons().forEach((b) => (b.iconPosition = this._iconPosition));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrButtonsGroupComponent, isStandalone: true, selector: "dsfr-buttons-group", inputs: { inline: "inline", alignment: "alignment", equisized: ["equisized", "equisized", booleanAttribute], buttonGroupSize: "buttonGroupSize", groupMarkup: "groupMarkup", size: "size", iconPosition: "iconPosition" }, queries: [{ propertyName: "buttonsElements", predicate: DsfrButtonComponent, read: ElementRef, isSignal: true }, { propertyName: "buttons", predicate: DsfrButtonComponent, isSignal: true }], ngImport: i0, template: "@if (groupMarkup === DsfrButtonsGroupMarkup.DIV) {\n <div class=\"fr-btns-group\" [ngClass]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"buttonsTemplate\"></ng-container>\n </div>\n} @else {\n <ul class=\"fr-btns-group\" [ngClass]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"buttonsTemplate\"></ng-container>\n </ul>\n}\n\n<ng-template #buttonsTemplate>\n <ng-content></ng-content>\n</ng-template>\n", styles: ["@charset \"UTF-8\";dsfr-buttons-group div.fr-btns-group:not([class*=fr-btns-group--inline]) dsfr-button{width:calc(100% - 1rem)}dsfr-buttons-group .fr-btns-group li{margin:0 .5rem 1rem}dsfr-buttons-group .fr-btns-group li .fr-btn{margin:0}dsfr-buttons-group .fr-btns-group--inline li{margin-left:.5rem;margin-right:.5rem}dsfr-buttons-group .fr-btns-group--inline li .fr-btn{margin:0}dsfr-buttons-group dsfr-button{pointer-events:none}dsfr-buttons-group dsfr-button .fr-btn{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-buttons-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "@if (groupMarkup === DsfrButtonsGroupMarkup.DIV) {\n <div class=\"fr-btns-group\" [ngClass]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"buttonsTemplate\"></ng-container>\n </div>\n} @else {\n <ul class=\"fr-btns-group\" [ngClass]=\"getClasses()\">\n <ng-container *ngTemplateOutlet=\"buttonsTemplate\"></ng-container>\n </ul>\n}\n\n<ng-template #buttonsTemplate>\n <ng-content></ng-content>\n</ng-template>\n", styles: ["@charset \"UTF-8\";dsfr-buttons-group div.fr-btns-group:not([class*=fr-btns-group--inline]) dsfr-button{width:calc(100% - 1rem)}dsfr-buttons-group .fr-btns-group li{margin:0 .5rem 1rem}dsfr-buttons-group .fr-btns-group li .fr-btn{margin:0}dsfr-buttons-group .fr-btns-group--inline li{margin-left:.5rem;margin-right:.5rem}dsfr-buttons-group .fr-btns-group--inline li .fr-btn{margin:0}dsfr-buttons-group dsfr-button{pointer-events:none}dsfr-buttons-group dsfr-button .fr-btn{pointer-events:auto}\n"] }]
}], ctorParameters: () => [], propDecorators: { inline: [{
type: Input
}], alignment: [{
type: Input
}], equisized: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], buttonGroupSize: [{
type: Input
}], groupMarkup: [{
type: Input
}], buttonsElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrButtonComponent), { ...{ read: ElementRef }, isSignal: true }] }], buttons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrButtonComponent), { isSignal: true }] }], size: [{
type: Input
}], iconPosition: [{
type: Input
}] } });
class DsfrButtonsGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupModule, imports: [CommonModule, DsfrButtonsGroupComponent], exports: [DsfrButtonsGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupModule, imports: [CommonModule, DsfrButtonsGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrButtonsGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrButtonsGroupComponent],
imports: [CommonModule, DsfrButtonsGroupComponent],
}]
}] });
/**
* Définit lees différents balisages HTML supportés par la description du callout.
*/
var DsfrCalloutDescMarkupConst;
(function (DsfrCalloutDescMarkupConst) {
/**
* Pour obtenir un conteneur `<DIV>`.
*/
DsfrCalloutDescMarkupConst["DIV"] = "div";
/**
* Pour obtenir un conteneur `<P>` par défault.
*/
DsfrCalloutDescMarkupConst["P"] = "p";
})(DsfrCalloutDescMarkupConst || (DsfrCalloutDescMarkupConst = {}));
class DsfrCalloutComponent {
constructor() {
/**
* Le niveau de titre devant être utilisé.
* Valeurs supportées : H2, H3, H4, H5, H6 ou P si vous ne souhaitez pas positionner un titre de section.
*/
this.headingLevel = DsfrHeadingLevelConst.H3;
/**
* Type de balise HTML pour le conteneur de la description.
*/
this.descriptionMarkup = DsfrCalloutDescMarkupConst.P;
this.DsfrCalloutDescMarkup = DsfrCalloutDescMarkupConst;
}
/**
* Détermine la liste des classes CSS à positioner sur le parent.
* @returns un tableau de classes CSS
*
* @internal
*/
getClasses() {
let results = ['fr-callout'];
if (this.icon) {
results = results.concat(this.icon.trim().split(/\s+/));
}
if (this.customClass) {
results = results.concat(this.customClass.trim().split(/\s+/));
}
return results;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrCalloutComponent, isStandalone: true, selector: "dsfr-callout", inputs: { heading: "heading", headingLevel: "headingLevel", icon: "icon", customClass: "customClass", descriptionMarkup: "descriptionMarkup" }, ngImport: i0, template: "<div [class]=\"getClasses()\">\n @if (heading) {\n @switch (headingLevel) {\n @case ('none') {\n <p class=\"fr-callout__title\" [innerHTML]=\"heading\"></p>\n }\n @case ('H2') {\n <h2 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h2>\n }\n @case ('H4') {\n <h4 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h4>\n }\n @case ('H5') {\n <h5 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h5>\n }\n @case ('H6') {\n <h6 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h6>\n }\n @default {\n <h3 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h3>\n }\n }\n }\n <!-- Description obligatoire -->\n @if (descriptionMarkup === DsfrCalloutDescMarkup.DIV) {\n <div class=\"fr-callout__text\">\n <ng-container *ngTemplateOutlet=\"descTemplate\"></ng-container>\n </div>\n } @else {\n <p class=\"fr-callout__text\">\n <ng-container *ngTemplateOutlet=\"descTemplate\"></ng-container>\n </p>\n }\n</div>\n\n<ng-template #descTemplate>\n <ng-content></ng-content>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-callout', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<div [class]=\"getClasses()\">\n @if (heading) {\n @switch (headingLevel) {\n @case ('none') {\n <p class=\"fr-callout__title\" [innerHTML]=\"heading\"></p>\n }\n @case ('H2') {\n <h2 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h2>\n }\n @case ('H4') {\n <h4 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h4>\n }\n @case ('H5') {\n <h5 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h5>\n }\n @case ('H6') {\n <h6 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h6>\n }\n @default {\n <h3 class=\"fr-callout__title\" [innerHTML]=\"heading\"></h3>\n }\n }\n }\n <!-- Description obligatoire -->\n @if (descriptionMarkup === DsfrCalloutDescMarkup.DIV) {\n <div class=\"fr-callout__text\">\n <ng-container *ngTemplateOutlet=\"descTemplate\"></ng-container>\n </div>\n } @else {\n <p class=\"fr-callout__text\">\n <ng-container *ngTemplateOutlet=\"descTemplate\"></ng-container>\n </p>\n }\n</div>\n\n<ng-template #descTemplate>\n <ng-content></ng-content>\n</ng-template>\n" }]
}], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], icon: [{
type: Input
}], customClass: [{
type: Input
}], descriptionMarkup: [{
type: Input
}] } });
class DsfrCalloutModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutModule, imports: [CommonModule, DsfrCalloutComponent], exports: [DsfrCalloutComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutModule, imports: [CommonModule, DsfrCalloutComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCalloutModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrCalloutComponent],
imports: [CommonModule, DsfrCalloutComponent],
}]
}] });
/**
* Retourne la classe dsfr correspondant au ratio
*/
function ratioClass(ratio) {
return {
'fr-ratio-32x9': ratio === '16:9/2',
'fr-ratio-16x9': ratio === '16:9',
'fr-ratio-3x2': ratio === '3:2',
'fr-ratio-4x3': ratio === '4:3',
'fr-ratio-1x1': ratio === '1:1',
'fr-ratio-3x4': ratio === '3:4',
'fr-ratio-2x3': ratio === '2:3',
};
}
/**
* Définit les modes d'affichage supporté pour un composant `Tag`.
*/
var DsfrTagModeConst;
(function (DsfrTagModeConst) {
/**
* Par défaut, le tag ne peut pas changer d'état ni accepter aucun lien.
*/
DsfrTagModeConst["DEFAULT"] = "default";
/**
* Un tag cliquable permet de débrancher sur un lien ou un path Angular.
*/
DsfrTagModeConst["CLICKABLE"] = "clickable";
/**
* Un tag sélectionnable possède un état de sélection.
*/
DsfrTagModeConst["SELECTABLE"] = "selectable";
/**
* Un tag supprimable.
*/
DsfrTagModeConst["DELETABLE"] = "deletable";
})(DsfrTagModeConst || (DsfrTagModeConst = {}));
class DsfrTagComponent {
constructor() {
/**
* Permet d'avoir un tag cliquable disabled.
*
* @since 1.3
*/
this.disabled = false;
/** État d'un tag 'selectable'. */
// 1.3 Non automatiquement modifié lorsque l'état du bouton 'aria-pressed' change, 'aria-pressed' est géré
// le JavaScript du DSFR, d'où la gestion de l'événement '(click)' pour synchroniser 'selected' et 'aria-pressed'.
this.selected = false;
/** Taille du tag (small ou médium). */
this.small = false;
/**
* Événement émis suite au click sur un tag, le contenu de l'événement est soit le lien, la route ou à défaut le label du tag.
* @since 1.4.0, si l'id du tag est renseigné, l'événement émet un objet de type DsfrTagEvent.
*/
this.tagSelect = new EventEmitter();
}
get mode() {
if (!this._mode) {
if (this.selected)
this._mode = DsfrTagModeConst.SELECTABLE;
else if (this.link || this.route || this.routePath)
this._mode = DsfrTagModeConst.CLICKABLE;
}
return this._mode;
}
/** Mode spécifique : default / selectable / clickable / deletable (les modes exclusifs entre eux). */
set mode(value) {
this._mode = value;
}
/**
* Attention en cas d'utilisation de cet attribut, il doit être utilisé en tant que propriété et non en attribut,
* ex. [id]="'monid'"
*
* @deprecated (since 1.5) utiliser tagId.
*/
set id(value) {
if (value) {
this._id = value;
this.tagId ??= this._id;
}
}
/**
* Activtion du router Angular.
*
* routerLink provoque un bug accessibilité sur la navigation au clavier (ajout d'un tabindex=0)
*
* @deprecated (since 1.11.5) utiliser `routePath` à la place.
**/
set routerLink(value) {
if (value)
this.routePath = value;
}
ngOnChanges({ selected }) {
// On ne se préoccupe pas trop du mode qui à l'initialisation est 'default' (comme ça chacun porte ses responsabilités)
if (selected) {
// On synchronise l'attribut 'aria-pressed' avec la nouvelle valeur de selected
this.ariaPressed = this.selected || false; // || false au cas où on reçoit undefined ou null
// On n'émet pas d'événement
}
}
/**
* Valeur pour le 'href' dans le template.
* @internal
*/
getHrefValue() {
return this.route || this.link;
}
/**
* Tag `selectable` : événement `tagSelect()` avec le contenu de du lien ou à défaut son label.
* @internal
*/
onSelectable() {
this.selected = !this.selected; // @since 1.3.2
this.emitTagSelect(this.getHrefValue() || this.label);
}
/**
* Événement (click) sur un lien.
* @internal
*/
onClickable(event) {
if (this.route) {
event.preventDefault();
this.emitTagSelect(this.route);
}
}
/**
* Tag `selectable` : événement `tagSelect()` avec le contenu de `link` ou à défaut son `label`.
* @internal
*/
onDeletable() {
this.emitTagSelect(this.getHrefValue() || this.label);
}
/** @internal */
getSelectableClasses() {
return this.getDefaultClasses();
}
/** @internal */
getClickableClasses() {
const classes = this.getDefaultClasses();
if (this.customClass)
classes.push(this.customClass);
return classes.join(' ');
}
/** @internal */
getNotClickableClasses() {
return this.getDefaultClasses();
}
emitTagSelect(value) {
if (!this.tagId)
this.tagSelect.emit(value);
else {
this.tagSelect.emit({
id: this.tagId,
label: this.label,
link: this.link ?? '',
route: this.route ?? '',
selected: this.selected,
});
}
}
getDefaultClasses() {
const classes = ['fr-tag'];
if (this.small)
classes.push('fr-tag--sm');
if (this.icon) {
classes.push('fr-tag--icon-left');
classes.push(this.icon);
}
// Un tag non cliquable ne peut pas avoir de custom class
return classes;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTagComponent, isStandalone: true, selector: "dsfr-tag", inputs: { tagId: "tagId", customClass: "customClass", disabled: ["disabled", "disabled", booleanAttribute], icon: "icon", label: "label", link: "link", linkTarget: "linkTarget", route: "route", routePath: "routePath", routerLinkActive: "routerLinkActive", routerLinkExtras: "routerLinkExtras", selected: "selected", small: "small", tooltipMessage: "tooltipMessage", ariaLabel: "ariaLabel", mode: "mode", id: "id", routerLink: "routerLink" }, outputs: { tagSelect: "tagSelect" }, usesOnChanges: true, ngImport: i0, template: "@switch (mode) {\n <!-- Tag supprimable -->\n @case ('deletable') {\n <button\n type=\"button\"\n [attr.id]=\"tagId || null\"\n [class]=\"{\n 'fr-tag': true,\n 'fr-tag--dismiss': true,\n 'fr-tag--sm': small,\n }\"\n [attr.title]=\"tooltipMessage || null\"\n [attr.aria-label]=\"ariaLabel ? ariaLabel : label + ', ' + ('tag.removeFilter' | dsfrI18n) + ' ' + label\"\n [disabled]=\"disabled || null\"\n (click)=\"onDeletable()\">\n {{ label }}<span class=\"fr-sr-only\"> ({{ 'tag.removable' | dsfrI18n }})</span>\n </button>\n }\n <!-- Tag s\u00E9lectionnable -->\n @case ('selectable') {\n <button\n type=\"button\"\n [attr.id]=\"tagId || null\"\n [ngClass]=\"getSelectableClasses()\"\n [attr.class]=\"icon || null\"\n [attr.title]=\"tooltipMessage || null\"\n [attr.aria-label]=\"ariaLabel ? ariaLabel : ('tag.selectFilter' | dsfrI18n) + ' ' + label\"\n [attr.aria-pressed]=\"ariaPressed\"\n (click)=\"onSelectable()\"\n [disabled]=\"disabled || null\">\n {{ label }}\n <span class=\"fr-sr-only\"\n > ({{ selected ? ('tag.selected' | dsfrI18n) : ('tag.unselected' | dsfrI18n) }})</span\n >\n </button>\n }\n <!-- Tag cliquable -->\n @case ('clickable') {\n <dsfr-link\n [linkId]=\"tagId\"\n [customClass]=\"getClickableClasses()\"\n [tooltipMessage]=\"tooltipMessage\"\n [ariaLabel]=\"ariaLabel ? ariaLabel : label\"\n (click)=\"onClickable($event)\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [label]=\"label\"\n [disabled]=\"disabled\">\n </dsfr-link>\n }\n <!-- Tag non cliquable -->\n @default {\n <p [attr.id]=\"tagId || null\" [ngClass]=\"getNotClickableClasses()\" [attr.title]=\"tooltipMessage || null\">\n {{ label }}\n </p>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tag', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe], template: "@switch (mode) {\n <!-- Tag supprimable -->\n @case ('deletable') {\n <button\n type=\"button\"\n [attr.id]=\"tagId || null\"\n [class]=\"{\n 'fr-tag': true,\n 'fr-tag--dismiss': true,\n 'fr-tag--sm': small,\n }\"\n [attr.title]=\"tooltipMessage || null\"\n [attr.aria-label]=\"ariaLabel ? ariaLabel : label + ', ' + ('tag.removeFilter' | dsfrI18n) + ' ' + label\"\n [disabled]=\"disabled || null\"\n (click)=\"onDeletable()\">\n {{ label }}<span class=\"fr-sr-only\"> ({{ 'tag.removable' | dsfrI18n }})</span>\n </button>\n }\n <!-- Tag s\u00E9lectionnable -->\n @case ('selectable') {\n <button\n type=\"button\"\n [attr.id]=\"tagId || null\"\n [ngClass]=\"getSelectableClasses()\"\n [attr.class]=\"icon || null\"\n [attr.title]=\"tooltipMessage || null\"\n [attr.aria-label]=\"ariaLabel ? ariaLabel : ('tag.selectFilter' | dsfrI18n) + ' ' + label\"\n [attr.aria-pressed]=\"ariaPressed\"\n (click)=\"onSelectable()\"\n [disabled]=\"disabled || null\">\n {{ label }}\n <span class=\"fr-sr-only\"\n > ({{ selected ? ('tag.selected' | dsfrI18n) : ('tag.unselected' | dsfrI18n) }})</span\n >\n </button>\n }\n <!-- Tag cliquable -->\n @case ('clickable') {\n <dsfr-link\n [linkId]=\"tagId\"\n [customClass]=\"getClickableClasses()\"\n [tooltipMessage]=\"tooltipMessage\"\n [ariaLabel]=\"ariaLabel ? ariaLabel : label\"\n (click)=\"onClickable($event)\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [label]=\"label\"\n [disabled]=\"disabled\">\n </dsfr-link>\n }\n <!-- Tag non cliquable -->\n @default {\n <p [attr.id]=\"tagId || null\" [ngClass]=\"getNotClickableClasses()\" [attr.title]=\"tooltipMessage || null\">\n {{ label }}\n </p>\n }\n}\n" }]
}], propDecorators: { tagId: [{
type: Input
}], customClass: [{
type: Input
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], icon: [{
type: Input
}], label: [{
type: Input,
args: [{ required: true }]
}], link: [{
type: Input
}], linkTarget: [{
type: Input
}], route: [{
type: Input
}], routePath: [{
type: Input
}], routerLinkActive: [{
type: Input
}], routerLinkExtras: [{
type: Input
}], selected: [{
type: Input
}], small: [{
type: Input
}], tooltipMessage: [{
type: Input
}], ariaLabel: [{
type: Input
}], tagSelect: [{
type: Output
}], mode: [{
type: Input
}], id: [{
type: Input
}], routerLink: [{
type: Input
}] } });
class DsfrTagModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagModule, imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe, DsfrTagComponent], exports: [DsfrTagComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagModule, imports: [CommonModule, DsfrLinkComponent, DsfrTagComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrTagComponent],
imports: [CommonModule, DsfrLinkComponent, DsfrI18nPipe, DsfrTagComponent],
}]
}] });
class DsfrTagsGroupComponent {
constructor() {
/** Modèle de présentation des tags. */
this.tags = [];
/**
* Événement émis suite au click sur un tag, le contenu de l'événement est soit le link, la route ou à défaut le label du tag.
* @since 1.4.0, si l'id du tag est renseigné, l'événement émet un objet de type DsfrTagEvent.
*/
this.tagSelect = new EventEmitter();
this.tagsElements = contentChildren(DsfrTagComponent, ...(ngDevMode ? [{ debugName: "tagsElements", read: ElementRef }] : [{ read: ElementRef }]));
this.tagsComponents = contentChildren(DsfrTagComponent, ...(ngDevMode ? [{ debugName: "tagsComponents" }] : []));
this._elementRef = inject(ElementRef);
effect(() => {
this.setGroupeMode();
this.surroundTagsWithLi();
});
}
/**
* Surcharge le mode de chaque tag avec le mode du groupe en cas d'utilisation de ng-content.
* @internal
*/
setGroupeMode() {
if (this.mode && this.tagsComponents()) {
this.tagsComponents().map((t) => (t.mode = this.mode ?? 'default'));
}
}
/** @internal */
onTagSelect(event) {
this.tagSelect.emit(event);
}
/* Entoure chaque composant tag par une balise <li> si nécessaire */
surroundTagsWithLi() {
DomUtils.surroundElementsWithLi(this._elementRef, this.tagsElements());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTagsGroupComponent, isStandalone: true, selector: "dsfr-tags-group", inputs: { tags: "tags", mode: "mode" }, outputs: { tagSelect: "tagSelect" }, queries: [{ propertyName: "tagsElements", predicate: DsfrTagComponent, read: ElementRef, isSignal: true }, { propertyName: "tagsComponents", predicate: DsfrTagComponent, isSignal: true }], ngImport: i0, template: "<ul class=\"fr-tags-group\">\n @for (tag of tags; track tag.id ?? tag.label) {\n <li>\n <dsfr-tag\n [tagId]=\"tag.id ?? ''\"\n [customClass]=\"tag.customClass ?? ''\"\n [icon]=\"tag.icon\"\n [label]=\"tag.label\"\n [ariaLabel]=\"tag.ariaLabel ?? ''\"\n [link]=\"tag.link ?? ''\"\n [mode]=\"mode ? mode : (tag.mode ?? 'default')\"\n [route]=\"tag.route ?? ''\"\n [selected]=\"tag.selected ?? false\"\n [small]=\"tag.small ?? false\"\n [tooltipMessage]=\"tag.title ?? ''\"\n (tagSelect)=\"onTagSelect($event)\"></dsfr-tag>\n </li>\n }\n <ng-content></ng-content>\n</ul>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrTagComponent, selector: "dsfr-tag", inputs: ["tagId", "customClass", "disabled", "icon", "label", "link", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkExtras", "selected", "small", "tooltipMessage", "ariaLabel", "mode", "id", "routerLink"], outputs: ["tagSelect"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tags-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrTagComponent], template: "<ul class=\"fr-tags-group\">\n @for (tag of tags; track tag.id ?? tag.label) {\n <li>\n <dsfr-tag\n [tagId]=\"tag.id ?? ''\"\n [customClass]=\"tag.customClass ?? ''\"\n [icon]=\"tag.icon\"\n [label]=\"tag.label\"\n [ariaLabel]=\"tag.ariaLabel ?? ''\"\n [link]=\"tag.link ?? ''\"\n [mode]=\"mode ? mode : (tag.mode ?? 'default')\"\n [route]=\"tag.route ?? ''\"\n [selected]=\"tag.selected ?? false\"\n [small]=\"tag.small ?? false\"\n [tooltipMessage]=\"tag.title ?? ''\"\n (tagSelect)=\"onTagSelect($event)\"></dsfr-tag>\n </li>\n }\n <ng-content></ng-content>\n</ul>\n" }]
}], ctorParameters: () => [], propDecorators: { tags: [{
type: Input
}], mode: [{
type: Input
}], tagSelect: [{
type: Output
}], tagsElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrTagComponent), { ...{ read: ElementRef }, isSignal: true }] }], tagsComponents: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrTagComponent), { isSignal: true }] }] } });
class DsfrTagsGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupModule, imports: [CommonModule, DsfrTagModule, DsfrTagsGroupComponent], exports: [DsfrTagsGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupModule, imports: [CommonModule, DsfrTagModule, DsfrTagsGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTagsGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrTagsGroupComponent],
imports: [CommonModule, DsfrTagModule, DsfrTagsGroupComponent],
}]
}] });
var DsfrProportionConst;
(function (DsfrProportionConst) {
DsfrProportionConst["TIER"] = "1tier/2tier";
DsfrProportionConst["DEFAULT"] = "40%/60%";
DsfrProportionConst["HALF"] = "moiti\u00E9/moiti\u00E9";
})(DsfrProportionConst || (DsfrProportionConst = {}));
class DsfrCardComponent extends BasePanelComponent {
constructor() {
super();
this.badgesOnMedia = false;
/**
* Zone d'actions, composée de bouton ou de liens, optionnelle (mais incompatible avec la
* deuxième zone de détail). Si activée elle affiche le slot avec sélecteur `footer`
*/
this.hasFooter = false;
/** Texte alternatif d'une image à utiliser uniquement si l'image à une information à passer. */
this.imageAlt = '';
/** En mode horizontal: proportion de l'image par rapport au contenu en horizontal (par défaut: 40%/60%). */
this.horizontalProportion = DsfrProportionConst.DEFAULT;
/**
* Supprime l'icône du lien.
*
* @since 1.12
*/
this.noIcon = false;
/**
* Type d'illustration
* - `img` : pour l'utilisation d'une balise '<img>'.
* - `svg` : pour l'utilisation de '<edu-svg-icon>'.
* Si c'est le cas, `@ImagePath` devient le path du fichier `sprite.svg` et la concaténation de l'id du svg à afficher.
*/
this.imageType = 'img';
/** Remplace le lien du bouton de download par un markup de bouton */
this.isDownloadButton = false;
/** Signale quand la carte est sélectionnée. */
this.cardSelect = new EventEmitter();
this.i18n = inject(DsfrI18nService);
/** @internal */
this.DsfrBorder = DsfrPanelBorderConst;
/** @internal */
this.DsfrBackground = DsfrPanelBackgroundConst;
/** @internal */
this.DsfrSize = DsfrSizeConst;
/** @internal */
this.DsfrProportionMediaInfo = DsfrProportionConst;
}
get cardClasses() {
return {
'fr-card': true,
'fr-card--horizontal': this.horizontal,
'fr-card--horizontal-half': this.horizontal && this.horizontalProportion === DsfrProportionConst.HALF,
'fr-card--horizontal-tier': this.horizontal && this.horizontalProportion === DsfrProportionConst.TIER,
'fr-card--download': this.download,
'fr-enlarge-link': this.enlargeLink && this.hasLink() && !this.hasFooter,
'fr-enlarge-button': this.enlargeButton || this.isDownloadButton,
'fr-card--no-arrow': this.hasFooter,
'fr-card--no-icon': this.noIcon,
'fr-card--no-border': this.customBorder === DsfrPanelBorderConst.NO_BORDER,
'fr-card--shadow': this.customBorder === DsfrPanelBorderConst.SHADOW,
'fr-card--grey': this.customBackground === DsfrPanelBackgroundConst.GREY,
'fr-card--no-background': this.customBackground === DsfrPanelBackgroundConst.TRANSPARENT,
'fr-card--sm': this.panelSize === DsfrSizeConst.SM,
'fr-card--lg': this.panelSize === DsfrSizeConst.LG,
'fr-card--has-footer': this.hasFooter,
'fr-card--badges-on-media': this.badgesOnMedia,
};
}
get imageRatioClass() {
return ratioClass(this.imageRatio);
}
/** @internal */
onLinkSelect() {
// on propage l'output, pas besoin de gérer ici le preventDefault, c'est géré en amont
if (this.route) {
this.cardSelect.emit(this.route);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrCardComponent, isStandalone: true, selector: "dsfr-card", inputs: { badgesOnMedia: ["badgesOnMedia", "badgesOnMedia", booleanAttribute], detail: "detail", detailIcon: "detailIcon", detailBottomIcon: "detailBottomIcon", hasFooter: "hasFooter", imageAlt: "imageAlt", imageFit: "imageFit", imagePath: "imagePath", imageRatio: "imageRatio", horizontalProportion: "horizontalProportion", noIcon: "noIcon", imageType: "imageType", isDownloadButton: "isDownloadButton" }, outputs: { cardSelect: "cardSelect" }, queries: [{ propertyName: "descriptionTemplate", first: true, predicate: ["descriptionTemplate"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div [class]=\"cardClasses\">\n <!-- Card body -->\n <div class=\"fr-card__body\">\n <div class=\"fr-card__content\">\n <!-- Titre du contenu -->\n <ng-container *ngTemplateOutlet=\"cardHeadingTemplate\"></ng-container>\n\n <!-- D\u00E9tail du haut -->\n @if (detail || (tags && tags.length) || (badges && badges.length && !badgesOnMedia)) {\n <div class=\"fr-card__start\">\n @if (tags && tags.length === 1) {\n <dsfr-tag\n [label]=\"tags[0]?.label ?? ''\"\n [tagId]=\"tags[0]?.id ?? ''\"\n [link]=\"tags[0]?.link ?? ''\"\n [route]=\"tags[0]?.route ?? ''\"\n [selected]=\"tags[0]?.selected ?? false\"></dsfr-tag>\n } @else if (tags && tags.length > 1) {\n <dsfr-tags-group [tags]=\"tags\"></dsfr-tags-group>\n }\n @if (badges && badges.length && !badgesOnMedia) {\n @if (badges.length === 1) {\n <dsfr-badge\n [label]=\"badges[0]?.label ?? ''\"\n [severity]=\"badges[0]?.severity\"\n [noIcon]=\"badges[0]?.noIcon ?? false\"\n [customClass]=\"badges[0]?.customClass ?? ''\"></dsfr-badge>\n } @else if (badges.length > 1) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n }\n\n @if (detail || detailIcon) {\n <p class=\"fr-card__detail\" [ngClass]=\"detailIcon\">\n {{ detail }}\n </p>\n }\n </div>\n }\n\n <!-- Description -->\n @if (description) {\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-card__desc\" [innerHTML]=\"description\"></p>\n } @else {\n <div class=\"fr-card__desc\" [innerHTML]=\"description\"></div>\n }\n }\n\n @if (!description && descriptionTemplate) {\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-card__desc\"><ng-container *ngTemplateOutlet=\"descriptionTemplate\"></ng-container></p>\n } @else {\n <div class=\"fr-card__desc\"><ng-container *ngTemplateOutlet=\"descriptionTemplate\"></ng-container></div>\n }\n }\n\n <!-- D\u00E9tail du bas -->\n @if (detailBottom || detailBottomIcon || (downloadAssessFile && download)) {\n <div class=\"fr-card__end\">\n <p class=\"fr-card__detail\" [ngClass]=\"detailBottomIcon\">\n {{ detailBottom }}\n </p>\n </div>\n }\n </div>\n\n <!-- Card footer -->\n @if (hasFooter) {\n <div class=\"fr-card__footer\">\n <ng-content select=\"[footer]\"></ng-content>\n </div>\n }\n </div>\n\n <!-- Card header -->\n @if (imagePath) {\n <div class=\"fr-card__header\">\n <div class=\"fr-card__img\">\n @if (imageType === 'img') {\n <img\n [src]=\"imagePath\"\n class=\"fr-responsive-img\"\n [class]=\"imageRatioClass\"\n [ngStyle]=\"{ 'object-fit': imageFit }\"\n [attr.alt]=\"imageAlt\" />\n }\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit toujours \u00EAtre pr\u00E9sente, sa valeur peut-\u00EAtre vide (image n\u2019apportant pas de sens suppl\u00E9mentaire au contexte) ou non (porteuse de texte ou apportant du sens) selon votre contexte -->\n @if (imageType === 'svg') {\n <edu-svg-icon [iconPath]=\"imagePath\" [imageRatio]=\"imageRatio\"></edu-svg-icon>\n }\n </div>\n @if (badges && badgesOnMedia) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n </div>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #cardHeadingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @case ('H4') {\n <h4 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n @default {\n <h3 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n }\n</ng-template>\n\n<ng-template #headingTemplate>\n <!-- Definition des templates directement ici pour une projection du slot correcte (pas de else)-->\n\n <!-- Titre simple -------------------------------------------------------->\n @if (!hasLink()) {\n @if (heading) {\n <!-- FIXME V2 supprimer le innerHtml-et le span associ\u00E9 -->\n <span [innerHTML]=\"heading\"></span>\n } @else {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n }\n\n <!-- Lien --------------------------------------------------------------->\n @if (hasLink()) {\n <!-- Lien sans t\u00E9l\u00E9chargement -->\n @if (!download) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [ariaLabel]=\"ariaLabel ?? ''\"\n [disabled]=\"disabled\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [label]=\"heading ?? ''\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [mode]=\"enlargeButton ? 'button' : 'link'\"\n (linkSelect)=\"onLinkSelect()\">\n @if (!heading) {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n </dsfr-link>\n } @else {\n <!-- Lien de t\u00E9l\u00E9chargement -->\n <edu-link-download\n [item]=\"itemLink\"\n [downloadDirect]=\"downloadDirect\"\n [downloadAssessFile]=\"downloadAssessFile\"\n [linkTarget]=\"linkTarget\"\n [langCode]=\"downloadLangCode ?? ''\"\n [isButton]=\"isDownloadButton\"\n (linkSelect)=\"onLinkSelect()\"></edu-link-download>\n }\n }\n</ng-template>\n\n<!-- Template s\u00E9par\u00E9 pour avoir une seule d\u00E9finition du select heading (DO NOT DELETE) -->\n<ng-template #headingTemplateSlot>\n <ng-content select=\"[heading]\"></ng-content>\n</ng-template>\n", styles: [".fr-card{height:100%}.fr-card__start>dsfr-tag,.fr-card__start>dsfr-badge{display:block;margin-bottom:1rem}.fr-card__start>dsfr-badge{margin-bottom:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: DsfrBadgesGroupComponent, selector: "dsfr-badges-group", inputs: ["badges", "small"] }, { kind: "component", type: DsfrBadgeComponent, selector: "dsfr-badge", inputs: ["label", "noIcon", "badgeSize", "customClass", "size", "severity"] }, { kind: "component", type: DsfrTagsGroupComponent, selector: "dsfr-tags-group", inputs: ["tags", "mode"], outputs: ["tagSelect"] }, { kind: "component", type: DsfrTagComponent, selector: "dsfr-tag", inputs: ["tagId", "customClass", "disabled", "icon", "label", "link", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkExtras", "selected", "small", "tooltipMessage", "ariaLabel", "mode", "id", "routerLink"], outputs: ["tagSelect"] }, { kind: "component", type: SvgIconComponent, selector: "edu-svg-icon", inputs: ["iconPath", "role", "ariaHidden", "alt", "imageRatio"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: LinkDownloadComponent, selector: "edu-link-download", inputs: ["customClass", "downloadAssessFile", "langCode", "linkTarget", "isButton", "downloadDirect", "item"], outputs: ["linkSelect"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-card', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
DsfrBadgesGroupComponent,
DsfrBadgeComponent,
DsfrTagsGroupComponent,
DsfrTagComponent,
SvgIconComponent,
DsfrLinkComponent,
LinkDownloadComponent,
], template: "<div [class]=\"cardClasses\">\n <!-- Card body -->\n <div class=\"fr-card__body\">\n <div class=\"fr-card__content\">\n <!-- Titre du contenu -->\n <ng-container *ngTemplateOutlet=\"cardHeadingTemplate\"></ng-container>\n\n <!-- D\u00E9tail du haut -->\n @if (detail || (tags && tags.length) || (badges && badges.length && !badgesOnMedia)) {\n <div class=\"fr-card__start\">\n @if (tags && tags.length === 1) {\n <dsfr-tag\n [label]=\"tags[0]?.label ?? ''\"\n [tagId]=\"tags[0]?.id ?? ''\"\n [link]=\"tags[0]?.link ?? ''\"\n [route]=\"tags[0]?.route ?? ''\"\n [selected]=\"tags[0]?.selected ?? false\"></dsfr-tag>\n } @else if (tags && tags.length > 1) {\n <dsfr-tags-group [tags]=\"tags\"></dsfr-tags-group>\n }\n @if (badges && badges.length && !badgesOnMedia) {\n @if (badges.length === 1) {\n <dsfr-badge\n [label]=\"badges[0]?.label ?? ''\"\n [severity]=\"badges[0]?.severity\"\n [noIcon]=\"badges[0]?.noIcon ?? false\"\n [customClass]=\"badges[0]?.customClass ?? ''\"></dsfr-badge>\n } @else if (badges.length > 1) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n }\n\n @if (detail || detailIcon) {\n <p class=\"fr-card__detail\" [ngClass]=\"detailIcon\">\n {{ detail }}\n </p>\n }\n </div>\n }\n\n <!-- Description -->\n @if (description) {\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-card__desc\" [innerHTML]=\"description\"></p>\n } @else {\n <div class=\"fr-card__desc\" [innerHTML]=\"description\"></div>\n }\n }\n\n @if (!description && descriptionTemplate) {\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-card__desc\"><ng-container *ngTemplateOutlet=\"descriptionTemplate\"></ng-container></p>\n } @else {\n <div class=\"fr-card__desc\"><ng-container *ngTemplateOutlet=\"descriptionTemplate\"></ng-container></div>\n }\n }\n\n <!-- D\u00E9tail du bas -->\n @if (detailBottom || detailBottomIcon || (downloadAssessFile && download)) {\n <div class=\"fr-card__end\">\n <p class=\"fr-card__detail\" [ngClass]=\"detailBottomIcon\">\n {{ detailBottom }}\n </p>\n </div>\n }\n </div>\n\n <!-- Card footer -->\n @if (hasFooter) {\n <div class=\"fr-card__footer\">\n <ng-content select=\"[footer]\"></ng-content>\n </div>\n }\n </div>\n\n <!-- Card header -->\n @if (imagePath) {\n <div class=\"fr-card__header\">\n <div class=\"fr-card__img\">\n @if (imageType === 'img') {\n <img\n [src]=\"imagePath\"\n class=\"fr-responsive-img\"\n [class]=\"imageRatioClass\"\n [ngStyle]=\"{ 'object-fit': imageFit }\"\n [attr.alt]=\"imageAlt\" />\n }\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit toujours \u00EAtre pr\u00E9sente, sa valeur peut-\u00EAtre vide (image n\u2019apportant pas de sens suppl\u00E9mentaire au contexte) ou non (porteuse de texte ou apportant du sens) selon votre contexte -->\n @if (imageType === 'svg') {\n <edu-svg-icon [iconPath]=\"imagePath\" [imageRatio]=\"imageRatio\"></edu-svg-icon>\n }\n </div>\n @if (badges && badgesOnMedia) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n </div>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #cardHeadingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @case ('H4') {\n <h4 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n @default {\n <h3 class=\"fr-card__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n }\n</ng-template>\n\n<ng-template #headingTemplate>\n <!-- Definition des templates directement ici pour une projection du slot correcte (pas de else)-->\n\n <!-- Titre simple -------------------------------------------------------->\n @if (!hasLink()) {\n @if (heading) {\n <!-- FIXME V2 supprimer le innerHtml-et le span associ\u00E9 -->\n <span [innerHTML]=\"heading\"></span>\n } @else {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n }\n\n <!-- Lien --------------------------------------------------------------->\n @if (hasLink()) {\n <!-- Lien sans t\u00E9l\u00E9chargement -->\n @if (!download) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [ariaLabel]=\"ariaLabel ?? ''\"\n [disabled]=\"disabled\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [label]=\"heading ?? ''\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [mode]=\"enlargeButton ? 'button' : 'link'\"\n (linkSelect)=\"onLinkSelect()\">\n @if (!heading) {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n </dsfr-link>\n } @else {\n <!-- Lien de t\u00E9l\u00E9chargement -->\n <edu-link-download\n [item]=\"itemLink\"\n [downloadDirect]=\"downloadDirect\"\n [downloadAssessFile]=\"downloadAssessFile\"\n [linkTarget]=\"linkTarget\"\n [langCode]=\"downloadLangCode ?? ''\"\n [isButton]=\"isDownloadButton\"\n (linkSelect)=\"onLinkSelect()\"></edu-link-download>\n }\n }\n</ng-template>\n\n<!-- Template s\u00E9par\u00E9 pour avoir une seule d\u00E9finition du select heading (DO NOT DELETE) -->\n<ng-template #headingTemplateSlot>\n <ng-content select=\"[heading]\"></ng-content>\n</ng-template>\n", styles: [".fr-card{height:100%}.fr-card__start>dsfr-tag,.fr-card__start>dsfr-badge{display:block;margin-bottom:1rem}.fr-card__start>dsfr-badge{margin-bottom:.75rem}\n"] }]
}], ctorParameters: () => [], propDecorators: { badgesOnMedia: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], detail: [{
type: Input
}], detailIcon: [{
type: Input
}], detailBottomIcon: [{
type: Input
}], hasFooter: [{
type: Input
}], imageAlt: [{
type: Input
}], imageFit: [{
type: Input
}], imagePath: [{
type: Input
}], imageRatio: [{
type: Input
}], horizontalProportion: [{
type: Input
}], noIcon: [{
type: Input
}], imageType: [{
type: Input
}], isDownloadButton: [{
type: Input
}], cardSelect: [{
type: Output
}], descriptionTemplate: [{
type: ContentChild,
args: ['descriptionTemplate', { static: true }]
}] } });
// -- Background -------------------------------------------------------------------------------------------------------
/**
* @deprecated (since 1.5.0) use {@link DsfrPanelBackgroundConst} instead
*/
var DsfrCardBackgroundConst;
(function (DsfrCardBackgroundConst) {
DsfrCardBackgroundConst["DEFAULT"] = "default";
DsfrCardBackgroundConst["GREY"] = "grey";
DsfrCardBackgroundConst["TRANSPARENT"] = "transparent";
})(DsfrCardBackgroundConst || (DsfrCardBackgroundConst = {}));
// -- Border -----------------------------------------------------------------------------------------------------------
/**
* @deprecated (since 1.5.0) use {@link DsfrPanelBorderConst} instead
*/
var DsfrCardBorderConst;
(function (DsfrCardBorderConst) {
DsfrCardBorderConst["DEFAULT"] = "default";
DsfrCardBorderConst["NO_BORDER"] = "no-border";
DsfrCardBorderConst["SHADOW"] = "shadow";
})(DsfrCardBorderConst || (DsfrCardBorderConst = {}));
// -- Image Fit --------------------------------------------------------------------------------------------------------
/**
* Définit les différentes façons dont l'image doit s'adapter à son conteneur.
*/
var DsfrImageFitConst;
(function (DsfrImageFitConst) {
DsfrImageFitConst["FILL"] = "fill";
DsfrImageFitConst["CONTAIN"] = "contain";
DsfrImageFitConst["COVER"] = "cover";
DsfrImageFitConst["NONE"] = "none";
DsfrImageFitConst["SCALE_DOWN"] = "scale-down";
})(DsfrImageFitConst || (DsfrImageFitConst = {}));
// -- Image Ratio ------------------------------------------------------------------------------------------------------
/**
* Définit les différents ratios supportés par le DSFR pour l'illustration d'un composant `Card`.
*/
var DsfrImageRatioConst;
(function (DsfrImageRatioConst) {
/**
* Ratio largeur sur hauteur de 32 neuvièmes.
*/
DsfrImageRatioConst["RATIO_32_9"] = "16:9/2";
/**
* Ratio largeur sur hauteur de 16 neuvièmes.
*/
DsfrImageRatioConst["RATIO_16_9"] = "16:9";
/**
* Ratio largeur sur hauteur de trois à deux.
*/
DsfrImageRatioConst["RATIO_3_2"] = "3:2";
/**
* Ratio largeur sur hauteur de quatre tiers.
*/
DsfrImageRatioConst["RATIO_4_3"] = "4:3";
/**
* Ratio largeur sur hauteur de un pour un (largeur égale à la hauteur).
*/
DsfrImageRatioConst["RATIO_1_1"] = "1:1";
/**
* Ratio largeur sur hauteur de trois quarts.
*/
DsfrImageRatioConst["RATIO_3_4"] = "3:4";
/**
* Ratio largeur sur hauteur de deux tiers.
*/
DsfrImageRatioConst["RATIO_2_3"] = "2:3";
})(DsfrImageRatioConst || (DsfrImageRatioConst = {}));
// -- Image Type ------------------------------------------------------------------------------------------------------
/**
* Définit les différents formats d'image supportés par le DSFR en illustration d'un composant `Card`.
*/
var DsfrImageTypeConst;
(function (DsfrImageTypeConst) {
/**
* Pour une balise `<img>`.
*/
DsfrImageTypeConst["IMG"] = "img";
/**
* Pour une balise `<svg>`.
*/
DsfrImageTypeConst["SVG"] = "svg";
})(DsfrImageTypeConst || (DsfrImageTypeConst = {}));
class DsfrCardModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardModule, imports: [CommonModule,
DsfrBadgesGroupModule,
DsfrTagsGroupModule,
SvgIconModule,
DsfrLinkModule,
LinkDownloadComponent,
DsfrCardComponent], exports: [DsfrCardComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardModule, imports: [CommonModule,
DsfrBadgesGroupModule,
DsfrTagsGroupModule,
SvgIconModule,
DsfrLinkModule,
LinkDownloadComponent,
DsfrCardComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCardModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrCardComponent],
imports: [
CommonModule,
DsfrBadgesGroupModule,
DsfrTagsGroupModule,
SvgIconModule,
DsfrLinkModule,
LinkDownloadComponent,
DsfrCardComponent,
],
}]
}] });
class ConsentServiceComponent {
constructor() {
/**
* Émis lors l'utilisateur accepte/refuse la finalité.
*/
this.finalityChange = new EventEmitter(); // changement accept/refuse d'une finalité ou sous-finalité
this.fieldsetLabelledBy = '';
this.buttonDescribedBy = 'finality-{{idxFinality}}-legend';
this.buttonControls = 'finality-{{idxFinality}}-collapse';
}
/**
* Au changement utilisateur accepter tout / refuser tout, mettre a jour la finalité et finalités enfants
* Pas de mise à jour si la finalité est en disabled ou exempt (toujours accept=true)
* @param value true (accept all), false (refuse all), undefined (pas de sélection)
*/
set acceptAll(value) {
this._acceptAll = value;
if (this._acceptAll !== undefined && !this.finality.disabled && !this.finality.exempt) {
this.finality.accept = this._acceptAll;
this.finality.subFinalities?.map((f) => (f.accept = this._acceptAll));
}
}
ngOnInit() {
const index = this.idxFinality;
this.fieldsetLabelledBy = `finality-${index}-legend finality-${index}}-desc`;
this.buttonDescribedBy = `finality-${index}-legend`;
this.buttonControls = `finality-${index}-collapse`;
if (this.finality.exempt || this.finality.disabled) {
this.finality.accept = true;
}
}
/**
* Changement accept/refuse de la finalité, mettre a jour les sous-finalités enfant
* @param subFinality sous-finalité concernée
*/
onFinalityRadioChange() {
if (this.finality.subFinalities) {
this.finality.subFinalities.map((f) => (f.accept = this.finality.accept));
}
this.finalityChange.emit(this.finality);
}
/**
* Changement accept/refuse d'une sous finalité, remettre la finalité parente en accept undefined si necessaire
* @param subFinality sous-finalité concernée
*/
onSubFinalityRadioChange(subFinality) {
if ((this.finality.accept && subFinality.accept === false) ||
(this.finality.accept === false && subFinality.accept)) {
this.finality.accept = undefined;
}
this.finalityChange.emit(this.finality);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConsentServiceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: ConsentServiceComponent, isStandalone: true, selector: "edu-consent-service", inputs: { finality: "finality", idxFinality: "idxFinality", acceptAll: "acceptAll" }, outputs: { finalityChange: "finalityChange" }, ngImport: i0, template: "<div class=\"fr-consent-service\">\n <fieldset\n class=\"fr-fieldset\"\n [attr.role]=\"finality.description ? 'group' : null\"\n [attr.aria-labelledby]=\"finality.description ? fieldsetLabelledBy : null\">\n <legend class=\"fr-consent-service__title\">{{ finality.name }}</legend>\n <div class=\"fr-consent-service__radios\">\n @if (!finality.exempt) {\n <div class=\"fr-radio-group\">\n <input\n (change)=\"onFinalityRadioChange()\"\n [value]=\"true\"\n [(ngModel)]=\"finality.accept\"\n [disabled]=\"finality.disabled ? true : false\"\n type=\"radio\"\n id=\"consent-finality-{{ idxFinality }}-accept\"\n name=\"consent-finality-{{ idxFinality }}\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-accept\">\n {{ 'commons.accept' | dsfrI18n }}\n </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n [value]=\"false\"\n [(ngModel)]=\"finality.accept\"\n [disabled]=\"finality.disabled ? true : false\"\n (change)=\"onFinalityRadioChange()\"\n type=\"radio\"\n id=\"consent-finality-{{ idxFinality }}-refuse\"\n name=\"consent-finality-{{ idxFinality }}\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-refuse\">\n {{ 'commons.refuse' | dsfrI18n }}\n </label>\n </div>\n } @else {\n <p>{{ 'consent.manager.finalityExempt' | dsfrI18n }}</p>\n }\n </div>\n @if (finality.description) {\n <p id=\"finality-{{ idxFinality }}-desc\" class=\"fr-consent-service__desc\">\n {{ finality.description }}\n </p>\n }\n @if (finality.subFinalities) {\n <div class=\"fr-consent-service__collapse\">\n <button\n type=\"button\"\n class=\"fr-consent-service__collapse-btn\"\n aria-expanded=\"false\"\n [attr.aria-describedby]=\"buttonDescribedBy\"\n [attr.aria-controls]=\"buttonControls\">\n {{ 'consent.manager.showDetails' | dsfrI18n }}\n </button>\n </div>\n }\n <div class=\"fr-consent-services fr-collapse\" id=\"finality-{{ idxFinality }}-collapse\">\n <!-- Sous finalit\u00E9s -->\n @for (subfinality of finality.subFinalities; track idx_sub; let idx_sub = $index) {\n <div class=\"fr-consent-service\">\n <fieldset class=\"fr-fieldset fr-fieldset--inline\">\n <legend id=\"finality-{{ idxFinality }}-sub-{{ idx_sub }}-legend\" class=\"fr-consent-service__title\">\n {{ subfinality.name }}\n </legend>\n <div class=\"fr-consent-service__radios fr-fieldset--inline\">\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"subfinality.accept\"\n type=\"radio\"\n (change)=\"onSubFinalityRadioChange(subfinality)\"\n [value]=\"true\"\n id=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-accept\"\n name=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-accept\">\n {{ 'commons.accept' | dsfrI18n }}\n </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n (change)=\"onSubFinalityRadioChange(subfinality)\"\n type=\"radio\"\n [value]=\"false\"\n [(ngModel)]=\"subfinality.accept\"\n id=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-refuse\"\n name=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-refuse\">\n {{ 'commons.refuse' | dsfrI18n }}\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n }\n </div>\n </fieldset>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConsentServiceComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-consent-service', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrI18nPipe], template: "<div class=\"fr-consent-service\">\n <fieldset\n class=\"fr-fieldset\"\n [attr.role]=\"finality.description ? 'group' : null\"\n [attr.aria-labelledby]=\"finality.description ? fieldsetLabelledBy : null\">\n <legend class=\"fr-consent-service__title\">{{ finality.name }}</legend>\n <div class=\"fr-consent-service__radios\">\n @if (!finality.exempt) {\n <div class=\"fr-radio-group\">\n <input\n (change)=\"onFinalityRadioChange()\"\n [value]=\"true\"\n [(ngModel)]=\"finality.accept\"\n [disabled]=\"finality.disabled ? true : false\"\n type=\"radio\"\n id=\"consent-finality-{{ idxFinality }}-accept\"\n name=\"consent-finality-{{ idxFinality }}\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-accept\">\n {{ 'commons.accept' | dsfrI18n }}\n </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n [value]=\"false\"\n [(ngModel)]=\"finality.accept\"\n [disabled]=\"finality.disabled ? true : false\"\n (change)=\"onFinalityRadioChange()\"\n type=\"radio\"\n id=\"consent-finality-{{ idxFinality }}-refuse\"\n name=\"consent-finality-{{ idxFinality }}\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-refuse\">\n {{ 'commons.refuse' | dsfrI18n }}\n </label>\n </div>\n } @else {\n <p>{{ 'consent.manager.finalityExempt' | dsfrI18n }}</p>\n }\n </div>\n @if (finality.description) {\n <p id=\"finality-{{ idxFinality }}-desc\" class=\"fr-consent-service__desc\">\n {{ finality.description }}\n </p>\n }\n @if (finality.subFinalities) {\n <div class=\"fr-consent-service__collapse\">\n <button\n type=\"button\"\n class=\"fr-consent-service__collapse-btn\"\n aria-expanded=\"false\"\n [attr.aria-describedby]=\"buttonDescribedBy\"\n [attr.aria-controls]=\"buttonControls\">\n {{ 'consent.manager.showDetails' | dsfrI18n }}\n </button>\n </div>\n }\n <div class=\"fr-consent-services fr-collapse\" id=\"finality-{{ idxFinality }}-collapse\">\n <!-- Sous finalit\u00E9s -->\n @for (subfinality of finality.subFinalities; track idx_sub; let idx_sub = $index) {\n <div class=\"fr-consent-service\">\n <fieldset class=\"fr-fieldset fr-fieldset--inline\">\n <legend id=\"finality-{{ idxFinality }}-sub-{{ idx_sub }}-legend\" class=\"fr-consent-service__title\">\n {{ subfinality.name }}\n </legend>\n <div class=\"fr-consent-service__radios fr-fieldset--inline\">\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"subfinality.accept\"\n type=\"radio\"\n (change)=\"onSubFinalityRadioChange(subfinality)\"\n [value]=\"true\"\n id=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-accept\"\n name=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-accept\">\n {{ 'commons.accept' | dsfrI18n }}\n </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n (change)=\"onSubFinalityRadioChange(subfinality)\"\n type=\"radio\"\n [value]=\"false\"\n [(ngModel)]=\"subfinality.accept\"\n id=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-refuse\"\n name=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-\" />\n <label class=\"fr-label\" for=\"consent-finality-{{ idxFinality }}-sub-{{ idx_sub }}-refuse\">\n {{ 'commons.refuse' | dsfrI18n }}\n </label>\n </div>\n </div>\n </fieldset>\n </div>\n }\n </div>\n </fieldset>\n</div>\n" }]
}], propDecorators: { finality: [{
type: Input,
args: [{ required: true }]
}], idxFinality: [{
type: Input,
args: [{ required: true }]
}], finalityChange: [{
type: Output
}], acceptAll: [{
type: Input
}] } });
const CONSENT_MODAL_ID = 'consent-modal-id';
class ConsentManagerComponent {
constructor() {
/** Modèle de présentation : liste de finalités. */
this.finalities = [];
/** Confirmation de la personnalisation des finalités. */
this.confirmSelect = new EventEmitter();
/** Changement selection accept/refuse d'une finalité ou sous-finalité. */
this.finalityChange = new EventEmitter();
/**
* Signale quand la route rgdp est sélectionnée.
*
* @since 1.7
*/
this.rgpdRouteSelect = new EventEmitter();
/*Identifiant de la modale ('consent-modal-id' par défaut) */
this.modalId = CONSENT_MODAL_ID;
/** internal */ this.titleId = '';
}
ngOnInit() {
this.acceptAll = undefined;
this.titleId = this.modalId + '-title';
}
/**
* Changement selection accept/refuse d'une finalité ou sous-finalité
* @param finality
*/
onFinalityChange(finality) {
// update selon l'état des sous-finalités
if (finality.subFinalities) {
if (finality.subFinalities.every((f) => f.accept === true)) {
finality.accept = true;
}
else if (finality.subFinalities.every((f) => f.accept === false)) {
finality.accept = false;
}
}
// update état global
this.acceptAll = this.finalities.every((f) => f.accept === finality.accept || f.exempt || f.disabled)
? finality.accept
: undefined;
this.finalityChange.emit(finality);
}
/**
* Confirmation de la sélection
*/
onConfirm() {
this.confirmSelect.emit(this.finalities);
}
/** @since 1.7 */
onRgpdSelect() {
// Signale quand la route rgdp est sélectionnée
const route = this.rgpdNavigation?.route;
if (route)
this.rgpdRouteSelect.emit(route);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConsentManagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: ConsentManagerComponent, isStandalone: true, selector: "edu-consent-manager", inputs: { rgpdNavigation: "rgpdNavigation", finalities: "finalities", headingLevel: "headingLevel", modalId: "modalId" }, outputs: { confirmSelect: "confirmSelect", finalityChange: "finalityChange", rgpdRouteSelect: "rgpdRouteSelect" }, ngImport: i0, template: "<dialog [id]=\"modalId\" class=\"fr-modal\" role=\"dialog\" [attr.aria-labelledby]=\"titleId\">\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-10 fr-col-lg-8\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"modalId\"\n [attr.aria-label]=\"'modal.ariaLabel' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <edu-heading\n [allowFirstLevel]=\"true\"\n [headingId]=\"titleId\"\n [level]=\"headingLevel\"\n defaultLevel=\"H2\"\n customClass=\"fr-modal__title\">\n {{ 'consent.manager.title' | dsfrI18n }}\n </edu-heading>\n\n <!-- BUG: Classe 'fr-consent-manager' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-consent-manager\">\n <!-- Finalit\u00E9s -->\n <div class=\"fr-consent-service fr-consent-manager__header\">\n <fieldset class=\"fr-fieldset\">\n <legend id=\"finality-legend\" class=\"fr-consent-service__title\">\n {{ 'consent.manager.preferences' | dsfrI18n }}\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"rgpdNavigation.link ?? ''\"\n [linkTarget]=\"rgpdNavigation.linkTarget\"\n [route]=\"rgpdNavigation.route ?? ''\"\n [routePath]=\"rgpdNavigation.routerLink ?? ''\"\n [routerLinkActive]=\"rgpdNavigation.routerLinkActive ?? ''\"\n [routerLinkExtras]=\"rgpdNavigation.routerLinkExtras\"\n (linkSelect)=\"onRgpdSelect()\"\n >{{ 'consent.manager.personalData' | dsfrI18n }}</dsfr-link\n >\n </legend>\n <div class=\"fr-consent-service__radios\">\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"acceptAll\"\n [value]=\"true\"\n type=\"radio\"\n id=\"consent-all-accept\"\n name=\"consent-all\" />\n <label class=\"fr-label\" for=\"consent-all-accept\"> {{ 'consent.acceptAll' | dsfrI18n }} </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"acceptAll\"\n [value]=\"false\"\n type=\"radio\"\n id=\"consent-all-refuse\"\n name=\"consent-all\" />\n <label class=\"fr-label\" for=\"consent-all-refuse\"> {{ 'consent.refuseAll' | dsfrI18n }} </label>\n </div>\n </div>\n </fieldset>\n </div>\n\n @for (finality of finalities; track i; let i = $index) {\n <edu-consent-service\n [finality]=\"finality\"\n [idxFinality]=\"i\"\n [acceptAll]=\"acceptAll\"\n (finalityChange)=\"onFinalityChange(finality)\"></edu-consent-service>\n }\n\n <!-- Bouton de confirmation/fermeture -->\n <ul class=\"fr-consent-manager__buttons fr-btns-group fr-btns-group--right fr-btns-group--inline-sm\">\n <li>\n <button type=\"button\" (click)=\"onConfirm()\" [attr.aria-controls]=\"modalId\" class=\"fr-btn\">\n {{ 'consent.manager.confirmChoices' | dsfrI18n }}\n </button>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</dialog>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: ConsentServiceComponent, selector: "edu-consent-service", inputs: ["finality", "idxFinality", "acceptAll"], outputs: ["finalityChange"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ConsentManagerComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-consent-manager', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, HeadingComponent, DsfrLinkComponent, DsfrI18nPipe, ConsentServiceComponent], template: "<dialog [id]=\"modalId\" class=\"fr-modal\" role=\"dialog\" [attr.aria-labelledby]=\"titleId\">\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-10 fr-col-lg-8\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"modalId\"\n [attr.aria-label]=\"'modal.ariaLabel' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <edu-heading\n [allowFirstLevel]=\"true\"\n [headingId]=\"titleId\"\n [level]=\"headingLevel\"\n defaultLevel=\"H2\"\n customClass=\"fr-modal__title\">\n {{ 'consent.manager.title' | dsfrI18n }}\n </edu-heading>\n\n <!-- BUG: Classe 'fr-consent-manager' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-consent-manager\">\n <!-- Finalit\u00E9s -->\n <div class=\"fr-consent-service fr-consent-manager__header\">\n <fieldset class=\"fr-fieldset\">\n <legend id=\"finality-legend\" class=\"fr-consent-service__title\">\n {{ 'consent.manager.preferences' | dsfrI18n }}\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"rgpdNavigation.link ?? ''\"\n [linkTarget]=\"rgpdNavigation.linkTarget\"\n [route]=\"rgpdNavigation.route ?? ''\"\n [routePath]=\"rgpdNavigation.routerLink ?? ''\"\n [routerLinkActive]=\"rgpdNavigation.routerLinkActive ?? ''\"\n [routerLinkExtras]=\"rgpdNavigation.routerLinkExtras\"\n (linkSelect)=\"onRgpdSelect()\"\n >{{ 'consent.manager.personalData' | dsfrI18n }}</dsfr-link\n >\n </legend>\n <div class=\"fr-consent-service__radios\">\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"acceptAll\"\n [value]=\"true\"\n type=\"radio\"\n id=\"consent-all-accept\"\n name=\"consent-all\" />\n <label class=\"fr-label\" for=\"consent-all-accept\"> {{ 'consent.acceptAll' | dsfrI18n }} </label>\n </div>\n <div class=\"fr-radio-group\">\n <input\n [(ngModel)]=\"acceptAll\"\n [value]=\"false\"\n type=\"radio\"\n id=\"consent-all-refuse\"\n name=\"consent-all\" />\n <label class=\"fr-label\" for=\"consent-all-refuse\"> {{ 'consent.refuseAll' | dsfrI18n }} </label>\n </div>\n </div>\n </fieldset>\n </div>\n\n @for (finality of finalities; track i; let i = $index) {\n <edu-consent-service\n [finality]=\"finality\"\n [idxFinality]=\"i\"\n [acceptAll]=\"acceptAll\"\n (finalityChange)=\"onFinalityChange(finality)\"></edu-consent-service>\n }\n\n <!-- Bouton de confirmation/fermeture -->\n <ul class=\"fr-consent-manager__buttons fr-btns-group fr-btns-group--right fr-btns-group--inline-sm\">\n <li>\n <button type=\"button\" (click)=\"onConfirm()\" [attr.aria-controls]=\"modalId\" class=\"fr-btn\">\n {{ 'consent.manager.confirmChoices' | dsfrI18n }}\n </button>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</dialog>\n" }]
}], propDecorators: { rgpdNavigation: [{
type: Input,
args: [{ required: true }]
}], finalities: [{
type: Input
}], headingLevel: [{
type: Input
}], confirmSelect: [{
type: Output
}], finalityChange: [{
type: Output
}], rgpdRouteSelect: [{
type: Output
}], modalId: [{
type: Input
}] } });
class DsfrConsentBannerComponent {
constructor() {
/**
* URL de présentation des données personnelles. Utilisé si welcome n'est pas renseigné
* RGPD : Règlement Général sur la Protection des Données
*/
this.rgpdLink = 'https://www.transformation.gouv.fr/donnees-personnelles-et-cookies';
/**
* Modèle de présentation des finalités.
*/
this.finalities = [];
/**
* Sélection bouton Tout accepter.
*/
this.acceptAllSelect = new EventEmitter();
/**
* Sélection bouton Tout refuser.
*/
this.refuseAllSelect = new EventEmitter();
/** Sélection bouton Personnaliser */
this.customizeSelect = new EventEmitter();
/** Validation de la personnalisation */
this.confirmCustomizeSelect = new EventEmitter();
/** Sélection de Accepter ou Refuser pour une finalité */
this.finalityChange = new EventEmitter();
/**
* Signale quand la route rgdp est sélectionnée.
*
* @since 1.7
*/
this.rgpdRouteSelect = new EventEmitter();
/** Identifiant de la modale ('consent-modal' par défaut) */
this.modalId = CONSENT_MODAL_ID;
}
/** @deprecated (since 1.7) utiliser `rgdpLink` à la place. */
get rgpdUrl() {
return this.rgpdLink;
}
/** @deprecated (since 1.7) utiliser 'heading' à la place. */
get title() {
return this.heading;
}
/** @deprecated (since 1.7) utiliser `rgdpLink` à la place. */
set rgpdUrl(value) {
this.rgpdLink = value;
}
/** @deprecated (since 1.7) utiliser `heading` à la place. */
set title(value) {
this.heading = value;
}
/**
* Accepter tout
* @internal
*/
onAcceptAll() {
this.acceptAllSelect.emit();
}
/**
* Refuser tout
* @internal
*/
onRefuseAll() {
this.refuseAllSelect.emit();
}
/**
* Changement de sélection accept/refuse d'une finalité ou sous-finalité.
* @internal
*/
onFinalityChange(finality) {
this.finalityChange.emit(finality);
}
/**
* Ouverture du panneau de personnalisation des finalités.
* @internal
*/
onCustomizeSelect() {
this.customizeSelect.emit();
}
/**
* Confirmation de la personnalisation des finalités.
* @param finalities liste des finalités
* @internal
*/
onConfirmCustomize(finalities) {
this.confirmCustomizeSelect.emit(finalities);
}
/**
* Emet un événement lors de la sélection du lien RGPD si l'accès aux RGPD est fait via la propriété 'route'.
*
* @since 1.7
* @internal
*/
onRgpdSelect() {
// Relais. Signale quand la route rgdp est sélectionnée
if (this.rgpdRoute)
this.rgpdRouteSelect.emit(this.rgpdRoute);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrConsentBannerComponent, isStandalone: true, selector: "dsfr-consent-banner", inputs: { rgpdLink: "rgpdLink", rgpdLinkTarget: "rgpdLinkTarget", rgpdRoute: "rgpdRoute", rgpdRouterLink: "rgpdRouterLink", rgpdRouterLinkActive: "rgpdRouterLinkActive", rgpdRouterLinkExtras: "rgpdRouterLinkExtras", heading: "heading", finalities: "finalities", welcome: "welcome", modalId: "modalId", rgpdUrl: "rgpdUrl", title: "title" }, outputs: { acceptAllSelect: "acceptAllSelect", refuseAllSelect: "refuseAllSelect", customizeSelect: "customizeSelect", confirmCustomizeSelect: "confirmCustomizeSelect", finalityChange: "finalityChange", rgpdRouteSelect: "rgpdRouteSelect" }, ngImport: i0, template: "<div class=\"fr-consent-banner\">\n @if (title) {\n <h2 class=\"fr-h6\">{{ title }}</h2>\n }\n <!-- BUG: Classe 'fr-consent-banner__content' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-consent-banner__content\">\n @if (welcome) {\n <p class=\"fr-text--sm\" [innerHtml]=\"welcome\"></p>\n } @else {\n <p class=\"fr-text--sm\">\n {{ 'consent.description.prelink' | dsfrI18n }}\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"rgpdLink\"\n [linkTarget]=\"rgpdLinkTarget\"\n [route]=\"rgpdRoute ?? ''\"\n [routePath]=\"rgpdRouterLink ?? ''\"\n [routerLinkActive]=\"rgpdRouterLinkActive ?? ''\"\n [routerLinkExtras]=\"rgpdRouterLinkExtras\"\n [tooltipMessage]=\"'consent.welcome[1]' | dsfrI18n\"\n (linkSelect)=\"onRgpdSelect()\"\n >{{ 'consent.description.link' | dsfrI18n }}</dsfr-link\n >{{ 'consent.description.postlink' | dsfrI18n }}\n </p>\n }\n </div>\n <ul\n class=\"fr-consent-banner__buttons fr-btns-group fr-btns-group--right fr-btns-group--inline-reverse fr-btns-group--inline-sm\">\n <li>\n <button type=\"button\" (click)=\"onAcceptAll()\" class=\"fr-btn\" title=\"{{ 'consent.allowAllCookies' | dsfrI18n }}\">\n {{ 'consent.acceptAll' | dsfrI18n }}\n </button>\n </li>\n <li>\n <button type=\"button\" (click)=\"onRefuseAll()\" class=\"fr-btn\" title=\"{{ 'consent.refuseAllCookies' | dsfrI18n }}\">\n {{ 'consent.refuseAll' | dsfrI18n }}\n </button>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--secondary\"\n (click)=\"onCustomizeSelect()\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"modalId\"\n title=\"{{ 'consent.customizeCookies' | dsfrI18n }}\">\n {{ 'consent.customize' | dsfrI18n }}\n </button>\n </li>\n </ul>\n</div>\n\n<edu-consent-manager\n [rgpdNavigation]=\"{\n link: rgpdLink,\n linkTarget: rgpdLinkTarget,\n route: rgpdRoute,\n routerLink: rgpdRouterLink,\n routerLinkActive: rgpdRouterLinkActive,\n routerLinkExtras: rgpdRouterLinkExtras,\n }\"\n [finalities]=\"finalities\"\n (finalityChange)=\"onFinalityChange($event)\"\n [modalId]=\"modalId\"\n (confirmSelect)=\"onConfirmCustomize(finalities)\"\n (rgpdRouteSelect)=\"onRgpdSelect()\"></edu-consent-manager>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: ConsentManagerComponent, selector: "edu-consent-manager", inputs: ["rgpdNavigation", "finalities", "headingLevel", "modalId"], outputs: ["confirmSelect", "finalityChange", "rgpdRouteSelect"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-consent-banner', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, ConsentManagerComponent, DsfrLinkComponent, DsfrI18nPipe], template: "<div class=\"fr-consent-banner\">\n @if (title) {\n <h2 class=\"fr-h6\">{{ title }}</h2>\n }\n <!-- BUG: Classe 'fr-consent-banner__content' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-consent-banner__content\">\n @if (welcome) {\n <p class=\"fr-text--sm\" [innerHtml]=\"welcome\"></p>\n } @else {\n <p class=\"fr-text--sm\">\n {{ 'consent.description.prelink' | dsfrI18n }}\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"rgpdLink\"\n [linkTarget]=\"rgpdLinkTarget\"\n [route]=\"rgpdRoute ?? ''\"\n [routePath]=\"rgpdRouterLink ?? ''\"\n [routerLinkActive]=\"rgpdRouterLinkActive ?? ''\"\n [routerLinkExtras]=\"rgpdRouterLinkExtras\"\n [tooltipMessage]=\"'consent.welcome[1]' | dsfrI18n\"\n (linkSelect)=\"onRgpdSelect()\"\n >{{ 'consent.description.link' | dsfrI18n }}</dsfr-link\n >{{ 'consent.description.postlink' | dsfrI18n }}\n </p>\n }\n </div>\n <ul\n class=\"fr-consent-banner__buttons fr-btns-group fr-btns-group--right fr-btns-group--inline-reverse fr-btns-group--inline-sm\">\n <li>\n <button type=\"button\" (click)=\"onAcceptAll()\" class=\"fr-btn\" title=\"{{ 'consent.allowAllCookies' | dsfrI18n }}\">\n {{ 'consent.acceptAll' | dsfrI18n }}\n </button>\n </li>\n <li>\n <button type=\"button\" (click)=\"onRefuseAll()\" class=\"fr-btn\" title=\"{{ 'consent.refuseAllCookies' | dsfrI18n }}\">\n {{ 'consent.refuseAll' | dsfrI18n }}\n </button>\n </li>\n <li>\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--secondary\"\n (click)=\"onCustomizeSelect()\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"modalId\"\n title=\"{{ 'consent.customizeCookies' | dsfrI18n }}\">\n {{ 'consent.customize' | dsfrI18n }}\n </button>\n </li>\n </ul>\n</div>\n\n<edu-consent-manager\n [rgpdNavigation]=\"{\n link: rgpdLink,\n linkTarget: rgpdLinkTarget,\n route: rgpdRoute,\n routerLink: rgpdRouterLink,\n routerLinkActive: rgpdRouterLinkActive,\n routerLinkExtras: rgpdRouterLinkExtras,\n }\"\n [finalities]=\"finalities\"\n (finalityChange)=\"onFinalityChange($event)\"\n [modalId]=\"modalId\"\n (confirmSelect)=\"onConfirmCustomize(finalities)\"\n (rgpdRouteSelect)=\"onRgpdSelect()\"></edu-consent-manager>\n" }]
}], propDecorators: { rgpdLink: [{
type: Input
}], rgpdLinkTarget: [{
type: Input
}], rgpdRoute: [{
type: Input
}], rgpdRouterLink: [{
type: Input
}], rgpdRouterLinkActive: [{
type: Input
}], rgpdRouterLinkExtras: [{
type: Input
}], heading: [{
type: Input
}], finalities: [{
type: Input
}], acceptAllSelect: [{
type: Output
}], refuseAllSelect: [{
type: Output
}], customizeSelect: [{
type: Output
}], confirmCustomizeSelect: [{
type: Output
}], finalityChange: [{
type: Output
}], rgpdRouteSelect: [{
type: Output
}], welcome: [{
type: Input
}], modalId: [{
type: Input
}], rgpdUrl: [{
type: Input
}], title: [{
type: Input
}] } });
class DsfrConsentBannerModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerModule, imports: [CommonModule,
FormsModule,
ConsentManagerComponent,
DsfrLinkComponent,
DsfrI18nPipe,
DsfrConsentBannerComponent], exports: [DsfrConsentBannerComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerModule, imports: [CommonModule,
FormsModule,
ConsentManagerComponent,
DsfrLinkComponent,
DsfrConsentBannerComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrConsentBannerModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrConsentBannerComponent],
imports: [
CommonModule,
FormsModule,
ConsentManagerComponent,
DsfrLinkComponent,
DsfrI18nPipe,
DsfrConsentBannerComponent,
],
}]
}] });
class DsfrTranscriptionComponent {
constructor() {
this.collapseId = '';
this.dialogId = '';
this.titleId = '';
this.roleDialog = false;
this.elementRef = inject(ElementRef);
}
ngAfterContentInit() {
this.collapseId = newUniqueId();
this.dialogId = newUniqueId();
this.titleId = newUniqueId();
}
ngAfterViewInit() {
// Pour pallier `.fr-content-media + .fr-transcription`
const previousElt = this.elementRef.nativeElement.previousElementSibling;
if (previousElt && previousElt.classList.contains('fr-content-media')) {
const transcriptionElt = this.elementRef.nativeElement.querySelector('.fr-transcription');
transcriptionElt?.classList.add('follows-content');
}
}
enlargeScript() {
this.roleDialog = !this.roleDialog;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranscriptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTranscriptionComponent, isStandalone: true, selector: "dsfr-transcription", inputs: { content: "content", heading: "heading" }, ngImport: i0, template: "<div class=\"fr-transcription\">\n <button type=\"button\" class=\"fr-transcription__btn\" aria-expanded=\"false\" [attr.aria-controls]=\"collapseId\">\n {{ 'transcription.button' | dsfrI18n }}\n </button>\n <div class=\"fr-collapse\" [id]=\"collapseId\">\n <div class=\"fr-transcription__footer\">\n <div class=\"fr-transcription__actions-group\">\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--fullscreen\"\n [attr.aria-controls]=\"dialogId\"\n [attr.aria-label]=\"'transcription.ariaLabel' | dsfrI18n\"\n data-fr-opened=\"false\">\n {{ 'commons.enlarge' | dsfrI18n }}\n </button>\n </div>\n </div>\n <div [id]=\"dialogId\" class=\"fr-modal\" [attr.role]=\"roleDialog ? 'dialog' : null\" [attr.aria-labelledby]=\"titleId\">\n <!-- Suppression de \"fr-container fr-container-md\" -->\n <div class=\"fr-container--fluid\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <!-- Suppression de \"fr-col-12 fr-col-md-10 fr-col-lg-8\" -->\n <div class=\"\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"dialogId\"\n [title]=\"'commons.close' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <h1 [id]=\"titleId\" class=\"fr-modal__title\">\n {{ heading }}\n </h1>\n @if (content) {\n <div [innerHTML]=\"content\"></div>\n } @else {\n <ng-content></ng-content>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".follows-content{margin-top:-1rem;margin-bottom:2.5rem}\n"], dependencies: [{ kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranscriptionComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-transcription', encapsulation: ViewEncapsulation.None, imports: [DsfrI18nPipe], template: "<div class=\"fr-transcription\">\n <button type=\"button\" class=\"fr-transcription__btn\" aria-expanded=\"false\" [attr.aria-controls]=\"collapseId\">\n {{ 'transcription.button' | dsfrI18n }}\n </button>\n <div class=\"fr-collapse\" [id]=\"collapseId\">\n <div class=\"fr-transcription__footer\">\n <div class=\"fr-transcription__actions-group\">\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--fullscreen\"\n [attr.aria-controls]=\"dialogId\"\n [attr.aria-label]=\"'transcription.ariaLabel' | dsfrI18n\"\n data-fr-opened=\"false\">\n {{ 'commons.enlarge' | dsfrI18n }}\n </button>\n </div>\n </div>\n <div [id]=\"dialogId\" class=\"fr-modal\" [attr.role]=\"roleDialog ? 'dialog' : null\" [attr.aria-labelledby]=\"titleId\">\n <!-- Suppression de \"fr-container fr-container-md\" -->\n <div class=\"fr-container--fluid\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <!-- Suppression de \"fr-col-12 fr-col-md-10 fr-col-lg-8\" -->\n <div class=\"\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"dialogId\"\n [title]=\"'commons.close' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <h1 [id]=\"titleId\" class=\"fr-modal__title\">\n {{ heading }}\n </h1>\n @if (content) {\n <div [innerHTML]=\"content\"></div>\n } @else {\n <ng-content></ng-content>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".follows-content{margin-top:-1rem;margin-bottom:2.5rem}\n"] }]
}], propDecorators: { content: [{
type: Input
}], heading: [{
type: Input
}] } });
class DsfrContentComponent {
constructor() {
// -- Commun images / vidéos -----------------------------------------------------------------------------------------
/** Type du média 'image' | 'video', 'image' par défaut. */
this.type = 'image';
/** Le format conseillé par défaut est le 16:9. */
this.ratio = '16:9';
/** Redimensionnement du composant : `LG` : 125%, `MD` : 100% (défault), `SM` : 75%. */
this.contentSize = DsfrSizeConst.MD;
// -- Images ---------------------------------------------------------------------------------------------------------
/**
* Spécifique aux images.
* Alternative d'une image (attribut alt), doit toujours être présente, sa valeur peut être vide (image n’apportant
* pas de sens supplémentaire au contexte) ou non (porteuse de texte ou apportant du sens) selon votre contexte.
*/
this.alternate = '';
// -------------------------------------------------------------------------------------------------------------------
/**
* Si une route est spécifiée, émet un événement avec la route comme valeur, sinon n'émet rien.
*/
this.linkSelect = new EventEmitter();
this.sanitizer = inject(DomSanitizer);
this.loggerService = inject(LoggerService);
}
get size() {
return this.contentSize;
}
/** @deprecated (since 1.6) utiliser `transcriptionContent` à la place */
get transcription() {
return this.transcriptionContent;
}
/** @deprecated (since 1.6) utiliser `transcriptionLinkLabel` à la place */
get transcriptionLabel() {
return this.linkLabel;
}
get imageRatioClass() {
return ratioClass(this.ratio);
}
/** @deprecated (since 1.6) utiliser `transcriptionContent` à la place */
set transcription(value) {
this.transcriptionContent = value;
}
/** @deprecated (since 1.6) utiliser `transcriptionLinkLabel` à la place */
set transcriptionLabel(value) {
this.linkLabel = value;
}
/**
* Redimensionnement du composant : `LG` : 125%, `MD` : 100% (défault), `SM` : 75%.
*
* @deprecated (since 1.17.0) utiliser contentSize à la place
*/
set size(value) {
this.contentSize = value?.toUpperCase();
}
/**
* @deprecated (since 1.11.5) utiliser `routePath` à la place.
* routerLink provoque un bug accessibilité sur la navigation au clavier (ajout d'un tabindex=0)
**/
set routerLink(value) {
if (value)
this.routePath = value;
}
ngOnInit() {
this.verifyAccessibility();
}
/** @internal */
onLinkSelect() {
if (this.route) {
this.linkSelect.emit(this.route);
}
}
/**
* @internal
*/
sanitizedSource() {
return this.sanitizer.bypassSecurityTrustResourceUrl(this.source);
}
verifyAccessibility() {
const missingVideoTitle = this.type === 'video' && !this.tooltipMessage;
if (missingVideoTitle) {
this.loggerService.warn('dsfr-content : Renseigner le titre du contenu est obligatoire (tooltipMessage).');
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrContentComponent, isStandalone: true, selector: "dsfr-content", inputs: { type: "type", legend: "legend", ratio: "ratio", source: "source", linkLabel: "linkLabel", link: "link", linkTarget: "linkTarget", route: "route", routePath: "routePath", routerLinkActive: "routerLinkActive", routerLinkExtras: "routerLinkExtras", tooltipMessage: "tooltipMessage", contentSize: "contentSize", transcriptionContent: "transcriptionContent", transcriptionHeading: "transcriptionHeading", alternate: "alternate", transcription: "transcription", transcriptionLabel: "transcriptionLabel", size: "size", routerLink: "routerLink" }, outputs: { linkSelect: "linkSelect" }, queries: [{ propertyName: "contentMediaTemplate", first: true, predicate: ["contentMediaTemplate"], descendants: true, static: true }], ngImport: i0, template: "<figure\n class=\"fr-content-media\"\n [class.fr-content-media--lg]=\"contentSize === 'LG'\"\n [class.fr-content-media--sm]=\"contentSize === 'SM'\"\n role=\"group\"\n [attr.aria-label]=\"legend\">\n @if (contentMediaTemplate) {\n <ng-container *ngTemplateOutlet=\"contentMediaTemplate\"></ng-container>\n } @else {\n @if (type === 'image') {\n <div class=\"fr-content-media__img\">\n @if (source) {\n <img\n class=\"fr-responsive-img\"\n [attr.title]=\"tooltipMessage || null\"\n [class]=\"imageRatioClass\"\n [src]=\"source\"\n [alt]=\"alternate\" />\n }\n <!-- since 1.6 slot par d\u00E9faut d\u00E9pr\u00E9ci\u00E9 -->\n <ng-content></ng-content>\n <!-- since 1.6 ajout du slot 'svg' -->\n <ng-content select=\"[svg]\"></ng-content>\n </div>\n } @else if (type === 'video' && source) {\n <iframe\n [attr.title]=\"tooltipMessage || null\"\n class=\"fr-responsive-vid\"\n [class.fr-ratio-16x9]=\"ratio === '16:9'\"\n [class.fr-ratio-4x3]=\"ratio === '4:3'\"\n [class.fr-ratio-1x1]=\"ratio === '1:1'\"\n [src]=\"sanitizedSource()\"\n allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n allowfullscreen></iframe>\n }\n }\n\n @if (legend || linkLabel) {\n <figcaption class=\"fr-content-media__caption\">\n {{ legend }}\n @if (linkLabel) {\n <dsfr-link\n [link]=\"link\"\n [label]=\"linkLabel\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n (linkSelect)=\"onLinkSelect()\">\n </dsfr-link>\n }\n </figcaption>\n }\n</figure>\n@if (transcriptionContent) {\n <dsfr-transcription [heading]=\"transcriptionHeading\" [content]=\"transcriptionContent\"></dsfr-transcription>\n} @else {\n <!-- since 1.6 ajout du slot 'transcription' -->\n <ng-content select=\"[transcription]\"></ng-content>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrTranscriptionComponent, selector: "dsfr-transcription", inputs: ["content", "heading"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-content', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrTranscriptionComponent, DsfrLinkComponent], template: "<figure\n class=\"fr-content-media\"\n [class.fr-content-media--lg]=\"contentSize === 'LG'\"\n [class.fr-content-media--sm]=\"contentSize === 'SM'\"\n role=\"group\"\n [attr.aria-label]=\"legend\">\n @if (contentMediaTemplate) {\n <ng-container *ngTemplateOutlet=\"contentMediaTemplate\"></ng-container>\n } @else {\n @if (type === 'image') {\n <div class=\"fr-content-media__img\">\n @if (source) {\n <img\n class=\"fr-responsive-img\"\n [attr.title]=\"tooltipMessage || null\"\n [class]=\"imageRatioClass\"\n [src]=\"source\"\n [alt]=\"alternate\" />\n }\n <!-- since 1.6 slot par d\u00E9faut d\u00E9pr\u00E9ci\u00E9 -->\n <ng-content></ng-content>\n <!-- since 1.6 ajout du slot 'svg' -->\n <ng-content select=\"[svg]\"></ng-content>\n </div>\n } @else if (type === 'video' && source) {\n <iframe\n [attr.title]=\"tooltipMessage || null\"\n class=\"fr-responsive-vid\"\n [class.fr-ratio-16x9]=\"ratio === '16:9'\"\n [class.fr-ratio-4x3]=\"ratio === '4:3'\"\n [class.fr-ratio-1x1]=\"ratio === '1:1'\"\n [src]=\"sanitizedSource()\"\n allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n allowfullscreen></iframe>\n }\n }\n\n @if (legend || linkLabel) {\n <figcaption class=\"fr-content-media__caption\">\n {{ legend }}\n @if (linkLabel) {\n <dsfr-link\n [link]=\"link\"\n [label]=\"linkLabel\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n (linkSelect)=\"onLinkSelect()\">\n </dsfr-link>\n }\n </figcaption>\n }\n</figure>\n@if (transcriptionContent) {\n <dsfr-transcription [heading]=\"transcriptionHeading\" [content]=\"transcriptionContent\"></dsfr-transcription>\n} @else {\n <!-- since 1.6 ajout du slot 'transcription' -->\n <ng-content select=\"[transcription]\"></ng-content>\n}\n" }]
}], propDecorators: { type: [{
type: Input
}], legend: [{
type: Input
}], ratio: [{
type: Input
}], source: [{
type: Input
}], linkLabel: [{
type: Input
}], link: [{
type: Input
}], linkTarget: [{
type: Input
}], route: [{
type: Input
}], routePath: [{
type: Input
}], routerLinkActive: [{
type: Input
}], routerLinkExtras: [{
type: Input
}], tooltipMessage: [{
type: Input
}], contentSize: [{
type: Input
}], transcriptionContent: [{
type: Input
}], transcriptionHeading: [{
type: Input
}], alternate: [{
type: Input
}], linkSelect: [{
type: Output
}], contentMediaTemplate: [{
type: ContentChild,
args: ['contentMediaTemplate', { static: true }]
}], transcription: [{
type: Input
}], transcriptionLabel: [{
type: Input
}], size: [{
type: Input
}], routerLink: [{
type: Input
}] } });
class DsfrContentModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentModule, imports: [CommonModule, DsfrTranscriptionComponent, DsfrLinkComponent, DsfrContentComponent], exports: [DsfrContentComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentModule, imports: [CommonModule, DsfrLinkComponent, DsfrContentComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrContentModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrContentComponent],
imports: [CommonModule, DsfrTranscriptionComponent, DsfrLinkComponent, DsfrContentComponent],
}]
}] });
/**
* Définit les types de medias supportés par le DSFR.
*/
var DsfrMediaConst;
(function (DsfrMediaConst) {
/**
* Media image.
*/
DsfrMediaConst["IMAGE"] = "image";
/**
* Media vidéo.
*/
DsfrMediaConst["VIDEO"] = "video";
})(DsfrMediaConst || (DsfrMediaConst = {}));
/**
* Directive structurelle permettant au composant parent d'identifier les composants devant être encapsulés au sein
* d'une structure HTML complémentaire.
*/
class DsfrFormFieldsetElementDirective {
constructor() {
this.templateRef = inject(TemplateRef);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetElementDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: DsfrFormFieldsetElementDirective, isStandalone: true, selector: "[fieldsetElement]", inputs: { fieldsetElement: "fieldsetElement" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetElementDirective, decorators: [{
type: Directive,
args: [{
selector: '[fieldsetElement]',
standalone: true,
}]
}], propDecorators: { fieldsetElement: [{
type: Input
}] } });
/**
* EduHintComponent contient la description additionnelle d'un champ de formulaire
* @since 1.16.0
*/
class EduHintComponent {
isArray(hint) {
return Array.isArray(hint);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduHintComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: EduHintComponent, isStandalone: true, selector: "edu-hint-form", inputs: { hint: "hint" }, ngImport: i0, template: `
@if (isArray(hint)) {
@for (elem of hint; track $index) {
<span class="fr-hint-text">{{ elem }}</span>
}
} @else {
<span class="fr-hint-text">{{ hint }}</span>
}
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduHintComponent, decorators: [{
type: Component,
args: [{
selector: 'edu-hint-form',
encapsulation: ViewEncapsulation.None,
imports: [CommonModule],
template: `
@if (isArray(hint)) {
@for (elem of hint; track $index) {
<span class="fr-hint-text">{{ elem }}</span>
}
} @else {
<span class="fr-hint-text">{{ hint }}</span>
}
`,
}]
}], propDecorators: { hint: [{
type: Input
}] } });
class BaseFieldsetComponent {
constructor() {
/**
* Permet de rétablir une graisse standard sur la légende.
*/
this.legendRegular = false;
/**
* Permet de masquer la légende en la préservant pour les lecteurs d'écran.
*/
this.legendSrOnly = false;
/**
* Permet de désactiver l'ensemble des champs du fieldset.
*/
this.disabled = input(...(ngDevMode ? [undefined, { debugName: "disabled" }] : []));
/**
* Positionne un message d'erreur.
* @deprecated (since 1.14) utiliser message et messageSeverity à la place
**/
this.error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : []));
/**
* Positionne un message de validation.
* @deprecated (since 1.14) utiliser message et messageSeverity à la place
*/
this.valid = input(...(ngDevMode ? [undefined, { debugName: "valid" }] : []));
/**
* Message d'information lié au composant.
*/
this.message = input(...(ngDevMode ? [undefined, { debugName: "message" }] : []));
/**
* Représente la sévérité du message.
*/
this.messageSeverity = input(...(ngDevMode ? [undefined, { debugName: "messageSeverity" }] : []));
/**
* Gestion de l'affichage de la combinaison des messages internes et externes du composant.
*/
this.newMessages = computed(() => {
const [message, error] = [this._message(), this._internalError()];
return message || error ? this.handleMessagesConcat() : undefined;
}, ...(ngDevMode ? [{ debugName: "newMessages" }] : []));
/**
* Gestion de l'affichage dans le cas où le composant serait dans un autre statut qu'en erreur,
* et qu'une erreur interne se manifeste. Dans ce cas, seules sont affichées les erreurs internes, et le statut
* affiché du composant est forcé en erreur.
*/
this.displayedMessageStatus = computed(() => {
const [newMessages, messageSeverity, internalError] = [
this.newMessages(),
this._messageSeverity(),
toArray(this._internalError()),
];
if (messageSeverity !== DsfrSeverityConst.ERROR && internalError.length > 0) {
return {
message: internalError,
messageSeverity: DsfrSeverityConst.ERROR,
};
}
else {
return {
message: newMessages,
messageSeverity: messageSeverity,
};
}
}, ...(ngDevMode ? [{ debugName: "displayedMessageStatus" }] : []));
this.fieldsetSeverity = computed(() => {
const status = this.displayedMessageStatus();
if (!status.message || status.message.length === 0 || !status.messageSeverity)
return null;
const severityToClass = {
[this.DsfrSeverity.ERROR]: 'fr-fieldset--error',
[this.DsfrSeverity.SUCCESS]: 'fr-fieldset--valid',
[this.DsfrSeverity.VALID]: 'fr-fieldset--valid',
[this.DsfrSeverity.INFO]: '',
[this.DsfrSeverity.WARNING]: '',
};
return severityToClass[status.messageSeverity];
}, ...(ngDevMode ? [{ debugName: "fieldsetSeverity" }] : []));
this.DsfrSeverity = DsfrSeverityConst;
this.legendId = '';
this.messagesId = '';
this.nativeElementId = ''; // TODO : à enlever en v2
/**
* Attribut utilisés par les composants héritant de la class BaseFieldset afin d'afficher des messages d'erreurs relatif
* à une validation interne spécifique au composant.
* @internal
*/
this._internalError = signal(undefined, ...(ngDevMode ? [{ debugName: "_internalError" }] : []));
/** @internal */
this._message = computed(() => {
if (this.error())
return this.error();
if (this.valid())
return this.valid();
return this.message();
}, ...(ngDevMode ? [{ debugName: "_message" }] : []));
/** @internal */
this._messageSeverity = computed(() => {
const message = this._message();
if (!message?.length)
return undefined;
if (this.error())
return DsfrSeverityConst.ERROR;
if (this.valid())
return DsfrSeverityConst.VALID;
return this.messageSeverity();
}, ...(ngDevMode ? [{ debugName: "_messageSeverity" }] : []));
}
/**
* Attribut utilisés par les composants héritant de la class BaseFieldset afin d'afficher des messages d'erreurs relatif
* à une validation interne spécifique au composant.
*/
get internalError() {
return this._internalError();
}
/**
* Attribut utilisés par les composants héritant de la class BaseFieldset afin d'afficher des messages d'erreurs relatif
* à une validation interne spécifique au composant.
* @internal
*/
set internalError(value) {
this._internalError.set(value);
}
ngOnInit() {
this.nativeElementId = this.fieldsetId ?? this.id ?? newUniqueId();
this.legendId = this.nativeElementId + '-legend';
this.messagesId = this.nativeElementId + '-messages';
}
isLegendRegular() {
return this.legendRegular;
}
/**
* Gère la concaténation des messages internes et externes
*/
handleMessagesConcat() {
const [internalError, message, messageSeverity] = [
toArray(this._internalError()),
toArray(this._message()),
this._messageSeverity(),
];
if (internalError.length > 0 || messageSeverity === DsfrSeverityConst.ERROR) {
const combinedMessages = [];
combinedMessages.push(...internalError);
if (messageSeverity === DsfrSeverityConst.ERROR) {
combinedMessages.push(...message);
}
return combinedMessages;
}
return message.length === 0 ? undefined : message;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseFieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: BaseFieldsetComponent, isStandalone: true, selector: "ng-component", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: false, isRequired: false, transformFunction: null }, fieldsetId: { classPropertyName: "fieldsetId", publicName: "fieldsetId", isSignal: false, isRequired: false, transformFunction: null }, legend: { classPropertyName: "legend", publicName: "legend", isSignal: false, isRequired: false, transformFunction: null }, legendRegular: { classPropertyName: "legendRegular", publicName: "legendRegular", isSignal: false, isRequired: false, transformFunction: null }, legendSrOnly: { classPropertyName: "legendSrOnly", publicName: "legendSrOnly", isSignal: false, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, valid: { classPropertyName: "valid", publicName: "valid", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, messageSeverity: { classPropertyName: "messageSeverity", publicName: "messageSeverity", isSignal: true, isRequired: false, transformFunction: null }, internalError: { classPropertyName: "internalError", publicName: "internalError", isSignal: false, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseFieldsetComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { id: [{
type: Input
}], fieldsetId: [{
type: Input
}], legend: [{
type: Input
}], legendRegular: [{
type: Input
}], legendSrOnly: [{
type: Input
}], hint: [{
type: Input
}], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], valid: [{ type: i0.Input, args: [{ isSignal: true, alias: "valid", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], messageSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageSeverity", required: false }] }], internalError: [{
type: Input
}] } });
class DsfrFormFieldsetComponent extends BaseFieldsetComponent {
constructor() {
super(...arguments);
/**
* Ensemble des champs de formulaire sur une même ligne.
*/
this.inline = false;
/**
* Attribut aria-labelledBy en fonction de la lagende et du groupe de message
*/
this.ariaLabelledyBy = computed(() => {
const messageStatus = this.displayedMessageStatus();
return this.legendId + (messageStatus.message && messageStatus.messageSeverity ? ' ' + this.messagesId : '');
}, ...(ngDevMode ? [{ debugName: "ariaLabelledyBy" }] : []));
}
getFieldsetElementClass(elem) {
const classes = ['fr-fieldset__element'];
if (this.inline) {
classes.push('fr-fieldset__element--inline');
}
if (elem.fieldsetElement) {
const elemClasses = elem.fieldsetElement.split(/\s+/g);
elemClasses.forEach((c) => classes.push(c));
}
return classes;
}
isMessageArray() {
return Array.isArray(this.message);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormFieldsetComponent, isStandalone: true, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: { inline: "inline" }, queries: [{ propertyName: "elements", predicate: DsfrFormFieldsetElementDirective }], usesInheritance: true, ngImport: i0, template: "<fieldset\n [attr.id]=\"nativeElementId\"\n class=\"fr-fieldset\"\n [ngClass]=\"fieldsetSeverity()\"\n [attr.aria-labelledby]=\"ariaLabelledyBy()\"\n [disabled]=\"disabled() || null\">\n <legend\n id=\"{{ legendId }}\"\n [class]=\"{\n 'fr-fieldset__legend': true,\n 'fr-fieldset__legend--regular': isLegendRegular(),\n 'fr-sr-only': legendSrOnly,\n }\">\n @if (legend) {\n {{ legend }}\n } @else {\n <ng-content select=\"[legend]\"></ng-content>\n }\n\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </legend>\n\n @for (elem of elements; track $index) {\n <div [class]=\"getFieldsetElementClass(elem)\">\n <ng-container *ngTemplateOutlet=\"elem.templateRef\"></ng-container>\n </div>\n }\n\n <div class=\"fr-messages-group\" id=\"{{ messagesId }}\" aria-live=\"polite\">\n @let messageStatus = displayedMessageStatus();\n\n @if (messageStatus.messageSeverity) {\n @for (individualMessage of messageStatus.message; track individualMessage) {\n <!-- prettier-ignore -->\n <p class=\"fr-message\" [eduMessageSeverity]=\"messageStatus.messageSeverity\">{{ individualMessage }}</p>\n }\n }\n </div>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-fieldset, dsfr-form-fieldset', encapsulation: ViewEncapsulation.None, imports: [CommonModule, EduMessageSeverityDirective, EduHintComponent], template: "<fieldset\n [attr.id]=\"nativeElementId\"\n class=\"fr-fieldset\"\n [ngClass]=\"fieldsetSeverity()\"\n [attr.aria-labelledby]=\"ariaLabelledyBy()\"\n [disabled]=\"disabled() || null\">\n <legend\n id=\"{{ legendId }}\"\n [class]=\"{\n 'fr-fieldset__legend': true,\n 'fr-fieldset__legend--regular': isLegendRegular(),\n 'fr-sr-only': legendSrOnly,\n }\">\n @if (legend) {\n {{ legend }}\n } @else {\n <ng-content select=\"[legend]\"></ng-content>\n }\n\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </legend>\n\n @for (elem of elements; track $index) {\n <div [class]=\"getFieldsetElementClass(elem)\">\n <ng-container *ngTemplateOutlet=\"elem.templateRef\"></ng-container>\n </div>\n }\n\n <div class=\"fr-messages-group\" id=\"{{ messagesId }}\" aria-live=\"polite\">\n @let messageStatus = displayedMessageStatus();\n\n @if (messageStatus.messageSeverity) {\n @for (individualMessage of messageStatus.message; track individualMessage) {\n <!-- prettier-ignore -->\n <p class=\"fr-message\" [eduMessageSeverity]=\"messageStatus.messageSeverity\">{{ individualMessage }}</p>\n }\n }\n </div>\n</fieldset>\n" }]
}], propDecorators: { inline: [{
type: Input
}], elements: [{
type: ContentChildren,
args: [DsfrFormFieldsetElementDirective]
}] } });
class DsfrFormFieldsetModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetModule, imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective], exports: [DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetModule, imports: [CommonModule, DsfrFormFieldsetComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormFieldsetModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective],
exports: [DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective],
}]
}] });
class DefaultRadioComponent extends DefaultControlComponent {
constructor() {
super(...arguments);
/*** Bascule l'affichage des radio-buttons en ligne. */
this.inline = false;
/** Attribut required du radio-button. */
this.required = false;
/** Permet de rétablir une graisse standard sur la légende. */
this.legendRegular = false;
}
get fieldsetLabelledBy() {
return this.legendId + ' ' + this.inputId + '-messages';
}
get hintId() {
return this.inputId + '-hint';
}
get legendId() {
return this.inputId + '-legend';
}
/** Légende du groupe de boutons radios (alias sur la propriété label). */
set legend(legend) {
this.label = legend;
}
ngOnInit() {
super.ngOnInit();
this.name ??= newUniqueId();
}
/** @internal */
getRadioId(index) {
return `${this.name}_${index + 1}`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultRadioComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DefaultRadioComponent, isStandalone: true, selector: "ng-component", inputs: { inline: "inline", ariaInvalid: "ariaInvalid", required: "required", legendRegular: "legendRegular", legend: "legend" }, usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DefaultRadioComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { inline: [{
type: Input
}], ariaInvalid: [{
type: Input
}], required: [{
type: Input
}], legendRegular: [{
type: Input
}], legend: [{
type: Input
}] } });
class DsfrFormRadioRichComponent extends DefaultRadioComponent {
/**
* @deprecated (@since 1.1.0) utiliser la propriété options à la place.
*/
set radioRich(options) {
this.options = options;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormRadioRichComponent, isStandalone: true, selector: "dsfr-form-radio-rich", inputs: { options: "options", radioRich: "radioRich" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormRadioRichComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<dsfr-fieldset\n [fieldsetId]=\"inputId\"\n [legend]=\"label\"\n [legendRegular]=\"legendRegular\"\n [disabled]=\"disabled\"\n [hint]=\"hint ?? ''\"\n [inline]=\"inline\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\">\n @if (!label) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n @for (radio of options; track i; let i = $index) {\n <div *fieldsetElement class=\"fr-radio-group fr-radio-rich\">\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n type=\"radio\"\n [attr.id]=\"getRadioId(i)\"\n [name]=\"name!\"\n [attr.name]=\"name\"\n [value]=\"radio.value\"\n [disabled]=\"radio.disabled ? radio.disabled : false\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [(ngModel)]=\"value\"\n (blur)=\"onBlur()\"\n [required]=\"required\" />\n <label class=\"fr-label\" [for]=\"getRadioId(i)\">\n <span [innerHTML]=\"radio.label\"></span>\n @if (radio.hint) {\n <span class=\"fr-hint-text\">{{ radio.hint }}</span>\n }\n </label>\n @if (radio.imagePath || radio.artworkFilePath) {\n <div\n [class]=\"{\n 'fr-radio-rich__img': radio.imagePath,\n 'fr-radio-rich__pictogram': !radio.imagePath && radio.artworkFilePath,\n }\">\n @if (radio.imagePath) {\n <img [src]=\"radio.imagePath\" [attr.alt]=\"radio.imageAlt\" />\n } @else if (radio.artworkFilePath && !radio.imagePath) {\n <edu-pictogram [artworkFilePath]=\"radio.artworkFilePath\"></edu-pictogram>\n }\n </div>\n }\n </div>\n }\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "component", type: PictogramComponent, selector: "edu-pictogram", inputs: ["artworkDirPath", "artworkFilePath", "download"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-radio-rich', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormRadioRichComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, PictogramComponent, DsfrFormFieldsetElementDirective], template: "<dsfr-fieldset\n [fieldsetId]=\"inputId\"\n [legend]=\"label\"\n [legendRegular]=\"legendRegular\"\n [disabled]=\"disabled\"\n [hint]=\"hint ?? ''\"\n [inline]=\"inline\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\">\n @if (!label) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n @for (radio of options; track i; let i = $index) {\n <div *fieldsetElement class=\"fr-radio-group fr-radio-rich\">\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n type=\"radio\"\n [attr.id]=\"getRadioId(i)\"\n [name]=\"name!\"\n [attr.name]=\"name\"\n [value]=\"radio.value\"\n [disabled]=\"radio.disabled ? radio.disabled : false\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [(ngModel)]=\"value\"\n (blur)=\"onBlur()\"\n [required]=\"required\" />\n <label class=\"fr-label\" [for]=\"getRadioId(i)\">\n <span [innerHTML]=\"radio.label\"></span>\n @if (radio.hint) {\n <span class=\"fr-hint-text\">{{ radio.hint }}</span>\n }\n </label>\n @if (radio.imagePath || radio.artworkFilePath) {\n <div\n [class]=\"{\n 'fr-radio-rich__img': radio.imagePath,\n 'fr-radio-rich__pictogram': !radio.imagePath && radio.artworkFilePath,\n }\">\n @if (radio.imagePath) {\n <img [src]=\"radio.imagePath\" [attr.alt]=\"radio.imageAlt\" />\n } @else if (radio.artworkFilePath && !radio.imagePath) {\n <edu-pictogram [artworkFilePath]=\"radio.artworkFilePath\"></edu-pictogram>\n }\n </div>\n }\n </div>\n }\n</dsfr-fieldset>\n" }]
}], propDecorators: { options: [{
type: Input,
args: [{ required: true }]
}], radioRich: [{
type: Input
}] } });
class DsfrFormRadioRichModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichModule, imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, PictogramComponent, DsfrFormRadioRichComponent], exports: [DsfrFormRadioRichComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichModule, imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, PictogramComponent, DsfrFormRadioRichComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioRichModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormRadioRichComponent],
imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, PictogramComponent, DsfrFormRadioRichComponent],
}]
}] });
const DISPLAY_MODAL_ID = 'theme-modal-id';
class DsfrDisplayComponent {
/** @internal */
constructor() {
/**
* l'identifiant de la modale portant les paramètres d'affichage.
*/
this.displayId = DISPLAY_MODAL_ID;
/**
* Événement émis lorsque le thème change avec la valeur du nouveau thème : `light`, `dark` ou `system`.
*/
this.displayChange = new EventEmitter();
this.i18n = inject(DsfrI18nService);
/** @internal */
this.themes = [
{
value: 'light',
label: this.i18n.t('display.light.label'),
link: '/pictograms/environment/sun.svg',
deprecatedLink: '/sun.svg',
},
{
value: 'dark',
label: this.i18n.t('display.dark.label'),
link: '/pictograms/environment/moon.svg',
deprecatedLink: '/moon.svg',
},
{
value: 'system',
label: this.i18n.t('display.system.label'),
hint: this.i18n.t('display.system.hint'),
link: '/pictograms/system/system.svg',
deprecatedLink: '/system.svg',
},
];
this._useDeprecatedLink = false;
this.config = inject(DSFR_CONFIG_TOKEN);
this.elementRef = inject(ElementRef);
this.document = inject(DOCUMENT);
}
get pictoPath() {
return this.artworkDirPath;
}
/**
* Chemin des pictogrammes (du composant display) renseigné par le développeur.
*
* Note: ce chemin doit permettre de récupérer directement les fichiers SVG suivants : moon.svg, sun.svg, system.svg
*
* @deprecated Use `artworkDirPath` instead.
*/
set pictoPath(path) {
this.artworkDirPath = path;
this._useDeprecatedLink = true;
}
ngOnInit() {
this.initDisplayTheme();
if (this.artworkDirPath === undefined && this.config.artworkDirPath) {
this.artworkDirPath = this.config.artworkDirPath;
}
for (let theme of this.themes) {
theme.artworkFilePath = this.buildSvgPath(theme);
}
}
/** @internal */
onChange() {
this.setHtmlSchemeAttribute(this.currentTheme);
this.displayChange.emit(this.currentTheme);
}
/** @internal */
buildSvgPath(theme) {
return this.artworkDirPath + (this._useDeprecatedLink ? theme.deprecatedLink : theme.link);
}
initDisplayTheme() {
if (!isOnBrowser())
return;
// Si l’utilisateur effectue une modification, son choix est conservé (dans le local storage) pour les visites ultérieures.
const savedTheme = LocalStorage.get(StorageEnum.SCHEME);
if (savedTheme) {
this.setHtmlSchemeAttribute(savedTheme);
this.currentTheme = savedTheme;
}
// Sinon on prend le theme de la balise HTML
else {
const html = this.document.documentElement;
const themeInHtml = html.getAttribute('data-fr-scheme');
this.currentTheme = themeInHtml || undefined;
}
}
setHtmlSchemeAttribute(value) {
const nativeElt = this.elementRef?.nativeElement;
const html = nativeElt?.ownerDocument?.documentElement;
html?.setAttribute('data-fr-scheme', value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrDisplayComponent, isStandalone: true, selector: "dsfr-display", inputs: { displayId: "displayId", headingLevel: "headingLevel", artworkDirPath: "artworkDirPath", pictoPath: "pictoPath" }, outputs: { displayChange: "displayChange" }, ngImport: i0, template: "<dialog [id]=\"displayId\" class=\"fr-modal\" role=\"dialog\" [attr.aria-labelledby]=\"displayId + '-title'\">\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-6 fr-col-lg-4\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"displayId\"\n [title]=\"'commons.close' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <edu-heading\n [headingId]=\"displayId + '-title'\"\n [level]=\"headingLevel\"\n [allowFirstLevel]=\"true\"\n defaultLevel=\"H2\"\n customClass=\"fr-modal__title\">\n {{ 'display.heading' | dsfrI18n }}\n </edu-heading>\n <dsfr-form-radio-rich\n [inputId]=\"displayId + '-fieldset'\"\n legend=\" {{ 'display.hint' | dsfrI18n }}\"\n [name]=\"displayId + '-options'\"\n [options]=\"themes\"\n [legendRegular]=\"true\"\n [required]=\"true\"\n (change)=\"onChange()\"\n [(ngModel)]=\"currentTheme\">\n </dsfr-form-radio-rich>\n </div>\n </div>\n </div>\n </div>\n </div>\n</dialog>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "component", type: DsfrFormRadioRichComponent, selector: "dsfr-form-radio-rich", inputs: ["options", "radioRich"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-display', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, HeadingComponent, DsfrI18nPipe, DsfrFormRadioRichComponent], template: "<dialog [id]=\"displayId\" class=\"fr-modal\" role=\"dialog\" [attr.aria-labelledby]=\"displayId + '-title'\">\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-6 fr-col-lg-4\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"displayId\"\n [title]=\"'commons.close' | dsfrI18n\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <edu-heading\n [headingId]=\"displayId + '-title'\"\n [level]=\"headingLevel\"\n [allowFirstLevel]=\"true\"\n defaultLevel=\"H2\"\n customClass=\"fr-modal__title\">\n {{ 'display.heading' | dsfrI18n }}\n </edu-heading>\n <dsfr-form-radio-rich\n [inputId]=\"displayId + '-fieldset'\"\n legend=\" {{ 'display.hint' | dsfrI18n }}\"\n [name]=\"displayId + '-options'\"\n [options]=\"themes\"\n [legendRegular]=\"true\"\n [required]=\"true\"\n (change)=\"onChange()\"\n [(ngModel)]=\"currentTheme\">\n </dsfr-form-radio-rich>\n </div>\n </div>\n </div>\n </div>\n </div>\n</dialog>\n" }]
}], ctorParameters: () => [], propDecorators: { displayId: [{
type: Input
}], headingLevel: [{
type: Input
}], artworkDirPath: [{
type: Input
}], displayChange: [{
type: Output
}], pictoPath: [{
type: Input
}] } });
class DsfrDisplayModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayModule, imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrDisplayComponent, DsfrFormRadioRichComponent], exports: [DsfrDisplayComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayModule, imports: [CommonModule, FormsModule, DsfrDisplayComponent, DsfrFormRadioRichComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDisplayModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrDisplayComponent],
imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrDisplayComponent, DsfrFormRadioRichComponent],
}]
}] });
/**
* Constantes correspondant aux différentes variantes du composant download.
*
* @deprecated
*/
var DsfrDownloadVariantConst;
(function (DsfrDownloadVariantConst) {
DsfrDownloadVariantConst["LINK"] = "link";
DsfrDownloadVariantConst["BLOCK"] = "block";
})(DsfrDownloadVariantConst || (DsfrDownloadVariantConst = {}));
class DsfrDownloadComponent {
constructor() {
/**
* Permet de basculer la présentation en Card.
*
* @deprecated Le mode 'block' est déprécié, utiliser `Card#download` à la place
*/
this.variant = DsfrDownloadVariantConst.LINK;
/**
* Indique que les métadonnées du fichier seront positionnées automatiquement par le script DSFR. Les propriétés
* `mimeType`, `sizeBytes`, `langCode` sont alors ignorées. Si la propriété est positionnée à false alors ce sont
* les valeurs des propriétés `mimeType`, `sizeBytes`, `langCode` qui seront utlisées.
*/
this.assessFile = true;
/**
* Equivalent à l'attribut html natif 'download'.
* Si == 'true', télécharge directement le fichier sans l'ouvrir, 'false' par défaut.
* Peut prendre le nom du fichier à télécharger si on souhaite renommer ce fichier.
*/
this.directDownload = false;
/**
* Propage la valeur de 'route' ou 'link' lors du clic sur le lien.
*/
this.downloadSelect = new EventEmitter();
this.i18n = inject(DsfrI18nService);
this.langService = inject(LangService);
}
get currentLang() {
return this.langService.lang;
}
get itemLink() {
let label = this.label ? this.label : `${this.i18n.t('commons.download')} ${this.fileName}`;
label += `<span class="fr-link__detail">${this.detail}</span>`;
const itemLink = { label: label, ariaLabel: this.ariaLabel ?? '' };
if (this.link) {
itemLink.link = this.link;
}
else if (this.route) {
itemLink.route = this.route;
}
return itemLink;
}
/**
* Retourne l'affichage des détails si assetFile est positionné et que l'utilisateur n'est pas en mode action
* (activation via l'input route), sinon retourne '' ce qui aura pour effet de laisser calculer l'affichage par
* le script DSFR.
*/
get detail() {
return this.assessFile && !this.route ? '' : downloadDetail(this.mimeType, this.sizeBytes, this.sizeUnit);
}
get sizeUnit() {
return this._sizeUnit ? this._sizeUnit : this.langService.lang === 'fr' ? 'octets' : 'bytes';
}
/**
* Permet d'afficher la taille soit en bytes (KB, MB, ...) soit en octets (Ko, Mo, ...). Par défaut, l'unité est
* en octets lorsque la langue courante est 'fr', 'bytes' dans les autres cas.
*/
set sizeUnit(value) {
this._sizeUnit = value;
}
/** @internal */
onLinkSelect(link) {
this.downloadSelect.emit(link);
}
onButtonSelect() {
if (this.route) {
this.downloadSelect.emit(this.route);
}
}
/**
* Retourne vrai si on affiche un bouton à la place d'une ancre.
* @internal
*/
hasButtonMarkup() {
return !!this.route || !this.link;
}
/**
* Si 'true' ou avec un nom de fichier, télécharge directement le fichier sans l'ouvrir. False par défaut.
* @internal
*/
isDirectDownload() {
return !!this.directDownload;
}
/** @internal */
getNewFileName() {
return typeof this.directDownload === 'string' ? this.directDownload : '';
}
/** @internal */
isBlockMode() {
return this.variant === DsfrDownloadVariantConst.BLOCK;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrDownloadComponent, isStandalone: true, selector: "dsfr-download", inputs: { label: "label", ariaLabel: "ariaLabel", link: "link", mimeType: "mimeType", fileName: "fileName", sizeBytes: "sizeBytes", langCode: "langCode", variant: "variant", description: "description", assessFile: "assessFile", directDownload: "directDownload", route: "route", linkTarget: "linkTarget", sizeUnit: "sizeUnit" }, outputs: { downloadSelect: "downloadSelect" }, ngImport: i0, template: "@if (!isBlockMode()) {\n <edu-link-download\n [isButton]=\"hasButtonMarkup()\"\n [item]=\"itemLink\"\n [downloadDirect]=\"directDownload ?? false\"\n [downloadAssessFile]=\"assessFile\"\n [linkTarget]=\"linkTarget\"\n customClass=\"fr-link fr-link--download\"\n [langCode]=\"langCode ?? ''\"\n (linkSelect)=\"onLinkSelect($event)\">\n </edu-link-download>\n} @else {\n <!-- Pour r\u00E9trocompatibilit\u00E9 avec mode \"block\" (d\u00E9pr\u00E9ci\u00E9) -->\n <div\n class=\"fr-download\"\n [ngClass]=\"{\n 'fr-enlarge-link fr-download--card': isBlockMode(),\n 'edu-cardWithButton': hasButtonMarkup(),\n }\"\n [attr.lang]=\"currentLang || null\">\n <p>\n <!-- T\u00E9l\u00E9chargement avec une ancre -->\n @if (!hasButtonMarkup()) {\n <a\n [href]=\"link\"\n [attr.download]=\"!isDirectDownload() ? null : getNewFileName()\"\n [attr.hreflang]=\"langCode ? langCode : null\"\n class=\"fr-download__link\"\n [attr.data-fr-assess-file]=\"assessFile && sizeBytes ? sizeUnit : null\">\n {{ 'commons.download' | dsfrI18n }} {{ fileName }}\n <span class=\"fr-download__detail\">{{ detail }}</span>\n </a>\n } @else {\n <!-- T\u00E9l\u00E9chargement avec un bouton (sans m\u00E9tadata) -->\n <button (linkSelect)=\"onButtonSelect()\" type=\"button\" class=\"fr-download__link\">\n {{ 'commons.download' | dsfrI18n }} {{ fileName }}\n <span class=\"fr-download__detail\">{{ detail }}</span>\n </button>\n }\n </p>\n @if (description) {\n <p class=\"fr-download__desc\">{{ description }}</p>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";button.fr-download__link{background-position:0% 100%,0% calc(100% - .0625em);background-position:var(--underline-x) 100%,var(--underline-x) calc(100% - .0625em);background-repeat:no-repeat,no-repeat;transition:background-size 0s;background-size:0 .125em,100% .0625em;background-size:var(--underline-hover-width) .125em,var(--underline-idle-width) .0625em}button.fr-download__link:before{content:\"\";display:block;position:absolute;inset:0;width:100%;height:100%;outline-offset:2px;outline-style:inherit;outline-color:inherit;outline-width:2px;z-index:1}button.fr-download__link:focus-visible:before{outline-style:solid;outline-color:#0a76f6}.fr-links-group button.fr-link--download{margin-bottom:0}.edu-cardWithButton{cursor:pointer}.edu-cardWithButton button{background-image:none}.edu-cardWithButton button:focus-visible{outline-style:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: LinkDownloadComponent, selector: "edu-link-download", inputs: ["customClass", "downloadAssessFile", "langCode", "linkTarget", "isButton", "downloadDirect", "item"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-download', encapsulation: ViewEncapsulation.None, imports: [CommonModule, LinkDownloadComponent, DsfrI18nPipe], template: "@if (!isBlockMode()) {\n <edu-link-download\n [isButton]=\"hasButtonMarkup()\"\n [item]=\"itemLink\"\n [downloadDirect]=\"directDownload ?? false\"\n [downloadAssessFile]=\"assessFile\"\n [linkTarget]=\"linkTarget\"\n customClass=\"fr-link fr-link--download\"\n [langCode]=\"langCode ?? ''\"\n (linkSelect)=\"onLinkSelect($event)\">\n </edu-link-download>\n} @else {\n <!-- Pour r\u00E9trocompatibilit\u00E9 avec mode \"block\" (d\u00E9pr\u00E9ci\u00E9) -->\n <div\n class=\"fr-download\"\n [ngClass]=\"{\n 'fr-enlarge-link fr-download--card': isBlockMode(),\n 'edu-cardWithButton': hasButtonMarkup(),\n }\"\n [attr.lang]=\"currentLang || null\">\n <p>\n <!-- T\u00E9l\u00E9chargement avec une ancre -->\n @if (!hasButtonMarkup()) {\n <a\n [href]=\"link\"\n [attr.download]=\"!isDirectDownload() ? null : getNewFileName()\"\n [attr.hreflang]=\"langCode ? langCode : null\"\n class=\"fr-download__link\"\n [attr.data-fr-assess-file]=\"assessFile && sizeBytes ? sizeUnit : null\">\n {{ 'commons.download' | dsfrI18n }} {{ fileName }}\n <span class=\"fr-download__detail\">{{ detail }}</span>\n </a>\n } @else {\n <!-- T\u00E9l\u00E9chargement avec un bouton (sans m\u00E9tadata) -->\n <button (linkSelect)=\"onButtonSelect()\" type=\"button\" class=\"fr-download__link\">\n {{ 'commons.download' | dsfrI18n }} {{ fileName }}\n <span class=\"fr-download__detail\">{{ detail }}</span>\n </button>\n }\n </p>\n @if (description) {\n <p class=\"fr-download__desc\">{{ description }}</p>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";button.fr-download__link{background-position:0% 100%,0% calc(100% - .0625em);background-position:var(--underline-x) 100%,var(--underline-x) calc(100% - .0625em);background-repeat:no-repeat,no-repeat;transition:background-size 0s;background-size:0 .125em,100% .0625em;background-size:var(--underline-hover-width) .125em,var(--underline-idle-width) .0625em}button.fr-download__link:before{content:\"\";display:block;position:absolute;inset:0;width:100%;height:100%;outline-offset:2px;outline-style:inherit;outline-color:inherit;outline-width:2px;z-index:1}button.fr-download__link:focus-visible:before{outline-style:solid;outline-color:#0a76f6}.fr-links-group button.fr-link--download{margin-bottom:0}.edu-cardWithButton{cursor:pointer}.edu-cardWithButton button{background-image:none}.edu-cardWithButton button:focus-visible{outline-style:none}\n"] }]
}], propDecorators: { label: [{
type: Input
}], ariaLabel: [{
type: Input
}], link: [{
type: Input
}], mimeType: [{
type: Input,
args: [{ required: true }]
}], fileName: [{
type: Input,
args: [{ required: true }]
}], sizeBytes: [{
type: Input
}], langCode: [{
type: Input
}], variant: [{
type: Input
}], description: [{
type: Input
}], assessFile: [{
type: Input
}], directDownload: [{
type: Input
}], route: [{
type: Input
}], linkTarget: [{
type: Input
}], downloadSelect: [{
type: Output
}], sizeUnit: [{
type: Input
}] } });
class DsfrDownloadModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadModule, imports: [CommonModule, LinkDownloadComponent, DsfrI18nPipe, DsfrDownloadComponent], exports: [DsfrDownloadComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadModule, imports: [CommonModule, LinkDownloadComponent, DsfrDownloadComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrDownloadComponent],
imports: [CommonModule, LinkDownloadComponent, DsfrI18nPipe, DsfrDownloadComponent],
}]
}] });
/**
* Ce composant permet de proposer une liste de plusieurs téléchargements. Les téléchargements sont des liens
* présentés sous forme de liste avec des puces. Un lien ne peut pas être un bloc. La description des liens est faite
* sous forme de tableau.
*/
class DsfrDownloadsGroupComponent {
constructor() {
/** Tableau des fichiers à lister. */
this.downloads = [];
/**
* Propage la valeur de 'route' ou 'link' lors du clic sur un lien.
*/
this.downloadSelect = new EventEmitter();
}
onDownloadSelect(link) {
this.downloadSelect.emit(link);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrDownloadsGroupComponent, isStandalone: true, selector: "dsfr-downloads-group", inputs: { heading: "heading", headingLevel: "headingLevel", downloads: "downloads" }, outputs: { downloadSelect: "downloadSelect" }, ngImport: i0, template: "@if (heading) {\n <div class=\"fr-downloads-group\">\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-downloads-group__title\">{{ heading }}</h2>\n }\n @case ('H3') {\n <h3 class=\"fr-downloads-group__title\">{{ heading }}</h3>\n }\n @case ('H5') {\n <h5 class=\"fr-downloads-group__title\">{{ heading }}</h5>\n }\n @case ('H6') {\n <h6 class=\"fr-downloads-group__title\">{{ heading }}</h6>\n }\n @default {\n <h4 class=\"fr-downloads-group__title\">{{ heading }}</h4>\n }\n }\n <ng-container *ngTemplateOutlet=\"links\"></ng-container>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"links\"></ng-container>\n}\n\n<ng-template #links>\n <ul class=\"fr-links-group\">\n @for (item of downloads; track $index) {\n <li>\n <dsfr-download\n (downloadSelect)=\"onDownloadSelect($event)\"\n [fileName]=\"item.fileName\"\n [label]=\"item.label\"\n [ariaLabel]=\"item.ariaLabel\"\n [link]=\"item.link\"\n [route]=\"item.route\"\n [mimeType]=\"item.mimeType\"\n [linkTarget]=\"item.linkTarget\"\n [sizeBytes]=\"item.sizeBytes\"\n [langCode]=\"item.langCode\"\n [assessFile]=\"item.assessFile ?? true\"\n [directDownload]=\"item.directDownload\"></dsfr-download>\n </li>\n }\n </ul>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrDownloadComponent, selector: "dsfr-download", inputs: ["label", "ariaLabel", "link", "mimeType", "fileName", "sizeBytes", "langCode", "variant", "description", "assessFile", "directDownload", "route", "linkTarget", "sizeUnit"], outputs: ["downloadSelect"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-downloads-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrDownloadComponent], template: "@if (heading) {\n <div class=\"fr-downloads-group\">\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-downloads-group__title\">{{ heading }}</h2>\n }\n @case ('H3') {\n <h3 class=\"fr-downloads-group__title\">{{ heading }}</h3>\n }\n @case ('H5') {\n <h5 class=\"fr-downloads-group__title\">{{ heading }}</h5>\n }\n @case ('H6') {\n <h6 class=\"fr-downloads-group__title\">{{ heading }}</h6>\n }\n @default {\n <h4 class=\"fr-downloads-group__title\">{{ heading }}</h4>\n }\n }\n <ng-container *ngTemplateOutlet=\"links\"></ng-container>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"links\"></ng-container>\n}\n\n<ng-template #links>\n <ul class=\"fr-links-group\">\n @for (item of downloads; track $index) {\n <li>\n <dsfr-download\n (downloadSelect)=\"onDownloadSelect($event)\"\n [fileName]=\"item.fileName\"\n [label]=\"item.label\"\n [ariaLabel]=\"item.ariaLabel\"\n [link]=\"item.link\"\n [route]=\"item.route\"\n [mimeType]=\"item.mimeType\"\n [linkTarget]=\"item.linkTarget\"\n [sizeBytes]=\"item.sizeBytes\"\n [langCode]=\"item.langCode\"\n [assessFile]=\"item.assessFile ?? true\"\n [directDownload]=\"item.directDownload\"></dsfr-download>\n </li>\n }\n </ul>\n</ng-template>\n" }]
}], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], downloads: [{
type: Input
}], downloadSelect: [{
type: Output
}] } });
class DsfrDownloadsGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupModule, imports: [CommonModule, DsfrDownloadComponent, DsfrDownloadsGroupComponent], exports: [DsfrDownloadsGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupModule, imports: [CommonModule, DsfrDownloadComponent, DsfrDownloadsGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDownloadsGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrDownloadsGroupComponent],
imports: [CommonModule, DsfrDownloadComponent, DsfrDownloadsGroupComponent],
}]
}] });
/**
* Définit les différents balisages HTML supportés par la présentation du footer.
*/
var DsfrFooterPresMarkupConst;
(function (DsfrFooterPresMarkupConst) {
/**
* Pour obtenir un conteneur `<DIV>`.
*/
DsfrFooterPresMarkupConst["DIV"] = "div";
/**
* Pour obtenir un conteneur `<P>` par défault.
*/
DsfrFooterPresMarkupConst["P"] = "p";
})(DsfrFooterPresMarkupConst || (DsfrFooterPresMarkupConst = {}));
class DsfrFooterComponent {
/** Liens institutionnels */
/** @internal */
static { this.DEF_INSTITUTIONAL_LINKS = [
{
label: 'info.gouv.fr',
link: 'https://info.gouv.fr',
tooltipMessage: 'info.gouv.fr',
linkTarget: '_blank',
},
{
label: 'service-public.gouv.fr',
link: 'https://service-public.gouv.fr',
tooltipMessage: 'service-public.gouv.fr',
linkTarget: '_blank',
},
{
label: 'legifrance.gouv.fr',
link: 'https://legifrance.gouv.fr',
tooltipMessage: 'legifrance.gouv.fr',
linkTarget: '_blank',
},
{ label: 'data.gouv.fr', link: 'https://data.gouv.fr', tooltipMessage: 'data.gouv.fr', linkTarget: '_blank' },
]; }
/** Liens réglementaires obligatoires */
/** @internal */
static { this.DEF_MANDATORY_LINKS = [
{
label: 'Accessibilité : ',
link: 'https://www.info.gouv.fr/accessibilite',
linkTarget: '_blank',
},
{ label: 'Mentions légales', link: '#' },
{
label: 'Données personnelles',
link: 'https://www.info.gouv.fr/donnees-personnelles-et-cookies',
linkTarget: '_blank',
},
{
label: 'Gestion des cookies',
link: 'https://www.info.gouv.fr/donnees-personnelles-et-cookies',
linkTarget: '_blank',
},
]; }
/** @internal */
constructor() {
/**
* Affichage du lien 'Paramètre d'affichage' pour gérer les modes clair/sombre.
*/
this.display = false;
/** Bloc-marque Marianne. */
this.logo = {
label: 'République<br/>Française',
tooltipMessage: "Accueil - [À MODIFIER - texte alternatif de l’image : nom de l'opérateur ou du site serviciel] - République Française",
};
/** Tableau des liens institutionnels - obligatoires. Initialisé par défaut. */
this.institutionalLinks = DsfrFooterComponent.DEF_INSTITUTIONAL_LINKS;
/**
* Liens d'obligations légales. Cette liste doit être définie en fonction du site, toutefois les liens & contenus suivants
* sont obligatoires : “accessibilité : non/partiellement/totalement conforme”, mentions légales, données personnelles et gestion des cookies. - obligatoire.
*/
this.mandatoryLinks = DsfrFooterComponent.DEF_MANDATORY_LINKS;
/** Le niveau de titre devant être utilisé (tag `H2` par défaut) sur l'élément Nos Partenaires */
this.partnerMainHeadingLevel = DsfrHeadingLevelConst.H3;
/** Listes des logos partenaires. */
this.partnersSub = [];
/** Liste de liens de rebond catégorisé. */
this.reboundLinks = [];
/** Le niveau de titre devant être utilisé (tag `H3` par défaut) sur les catégories de liens de rebond. */
this.reboundLinksHeadingLevel = DsfrHeadingLevelConst.H3;
/** Mention de licence. */
this.license = 'Emplacement de la mention de licence - OBLIGATOIRE.';
/** Type de balise HTML pour le conteneur de la présentation. <p> par défaut. */
this.presentationMarkup = DsfrFooterPresMarkupConst.P;
/** Indique le lien mandatory sélectionné. */
this.mandatoryLinkSelect = new EventEmitter();
/**
* Événement émis lorsque le thème change avec la valeur du nouveau thème : `light`, `dark` ou `system`.
*/
this.displayChange = new EventEmitter();
this._useDeprecatedPictoPath = false;
this.i18n = inject(DsfrI18nService);
this.config = inject(DSFR_CONFIG_TOKEN);
}
get pictoPath() {
return this.artworkDirPath;
}
get displayModalId() {
return DISPLAY_MODAL_ID;
}
/**
* Chemin des pictogrammes (du composant display) renseigné par le développeur.
*
* Note: ce chemin doit permettre de récupérer directement les fichiers SVG suivants : moon.svg, sun.svg, system.svg
*
* @deprecated Use `artworkDirPath` instead.
*/
set pictoPath(path) {
this.artworkDirPath = path;
this._useDeprecatedPictoPath = true;
}
/** Mention légale de la conformité d'accessibilité (none / partially / fully-compliant) */
set accessibility(accessibility) {
let labelAccessibility = 'Accessibilité : non conforme';
switch (accessibility) {
case 'fully-compliant':
labelAccessibility = 'Accessibilité : totalement conforme';
break;
case 'partially':
labelAccessibility = 'Accessibilité : partiellement conforme';
break;
}
DsfrFooterComponent.DEF_MANDATORY_LINKS[0].label = labelAccessibility;
}
ngOnInit() {
if (this.artworkDirPath === undefined && this.config.artworkDirPath) {
this.artworkDirPath = this.config.artworkDirPath;
}
}
/** @internal */
getLinkTarget(item) {
return item.target ?? item.linkTarget;
}
/** @internal */
onMandatoryLinkSelect(link) {
this.mandatoryLinkSelect.emit(link);
}
onDisplayChange(theme) {
this.displayChange.emit(theme);
}
tooltipMessagePartner(partner) {
if (partner.linkTarget === DsfrLinkTargetConst.BLANK) {
return partner.tooltipMessage
? `${partner.tooltipMessage} - ${this.i18n.t('link.newWindow')}`
: `${this.i18n.t('link.newWindow')}`;
}
return partner.tooltipMessage;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFooterComponent, isStandalone: true, selector: "dsfr-footer", inputs: { display: ["display", "display", booleanAttribute], artworkDirPath: "artworkDirPath", logo: "logo", presentation: "presentation", institutionalLinks: "institutionalLinks", mandatoryLinks: "mandatoryLinks", partnerMain: "partnerMain", partnerMainHeadingLevel: "partnerMainHeadingLevel", partnersSub: "partnersSub", reboundLinks: "reboundLinks", reboundLinksHeadingLevel: "reboundLinksHeadingLevel", license: "license", presentationMarkup: "presentationMarkup", pictoPath: "pictoPath", accessibility: "accessibility" }, outputs: { mandatoryLinkSelect: "mandatoryLinkSelect", displayChange: "displayChange" }, queries: [{ propertyName: "licenseTemplate", first: true, predicate: ["licenseTemplate"], descendants: true, static: true }, { propertyName: "presentationTemplate", first: true, predicate: ["presentationTemplate"], descendants: true }], ngImport: i0, template: "<footer class=\"fr-footer\" role=\"contentinfo\" id=\"footer\">\n <!-- ReboundLinks -->\n @if (reboundLinks && reboundLinks.length >= 1) {\n <div class=\"fr-footer__top\">\n <div class=\"fr-container\">\n <!-- BUG: Classe 'fr-grid-row--start' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-grid-row fr-grid-row--start fr-grid-row--gutters\">\n @for (item of reboundLinks; track $index) {\n <div class=\"fr-col-12 fr-col-sm-3 fr-col-md-2\">\n <edu-heading defaultLevel=\"H3\" [level]=\"reboundLinksHeadingLevel\" customClass=\"fr-footer__top-cat\">\n {{ item.title }}\n </edu-heading>\n <ul class=\"fr-footer__top-list\">\n @for (link of item.links; track $index) {\n <li>\n <edu-item-link [item]=\"link\" customClass=\"fr-footer__top-link\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <div class=\"fr-container\">\n <div class=\"fr-footer__body\">\n <div class=\"fr-footer__brand fr-enlarge-link\">\n <!-- Bloc avec logo op\u00E9rateur -->\n @if (logo.imagePath) {\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n <dsfr-link\n [customClass]=\"'fr-footer__brand-link'\"\n [link]=\"logo.link ?? logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logo.navigation?.route ?? ''\"\n [tooltipMessage]=\"logo.tooltipMessage ? logo.tooltipMessage : ''\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n <img\n class=\"fr-footer__logo\"\n [ngClass]=\"logo.customClass || 'defaults'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n </dsfr-link>\n } @else {\n <!-- Bloc sans logo op\u00E9rateur -->\n <dsfr-link\n customClass=\"edu-link\"\n [link]=\"logo.link ?? logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logo.navigation?.route ?? ''\"\n [tooltipMessage]=\"logo.tooltipMessage ? logo.tooltipMessage : ''\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n </dsfr-link>\n }\n </div>\n <div class=\"fr-footer__content\">\n @if (presentation) {\n @if (presentationMarkup === 'p') {\n <p class=\"fr-footer__content-desc\" [innerHtml]=\"presentation\"></p>\n } @else {\n <div class=\"fr-footer__content-desc\" [innerHtml]=\"presentation\"></div>\n }\n }\n\n @if (presentationTemplate && !presentation) {\n @if (presentationMarkup === 'p') {\n <p class=\"fr-footer__content-desc\">\n <ng-container [ngTemplateOutlet]=\"presentationTemplate\"></ng-container>\n </p>\n } @else {\n <div class=\"fr-footer__content-desc\">\n <ng-container [ngTemplateOutlet]=\"presentationTemplate\"></ng-container>\n </div>\n }\n }\n\n <ul class=\"fr-footer__content-list\">\n @for (item of institutionalLinks; track $index) {\n <li class=\"fr-footer__content-item\">\n <!-- BUG: Classe 'fr-footer__content-item' inconnue en DSFR 1.9.3 -->\n <edu-item-link [item]=\"item\" customClass=\"fr-footer__content-link\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </div>\n <!--Bloc avec partenaires-->\n @if (partnerMain) {\n <div class=\"fr-footer__partners\">\n <edu-heading defaultLevel=\"H2\" [level]=\"partnerMainHeadingLevel\" customClass=\"fr-footer__partners-title\">\n {{ 'footer.partners.title' | dsfrI18n }}\n </edu-heading>\n <div class=\"fr-footer__partners-logos\">\n <div class=\"fr-footer__partners-main\">\n <a\n class=\"fr-footer__partners-link\"\n [href]=\"partnerMain.link\"\n [attr.title]=\"tooltipMessagePartner(partnerMain) ?? null\"\n [attr.target]=\"partnerMain.linkTarget || null\">\n <img\n class=\"fr-footer__logo\"\n [ngStyle]=\"{\n height: partnerMain.customHeight ? partnerMain.customHeight : '5.625rem',\n }\"\n [src]=\"partnerMain.imagePath\"\n [attr.alt]=\"partnerMain.imageAlt\" />\n </a>\n </div>\n @if (partnersSub) {\n <div class=\"fr-footer__partners-sub\">\n <ul>\n @for (item of partnersSub; track $index) {\n <li>\n <a\n class=\"fr-footer__partners-link\"\n [attr.title]=\"tooltipMessagePartner(item) ?? null\"\n [href]=\"item.link\"\n [attr.target]=\"item.linkTarget || null\">\n <img\n class=\"fr-footer__logo\"\n [ngStyle]=\"{ height: item.customHeight ? item.customHeight : '5.625rem' }\"\n [src]=\"item.imagePath\"\n [attr.alt]=\"item.imageAlt\" />\n </a>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n }\n <!-- Bottom ------------------------------------------------------------------------------------------------------->\n <div class=\"fr-footer__bottom\">\n <ul class=\"fr-footer__bottom-list\">\n @for (item of mandatoryLinks; track $index) {\n <li class=\"fr-footer__bottom-item\">\n <edu-item-link\n [item]=\"item\"\n customClass=\"fr-footer__bottom-link\"\n (linkSelect)=\"onMandatoryLinkSelect($event)\"></edu-item-link>\n </li>\n }\n @if (display) {\n <li class=\"fr-footer__bottom-item\">\n <button\n type=\"button\"\n class=\"fr-footer__bottom-link fr-fi-theme-fill fr-link--icon-left\"\n [attr.aria-controls]=\"displayModalId\"\n data-fr-opened=\"false\">\n {{ 'footer.display' | dsfrI18n }}\n </button>\n </li>\n }\n </ul>\n <div class=\"fr-footer__bottom-copy\">\n @if (licenseTemplate) {\n <p><ng-container [ngTemplateOutlet]=\"licenseTemplate\"></ng-container></p>\n } @else {\n <p [innerHTML]=\"license\"></p>\n }\n </div>\n </div>\n </div>\n</footer>\n\n@if (display && _useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [pictoPath]=\"artworkDirPath ?? ''\"></dsfr-display>\n} @else if (display && !_useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n", styles: ["dsfr-footer .fr-footer__brand .fr-footer__brand-link{margin-left:1.5rem}dsfr-footer .fr-footer__logo.defaults{width:3.5rem}@media(min-width:48em){dsfr-footer .fr-footer__brand .fr-footer__brand-link{margin-left:2rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: DsfrDisplayComponent, selector: "dsfr-display", inputs: ["displayId", "headingLevel", "artworkDirPath", "pictoPath"], outputs: ["displayChange"] }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-footer', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
RouterModule,
DsfrLinkComponent,
DsfrDisplayComponent,
DsfrI18nPipe,
ItemLinkComponent,
HeadingComponent,
], template: "<footer class=\"fr-footer\" role=\"contentinfo\" id=\"footer\">\n <!-- ReboundLinks -->\n @if (reboundLinks && reboundLinks.length >= 1) {\n <div class=\"fr-footer__top\">\n <div class=\"fr-container\">\n <!-- BUG: Classe 'fr-grid-row--start' inconnue en DSFR 1.9.3 -->\n <div class=\"fr-grid-row fr-grid-row--start fr-grid-row--gutters\">\n @for (item of reboundLinks; track $index) {\n <div class=\"fr-col-12 fr-col-sm-3 fr-col-md-2\">\n <edu-heading defaultLevel=\"H3\" [level]=\"reboundLinksHeadingLevel\" customClass=\"fr-footer__top-cat\">\n {{ item.title }}\n </edu-heading>\n <ul class=\"fr-footer__top-list\">\n @for (link of item.links; track $index) {\n <li>\n <edu-item-link [item]=\"link\" customClass=\"fr-footer__top-link\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <div class=\"fr-container\">\n <div class=\"fr-footer__body\">\n <div class=\"fr-footer__brand fr-enlarge-link\">\n <!-- Bloc avec logo op\u00E9rateur -->\n @if (logo.imagePath) {\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n <dsfr-link\n [customClass]=\"'fr-footer__brand-link'\"\n [link]=\"logo.link ?? logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logo.navigation?.route ?? ''\"\n [tooltipMessage]=\"logo.tooltipMessage ? logo.tooltipMessage : ''\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n <img\n class=\"fr-footer__logo\"\n [ngClass]=\"logo.customClass || 'defaults'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n </dsfr-link>\n } @else {\n <!-- Bloc sans logo op\u00E9rateur -->\n <dsfr-link\n customClass=\"edu-link\"\n [link]=\"logo.link ?? logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logo.navigation?.route ?? ''\"\n [tooltipMessage]=\"logo.tooltipMessage ? logo.tooltipMessage : ''\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n </dsfr-link>\n }\n </div>\n <div class=\"fr-footer__content\">\n @if (presentation) {\n @if (presentationMarkup === 'p') {\n <p class=\"fr-footer__content-desc\" [innerHtml]=\"presentation\"></p>\n } @else {\n <div class=\"fr-footer__content-desc\" [innerHtml]=\"presentation\"></div>\n }\n }\n\n @if (presentationTemplate && !presentation) {\n @if (presentationMarkup === 'p') {\n <p class=\"fr-footer__content-desc\">\n <ng-container [ngTemplateOutlet]=\"presentationTemplate\"></ng-container>\n </p>\n } @else {\n <div class=\"fr-footer__content-desc\">\n <ng-container [ngTemplateOutlet]=\"presentationTemplate\"></ng-container>\n </div>\n }\n }\n\n <ul class=\"fr-footer__content-list\">\n @for (item of institutionalLinks; track $index) {\n <li class=\"fr-footer__content-item\">\n <!-- BUG: Classe 'fr-footer__content-item' inconnue en DSFR 1.9.3 -->\n <edu-item-link [item]=\"item\" customClass=\"fr-footer__content-link\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </div>\n <!--Bloc avec partenaires-->\n @if (partnerMain) {\n <div class=\"fr-footer__partners\">\n <edu-heading defaultLevel=\"H2\" [level]=\"partnerMainHeadingLevel\" customClass=\"fr-footer__partners-title\">\n {{ 'footer.partners.title' | dsfrI18n }}\n </edu-heading>\n <div class=\"fr-footer__partners-logos\">\n <div class=\"fr-footer__partners-main\">\n <a\n class=\"fr-footer__partners-link\"\n [href]=\"partnerMain.link\"\n [attr.title]=\"tooltipMessagePartner(partnerMain) ?? null\"\n [attr.target]=\"partnerMain.linkTarget || null\">\n <img\n class=\"fr-footer__logo\"\n [ngStyle]=\"{\n height: partnerMain.customHeight ? partnerMain.customHeight : '5.625rem',\n }\"\n [src]=\"partnerMain.imagePath\"\n [attr.alt]=\"partnerMain.imageAlt\" />\n </a>\n </div>\n @if (partnersSub) {\n <div class=\"fr-footer__partners-sub\">\n <ul>\n @for (item of partnersSub; track $index) {\n <li>\n <a\n class=\"fr-footer__partners-link\"\n [attr.title]=\"tooltipMessagePartner(item) ?? null\"\n [href]=\"item.link\"\n [attr.target]=\"item.linkTarget || null\">\n <img\n class=\"fr-footer__logo\"\n [ngStyle]=\"{ height: item.customHeight ? item.customHeight : '5.625rem' }\"\n [src]=\"item.imagePath\"\n [attr.alt]=\"item.imageAlt\" />\n </a>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n }\n <!-- Bottom ------------------------------------------------------------------------------------------------------->\n <div class=\"fr-footer__bottom\">\n <ul class=\"fr-footer__bottom-list\">\n @for (item of mandatoryLinks; track $index) {\n <li class=\"fr-footer__bottom-item\">\n <edu-item-link\n [item]=\"item\"\n customClass=\"fr-footer__bottom-link\"\n (linkSelect)=\"onMandatoryLinkSelect($event)\"></edu-item-link>\n </li>\n }\n @if (display) {\n <li class=\"fr-footer__bottom-item\">\n <button\n type=\"button\"\n class=\"fr-footer__bottom-link fr-fi-theme-fill fr-link--icon-left\"\n [attr.aria-controls]=\"displayModalId\"\n data-fr-opened=\"false\">\n {{ 'footer.display' | dsfrI18n }}\n </button>\n </li>\n }\n </ul>\n <div class=\"fr-footer__bottom-copy\">\n @if (licenseTemplate) {\n <p><ng-container [ngTemplateOutlet]=\"licenseTemplate\"></ng-container></p>\n } @else {\n <p [innerHTML]=\"license\"></p>\n }\n </div>\n </div>\n </div>\n</footer>\n\n@if (display && _useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [pictoPath]=\"artworkDirPath ?? ''\"></dsfr-display>\n} @else if (display && !_useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n", styles: ["dsfr-footer .fr-footer__brand .fr-footer__brand-link{margin-left:1.5rem}dsfr-footer .fr-footer__logo.defaults{width:3.5rem}@media(min-width:48em){dsfr-footer .fr-footer__brand .fr-footer__brand-link{margin-left:2rem}}\n"] }]
}], ctorParameters: () => [], propDecorators: { display: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], artworkDirPath: [{
type: Input
}], logo: [{
type: Input
}], presentation: [{
type: Input
}], institutionalLinks: [{
type: Input
}], mandatoryLinks: [{
type: Input
}], partnerMain: [{
type: Input
}], partnerMainHeadingLevel: [{
type: Input
}], partnersSub: [{
type: Input
}], reboundLinks: [{
type: Input
}], reboundLinksHeadingLevel: [{
type: Input
}], license: [{
type: Input
}], presentationMarkup: [{
type: Input
}], licenseTemplate: [{
type: ContentChild,
args: ['licenseTemplate', { static: true }]
}], presentationTemplate: [{
type: ContentChild,
args: ['presentationTemplate']
}], mandatoryLinkSelect: [{
type: Output
}], displayChange: [{
type: Output
}], pictoPath: [{
type: Input
}], accessibility: [{
type: Input
}] } });
class DsfrFooterModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterModule, imports: [CommonModule,
RouterModule,
DsfrLinkComponent,
DsfrDisplayComponent,
DsfrI18nPipe,
ItemLinkComponent,
DsfrFooterComponent], exports: [DsfrFooterComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterModule, imports: [CommonModule,
RouterModule,
DsfrLinkComponent,
DsfrDisplayComponent,
ItemLinkComponent,
DsfrFooterComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFooterModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFooterComponent],
imports: [
CommonModule,
RouterModule,
DsfrLinkComponent,
DsfrDisplayComponent,
DsfrI18nPipe,
ItemLinkComponent,
DsfrFooterComponent,
],
}]
}] });
class DsfrFranceConnectComponent {
constructor() {
/** Utilisation de FranceConnect+ (qui est plus sécurisé). */
this.secure = false;
/** Demande de connexion par FranceConnect */
this.franceConnectSelect = new EventEmitter();
}
/** @internal */
onConnect() {
this.franceConnectSelect.emit(this.secure);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFranceConnectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.23", type: DsfrFranceConnectComponent, isStandalone: true, selector: "dsfr-franceconnect", inputs: { secure: ["secure", "secure", booleanAttribute] }, outputs: { franceConnectSelect: "franceConnectSelect" }, ngImport: i0, template: "<div class=\"fr-connect-group\">\n <button\n type=\"button\"\n [class]=\"{\n 'fr-connect': true,\n 'fr-connect--plus': secure,\n }\"\n (click)=\"onConnect()\">\n <span class=\"fr-connect__login\">{{ 'franceConnect.title' | dsfrI18n }}</span>\n <span class=\"fr-connect__brand\">FranceConnect</span>\n </button>\n <p>\n <a\n [href]=\"secure ? 'https://franceconnect.gouv.fr/france-connect-plus' : 'https://franceconnect.gouv.fr/'\"\n target=\"_blank\"\n rel=\"noopener\"\n [title]=\"'franceConnect.link.tooltip' | dsfrI18n\"\n >{{ 'franceConnect.link.label' | dsfrI18n }}{{ secure ? '+' : '' }} ?</a\n >\n </p>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFranceConnectComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-franceconnect', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrI18nPipe], template: "<div class=\"fr-connect-group\">\n <button\n type=\"button\"\n [class]=\"{\n 'fr-connect': true,\n 'fr-connect--plus': secure,\n }\"\n (click)=\"onConnect()\">\n <span class=\"fr-connect__login\">{{ 'franceConnect.title' | dsfrI18n }}</span>\n <span class=\"fr-connect__brand\">FranceConnect</span>\n </button>\n <p>\n <a\n [href]=\"secure ? 'https://franceconnect.gouv.fr/france-connect-plus' : 'https://franceconnect.gouv.fr/'\"\n target=\"_blank\"\n rel=\"noopener\"\n [title]=\"'franceConnect.link.tooltip' | dsfrI18n\"\n >{{ 'franceConnect.link.label' | dsfrI18n }}{{ secure ? '+' : '' }} ?</a\n >\n </p>\n</div>\n" }]
}], propDecorators: { secure: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], franceConnectSelect: [{
type: Output
}] } });
class DsfrMegaMenuComponent {
constructor() {
/**Emet l'évènement Event du DOM à la sélection d'un lien */
this.linkSelect = new EventEmitter();
/**Emet l'évènement Event du DOM à la fermeture du méga menu */
this.closeSelect = new EventEmitter();
}
onLink(event) {
this.linkSelect.emit(event);
}
onClose() {
this.closeSelect.emit();
}
// Support ancien type string (1.18), a supprimer en V2
isString(value) {
return typeof value === 'string';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrMegaMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrMegaMenuComponent, isStandalone: true, selector: "dsfr-mega-menu", inputs: { megaMenu: "megaMenu", idMenu: "idMenu" }, outputs: { linkSelect: "linkSelect", closeSelect: "closeSelect" }, ngImport: i0, template: "<div class=\"fr-container fr-container--fluid fr-container-lg\">\n <div class=\"fr-grid-row fr-grid-row-lg--gutters\">\n <div class=\"fr-col-12 fr-mb-n3v\">\n <button\n type=\"button\"\n (click)=\"onClose()\"\n class=\"fr-btn--close fr-btn\"\n [title]=\"'commons.close' | dsfrI18n\"\n [attr.aria-controls]=\"'mega-menu-' + idMenu\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n @if (megaMenu.leader) {\n <div class=\"fr-col-12 fr-col-lg-8 fr-col-offset-lg-4--right\">\n <div class=\"fr-mega-menu__leader\">\n <h4 class=\"fr-h4 fr-mb-2v\">{{ megaMenu.leader.title }}</h4>\n @if (megaMenu.leader.text) {\n <p>{{ megaMenu.leader.text }}</p>\n }\n @if (megaMenu.leader.link) {\n <edu-item-link\n [item]=\"megaMenu.leader.link\"\n [icon]=\"'fr-icon-arrow-right-line'\"\n (linkSelect)=\"onLink(megaMenu.leader.link)\"></edu-item-link>\n }\n </div>\n </div>\n }\n @for (category of megaMenu.categories; track category.label) {\n <div class=\"fr-col-12 fr-col-lg-3\">\n @if (category.label) {\n <h5 class=\"fr-mega-menu__category\">\n @if (!category.link) {\n <span class=\"fr-nav__link\">{{ category.label }}</span>\n } @else if (isString(category.link)) {\n <dsfr-link\n [label]=\"category.label\"\n [customClass]=\"'fr-nav__link'\"\n [link]=\"category.link\"\n linkTarget=\"_self\">\n </dsfr-link>\n } @else {\n <edu-item-link\n [item]=\"category.link\"\n [customClass]=\"'fr-nav__link'\"\n (linkSelect)=\"onLink(category.link)\"></edu-item-link>\n }\n </h5>\n }\n <ul class=\"fr-mega-menu__list\">\n @for (link of category.subItems; track $index) {\n <li>\n @if (link) {\n <edu-item-link [item]=\"link\" customClass=\"fr-nav__link\" (linkSelect)=\"onLink(link)\"></edu-item-link>\n }\n </li>\n }\n </ul>\n </div>\n }\n </div>\n</div>\n", styles: [".fr-mega-menu .fr-container:before{content:\"\";display:block;padding-top:1rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "ngmodule", type: RouterModule }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrMegaMenuComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-mega-menu', encapsulation: ViewEncapsulation.None, imports: [FormsModule, ItemLinkComponent, DsfrLinkComponent, RouterModule, DsfrI18nPipe], template: "<div class=\"fr-container fr-container--fluid fr-container-lg\">\n <div class=\"fr-grid-row fr-grid-row-lg--gutters\">\n <div class=\"fr-col-12 fr-mb-n3v\">\n <button\n type=\"button\"\n (click)=\"onClose()\"\n class=\"fr-btn--close fr-btn\"\n [title]=\"'commons.close' | dsfrI18n\"\n [attr.aria-controls]=\"'mega-menu-' + idMenu\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n @if (megaMenu.leader) {\n <div class=\"fr-col-12 fr-col-lg-8 fr-col-offset-lg-4--right\">\n <div class=\"fr-mega-menu__leader\">\n <h4 class=\"fr-h4 fr-mb-2v\">{{ megaMenu.leader.title }}</h4>\n @if (megaMenu.leader.text) {\n <p>{{ megaMenu.leader.text }}</p>\n }\n @if (megaMenu.leader.link) {\n <edu-item-link\n [item]=\"megaMenu.leader.link\"\n [icon]=\"'fr-icon-arrow-right-line'\"\n (linkSelect)=\"onLink(megaMenu.leader.link)\"></edu-item-link>\n }\n </div>\n </div>\n }\n @for (category of megaMenu.categories; track category.label) {\n <div class=\"fr-col-12 fr-col-lg-3\">\n @if (category.label) {\n <h5 class=\"fr-mega-menu__category\">\n @if (!category.link) {\n <span class=\"fr-nav__link\">{{ category.label }}</span>\n } @else if (isString(category.link)) {\n <dsfr-link\n [label]=\"category.label\"\n [customClass]=\"'fr-nav__link'\"\n [link]=\"category.link\"\n linkTarget=\"_self\">\n </dsfr-link>\n } @else {\n <edu-item-link\n [item]=\"category.link\"\n [customClass]=\"'fr-nav__link'\"\n (linkSelect)=\"onLink(category.link)\"></edu-item-link>\n }\n </h5>\n }\n <ul class=\"fr-mega-menu__list\">\n @for (link of category.subItems; track $index) {\n <li>\n @if (link) {\n <edu-item-link [item]=\"link\" customClass=\"fr-nav__link\" (linkSelect)=\"onLink(link)\"></edu-item-link>\n }\n </li>\n }\n </ul>\n </div>\n }\n </div>\n</div>\n", styles: [".fr-mega-menu .fr-container:before{content:\"\";display:block;padding-top:1rem}\n"] }]
}], propDecorators: { megaMenu: [{
type: Input,
args: [{ required: true }]
}], idMenu: [{
type: Input,
args: [{ required: true }]
}], linkSelect: [{
type: Output
}], closeSelect: [{
type: Output
}] } });
class DsfrToolLinkComponent {
constructor() {
/** Evènement émis au clic sur un lien */
this.linkSelect = new EventEmitter();
}
onLinkSelect(link) {
this.linkSelect.emit(link);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrToolLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrToolLinkComponent, isStandalone: true, selector: "dsfr-tool-link", inputs: { link: "link" }, outputs: { linkSelect: "linkSelect" }, ngImport: i0, template: `
<edu-item-link
[customClass]="'fr-btn ' + (link.customClass ?? '')"
[defaultIconPosition]="link.iconPosition ?? undefined"
(linkSelect)="onLinkSelect(link)"
[item]="link">
</edu-item-link>
`, isInline: true, styles: [".edu-tools-links.fr-header__tools-links{display:block}\n"], dependencies: [{ kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrToolLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tool-link', template: `
<edu-item-link
[customClass]="'fr-btn ' + (link.customClass ?? '')"
[defaultIconPosition]="link.iconPosition ?? undefined"
(linkSelect)="onLinkSelect(link)"
[item]="link">
</edu-item-link>
`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [ItemLinkComponent], styles: [".edu-tools-links.fr-header__tools-links{display:block}\n"] }]
}], propDecorators: { link: [{
type: Input,
args: [{ required: true }]
}], linkSelect: [{
type: Output
}] } });
class DsfrSearchBarComponent {
constructor() {
/** Emet le texte lors de l'évènement keyup sur l'input de recherche. */
this.searchChange = new EventEmitter();
/** Emet le texte lors du clic sur le bouton "rechercher". */
this.searchSelect = new EventEmitter();
/** Label (lecteurs d’écran). */
this.label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
/** Version large du composant. */
this.large = input(false, ...(ngDevMode ? [{ debugName: "large", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/** Placeholder de l’input. */
this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : []));
/** Attribut title du bouton. */
this.buttonTitle = input(...(ngDevMode ? [undefined, { debugName: "buttonTitle" }] : []));
/** Pattern de l’input. */
this.pattern = input(...(ngDevMode ? [undefined, { debugName: "pattern" }] : []));
/** Soumettre à l’appui sur Entrée. */
this.triggerSearchOnEnter = input(false, ...(ngDevMode ? [{ debugName: "triggerSearchOnEnter", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/** Attribut maxLength de l’input. */
this.maxLength = input(...(ngDevMode ? [undefined, { debugName: "maxLength" }] : []));
/**
* Le délai (en millisecondes) pour déclencher l'événement de changement de saisie et de filtrage.
* Par défaut la valeur est 0
*/
this.delay = input(0, ...(ngDevMode ? [{ debugName: "delay" }] : []));
this.i18n = inject(DsfrI18nService);
this.inputChange$ = new Subject(); // Subject pour capturer les changements d'input
this.destroyRef = inject(DestroyRef);
}
get formId() {
return 'search-form-' + this.inputId;
}
/**
* Positionne la valeur initiale de la barre de recherche.
*
* @deprecated (since 1.15) Utiliser la propriété `value` à la place
*/
set initialValue(initialValue) {
this.value = initialValue;
}
/**
* Cet attribut doit être utilisé en tant que propriété et non en attribut, ex. [id]="'monid'"
*
* @deprecated Utiliser la propriété `inputId` à la place
*/
set id(value) {
if (value) {
this._id = value;
this.inputId ??= this._id;
}
}
ngOnInit() {
this.inputChange$
.pipe(map((e) => e.target.value), debounceTime(this.delay()), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.handleSearchChange());
}
ngAfterContentInit() {
if (!this.inputId)
this.inputId = newUniqueId();
}
inputSearchChange(event) {
this.inputChange$.next(event);
}
/** @internal */
onSearchSelect() {
this.searchSelect.emit(this.value);
}
/**
* Si le libellé du bouton est identique au libellé potentiel du tooltip, il ne faut pas positionner de tooltip.
*/
getButtonTitleValue() {
const defaultTitle = this.i18n.t('commons.search');
return this.buttonText === defaultTitle ? null : defaultTitle;
}
handleSearchChange() {
this.searchChange.emit(this.value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrSearchBarComponent, isStandalone: true, selector: "dsfr-search-bar", inputs: { inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: false, isRequired: false, transformFunction: null }, buttonText: { classPropertyName: "buttonText", publicName: "buttonText", isSignal: false, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, large: { classPropertyName: "large", publicName: "large", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, buttonTitle: { classPropertyName: "buttonTitle", publicName: "buttonTitle", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, triggerSearchOnEnter: { classPropertyName: "triggerSearchOnEnter", publicName: "triggerSearchOnEnter", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, initialValue: { classPropertyName: "initialValue", publicName: "initialValue", isSignal: false, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { searchChange: "searchChange", searchSelect: "searchSelect" }, ngImport: i0, template: "<div [class.fr-search-bar--lg]=\"large()\" class=\"fr-search-bar\" [id]=\"formId\" role=\"search\">\n <label class=\"fr-label\" for=\"{{ inputId }}\"> {{ label() ?? ('commons.search' | dsfrI18n) }} </label>\n\n @if (triggerSearchOnEnter()) {\n <!-- edugouvfr-variant -->\n <form class=\"edu-search-bar-form\" (submit)=\"onSearchSelect()\">\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n </form>\n } @else {\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n }\n</div>\n\n<ng-template #searchTemplate>\n <input\n type=\"search\"\n class=\"fr-input\"\n [placeholder]=\"placeholder() ?? ('commons.search' | dsfrI18n)\"\n (keyup)=\"inputSearchChange($event)\"\n [attr.maxlength]=\"maxLength() || null\"\n [id]=\"inputId\"\n [pattern]=\"pattern() ?? ''\"\n name=\"{{ inputId || null }}\"\n [(ngModel)]=\"value\" />\n\n <button\n class=\"fr-btn\"\n id=\"search-btn-{{ inputId }}\"\n [type]=\"triggerSearchOnEnter() ? 'submit' : 'button'\"\n [attr.title]=\"buttonTitle() ?? this.getButtonTitleValue()\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n</ng-template>\n", styles: ["dsfr-search-bar .edu-search-bar-form{display:flex;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-search-bar', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrI18nPipe], template: "<div [class.fr-search-bar--lg]=\"large()\" class=\"fr-search-bar\" [id]=\"formId\" role=\"search\">\n <label class=\"fr-label\" for=\"{{ inputId }}\"> {{ label() ?? ('commons.search' | dsfrI18n) }} </label>\n\n @if (triggerSearchOnEnter()) {\n <!-- edugouvfr-variant -->\n <form class=\"edu-search-bar-form\" (submit)=\"onSearchSelect()\">\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n </form>\n } @else {\n <ng-container *ngTemplateOutlet=\"searchTemplate\"></ng-container>\n }\n</div>\n\n<ng-template #searchTemplate>\n <input\n type=\"search\"\n class=\"fr-input\"\n [placeholder]=\"placeholder() ?? ('commons.search' | dsfrI18n)\"\n (keyup)=\"inputSearchChange($event)\"\n [attr.maxlength]=\"maxLength() || null\"\n [id]=\"inputId\"\n [pattern]=\"pattern() ?? ''\"\n name=\"{{ inputId || null }}\"\n [(ngModel)]=\"value\" />\n\n <button\n class=\"fr-btn\"\n id=\"search-btn-{{ inputId }}\"\n [type]=\"triggerSearchOnEnter() ? 'submit' : 'button'\"\n [attr.title]=\"buttonTitle() ?? this.getButtonTitleValue()\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n</ng-template>\n", styles: ["dsfr-search-bar .edu-search-bar-form{display:flex;width:100%}\n"] }]
}], ctorParameters: () => [], propDecorators: { inputId: [{
type: Input
}], buttonText: [{
type: Input
}], value: [{
type: Input
}], searchChange: [{
type: Output
}], searchSelect: [{
type: Output
}], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], large: [{ type: i0.Input, args: [{ isSignal: true, alias: "large", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], buttonTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonTitle", required: false }] }], pattern: [{ type: i0.Input, args: [{ isSignal: true, alias: "pattern", required: false }] }], triggerSearchOnEnter: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerSearchOnEnter", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], initialValue: [{
type: Input
}], id: [{
type: Input
}] } });
class DsfrSearchBarModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarModule, imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrSearchBarComponent], exports: [DsfrSearchBarComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarModule, imports: [CommonModule, FormsModule, DsfrSearchBarComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSearchBarModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrSearchBarComponent],
imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrSearchBarComponent],
}]
}] });
/** Composant de sélection de la langue. */
class DsfrTranslateComponent {
constructor() {
/** Liste des langues proposées à l'utilisateur. */
this.languages = [];
/** Sans bordure si 'false' */
this.outline = true;
/** Événement émit uniquement si la langue sélectionnée ne contient pas de href. Il contient le code de la langue. */
this.langChange = new EventEmitter();
this.collapseId = '';
this.langService = inject(LangService);
this.elementRef = inject(ElementRef);
this.document = inject(DOCUMENT);
}
get currentLangCode() {
return this.langService.lang;
}
get currentLabel() {
let label = '';
const language = this.getCurrentLang();
if (language && language.label) {
label = language.label;
}
return label;
}
/** Force la langue courante. */
set currentLangCode(value) {
this.langService.lang = value; // Informe le service
this.setDocLang(value); // Positionne la langue dans le document html
}
ngOnInit() {
this.collapseId = newUniqueId();
}
onClick(event, lang) {
this.currentLangCode = lang.value;
if (!lang.link) {
event.preventDefault();
// Si on ne déclenche pas le href, on est obligé de fermer manuellement le composant
this.collapse();
this.langChange.emit(lang.value);
}
}
getCurrentLang() {
return this.languages.find((l) => l.value === this.currentLangCode);
}
collapse() {
const nativeElt = this.elementRef.nativeElement;
const buttonElt = nativeElt.querySelector('button');
if (buttonElt) {
buttonElt.setAttribute('aria-expanded', 'false');
buttonElt.setAttribute('data-fr-js-collapse-button', true);
}
}
/** Positionne l'attribut lang dans la balise html, ex : lang="fr" */
setDocLang(codeLang) {
this.document?.documentElement.setAttribute('lang', codeLang);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTranslateComponent, isStandalone: true, selector: "dsfr-translate", inputs: { languages: "languages", outline: "outline", currentLangCode: "currentLangCode" }, outputs: { langChange: "langChange" }, ngImport: i0, template: "<div class=\"fr-translate fr-nav\">\n <div class=\"fr-nav__item\">\n <button\n type=\"button\"\n [ngClass]=\"{\n 'fr-translate__btn': true,\n 'fr-btn fr-btn--tertiary': outline,\n 'fr-btn--tertiary-no-outline': !outline,\n }\"\n [attr.aria-controls]=\"collapseId\"\n aria-expanded=\"false\"\n title=\"{{ 'translate.tooltip' | dsfrI18n }}\">\n {{ currentLangCode.toUpperCase() }}<span class=\"fr-hidden-lg\"> - {{ currentLabel }}</span>\n </button>\n <div class=\"fr-collapse fr-translate__menu fr-menu\" [id]=\"collapseId\">\n <ul class=\"fr-menu__list\">\n @for (lang of languages; track $index) {\n <li>\n <a\n class=\"fr-translate__language fr-nav__link\"\n hreflang=\"{{ lang.value }}\"\n lang=\"{{ lang.value }}\"\n [href]=\"lang.link ? lang.link : lang.value\"\n [attr.aria-current]=\"lang.value === currentLangCode ? true : null\"\n (click)=\"onClick($event, lang)\">\n {{ lang.value.toUpperCase() }} - {{ lang.label }}\n </a>\n </li>\n }\n </ul>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-translate', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrI18nPipe], template: "<div class=\"fr-translate fr-nav\">\n <div class=\"fr-nav__item\">\n <button\n type=\"button\"\n [ngClass]=\"{\n 'fr-translate__btn': true,\n 'fr-btn fr-btn--tertiary': outline,\n 'fr-btn--tertiary-no-outline': !outline,\n }\"\n [attr.aria-controls]=\"collapseId\"\n aria-expanded=\"false\"\n title=\"{{ 'translate.tooltip' | dsfrI18n }}\">\n {{ currentLangCode.toUpperCase() }}<span class=\"fr-hidden-lg\"> - {{ currentLabel }}</span>\n </button>\n <div class=\"fr-collapse fr-translate__menu fr-menu\" [id]=\"collapseId\">\n <ul class=\"fr-menu__list\">\n @for (lang of languages; track $index) {\n <li>\n <a\n class=\"fr-translate__language fr-nav__link\"\n hreflang=\"{{ lang.value }}\"\n lang=\"{{ lang.value }}\"\n [href]=\"lang.link ? lang.link : lang.value\"\n [attr.aria-current]=\"lang.value === currentLangCode ? true : null\"\n (click)=\"onClick($event, lang)\">\n {{ lang.value.toUpperCase() }} - {{ lang.label }}\n </a>\n </li>\n }\n </ul>\n </div>\n </div>\n</div>\n" }]
}], propDecorators: { languages: [{
type: Input
}], outline: [{
type: Input
}], langChange: [{
type: Output
}], currentLangCode: [{
type: Input
}] } });
class DsfrTranslateModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateModule, imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrTranslateComponent], exports: [DsfrTranslateComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateModule, imports: [CommonModule, FormsModule, DsfrTranslateComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTranslateModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrTranslateComponent],
imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrTranslateComponent],
}]
}] });
/**
* Directive pour wrapper les éléments d'un container ul avec des li
* fixme : probleme lorsque les éléments sont générés dynamiquements et avec ng-template :
* Dans le cas ou des éléments sont supprimés dynamiquement, erreur insertBefore angular.
* Tests à refaire pour le cas du ng-content. Code commenté a supprimer si non concluant.
* On conserve la directive, pour ajouter des warning sur les éléments qui ne sont pas wrappés
*/
class EduWrapWithLiDirective {
constructor() {
this.el = inject(ElementRef);
}
ngAfterViewInit() {
// this.wrapChildren();
this.warningChildren();
/*
// Observe les ajouts dynamiques (si ngTemplateOutlet, etc.)
this.mutationObserver = new MutationObserver(() => this.wrapChildren());
this.mutationObserver.observe(this.el.nativeElement, {
childList: true,
subtree: false,
});*/
}
warningChildren() {
const container = this.el.nativeElement;
for (const elem of Array.from(container?.children)) {
if (elem?.parentElement?.tagName !== 'LI' && elem?.tagName !== 'LI') {
console.warn('Chaque élément de toolsLinksTemplate doit être contenu dans un <li>');
}
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduWrapWithLiDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: EduWrapWithLiDirective, isStandalone: true, selector: "[eduWrapWithLi]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduWrapWithLiDirective, decorators: [{
type: Directive,
args: [{
selector: '[eduWrapWithLi]',
standalone: true,
}]
}] });
/**
* Composant interne représentant le container des liens d'accès rapides du header
*/
class EduToolsLinksComponent {
constructor() {
/** Liste des liens rapides */
this.toolsLinks = [];
/** Composant display à afficher */
this.showDisplay = false;
/** Nombre maximum de liens d'accès rapides (3) */
this.maxToolsLinks = 3;
/** Evènement émis au clic sur un lien */
this.linkSelect = new EventEmitter();
/* Référence du container depuis le composant header (pour duplication en mobile) */
this.toolsLinksListRef = viewChild('toolsLinksList', ...(ngDevMode ? [{ debugName: "toolsLinksListRef" }] : []));
}
onLinkSelect(link) {
this.linkSelect.emit(link);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduToolsLinksComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: EduToolsLinksComponent, isStandalone: true, selector: "edu-tools-links", inputs: { toolsLinks: "toolsLinks", showDisplay: ["showDisplay", "showDisplay", booleanAttribute], displayId: "displayId", toolsLinksTemplate: "toolsLinksTemplate", maxToolsLinks: "maxToolsLinks" }, outputs: { linkSelect: "linkSelect" }, viewQueries: [{ propertyName: "toolsLinksListRef", first: true, predicate: ["toolsLinksList"], descendants: true, isSignal: true }], ngImport: i0, template: "@if ((toolsLinks && toolsLinks.length > 0) || showDisplay || toolsLinksTemplate) {\n <ul class=\"fr-btns-group\" eduWrapWithLi #toolsLinksList>\n @if (toolsLinks && toolsLinks.length > 0) {\n @for (item of toolsLinks; track i; let i = $index) {\n @if (i < maxToolsLinks) {\n <li><dsfr-tool-link (linkSelect)=\"onLinkSelect(item)\" [link]=\"item\"></dsfr-tool-link></li>\n }\n }\n }\n @if (toolsLinksTemplate) {\n <ng-container *ngTemplateOutlet=\"toolsLinksTemplate\"></ng-container>\n }\n @if (showDisplay) {\n <li>\n <button type=\"button\" class=\"fr-btn fr-icon-theme-fill\" [attr.aria-controls]=\"displayId\" data-fr-opened=\"false\">\n {{ 'display.heading' | dsfrI18n }}\n </button>\n </li>\n }\n </ul>\n}\n", styles: [".edu-tools-links.fr-header__tools-links{display:block}\n"], dependencies: [{ kind: "component", type: DsfrToolLinkComponent, selector: "dsfr-tool-link", inputs: ["link"], outputs: ["linkSelect"] }, { kind: "directive", type: EduWrapWithLiDirective, selector: "[eduWrapWithLi]" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduToolsLinksComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-tools-links', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [DsfrToolLinkComponent, EduWrapWithLiDirective, DsfrI18nPipe, CommonModule], template: "@if ((toolsLinks && toolsLinks.length > 0) || showDisplay || toolsLinksTemplate) {\n <ul class=\"fr-btns-group\" eduWrapWithLi #toolsLinksList>\n @if (toolsLinks && toolsLinks.length > 0) {\n @for (item of toolsLinks; track i; let i = $index) {\n @if (i < maxToolsLinks) {\n <li><dsfr-tool-link (linkSelect)=\"onLinkSelect(item)\" [link]=\"item\"></dsfr-tool-link></li>\n }\n }\n }\n @if (toolsLinksTemplate) {\n <ng-container *ngTemplateOutlet=\"toolsLinksTemplate\"></ng-container>\n }\n @if (showDisplay) {\n <li>\n <button type=\"button\" class=\"fr-btn fr-icon-theme-fill\" [attr.aria-controls]=\"displayId\" data-fr-opened=\"false\">\n {{ 'display.heading' | dsfrI18n }}\n </button>\n </li>\n }\n </ul>\n}\n", styles: [".edu-tools-links.fr-header__tools-links{display:block}\n"] }]
}], propDecorators: { toolsLinks: [{
type: Input,
args: [{ required: true }]
}], showDisplay: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], displayId: [{
type: Input,
args: [{ required: true }]
}], toolsLinksTemplate: [{
type: Input
}], maxToolsLinks: [{
type: Input
}], linkSelect: [{
type: Output
}], toolsLinksListRef: [{ type: i0.ViewChild, args: ['toolsLinksList', { isSignal: true }] }] } });
/**
* Modèle de présentation interne du menu.
*/
class ViewMenu {
constructor(/** Menu wrappé */ menu) {
this.menu = menu;
this.menuId = newUniqueId();
}
}
class DsfrHeaderComponent {
constructor() {
/**
* Boolean pour afficher le badge Beta. Pour un badge différent utiliser un dsfr-badge avec le slot serviceTitleTemplate.
*/
this.beta = false;
/**
* Définit le bloc-marque Marianne + opérateur.
*
* @since 1.16.0
*/
this.logo = { label: 'République<br/>Française' };
/**
* Permet d'afficher la barre de recherche.
*/
this.searchBar = false;
/**
* Label de la barre de recherche (pour les lecteurs d'écrans).
*/
this.searchLabel = 'Rechercher';
/**
* Permet de soumettre la recherche à l'appui sur la touche Entrée.
*/
this.triggerSearchOnEnter = false;
/**
* Renvoie le code de la langue au changement de langue (si `showTranslate`).
*/
this.langChange = new EventEmitter();
/**
* Renvoie le lien lors d'un clic sur un lien en mode `route`.
*/
this.linkSelect = new EventEmitter();
/**
* Renvoie la valeur de l'input de la barre de recherche au changement.
*/
this.searchChange = new EventEmitter();
/**
* Renvoie la valeur de l'input de la barre de recherche au clic sur rechercher.
*/
this.searchSelect = new EventEmitter();
/**
* Renvoie la valeur du nouveau thème lorsque le thème change : `light`, `dark` ou `system`.
*/
this.displayChange = new EventEmitter();
/**
* Notifie l'ouverture du menu en affichage mobile.
*/
this.menuDisclose = new EventEmitter();
/**
* Notifie la fermeture du menu en affichage mobile.
*/
this.menuConceal = new EventEmitter();
/** Menu de navigation principale */
this.menu = input([], ...(ngDevMode ? [{ debugName: "menu" }] : []));
this.searchInputId = newUniqueId();
/** Nombre maximum de liens d'accès rapides (3) */
this.maxToolsLinks = 3;
this._useDeprecatedPictoPath = false;
this.showDisplay = false;
// fixme: supprimer en V2 avec le déplacement de translate dans EduToolsLinksComponent
this.translateElement = viewChild('translateComponent', ...(ngDevMode ? [{ debugName: "translateElement", read: ElementRef }] : [{ read: ElementRef }]));
this.translateNativeElement = computed(() => this.translateElement()?.nativeElement, ...(ngDevMode ? [{ debugName: "translateNativeElement" }] : []));
this.toolsLinksCmp = viewChild('toolsLinksComponent', ...(ngDevMode ? [{ debugName: "toolsLinksCmp" }] : []));
this.toolsLinksNativeElement = computed(() => this.toolsLinksCmp()?.toolsLinksListRef()?.nativeElement, ...(ngDevMode ? [{ debugName: "toolsLinksNativeElement" }] : []));
this.toolsLinksMobile = viewChild('toolLinksMobile', ...(ngDevMode ? [{ debugName: "toolsLinksMobile" }] : []));
this.toolsLinksMobileContainer = computed(() => this.toolsLinksMobile()?.nativeElement, ...(ngDevMode ? [{ debugName: "toolsLinksMobileContainer" }] : []));
this.menuModal = viewChild('menuModal', ...(ngDevMode ? [{ debugName: "menuModal" }] : []));
this.viewMenu = computed(() => {
if (!this.menu || !this.menu().length)
return [];
return this.menu().map((m) => new ViewMenu(m));
}, ...(ngDevMode ? [{ debugName: "viewMenu" }] : []));
this._headerToolsLinks = [];
this.renderer = inject(Renderer2);
this.config = inject(DSFR_CONFIG_TOKEN);
this._unlisten = [];
// Ajout observer a l'init pour la copie du script DSFR puis sur l'ajout d'éléments
if (typeof MutationObserver !== 'undefined') {
this.mutationObserver = new MutationObserver(() => {
this.duplicateToolsLinksMobile(); // dupliquer les liens pour la version mobile
});
}
else {
this.duplicateToolsLinksMobile(); // mode SSR dégradé
}
effect(() => {
if (this.toolsLinksMobileContainer())
this.mutationObserver?.observe(this.toolsLinksMobileContainer(), { childList: true, subtree: true });
});
}
get display() {
return this.showDisplay;
}
get headerToolsLinks() {
return this._headerToolsLinks;
}
get displayModalId() {
return DISPLAY_MODAL_ID;
}
get pictoPath() {
return this.artworkDirPath;
}
get searchInputInitialValue() {
return this.searchInputValue;
}
/** Affichage du lien 'Paramètre d'affichage' pour gérer les modes clair/sombre. */
set display(value) {
this.showDisplay = value;
this.maxToolsLinks = this.showDisplay ? 2 : 3;
}
/**
* Tableau de lien d'accès rapide.
* Les icônes acceptées sont ceux du DSFR y compris ceux du tableau `DsfrBtnIcon`.
*/
set headerToolsLinks(links) {
if (links && links.length > this.maxToolsLinks) {
if (this.showDisplay) {
console.warn("Avec l'utilisation du paramètre d'affichage, le nombre d'accès rapides (tools links) est limité à " +
this.maxToolsLinks);
}
else {
console.warn("Le nombre d'accès rapides (tools links) est limité à " + this.maxToolsLinks);
}
}
this._headerToolsLinks = links;
}
/**
* Chemin des pictogrammes (du composant display) renseigné par le développeur.
*
* Note: ce chemin doit permettre de récupérer directement les fichiers SVG suivants : moon.svg, sun.svg, system.svg
*
* @deprecated Use `artworkDirPath` instead.
*/
set pictoPath(path) {
this.artworkDirPath = path;
this._useDeprecatedPictoPath = true;
}
/**
* Permet de positionner la valeur initiale de la barre de recherche, si cette dernière est utilisée.
*
* @deprecated (since 1.15) Utiliser la propriété `searchInputValue` à la place.
*/
set searchInputInitialValue(value) {
this.searchInputValue = value;
}
/**
* Url du lien 'retour accueil' du logo de la Marianne.
*
* @deprecated (sing 1.16) Passser par la propriété `logo.navigation` à la place.
*/
set logoLink(value) {
if (typeof value === 'string' || !value) {
this.logo.navigation = { link: value };
}
else {
this.logo.navigation = value;
}
}
/**
* Label associé au bloc marque (Marianne). Respectez la structure avec les <br>.
* (Ministère, gouvernement, république française)
*
* @deprecated (since 1.16) Utiliser `logo.label` à la place
*/
set logoLabel(label) {
this.logo.label = label;
}
/**
* Tooltip associé au lien 'retour accueil' du logo de la Marianne.
*
* @deprecated (since 1.16) Utiliser `logo.tooltipMessage` à la place
*/
set logoTooltipMessage(tooltip) {
this.logo.tooltipMessage = tooltip;
}
/**
* Text alternatif à utiliser uniquement si l'image à une information à passer.
*
* @deprecated (since 1.16) Utiliser `logo.imageAlt` à la place
*/
set operatorImageAlt(value) {
this.logo.imageAlt = value;
}
/**
* Path pour src d'image d'illustration.
*
* @deprecated (since 1.16) Utiliser `logo.imagePath` à la place
*/
set operatorImagePath(path) {
this.logo.imagePath = path;
}
/**
* Boolean pour passer le logo opérateur en mode vertical.
*
* @deprecated (since 1.16) Utiliser `logo.customClass` à la place
* */
set operatorImageVertical(vertical) {
if (vertical) {
this.logo.customClass = 'operator-logo--vertical';
}
else {
this.logo.customClass = 'operator-logo--horizontal';
}
}
ngOnInit() {
if (this.artworkDirPath === undefined && this.config.artworkDirPath) {
this.artworkDirPath = this.config.artworkDirPath;
}
this.navigationId ??= newUniqueId();
}
ngAfterViewInit() {
if (!this.menuModal() || !this.menuModal()?.nativeElement)
return;
this._unlisten.push(this.renderer.listen(this.menuModal()?.nativeElement, 'dsfr.conceal', (e) => {
if (e.target === this.menuModal()?.nativeElement) {
this.menuConceal.emit();
}
}), this.renderer.listen(this.menuModal()?.nativeElement, 'dsfr.disclose', (e) => {
if (e.target === this.menuModal()?.nativeElement) {
this.menuDisclose.emit();
}
}));
}
ngOnDestroy() {
this.mutationObserver?.disconnect();
this._unlisten.forEach((unlistenFunc) => unlistenFunc());
}
hasToolsLinks() {
return ((this.headerToolsLinks && this.headerToolsLinks.length > 0) ||
(this.translate && this.translate.languages?.length > 0) ||
this.display ||
!!this.headerTools ||
!!this.toolsLinksTemplate);
}
onLink(item) {
this.linkSelect.emit(item);
}
onSearchSelect(text) {
this.searchSelect.emit(text);
}
onSearchChange(text) {
this.searchChange.emit(text);
}
onLanguageChange(codeLang) {
this.langChange.emit(codeLang);
}
onMegaMenuClose(item) {
item.expanded = false;
}
onMenuItemClick(item) {
this.menu().forEach((i) => (i.expanded = false));
item.expanded = true;
}
getCustomClassHeaderToolsLink(item) {
const customClass = ['fr-btn'];
if (item.icon)
customClass.push(item.icon);
if (item.customClass)
customClass.push(item.customClass);
return customClass.join(' ');
}
onDisplayChange(theme) {
this.displayChange.emit(theme);
}
/**
* Dupliquer tous les composants de toolsLinks pour le menu mobile (edu-tools-links, translate etc.)
* /!\ interaction script DSFR : le script copie les liens la premiere fois, mais n'écoute pas les changements
* Les liens à l'interieur de fr-header__tools-links sont dupliqués dans fr-header__menu-links
*/
duplicateToolsLinksMobile() {
this.mutationObserver?.disconnect(); // deconnecter l'observer pour ne pas le déclencher sur le clonage
if (this.toolsLinksMobileContainer()) {
const childElements = this.toolsLinksMobileContainer().children;
// suppression des noeuds précédent
Array.from(childElements).forEach((e) => {
this.renderer.removeChild(this.toolsLinksMobileContainer(), e);
});
// Dupliquer les liens de menu en suffixant tous les ids par -mobile
// La duplication se fait en déplaçant le composant EduToolsLinksComponent créé en double pour conserver les évents
if (this.toolsLinksNativeElement()) {
const clone = this.toolsLinksNativeElement();
if (clone) {
this.suffixIdsToolsLinks(clone);
this.renderer.appendChild(this.toolsLinksMobileContainer(), clone);
}
}
// Dupliquer le composant translate - a supprimer en V2 (translate dans EduToolsLinksComponent)
if (this.translate) {
this.renderer.appendChild(this.toolsLinksMobileContainer(), this.translateNativeElement());
this.translateNativeElement().style.display = 'block';
}
// Dupliquer le custom slot - a supprimer en V2
if (this.headerToolsMobile) {
this.renderer.appendChild(this.toolsLinksMobileContainer(), this.headerToolsMobile.elementRef.nativeElement);
}
}
if (this.toolsLinksNativeElement()) {
this.mutationObserver?.observe(this.toolsLinksNativeElement(), { childList: true, subtree: true });
}
}
/**
* Reprise de la logique du script DSFR pour suffixer les ids dupliqués en mobile
* @param toolsLinks container concerné des liens d'accès rapides dupliqués
* @param suffix -mobile pour ne pas avoir plusieurs ids identiques
*/
suffixIdsToolsLinks(toolsLinks, suffix = '-mobile') {
const idMap = new Map();
// suffixes des ids
toolsLinks.querySelectorAll('[id]').forEach((el) => {
const newId = `${el.id}${suffix}`;
idMap.set(el.id, newId);
el.id = newId;
});
// Mise a jour des attributs qui peuvent référencer ces ids
const attributes = ['aria-controls', 'aria-describedby', 'aria-labelledby'];
toolsLinks.querySelectorAll('*').forEach((el) => {
attributes.forEach((attr) => {
const value = el.getAttribute(attr);
if (!value)
return;
const newId = idMap.get(value.trim());
if (newId) {
el.setAttribute(attr, newId);
}
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrHeaderComponent, isStandalone: true, selector: "dsfr-header", inputs: { artworkDirPath: { classPropertyName: "artworkDirPath", publicName: "artworkDirPath", isSignal: false, isRequired: false, transformFunction: null }, beta: { classPropertyName: "beta", publicName: "beta", isSignal: false, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: false, isRequired: false, transformFunction: null }, navigationId: { classPropertyName: "navigationId", publicName: "navigationId", isSignal: false, isRequired: false, transformFunction: null }, serviceTitle: { classPropertyName: "serviceTitle", publicName: "serviceTitle", isSignal: false, isRequired: false, transformFunction: null }, serviceTagline: { classPropertyName: "serviceTagline", publicName: "serviceTagline", isSignal: false, isRequired: false, transformFunction: null }, searchBar: { classPropertyName: "searchBar", publicName: "searchBar", isSignal: false, isRequired: false, transformFunction: null }, searchButtonTitle: { classPropertyName: "searchButtonTitle", publicName: "searchButtonTitle", isSignal: false, isRequired: false, transformFunction: null }, searchLabel: { classPropertyName: "searchLabel", publicName: "searchLabel", isSignal: false, isRequired: false, transformFunction: null }, searchInputValue: { classPropertyName: "searchInputValue", publicName: "searchInputValue", isSignal: false, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: false, isRequired: false, transformFunction: null }, triggerSearchOnEnter: { classPropertyName: "triggerSearchOnEnter", publicName: "triggerSearchOnEnter", isSignal: false, isRequired: false, transformFunction: null }, translate: { classPropertyName: "translate", publicName: "translate", isSignal: false, isRequired: false, transformFunction: null }, menu: { classPropertyName: "menu", publicName: "menu", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: false, isRequired: false, transformFunction: null }, headerToolsLinks: { classPropertyName: "headerToolsLinks", publicName: "headerToolsLinks", isSignal: false, isRequired: false, transformFunction: null }, pictoPath: { classPropertyName: "pictoPath", publicName: "pictoPath", isSignal: false, isRequired: false, transformFunction: null }, searchInputInitialValue: { classPropertyName: "searchInputInitialValue", publicName: "searchInputInitialValue", isSignal: false, isRequired: false, transformFunction: null }, logoLink: { classPropertyName: "logoLink", publicName: "logoLink", isSignal: false, isRequired: false, transformFunction: null }, logoLabel: { classPropertyName: "logoLabel", publicName: "logoLabel", isSignal: false, isRequired: false, transformFunction: null }, logoTooltipMessage: { classPropertyName: "logoTooltipMessage", publicName: "logoTooltipMessage", isSignal: false, isRequired: false, transformFunction: null }, operatorImageAlt: { classPropertyName: "operatorImageAlt", publicName: "operatorImageAlt", isSignal: false, isRequired: false, transformFunction: null }, operatorImagePath: { classPropertyName: "operatorImagePath", publicName: "operatorImagePath", isSignal: false, isRequired: false, transformFunction: null }, operatorImageVertical: { classPropertyName: "operatorImageVertical", publicName: "operatorImageVertical", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { langChange: "langChange", linkSelect: "linkSelect", searchChange: "searchChange", searchSelect: "searchSelect", displayChange: "displayChange", menuDisclose: "menuDisclose", menuConceal: "menuConceal" }, queries: [{ propertyName: "toolsLinksTemplate", first: true, predicate: ["toolsLinksTemplate"], descendants: true, static: true }, { propertyName: "searchBarTemplate", first: true, predicate: ["searchBarTemplate"], descendants: true, static: true }, { propertyName: "headerTools", first: true, predicate: ["headerTools"], descendants: true, static: true }, { propertyName: "headerToolsMobile", first: true, predicate: ["headerToolsMobile"], descendants: true, static: true }, { propertyName: "serviceTitleTemplate", first: true, predicate: ["serviceTitleTemplate"], descendants: true, static: true }, { propertyName: "serviceTaglineTemplate", first: true, predicate: ["serviceTaglineTemplate"], descendants: true, static: true }], viewQueries: [{ propertyName: "translateElement", first: true, predicate: ["translateComponent"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "toolsLinksCmp", first: true, predicate: ["toolsLinksComponent"], descendants: true, isSignal: true }, { propertyName: "toolsLinksMobile", first: true, predicate: ["toolLinksMobile"], descendants: true, isSignal: true }, { propertyName: "menuModal", first: true, predicate: ["menuModal"], descendants: true, isSignal: true }], ngImport: i0, template: "<header role=\"banner\" class=\"fr-header\">\n <div class=\"fr-header__body\">\n <div class=\"fr-container\">\n <div class=\"fr-header__body-row\">\n <div class=\"fr-header__brand fr-enlarge-link\">\n <div class=\"fr-header__brand-top\">\n <div class=\"fr-header__logo\">\n <!--loc Marianne sans intitul\u00E9 ni logo op\u00E9rateur-->\n @if (!serviceTitle && !logo.imagePath && !serviceTitleTemplate) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n } @else if (serviceTitle || logo.imagePath || serviceTitleTemplate) {\n <!--bloc Marianne avec intitul\u00E9 et/ou logo op\u00E9rateur-->\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n }\n </div>\n <!-- Avec logo op\u00E9rateur -->\n @if (logo.imagePath) {\n <div class=\"fr-header__operator\">\n <!-- Pas d'intitul\u00E9 pour le service -->\n @if (!serviceTitle && !serviceTitleTemplate) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n } @else {\n <!-- Avec un intitul\u00E9 pour le service -->\n <img\n class=\"fr-responsive-img\"\n [class]=\"logo.customClass || 'operator-logo--horizontal'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n }\n </div>\n }\n <div class=\"fr-header__navbar\">\n @if (searchBar || searchBarTemplate) {\n <button\n type=\"button\"\n class=\"fr-btn--search fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n id=\"{{ navigationId }}-button-search\"\n [title]=\"'commons.search' | dsfrI18n\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n }\n @if (hasToolsLinks() || menu().length > 0) {\n <button\n type=\"button\"\n class=\"fr-btn--menu fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId\"\n id=\"{{ navigationId }}-button-menu\"\n [title]=\"'header.menu.label' | dsfrI18n\">\n {{ 'header.menu.label' | dsfrI18n }}\n </button>\n }\n </div>\n </div>\n @if (serviceTitle || beta || serviceTitleTemplate) {\n <div class=\"fr-header__service\">\n @if (logo.navigation) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n }\n @if (!logo.navigation) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplateDefault\"></ng-container>\n }\n @if (serviceTagline || serviceTaglineTemplate) {\n <p class=\"fr-header__service-tagline\">\n @if (serviceTagline) {\n {{ serviceTagline }}\n } @else {\n <ng-container *ngTemplateOutlet=\"serviceTaglineTemplate\"></ng-container>\n }\n </p>\n }\n </div>\n }\n </div>\n\n <!-- Tools links -->\n @if (hasToolsLinks() || searchBar) {\n <div class=\"fr-header__tools\">\n @if (hasToolsLinks()) {\n <div class=\"fr-header__tools-links\">\n <!--headerTools pour r\u00E9trocompatibilit\u00E9, d\u00E9pr\u00E9ci\u00E9 et suppression en V2 -->\n @if (headerTools) {\n <div class=\"edu-header__tools--custom\">\n <ng-container *ngTemplateOutlet=\"headerTools\"></ng-container>\n </div>\n }\n <edu-tools-links\n (linkSelect)=\"onLink($event)\"\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n @if (translate) {\n <!-- en V2 apres suppression de headerTools, d\u00E9placer dans edu-tools-links -->\n <dsfr-translate\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n </div>\n }\n <!-- Recherche ------------------------------------------------------------------------------------------->\n @if (searchBarTemplate) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <ng-container *ngTemplateOutlet=\"closeSearchBtnTemplate\"></ng-container>\n <ng-container *ngTemplateOutlet=\"searchBarTemplate\"></ng-container>\n </div>\n </div>\n } @else if (searchBar) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <ng-container *ngTemplateOutlet=\"closeSearchBtnTemplate\"></ng-container>\n <dsfr-search-bar\n [label]=\"searchLabel\"\n [inputId]=\"searchInputId\"\n [buttonTitle]=\"searchButtonTitle\"\n [triggerSearchOnEnter]=\"triggerSearchOnEnter\"\n [placeholder]=\"searchInputPlaceholder\"\n [value]=\"searchInputValue\"\n (searchChange)=\"onSearchChange($event)\"\n (searchSelect)=\"onSearchSelect($event)\"></dsfr-search-bar>\n </div>\n </div>\n }\n </div>\n <ng-template #closeSearchBtnTemplate>\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n title=\"{{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </ng-template>\n }\n <!-- fin header tools ----------------------------------------------------------------------------------------->\n </div>\n </div>\n </div>\n <!-- Menu de navigation principale --------------------------------------------------------------------------------->\n @if (hasToolsLinks() || viewMenu().length > 0) {\n <div\n #menuModal\n class=\"fr-header__menu fr-modal\"\n [id]=\"navigationId\"\n [attr.aria-labelledby]=\"navigationId + '-button-menu'\">\n <div class=\"fr-container\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId\"\n title=\" {{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <div #toolLinksMobile class=\"fr-header__menu-links\"></div>\n <!-- Composant translate d\u00E9plac\u00E9 dans toolsLinksMobile. Display none pour \u00E9viter le flickering de l'element\n fixme: A supprimer en V2 -->\n @if (translate) {\n <dsfr-translate\n [ngStyle]=\"{ display: 'none' }\"\n #translateComponent\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n <!-- Duplication du composant tools links pour le mode mobile -->\n @if (hasToolsLinks()) {\n <edu-tools-links\n #toolsLinksComponent\n [ngStyle]=\"{ display: 'none' }\"\n (linkSelect)=\"onLink($event)\"\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n }\n <!-- Custom slot pour mobile. fixme: A supprimer en V2 -->\n @if (headerToolsMobile) {\n <div class=\"fr-header__menu-links\">\n <ng-container *ngTemplateOutlet=\"headerToolsMobile\"></ng-container>\n </div>\n }\n @if (viewMenu() && viewMenu().length > 0) {\n <nav\n class=\"fr-nav\"\n id=\"{{ navigationId }}-nav\"\n role=\"navigation\"\n [attr.aria-label]=\"'header.mainMenu' | dsfrI18n\">\n <ul class=\"fr-nav__list\">\n @for (item of viewMenu(); track item) {\n <!-- Entr\u00E9e de menu simple (lien direct) ------------------------------------------------------------------>\n @if (!item.menu.subItems && !item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <edu-item-link\n [item]=\"item.menu\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(item.menu)\"></edu-item-link>\n </li>\n }\n\n <!-- Entr\u00E9e de menu d\u00E9roulant ----------------------------------------------------------------------------->\n @if (item.menu.subItems) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'menu-' + item.menuId }\"></ng-container>\n <div class=\"fr-collapse fr-menu\" id=\"menu-{{ item.menuId }}\">\n <ul class=\"fr-menu__list\">\n @for (subItem of item.menu.subItems; track subItem) {\n <li>\n <edu-item-link\n [item]=\"subItem\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(subItem)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n <!-- Entr\u00E9e de m\u00E9ga menu ---------------------------------------------------------------------------------->\n @if (item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{\n item: item.menu,\n ariaId: 'mega-menu-' + item.menuId,\n }\"></ng-container>\n <div\n [class.fr-collapse--expanded]=\"item.menu.expanded\"\n class=\"fr-collapse fr-mega-menu\"\n id=\"mega-menu-{{ item.menuId }}\"\n tabindex=\"-1\">\n <dsfr-mega-menu\n [megaMenu]=\"item.menu.megaMenu\"\n [idMenu]=\"item.menuId\"\n (closeSelect)=\"onMegaMenuClose(item.menu)\"\n (linkSelect)=\"onLink($event)\"></dsfr-mega-menu>\n </div>\n </li>\n }\n }\n </ul>\n </nav>\n }\n </div>\n </div>\n }\n</header>\n\n@if (showDisplay && _useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [pictoPath]=\"artworkDirPath ?? ''\"></dsfr-display>\n} @else if (showDisplay && !_useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n\n<ng-template #buttonMenu let-item=\"item\" let-ariaId=\"ariaId\">\n @if (item.routerLink) {\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [ariaCurrentWhenActive]=\"true\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [disableNavigation]=\"true\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n } @else {\n <!-- S\u00E9parer de la d\u00E9finition avec routerLink pour que l'attribut aria-current ne soit pas \u00E9cras\u00E9 par la directive -->\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n }\n</ng-template>\n\n<ng-template #logoLink>\n <dsfr-link\n [link]=\"logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [route]=\"logo.navigation?.route ?? ''\"\n [ariaCurrentWhenActive]=\"false\"\n [tooltipMessage]=\"logo.tooltipMessage\"\n [customClass]=\"'edu-link'\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n @if (serviceTitle || serviceTitleTemplate) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplateDefault\"></ng-container>\n } @else if (logo.imagePath) {\n <img\n class=\"fr-responsive-img\"\n [class]=\"logo.customClass || 'operator-logo--horizontal'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n } @else {\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n }\n </dsfr-link>\n</ng-template>\n\n<ng-template #serviceTitleTemplateDefault>\n <p class=\"fr-header__service-title\">\n @if (serviceTitleTemplate) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplate\"></ng-container>\n } @else {\n {{ serviceTitle }}\n @if (beta) {\n <dsfr-badge label=\"BETA\" badgeSize=\"SM\" customClass=\"fr-badge--green-emeraude\"></dsfr-badge>\n }\n }\n </p>\n</ng-template>\n", styles: ["dsfr-header .fr-collapse:before{content:\"\"}dsfr-header .edu-header__tools--custom{display:none}dsfr-header .fr-header__tools-links .fr-btns-group:not(:has(dsfr-tool-link)){margin-bottom:0}dsfr-header .fr-header__operator img.operator-logo--horizontal{max-width:9.0625rem}dsfr-header .fr-header__operator img.operator-logo--vertical{width:3.5rem}@media(min-width:62em){dsfr-header .edu-header__tools--custom{display:flex;flex-direction:row;justify-content:flex-end;gap:.5rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: DsfrSearchBarComponent, selector: "dsfr-search-bar", inputs: ["inputId", "buttonText", "value", "label", "large", "placeholder", "buttonTitle", "pattern", "triggerSearchOnEnter", "maxLength", "delay", "initialValue", "id"], outputs: ["searchChange", "searchSelect"] }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }, { kind: "ngmodule", type: DsfrTranslateModule }, { kind: "component", type: DsfrTranslateComponent, selector: "dsfr-translate", inputs: ["languages", "outline", "currentLangCode"], outputs: ["langChange"] }, { kind: "component", type: DsfrDisplayComponent, selector: "dsfr-display", inputs: ["displayId", "headingLevel", "artworkDirPath", "pictoPath"], outputs: ["displayChange"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: DsfrMegaMenuComponent, selector: "dsfr-mega-menu", inputs: ["megaMenu", "idMenu"], outputs: ["linkSelect", "closeSelect"] }, { kind: "directive", type: DsfrDisableRouterLinkDirective, selector: "button[routerLink][disableNavigation]", inputs: ["disableNavigation"] }, { kind: "component", type: EduToolsLinksComponent, selector: "edu-tools-links", inputs: ["toolsLinks", "showDisplay", "displayId", "toolsLinksTemplate", "maxToolsLinks"], outputs: ["linkSelect"] }, { kind: "component", type: DsfrBadgeComponent, selector: "dsfr-badge", inputs: ["label", "noIcon", "badgeSize", "customClass", "size", "severity"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-header', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
FormsModule,
RouterModule,
DsfrSearchBarComponent,
ItemLinkComponent,
DsfrTranslateModule,
DsfrDisplayComponent,
DsfrLinkComponent,
DsfrMegaMenuComponent,
DsfrDisableRouterLinkDirective,
EduToolsLinksComponent,
DsfrI18nPipe,
DsfrBadgeComponent,
], template: "<header role=\"banner\" class=\"fr-header\">\n <div class=\"fr-header__body\">\n <div class=\"fr-container\">\n <div class=\"fr-header__body-row\">\n <div class=\"fr-header__brand fr-enlarge-link\">\n <div class=\"fr-header__brand-top\">\n <div class=\"fr-header__logo\">\n <!--loc Marianne sans intitul\u00E9 ni logo op\u00E9rateur-->\n @if (!serviceTitle && !logo.imagePath && !serviceTitleTemplate) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n } @else if (serviceTitle || logo.imagePath || serviceTitleTemplate) {\n <!--bloc Marianne avec intitul\u00E9 et/ou logo op\u00E9rateur-->\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n }\n </div>\n <!-- Avec logo op\u00E9rateur -->\n @if (logo.imagePath) {\n <div class=\"fr-header__operator\">\n <!-- Pas d'intitul\u00E9 pour le service -->\n @if (!serviceTitle && !serviceTitleTemplate) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n } @else {\n <!-- Avec un intitul\u00E9 pour le service -->\n <img\n class=\"fr-responsive-img\"\n [class]=\"logo.customClass || 'operator-logo--horizontal'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n }\n </div>\n }\n <div class=\"fr-header__navbar\">\n @if (searchBar || searchBarTemplate) {\n <button\n type=\"button\"\n class=\"fr-btn--search fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n id=\"{{ navigationId }}-button-search\"\n [title]=\"'commons.search' | dsfrI18n\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n }\n @if (hasToolsLinks() || menu().length > 0) {\n <button\n type=\"button\"\n class=\"fr-btn--menu fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId\"\n id=\"{{ navigationId }}-button-menu\"\n [title]=\"'header.menu.label' | dsfrI18n\">\n {{ 'header.menu.label' | dsfrI18n }}\n </button>\n }\n </div>\n </div>\n @if (serviceTitle || beta || serviceTitleTemplate) {\n <div class=\"fr-header__service\">\n @if (logo.navigation) {\n <ng-container [ngTemplateOutlet]=\"logoLink\"></ng-container>\n }\n @if (!logo.navigation) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplateDefault\"></ng-container>\n }\n @if (serviceTagline || serviceTaglineTemplate) {\n <p class=\"fr-header__service-tagline\">\n @if (serviceTagline) {\n {{ serviceTagline }}\n } @else {\n <ng-container *ngTemplateOutlet=\"serviceTaglineTemplate\"></ng-container>\n }\n </p>\n }\n </div>\n }\n </div>\n\n <!-- Tools links -->\n @if (hasToolsLinks() || searchBar) {\n <div class=\"fr-header__tools\">\n @if (hasToolsLinks()) {\n <div class=\"fr-header__tools-links\">\n <!--headerTools pour r\u00E9trocompatibilit\u00E9, d\u00E9pr\u00E9ci\u00E9 et suppression en V2 -->\n @if (headerTools) {\n <div class=\"edu-header__tools--custom\">\n <ng-container *ngTemplateOutlet=\"headerTools\"></ng-container>\n </div>\n }\n <edu-tools-links\n (linkSelect)=\"onLink($event)\"\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n @if (translate) {\n <!-- en V2 apres suppression de headerTools, d\u00E9placer dans edu-tools-links -->\n <dsfr-translate\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n </div>\n }\n <!-- Recherche ------------------------------------------------------------------------------------------->\n @if (searchBarTemplate) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <ng-container *ngTemplateOutlet=\"closeSearchBtnTemplate\"></ng-container>\n <ng-container *ngTemplateOutlet=\"searchBarTemplate\"></ng-container>\n </div>\n </div>\n } @else if (searchBar) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <ng-container *ngTemplateOutlet=\"closeSearchBtnTemplate\"></ng-container>\n <dsfr-search-bar\n [label]=\"searchLabel\"\n [inputId]=\"searchInputId\"\n [buttonTitle]=\"searchButtonTitle\"\n [triggerSearchOnEnter]=\"triggerSearchOnEnter\"\n [placeholder]=\"searchInputPlaceholder\"\n [value]=\"searchInputValue\"\n (searchChange)=\"onSearchChange($event)\"\n (searchSelect)=\"onSearchSelect($event)\"></dsfr-search-bar>\n </div>\n </div>\n }\n </div>\n <ng-template #closeSearchBtnTemplate>\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n title=\"{{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </ng-template>\n }\n <!-- fin header tools ----------------------------------------------------------------------------------------->\n </div>\n </div>\n </div>\n <!-- Menu de navigation principale --------------------------------------------------------------------------------->\n @if (hasToolsLinks() || viewMenu().length > 0) {\n <div\n #menuModal\n class=\"fr-header__menu fr-modal\"\n [id]=\"navigationId\"\n [attr.aria-labelledby]=\"navigationId + '-button-menu'\">\n <div class=\"fr-container\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId\"\n title=\" {{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <div #toolLinksMobile class=\"fr-header__menu-links\"></div>\n <!-- Composant translate d\u00E9plac\u00E9 dans toolsLinksMobile. Display none pour \u00E9viter le flickering de l'element\n fixme: A supprimer en V2 -->\n @if (translate) {\n <dsfr-translate\n [ngStyle]=\"{ display: 'none' }\"\n #translateComponent\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n <!-- Duplication du composant tools links pour le mode mobile -->\n @if (hasToolsLinks()) {\n <edu-tools-links\n #toolsLinksComponent\n [ngStyle]=\"{ display: 'none' }\"\n (linkSelect)=\"onLink($event)\"\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n }\n <!-- Custom slot pour mobile. fixme: A supprimer en V2 -->\n @if (headerToolsMobile) {\n <div class=\"fr-header__menu-links\">\n <ng-container *ngTemplateOutlet=\"headerToolsMobile\"></ng-container>\n </div>\n }\n @if (viewMenu() && viewMenu().length > 0) {\n <nav\n class=\"fr-nav\"\n id=\"{{ navigationId }}-nav\"\n role=\"navigation\"\n [attr.aria-label]=\"'header.mainMenu' | dsfrI18n\">\n <ul class=\"fr-nav__list\">\n @for (item of viewMenu(); track item) {\n <!-- Entr\u00E9e de menu simple (lien direct) ------------------------------------------------------------------>\n @if (!item.menu.subItems && !item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <edu-item-link\n [item]=\"item.menu\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(item.menu)\"></edu-item-link>\n </li>\n }\n\n <!-- Entr\u00E9e de menu d\u00E9roulant ----------------------------------------------------------------------------->\n @if (item.menu.subItems) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'menu-' + item.menuId }\"></ng-container>\n <div class=\"fr-collapse fr-menu\" id=\"menu-{{ item.menuId }}\">\n <ul class=\"fr-menu__list\">\n @for (subItem of item.menu.subItems; track subItem) {\n <li>\n <edu-item-link\n [item]=\"subItem\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(subItem)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n <!-- Entr\u00E9e de m\u00E9ga menu ---------------------------------------------------------------------------------->\n @if (item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{\n item: item.menu,\n ariaId: 'mega-menu-' + item.menuId,\n }\"></ng-container>\n <div\n [class.fr-collapse--expanded]=\"item.menu.expanded\"\n class=\"fr-collapse fr-mega-menu\"\n id=\"mega-menu-{{ item.menuId }}\"\n tabindex=\"-1\">\n <dsfr-mega-menu\n [megaMenu]=\"item.menu.megaMenu\"\n [idMenu]=\"item.menuId\"\n (closeSelect)=\"onMegaMenuClose(item.menu)\"\n (linkSelect)=\"onLink($event)\"></dsfr-mega-menu>\n </div>\n </li>\n }\n }\n </ul>\n </nav>\n }\n </div>\n </div>\n }\n</header>\n\n@if (showDisplay && _useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [pictoPath]=\"artworkDirPath ?? ''\"></dsfr-display>\n} @else if (showDisplay && !_useDeprecatedPictoPath) {\n <dsfr-display (displayChange)=\"onDisplayChange($event)\" [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n\n<ng-template #buttonMenu let-item=\"item\" let-ariaId=\"ariaId\">\n @if (item.routerLink) {\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [ariaCurrentWhenActive]=\"true\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [disableNavigation]=\"true\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n } @else {\n <!-- S\u00E9parer de la d\u00E9finition avec routerLink pour que l'attribut aria-current ne soit pas \u00E9cras\u00E9 par la directive -->\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n }\n</ng-template>\n\n<ng-template #logoLink>\n <dsfr-link\n [link]=\"logo.navigation?.link ?? ''\"\n [linkTarget]=\"logo.navigation?.linkTarget\"\n [route]=\"logo.navigation?.route ?? ''\"\n [ariaCurrentWhenActive]=\"false\"\n [tooltipMessage]=\"logo.tooltipMessage\"\n [customClass]=\"'edu-link'\"\n [routePath]=\"logo.navigation?.routerLink ?? ''\">\n @if (serviceTitle || serviceTitleTemplate) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplateDefault\"></ng-container>\n } @else if (logo.imagePath) {\n <img\n class=\"fr-responsive-img\"\n [class]=\"logo.customClass || 'operator-logo--horizontal'\"\n [src]=\"logo.imagePath\"\n [attr.alt]=\"logo.imageAlt\" />\n } @else {\n <p class=\"fr-logo\" [innerHTML]=\"logo.label\"></p>\n }\n </dsfr-link>\n</ng-template>\n\n<ng-template #serviceTitleTemplateDefault>\n <p class=\"fr-header__service-title\">\n @if (serviceTitleTemplate) {\n <ng-container *ngTemplateOutlet=\"serviceTitleTemplate\"></ng-container>\n } @else {\n {{ serviceTitle }}\n @if (beta) {\n <dsfr-badge label=\"BETA\" badgeSize=\"SM\" customClass=\"fr-badge--green-emeraude\"></dsfr-badge>\n }\n }\n </p>\n</ng-template>\n", styles: ["dsfr-header .fr-collapse:before{content:\"\"}dsfr-header .edu-header__tools--custom{display:none}dsfr-header .fr-header__tools-links .fr-btns-group:not(:has(dsfr-tool-link)){margin-bottom:0}dsfr-header .fr-header__operator img.operator-logo--horizontal{max-width:9.0625rem}dsfr-header .fr-header__operator img.operator-logo--vertical{width:3.5rem}@media(min-width:62em){dsfr-header .edu-header__tools--custom{display:flex;flex-direction:row;justify-content:flex-end;gap:.5rem}}\n"] }]
}], ctorParameters: () => [], propDecorators: { toolsLinksTemplate: [{
type: ContentChild,
args: ['toolsLinksTemplate', { static: true }]
}], searchBarTemplate: [{
type: ContentChild,
args: ['searchBarTemplate', { static: true }]
}], headerTools: [{
type: ContentChild,
args: ['headerTools', { static: true }]
}], headerToolsMobile: [{
type: ContentChild,
args: ['headerToolsMobile', { static: true }]
}], serviceTitleTemplate: [{
type: ContentChild,
args: ['serviceTitleTemplate', { static: true }]
}], serviceTaglineTemplate: [{
type: ContentChild,
args: ['serviceTaglineTemplate', { static: true }]
}], artworkDirPath: [{
type: Input
}], beta: [{
type: Input
}], logo: [{
type: Input
}], navigationId: [{
type: Input
}], serviceTitle: [{
type: Input
}], serviceTagline: [{
type: Input
}], searchBar: [{
type: Input
}], searchButtonTitle: [{
type: Input
}], searchLabel: [{
type: Input
}], searchInputValue: [{
type: Input
}], searchInputPlaceholder: [{
type: Input
}], triggerSearchOnEnter: [{
type: Input
}], translate: [{
type: Input
}], langChange: [{
type: Output
}], linkSelect: [{
type: Output
}], searchChange: [{
type: Output
}], searchSelect: [{
type: Output
}], displayChange: [{
type: Output
}], menuDisclose: [{
type: Output
}], menuConceal: [{
type: Output
}], menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "menu", required: false }] }], translateElement: [{ type: i0.ViewChild, args: ['translateComponent', { ...{ read: ElementRef }, isSignal: true }] }], toolsLinksCmp: [{ type: i0.ViewChild, args: ['toolsLinksComponent', { isSignal: true }] }], toolsLinksMobile: [{ type: i0.ViewChild, args: ['toolLinksMobile', { isSignal: true }] }], menuModal: [{ type: i0.ViewChild, args: ['menuModal', { isSignal: true }] }], display: [{
type: Input
}], headerToolsLinks: [{
type: Input
}], pictoPath: [{
type: Input
}], searchInputInitialValue: [{
type: Input
}], logoLink: [{
type: Input
}], logoLabel: [{
type: Input
}], logoTooltipMessage: [{
type: Input
}], operatorImageAlt: [{
type: Input
}], operatorImagePath: [{
type: Input
}], operatorImageVertical: [{
type: Input
}] } });
class DsfrHeaderModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderModule, imports: [CommonModule,
FormsModule,
RouterModule,
DsfrMegaMenuComponent,
DsfrSearchBarComponent,
ItemLinkComponent,
DsfrTranslateComponent,
DsfrDisplayComponent,
DsfrLinkComponent,
DsfrDisableRouterLinkDirective,
DsfrI18nPipe,
DsfrHeaderComponent,
DsfrToolLinkComponent,
EduToolsLinksComponent], exports: [DsfrHeaderComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderModule, imports: [CommonModule,
FormsModule,
RouterModule,
DsfrMegaMenuComponent,
DsfrSearchBarComponent,
ItemLinkComponent,
DsfrTranslateComponent,
DsfrDisplayComponent,
DsfrLinkComponent,
DsfrHeaderComponent,
DsfrToolLinkComponent,
EduToolsLinksComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHeaderModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrHeaderComponent],
imports: [
CommonModule,
FormsModule,
RouterModule,
DsfrMegaMenuComponent,
DsfrSearchBarComponent,
ItemLinkComponent,
DsfrTranslateComponent,
DsfrDisplayComponent,
DsfrLinkComponent,
DsfrDisableRouterLinkDirective,
DsfrI18nPipe,
DsfrHeaderComponent,
DsfrToolLinkComponent,
EduToolsLinksComponent,
],
}]
}] });
class DsfrHighlightComponent {
constructor() {
/** Choix de la taille du texte, medium par défaut. */
this.textSize = DsfrSizeConst.MD;
}
/** @internal */
getClasses() {
return { 'fr-text--sm': this.textSize === DsfrSizeConst.SM, 'fr-text--lg': this.textSize === DsfrSizeConst.LG };
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrHighlightComponent, isStandalone: true, selector: "dsfr-highlight", inputs: { text: "text", textSize: "textSize", customClass: "customClass" }, ngImport: i0, template: "<div class=\"fr-highlight\" [ngClass]=\"customClass ? customClass : ''\">\n @if (text) {\n <p [class]=\"getClasses()\" [innerHtml]=\"text\"></p>\n } @else {\n <p [class]=\"getClasses()\">\n <ng-content></ng-content>\n </p>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-highlight', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<div class=\"fr-highlight\" [ngClass]=\"customClass ? customClass : ''\">\n @if (text) {\n <p [class]=\"getClasses()\" [innerHtml]=\"text\"></p>\n } @else {\n <p [class]=\"getClasses()\">\n <ng-content></ng-content>\n </p>\n }\n</div>\n" }]
}], propDecorators: { text: [{
type: Input
}], textSize: [{
type: Input
}], customClass: [{
type: Input
}] } });
class DsfrHighlightModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightModule, imports: [CommonModule, DsfrHighlightComponent], exports: [DsfrHighlightComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightModule, imports: [CommonModule, DsfrHighlightComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrHighlightModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrHighlightComponent],
imports: [CommonModule, DsfrHighlightComponent],
}]
}] });
class DsfrLinksGroupComponent {
constructor() {
/**
* Permet de basculer la disposition des liens en mode horizontal.
*/
this.inline = false;
/**
* Taille des liens du groupe.
*/
this.linksGroupSize = DsfrSizeConst.MD;
/** @internal */
this.linksElements = contentChildren(DsfrLinkComponent, ...(ngDevMode ? [{ debugName: "linksElements", read: ElementRef }] : [{ read: ElementRef }]));
this.links = contentChildren(DsfrLinkComponent, ...(ngDevMode ? [{ debugName: "links" }] : []));
this._elementRef = inject(ElementRef);
effect(() => {
this.updateLinksIconPosition();
this.surroundLinksWithLi();
});
}
/**
* Taille des liens du groupe.
*
* @deprecated since(1.15.0) utiliser 'linksGroupSize' à la place
*/
get size() {
return this.linksGroupSize;
}
get iconPosition() {
return this._iconPosition;
}
/**
* Taille des liens du groupe.
*
* @deprecated since(1.15.0) utiliser 'linksGroupSize' à la place
*/
set size(value) {
this.linksGroupSize = value;
}
/**
* Positionnement (gauche ou droite) au niveau du groupe des icônes dans les boutons qui
* contiennent à la fois une icône et un label.
*/
set iconPosition(value) {
this._iconPosition = value;
this.updateLinksIconPosition();
}
/**
* Cette méthode calcule les styles du composant en évitant les magic strings.
* @internal
*/
getClasses() {
let classes = [];
// Inline class
if (this.inline) {
classes.push('fr-links-group--inline');
}
// Size class
if (this.linksGroupSize === DsfrSizeConst.SM) {
classes.push('fr-links-group--sm');
}
else if (this.linksGroupSize === DsfrSizeConst.LG) {
classes.push('fr-links-group--lg');
}
// IconPosition class
if (this._iconPosition) {
classes.push(this._iconPosition === DsfrPositionConst.LEFT ? 'fr-links-group--icon-left' : 'fr-links-group--icon-right');
}
return classes;
}
/** On force la position des icônes des boutons. */
updateLinksIconPosition() {
if (this.links()) {
this.links().forEach((link) => (link.iconPosition = this._iconPosition));
}
}
/* Entoure chaque composant link par une balise <li> si nécessaire */
surroundLinksWithLi() {
DomUtils.surroundElementsWithLi(this._elementRef, this.linksElements());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.23", type: DsfrLinksGroupComponent, isStandalone: true, selector: "dsfr-links-group", inputs: { inline: "inline", linksGroupSize: "linksGroupSize", size: "size", iconPosition: "iconPosition" }, queries: [{ propertyName: "linksElements", predicate: DsfrLinkComponent, read: ElementRef, isSignal: true }, { propertyName: "links", predicate: DsfrLinkComponent, isSignal: true }], ngImport: i0, template: "<ul class=\"fr-links-group\" [ngClass]=\"getClasses()\">\n <ng-content></ng-content>\n</ul>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-links-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<ul class=\"fr-links-group\" [ngClass]=\"getClasses()\">\n <ng-content></ng-content>\n</ul>\n" }]
}], ctorParameters: () => [], propDecorators: { inline: [{
type: Input
}], linksGroupSize: [{
type: Input
}], linksElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrLinkComponent), { ...{ read: ElementRef }, isSignal: true }] }], links: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrLinkComponent), { isSignal: true }] }], size: [{
type: Input
}], iconPosition: [{
type: Input
}] } });
class DsfrLinksGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupModule, imports: [CommonModule, DsfrLinksGroupComponent], exports: [DsfrLinksGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupModule, imports: [CommonModule, DsfrLinksGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLinksGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrLinksGroupComponent],
imports: [CommonModule, DsfrLinksGroupComponent],
}]
}] });
class DsfrLogoComponent {
constructor() {
/**
* Taille du logo, MD par défaut si non spécifié.
*/
this.logoSize = DsfrSizeConst.MD;
}
/**
* Taille du logo, MD par défaut si non spécifié.
*
* @deprecated since(1.15.0) utiliser 'logoSize' à la place
*/
get size() {
return this.logoSize;
}
/**
* Taille du logo, MD par défaut si non spécifié.
*
* @deprecated since(1.15.0) utiliser 'logoSize' à la place
*/
set size(value) {
this.logoSize = value;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrLogoComponent, isStandalone: true, selector: "dsfr-logo", inputs: { logoSize: "logoSize", label: "label", customClass: "customClass", size: "size" }, ngImport: i0, template: "<!-- FIXME supprimer le innerHTML -->\n@if (label) {\n <p\n [innerHTML]=\"label\"\n [class]=\"customClass || ''\"\n [ngClass]=\"{\n 'fr-logo': true,\n 'fr-logo--sm': logoSize === 'SM',\n 'fr-logo--lg': logoSize === 'LG',\n }\"></p>\n} @else {\n <p\n [class]=\"customClass || ''\"\n [ngClass]=\"{\n 'fr-logo': true,\n 'fr-logo--sm': logoSize === 'SM',\n 'fr-logo--lg': logoSize === 'LG',\n }\">\n <ng-content></ng-content>\n </p>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-logo', imports: [CommonModule], template: "<!-- FIXME supprimer le innerHTML -->\n@if (label) {\n <p\n [innerHTML]=\"label\"\n [class]=\"customClass || ''\"\n [ngClass]=\"{\n 'fr-logo': true,\n 'fr-logo--sm': logoSize === 'SM',\n 'fr-logo--lg': logoSize === 'LG',\n }\"></p>\n} @else {\n <p\n [class]=\"customClass || ''\"\n [ngClass]=\"{\n 'fr-logo': true,\n 'fr-logo--sm': logoSize === 'SM',\n 'fr-logo--lg': logoSize === 'LG',\n }\">\n <ng-content></ng-content>\n </p>\n}\n" }]
}], propDecorators: { logoSize: [{
type: Input
}], label: [{
type: Input
}], customClass: [{
type: Input
}], size: [{
type: Input
}] } });
class DsfrLogoModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoModule, imports: [CommonModule, DsfrLogoComponent], exports: [DsfrLogoComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoModule, imports: [CommonModule, DsfrLogoComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLogoModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrLogoComponent],
imports: [CommonModule, DsfrLogoComponent],
}]
}] });
class DsfrModalComponent {
constructor() {
/** Le tableau des actions à positionner dans la modale. */
// TODO Ne faudrait-il pas homogénéiser avec les tuiles et les cartes ?
this.actions = [];
/** Permet de ne pas fermer la modale automatiquement lorsqu'une action est exécutée. */
this.autoCloseOnAction = true;
/** Autorise la fermeture de la modale au click sur le fond. */
this.concealingBackdrop = true;
/**
* Le mode de contrôle vous permet, le cas échéant, de maintenir la capacité de la modale à être pilotée
* programmatiquement en absence de bouton de contrôle.
*
* @since 1.8.0
* @deprecated (since 1.14.0)
*/
this.controlMode = 'button';
/** Indique la classe d'icône à utiliser en haut à gauche. Par défaut `fr-icon-arrow-right-line` */
this.icon = 'fr-icon-arrow-right-line';
/** Type de balise HTML, dialog par défaut */
this.markup = 'dialog';
/** Positionnement de la modale en haut en mobile. */
this.mobileTop = false;
/** Signale l'ouverture de la modale. */
this.disclose = new EventEmitter();
/** Signale la fermeture de la modale. */
this.conceal = new EventEmitter();
/**
* La taille de la modale.
* Valeurs possibles : `SM` (pour small), `MD` (pour medium), `LG` (pour large).
*
* @deprecated (since 1.17.0) utiliser modalSize à la place
*/
this.size = input(...(ngDevMode ? [undefined, { debugName: "size" }] : []));
/**
* La taille de la modale.
* Valeurs possibles : `SM` (pour small), `MD` (pour medium), `LG` (pour large).
*/
this.modalSize = input('MD', ...(ngDevMode ? [{ debugName: "modalSize" }] : []));
/**
* L'identifiant unique de l'élément tenant titre descriptif de la modale.
*/
this.titleElementId = '';
this.sizeClasses = computed(() => {
const size = this.size() ?? this.modalSize();
return {
'fr-col-12': true,
'fr-col-md-4': size === DsfrSizeConst.SM,
'fr-col-md-8 fr-col-lg-6': size === DsfrSizeConst.MD,
'fr-col-md-8': size === DsfrSizeConst.LG,
};
}, ...(ngDevMode ? [{ debugName: "sizeClasses" }] : []));
this._unlisten = [];
this.renderer2 = inject(Renderer2);
}
ngOnInit() {
this.dialogId ??= newUniqueId();
this.titleElementId = newUniqueId();
}
ngAfterViewInit() {
this._unlisten.push(this.renderer2.listen(this.dsfrModal?.nativeElement, 'dsfr.conceal', (e) => {
if (e.target === this.dsfrModal?.nativeElement) {
const dialog = this.dsfrModal.nativeElement;
if (Boolean(dialog.getAttribute('open'))) {
this.conceal.emit();
}
}
}), this.renderer2.listen(this.dsfrModal?.nativeElement, 'dsfr.disclose', (e) => {
if (e.target === this.dsfrModal?.nativeElement) {
this.disclose.emit();
}
}));
}
ngOnDestroy() {
this._unlisten.forEach((unlistenFunc) => unlistenFunc());
}
/**
* Permet de déclencher programmatiquement l'ouverture de la modale.
*/
open() {
dsfr(this.dsfrModal?.nativeElement).modal.disclose();
}
/**
* Permet de déclencher programmatiquement la fermeture de la modale.
*/
close() {
dsfr(this.dsfrModal?.nativeElement).modal.conceal();
}
/** @internal */
performAction(action) {
if (action.callback) {
action.callback();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrModalComponent, isStandalone: true, selector: "dsfr-modal", inputs: { dialogId: { classPropertyName: "dialogId", publicName: "dialogId", isSignal: false, isRequired: false, transformFunction: null }, titleModal: { classPropertyName: "titleModal", publicName: "titleModal", isSignal: false, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: false, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: false, isRequired: false, transformFunction: null }, autoCloseOnAction: { classPropertyName: "autoCloseOnAction", publicName: "autoCloseOnAction", isSignal: false, isRequired: false, transformFunction: null }, concealingBackdrop: { classPropertyName: "concealingBackdrop", publicName: "concealingBackdrop", isSignal: false, isRequired: false, transformFunction: null }, controlMode: { classPropertyName: "controlMode", publicName: "controlMode", isSignal: false, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, markup: { classPropertyName: "markup", publicName: "markup", isSignal: false, isRequired: false, transformFunction: null }, mobileTop: { classPropertyName: "mobileTop", publicName: "mobileTop", isSignal: false, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, modalSize: { classPropertyName: "modalSize", publicName: "modalSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disclose: "disclose", conceal: "conceal" }, queries: [{ propertyName: "modalFooterTemplate", first: true, predicate: ["modalFooterTemplate"], descendants: true, static: true }], viewQueries: [{ propertyName: "dsfrModal", first: true, predicate: ["dsfrModal"], descendants: true }], ngImport: i0, template: "@if (markup === 'dialog') {\n <dialog\n [attr.aria-labelledby]=\"titleElementId\"\n [id]=\"dialogId\"\n class=\"fr-modal\"\n [class.fr-modal--top]=\"mobileTop\"\n [attr.data-fr-concealing-backdrop]=\"concealingBackdrop === false ? false : null\"\n #dsfrModal>\n <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n </dialog>\n} @else {\n <div\n [attr.aria-labelledby]=\"titleElementId\"\n [id]=\"dialogId\"\n role=\"dialog\"\n class=\"fr-modal\"\n [class.fr-modal--top]=\"mobileTop\"\n [attr.data-fr-concealing-backdrop]=\"concealingBackdrop === false ? false : null\"\n #dsfrModal>\n <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n </div>\n}\n\n<ng-template #dialogTemplate>\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div [ngClass]=\"sizeClasses()\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--close\"\n [title]=\"'modal.ariaLabel' | dsfrI18n\"\n [attr.aria-controls]=\"dialogId\"\n data-testid=\"modal-btn-close\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <!-- Niveau de titre par d\u00E9faut H2 -->\n <edu-heading\n [headingId]=\"titleElementId\"\n [level]=\"headingLevel\"\n defaultLevel=\"H2\"\n [allowFirstLevel]=\"true\"\n customClass=\"fr-modal__title\">\n <ng-container *ngTemplateOutlet=\"titleTemplate\"></ng-container>\n </edu-heading>\n <ng-content></ng-content>\n </div>\n @if (actions && actions.length > 0) {\n <div class=\"fr-modal__footer\">\n <div\n class=\"fr-btns-group fr-btns-group--right fr-btns-group--inline-reverse fr-btns-group--inline-lg fr-btns-group--icon-left\">\n @for (action of actions; track $index) {\n <dsfr-button\n [ariaControls]=\"autoCloseOnAction ? dialogId : ''\"\n [ariaLabel]=\"action.ariaLabel ?? ''\"\n [icon]=\"action.icon\"\n [label]=\"action.label\"\n [type]=\"action.type ?? 'button'\"\n [tooltipMessage]=\"action.tooltipMessage ?? ''\"\n [disabled]=\"action.disabled ?? false\"\n [uppercase]=\"action.uppercase ?? false\"\n [variant]=\"action.variant ?? 'primary'\"\n (click)=\"performAction(action)\">\n </dsfr-button>\n }\n </div>\n </div>\n }\n @if (modalFooterTemplate) {\n <div class=\"fr-modal__footer\">\n <ng-container *ngTemplateOutlet=\"modalFooterTemplate\"></ng-container>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</ng-template>\n\n<ng-template #titleTemplate>\n @if (icon) {\n <span class=\"{{ icon }} fr-icon--lg\" aria-hidden=\"true\"></span>\n }\n {{ titleModal }}\n</ng-template>\n", styles: [".fr-modal .fr-modal__footer dsfr-buttons-group{width:100%}.fr-modal .fr-modal__footer dsfr-button{width:100%}@media(min-width:62em){.fr-modal .fr-btns-group--inline-lg dsfr-button{width:auto;max-width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-modal', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrButtonComponent, HeadingComponent, DsfrI18nPipe], template: "@if (markup === 'dialog') {\n <dialog\n [attr.aria-labelledby]=\"titleElementId\"\n [id]=\"dialogId\"\n class=\"fr-modal\"\n [class.fr-modal--top]=\"mobileTop\"\n [attr.data-fr-concealing-backdrop]=\"concealingBackdrop === false ? false : null\"\n #dsfrModal>\n <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n </dialog>\n} @else {\n <div\n [attr.aria-labelledby]=\"titleElementId\"\n [id]=\"dialogId\"\n role=\"dialog\"\n class=\"fr-modal\"\n [class.fr-modal--top]=\"mobileTop\"\n [attr.data-fr-concealing-backdrop]=\"concealingBackdrop === false ? false : null\"\n #dsfrModal>\n <ng-container *ngTemplateOutlet=\"dialogTemplate\"></ng-container>\n </div>\n}\n\n<ng-template #dialogTemplate>\n <div class=\"fr-container fr-container--fluid fr-container-md\">\n <div class=\"fr-grid-row fr-grid-row--center\">\n <div [ngClass]=\"sizeClasses()\">\n <div class=\"fr-modal__body\">\n <div class=\"fr-modal__header\">\n <button\n type=\"button\"\n class=\"fr-btn fr-btn--close\"\n [title]=\"'modal.ariaLabel' | dsfrI18n\"\n [attr.aria-controls]=\"dialogId\"\n data-testid=\"modal-btn-close\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n </div>\n <div class=\"fr-modal__content\">\n <!-- Niveau de titre par d\u00E9faut H2 -->\n <edu-heading\n [headingId]=\"titleElementId\"\n [level]=\"headingLevel\"\n defaultLevel=\"H2\"\n [allowFirstLevel]=\"true\"\n customClass=\"fr-modal__title\">\n <ng-container *ngTemplateOutlet=\"titleTemplate\"></ng-container>\n </edu-heading>\n <ng-content></ng-content>\n </div>\n @if (actions && actions.length > 0) {\n <div class=\"fr-modal__footer\">\n <div\n class=\"fr-btns-group fr-btns-group--right fr-btns-group--inline-reverse fr-btns-group--inline-lg fr-btns-group--icon-left\">\n @for (action of actions; track $index) {\n <dsfr-button\n [ariaControls]=\"autoCloseOnAction ? dialogId : ''\"\n [ariaLabel]=\"action.ariaLabel ?? ''\"\n [icon]=\"action.icon\"\n [label]=\"action.label\"\n [type]=\"action.type ?? 'button'\"\n [tooltipMessage]=\"action.tooltipMessage ?? ''\"\n [disabled]=\"action.disabled ?? false\"\n [uppercase]=\"action.uppercase ?? false\"\n [variant]=\"action.variant ?? 'primary'\"\n (click)=\"performAction(action)\">\n </dsfr-button>\n }\n </div>\n </div>\n }\n @if (modalFooterTemplate) {\n <div class=\"fr-modal__footer\">\n <ng-container *ngTemplateOutlet=\"modalFooterTemplate\"></ng-container>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n</ng-template>\n\n<ng-template #titleTemplate>\n @if (icon) {\n <span class=\"{{ icon }} fr-icon--lg\" aria-hidden=\"true\"></span>\n }\n {{ titleModal }}\n</ng-template>\n", styles: [".fr-modal .fr-modal__footer dsfr-buttons-group{width:100%}.fr-modal .fr-modal__footer dsfr-button{width:100%}@media(min-width:62em){.fr-modal .fr-btns-group--inline-lg dsfr-button{width:auto;max-width:100%}}\n"] }]
}], propDecorators: { dialogId: [{
type: Input
}], titleModal: [{
type: Input
}], headingLevel: [{
type: Input
}], actions: [{
type: Input
}], autoCloseOnAction: [{
type: Input
}], concealingBackdrop: [{
type: Input
}], controlMode: [{
type: Input
}], icon: [{
type: Input
}], markup: [{
type: Input
}], mobileTop: [{
type: Input
}], disclose: [{
type: Output
}], conceal: [{
type: Output
}], dsfrModal: [{
type: ViewChild,
args: ['dsfrModal']
}], modalFooterTemplate: [{
type: ContentChild,
args: ['modalFooterTemplate', { static: true }]
}], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], modalSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "modalSize", required: false }] }] } });
class DsfrModalModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalModule, imports: [CommonModule, DsfrButtonComponent, DsfrI18nPipe, DsfrModalComponent], exports: [DsfrModalComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalModule, imports: [CommonModule, DsfrButtonComponent, DsfrModalComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrModalModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrModalComponent],
imports: [CommonModule, DsfrButtonComponent, DsfrI18nPipe, DsfrModalComponent],
}]
}] });
/**
* Définit les types de notice supportés par le DSFR.
*/
var DsfrNoticeTypeConst;
(function (DsfrNoticeTypeConst) {
DsfrNoticeTypeConst["INFO"] = "info";
DsfrNoticeTypeConst["WARNING"] = "warning";
DsfrNoticeTypeConst["ALERT"] = "alert";
DsfrNoticeTypeConst["WEATHER_ORANGE"] = "weather-orange";
DsfrNoticeTypeConst["WEATHER_RED"] = "weather-red";
DsfrNoticeTypeConst["WEATHER_PURPLE"] = "weather-purple";
DsfrNoticeTypeConst["ATTACK"] = "attack";
DsfrNoticeTypeConst["WITNESS"] = "witness";
DsfrNoticeTypeConst["CYBERATTACK"] = "cyberattack";
})(DsfrNoticeTypeConst || (DsfrNoticeTypeConst = {}));
class DsfrNoticeComponent {
constructor() {
/**
* Permet d'afficher le bouton servant à fermer le bandeau d'information.<br>
* La valeur 'controlled' permet d'afficher le bouton de fermeture, mais c'est vous qui devez contrôler l'action de
* fermeture du bandeau.
*/
this.closeable = false;
/**
* @since 1.12 Permet d'enlever l'icône du bandeau d'information
*/
this.noIcon = false;
/**
* @since 1.12 Sévérité du message
*/
this.type = DsfrNoticeTypeConst.INFO;
/**
* Signale la fermeture manuelle du bandeau d'information.
*/
this.noticeClose = new EventEmitter();
this.hostElement = inject(ElementRef);
}
get iconClass() {
return this.icon ?? '';
}
/** @deprecated since 1.12 use `closeable`instead. */
get closable() {
return this.closeable;
}
/** @deprecated since 1.12 use `heading` instead. */
get message() {
return this.heading;
}
/** @deprecated since 1.12 use `closeable`instead. */
set closable(value) {
this.closeable = value;
}
/**
* Le message qui sera affiché dans le bandeau.
*
* Renseigner le message via l'input est prioritaire sur le slot.
*
* @deprecated since 1.12 use `heading` instead.
*/
set message(value) {
this.heading = value;
}
/** @internal */
onClose() {
if (this.closeable && this.closeable !== 'controlled') {
this.hostElement.nativeElement.parentNode?.removeChild(this.hostElement.nativeElement);
}
this.noticeClose.emit(this.hostElement);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrNoticeComponent, isStandalone: true, selector: "dsfr-notice", inputs: { heading: "heading", headingLevel: "headingLevel", closeable: "closeable", noIcon: "noIcon", icon: "icon", description: "description", type: "type", closable: "closable", message: "message" }, outputs: { noticeClose: "noticeClose" }, ngImport: i0, template: "<div class=\"fr-notice\" [class]=\"'fr-notice--' + type\" [class.fr-notice--no-icon]=\"noIcon === true\">\n <div class=\"fr-container\">\n <div class=\"fr-notice__body\">\n <p>\n @if (heading) {\n <!-- Titre obligatoire -->\n <ng-container [ngTemplateOutlet]=\"headingTemplate\"></ng-container>\n } @else {\n <span class=\"fr-notice__title\" [ngClass]=\"iconClass\"><ng-content></ng-content></span>\n }\n\n @if (description) {\n <span class=\"fr-notice__desc\" [innerHTML]=\"description\"></span>\n }\n\n <ng-content select=\"[link]\"></ng-content>\n </p>\n\n @if (closeable) {\n <button type=\"button\" class=\"fr-btn--close fr-btn\" title=\"{{ 'notice.close' | dsfrI18n }}\" (click)=\"onClose()\">\n {{ 'notice.close' | dsfrI18n }}\n </button>\n }\n </div>\n </div>\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #headingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h2>\n }\n @case ('H3') {\n <h3 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h3>\n }\n @case ('H4') {\n <h4 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h4>\n }\n @case ('H5') {\n <h5 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h5>\n }\n @case ('H6') {\n <h6 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h6>\n }\n @default {\n <span class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</span>\n }\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-notice', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrI18nPipe], template: "<div class=\"fr-notice\" [class]=\"'fr-notice--' + type\" [class.fr-notice--no-icon]=\"noIcon === true\">\n <div class=\"fr-container\">\n <div class=\"fr-notice__body\">\n <p>\n @if (heading) {\n <!-- Titre obligatoire -->\n <ng-container [ngTemplateOutlet]=\"headingTemplate\"></ng-container>\n } @else {\n <span class=\"fr-notice__title\" [ngClass]=\"iconClass\"><ng-content></ng-content></span>\n }\n\n @if (description) {\n <span class=\"fr-notice__desc\" [innerHTML]=\"description\"></span>\n }\n\n <ng-content select=\"[link]\"></ng-content>\n </p>\n\n @if (closeable) {\n <button type=\"button\" class=\"fr-btn--close fr-btn\" title=\"{{ 'notice.close' | dsfrI18n }}\" (click)=\"onClose()\">\n {{ 'notice.close' | dsfrI18n }}\n </button>\n }\n </div>\n </div>\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #headingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h2>\n }\n @case ('H3') {\n <h3 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h3>\n }\n @case ('H4') {\n <h4 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h4>\n }\n @case ('H5') {\n <h5 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h5>\n }\n @case ('H6') {\n <h6 class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</h6>\n }\n @default {\n <span class=\"fr-notice__title\" [ngClass]=\"iconClass\">{{ heading }}</span>\n }\n }\n</ng-template>\n" }]
}], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], closeable: [{
type: Input
}], noIcon: [{
type: Input
}], icon: [{
type: Input
}], description: [{
type: Input
}], type: [{
type: Input
}], noticeClose: [{
type: Output
}], closable: [{
type: Input
}], message: [{
type: Input
}] } });
class DsfrNoticeModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeModule, imports: [CommonModule, DsfrI18nPipe, DsfrNoticeComponent], exports: [DsfrNoticeComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeModule, imports: [CommonModule, DsfrNoticeComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNoticeModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrNoticeComponent],
imports: [CommonModule, DsfrI18nPipe, DsfrNoticeComponent],
}]
}] });
class EduPageLinkComponent {
constructor() {
this.active = false;
this.disabled = false;
this.pageSelectEvent = new EventEmitter();
}
get tooltipMessage() {
return this._tooltipMessage ? this._tooltipMessage : this.label;
}
set tooltipMessage(value) {
this._tooltipMessage = value;
}
onPage(event, pageNum) {
event.preventDefault();
this.pageSelectEvent.emit(pageNum);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduPageLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: EduPageLinkComponent, isStandalone: true, selector: "edu-page-link", inputs: { active: "active", customClass: "customClass", disabled: "disabled", label: "label", labelCustomClass: "labelCustomClass", num: "num", tooltipMessage: "tooltipMessage" }, outputs: { pageSelectEvent: "pageSelectEvent" }, ngImport: i0, template: "<!-- ici on utilise des boutons a la place des liens pour l'accessibilit\u00E9 (pas de href) -->\n<button\n type=\"button\"\n class=\"fr-pagination__link\"\n [ngClass]=\"customClass ? customClass : ''\"\n [attr.aria-current]=\"active ? 'page' : null\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"tooltipMessage || null\"\n (click)=\"onPage($event, num)\">\n @if (label) {\n <span [class]=\"labelCustomClass || null\">{{ label }}</span>\n }\n</button>\n", styles: ["@media(max-width:36em){.fr-pagination__link{line-height:normal;min-height:auto;padding:.25rem 6px;min-width:auto;margin-bottom:.25rem}.fr-pagination__link,.fr-pagination__link--first:before,.fr-pagination__link--prev:before,.fr-pagination__link--next:before,.fr-pagination__link--last:before{margin-left:1px;margin-right:1px}.fr-pagination__link--first,.fr-pagination__link--prev,.fr-pagination__link--next,.fr-pagination__link--last{padding-left:.1rem;padding-right:.1rem}.label-sm{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}}\n/*! media sm et xs */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduPageLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-page-link', encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<!-- ici on utilise des boutons a la place des liens pour l'accessibilit\u00E9 (pas de href) -->\n<button\n type=\"button\"\n class=\"fr-pagination__link\"\n [ngClass]=\"customClass ? customClass : ''\"\n [attr.aria-current]=\"active ? 'page' : null\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"tooltipMessage || null\"\n (click)=\"onPage($event, num)\">\n @if (label) {\n <span [class]=\"labelCustomClass || null\">{{ label }}</span>\n }\n</button>\n", styles: ["@media(max-width:36em){.fr-pagination__link{line-height:normal;min-height:auto;padding:.25rem 6px;min-width:auto;margin-bottom:.25rem}.fr-pagination__link,.fr-pagination__link--first:before,.fr-pagination__link--prev:before,.fr-pagination__link--next:before,.fr-pagination__link--last:before{margin-left:1px;margin-right:1px}.fr-pagination__link--first,.fr-pagination__link--prev,.fr-pagination__link--next,.fr-pagination__link--last{padding-left:.1rem;padding-right:.1rem}.label-sm{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}}\n/*! media sm et xs */\n"] }]
}], propDecorators: { active: [{
type: Input
}], customClass: [{
type: Input
}], disabled: [{
type: Input
}], label: [{
type: Input
}], labelCustomClass: [{
type: Input
}], num: [{
type: Input
}], pageSelectEvent: [{
type: Output
}], tooltipMessage: [{
type: Input
}] } });
class DsfrFormSelectComponent extends DefaultControlComponent {
constructor() {
super();
/**
* Indique que le champ est obligatoire, nécessaire du point de vue de l'accessibilité.
*/
this.required = false;
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
/**
* Output équivalent de (ngModelChange) si celui n'est pas disponible.
*/
this.selectChange = new EventEmitter();
/**
* Le modèle de présentation permettant de transmettre la liste des options.
*/
this.options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
/** Fonction de comparaison */
this.compareFn = Object.is;
this.NO_VALUE = null; // valeur interne unique utilisée uniquement pour l’affichage du placeholder
/**
* Permet de conditionner la présence de l'option servant de placeholder. Sert à désactiver la gestion du placeholder
* dans le cas où l'utilisateur souhaite avoir une liste déroulante désélectionnable, dans ce cas, il fournit lui-même
* l'option servant de placeholder et d'option de désélection.
*/
this.addDefaultPlaceholder = true;
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-select-group': true,
'fr-select-group--disabled': this.disabled,
'fr-select-group--valid': message && (severity === DsfrSeverityConst.SUCCESS || severity === DsfrSeverityConst.VALID),
'fr-select-group--error': message && severity === DsfrSeverityConst.ERROR,
'fr-input-group--warning': message && severity === DsfrSeverityConst.WARNING,
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
effect(() => {
const options = this.options();
if (options?.length && options[0]) {
const firstOpt = options[0];
this.addDefaultPlaceholder =
firstOpt.options !== undefined || (firstOpt.value !== '' && firstOpt.value !== undefined);
}
else if (options?.length === 0) {
this.addDefaultPlaceholder = true;
}
else {
this.addDefaultPlaceholder = false;
}
});
}
/* fixme : remplacer par un signal input si https://github.com/compodoc/compodoc/pull/1491 */
// private _options: DsfrSelectOption[] = [];
/**
* @deprecated since 1.2, utiliser `placeholder` à la place (sans H majuscule).
*/
get placeHolder() {
return this.placeholder;
}
get value() {
return super.value;
}
/**
* @deprecated since 1.2, utiliser `placeholder` à la place (sans H majuscule).
*/
set placeHolder(value) {
this.placeholder = value;
}
set value(value) {
super.value = value;
this.updateInternalValue();
}
/**
* Personnalisation de la comparaison.
* `compareWith` est une fonction qui a deux arguments: valeur option1 et valeur option2.
* Si `compareWith` est fournie, sélection de l'option en fonction du retour de la fonction.
*/
set compareWith(fn) {
if (typeof fn !== 'function') {
throw Error('`compareWith` must be a function.');
}
this.compareFn = fn;
}
ngOnInit() {
super.ngOnInit();
this.messagesGroupId = `${this.inputId}-messages`;
if (this.value === undefined)
this.updateInternalValue();
}
writeValue(value) {
super.writeValue(value);
this.updateInternalValue();
}
/**
* Mise a jour de value selon internalValue (nécessaire pour le cas du undefined et placeholder)
* @param value
*/
onChange(value) {
this.value = value === this.NO_VALUE ? undefined : this.internalValue;
this.selectChange.emit(this.value);
}
/**
* Met à jour la valeur interne utilisée par le <select> pour afficher correctement le placeholder.
*
* En reactive form, la valeur [ngValue]="undefined" ne sera jamais sélectionnée par angular et
* le placeholder ne sera donc pas affiché. Fix : contournement avec une valeur interne positionnée
* à nulle (NO_VALUE) en cas de undefined sur this.value
*
* En template driven, il faut maj internalValue a l'initialisation pour que cette solution
* fonctionne également (pas de passage dans writeValue)
*/
updateInternalValue() {
if (this.value === undefined && this.addDefaultPlaceholder) {
// set manuellement une valeur autre que undefined pour que le placeholder soit affiché
this.internalValue = this.NO_VALUE;
}
else {
this.internalValue = this.value;
}
}
findOption(value, options) {
return options?.find((option) => this.compareFn(value, option.value) ||
option.value === value ||
(option.options && this.findOption(value, option.options)));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormSelectComponent, isStandalone: true, selector: "dsfr-form-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: false, isRequired: false, transformFunction: null }, ariaInvalid: { classPropertyName: "ariaInvalid", publicName: "ariaInvalid", isSignal: false, isRequired: false, transformFunction: null }, labelSrOnly: { classPropertyName: "labelSrOnly", publicName: "labelSrOnly", isSignal: false, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: false, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { selectChange: "selectChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormSelectComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<div [class]=\"groupClasses()\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <select\n class=\"fr-select\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [(ngModel)]=\"internalValue\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [compareWith]=\"compareFn\"\n (blur)=\"onBlur()\"\n (change)=\"onChange($event)\">\n @if (addDefaultPlaceholder === true) {\n <option [ngValue]=\"NO_VALUE\" disabled>\n {{ placeholder ?? ('select.placeholder' | dsfrI18n) }}\n </option>\n }\n @for (option of options(); track $index) {\n @if (option.options) {\n <!-- Affichage des groupes -->\n <optgroup [label]=\"option.label\">\n @for (option of option.options; track $index) {\n <option [ngValue]=\"option.value\" [disabled]=\"option.disabled || null\">\n {{ option.label }}\n </option>\n }\n </optgroup>\n } @else {\n <option [ngValue]=\"option.value\" [disabled]=\"option.disabled || null\">\n {{ option.label }}\n </option>\n }\n }\n </select>\n\n <div [id]=\"messagesGroupId\" class=\"fr-messages-group\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n", styles: [".fr-label.fr-sr-only+.fr-select{margin-top:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-select', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormSelectComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, DsfrI18nPipe, EduMessageSeverityDirective, EduHintComponent], template: "<div [class]=\"groupClasses()\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <select\n class=\"fr-select\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [(ngModel)]=\"internalValue\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [compareWith]=\"compareFn\"\n (blur)=\"onBlur()\"\n (change)=\"onChange($event)\">\n @if (addDefaultPlaceholder === true) {\n <option [ngValue]=\"NO_VALUE\" disabled>\n {{ placeholder ?? ('select.placeholder' | dsfrI18n) }}\n </option>\n }\n @for (option of options(); track $index) {\n @if (option.options) {\n <!-- Affichage des groupes -->\n <optgroup [label]=\"option.label\">\n @for (option of option.options; track $index) {\n <option [ngValue]=\"option.value\" [disabled]=\"option.disabled || null\">\n {{ option.label }}\n </option>\n }\n </optgroup>\n } @else {\n <option [ngValue]=\"option.value\" [disabled]=\"option.disabled || null\">\n {{ option.label }}\n </option>\n }\n }\n </select>\n\n <div [id]=\"messagesGroupId\" class=\"fr-messages-group\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n", styles: [".fr-label.fr-sr-only+.fr-select{margin-top:0}\n"] }]
}], ctorParameters: () => [], propDecorators: { placeholder: [{
type: Input
}], required: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaInvalid: [{
type: Input
}], labelSrOnly: [{
type: Input
}], selectChange: [{
type: Output
}], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeHolder: [{
type: Input
}], value: [{
type: Input
}], compareWith: [{
type: Input
}] } });
class DsfrFormSelectModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectModule, imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrFormSelectComponent], exports: [DsfrFormSelectComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectModule, imports: [CommonModule, FormsModule, DsfrFormSelectComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormSelectModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormSelectComponent],
imports: [CommonModule, FormsModule, DsfrI18nPipe, DsfrFormSelectComponent],
}]
}] });
/**
* Permet de calculer le ou les intervalles à afficher dans le composant de pagination.
*
* @param currentPage n° de la page courante, démarre à 1
* @param pagesTotal nombre de pages
* @param extended affichage sur 7 numéros plutôt que 5 (default: false)
*/
function calcPages(currentPage, pagesTotal, extended = false) {
if (extended)
return calcPagesExtended(currentPage, pagesTotal);
else
return calcPagesRestricted(currentPage, pagesTotal);
}
/**
* Établit les intervalles affichés dans la pagination
* avec au plus 5 numéros de page visibles.
*
* Configurations possibles (p = page courante / n = nombre total de pages) :
* - 1 2 3 4 5 (si n <= 5)
* - 1 2 3 ... n-1 n (si p = 1 ou 2)
* - 1 2 3 4 ... n (si p = 3)
* - 1 ... n-3 n-2 n-1 n (si p = n-2)
* - 1 2 ... n-2 n-1 n (si p = n ou n-1)
* - 1 ... p-1 p p+1 ... n (sinon)
*
* @param currentPage page courante
* @param pagesTotal nombre totale de pages
* @returns intervalles correspondants
*/
function calcPagesRestricted(currentPage, pagesTotal) {
if (pagesTotal <= 5) {
return [[1, pagesTotal]];
}
if (currentPage <= 2) {
return [
[1, 3],
[pagesTotal - 1, pagesTotal],
];
}
if (currentPage === 3) {
return [
[1, 4],
[pagesTotal, pagesTotal],
];
}
if (currentPage === pagesTotal - 2) {
return [
[1, 1],
[pagesTotal - 3, pagesTotal],
];
}
if (currentPage >= pagesTotal - 1) {
return [
[1, 2],
[pagesTotal - 2, pagesTotal],
];
}
return [
[1, 1],
[currentPage - 1, currentPage + 1],
[pagesTotal, pagesTotal],
];
}
/**
* Établit les intervalles affichés dans la pagination
* avec au plus 7 numéros de page visibles.
*
* Configurations possibles (p = page courante / n = nombre total de pages) :
* - 1 2 3 4 5 6 7 (si n <= 7)
* - 1 2 3 4 ... n-2 n-1 n (si p = 1, 2 ou 3)
* - 1 2 3 4 5 ... n-1 n (si p = 4)
* - 1 2 3 ... n-3 n-2 n-1 n (si p = n, n-1 ou n-2)
* - 1 2 ... n-4 n-3 n-2 n-1 n (si p = n-3)
* - 1 2 ... p-1 p p+1 ... n-1 n (sinon)
*
* @param currentPage page courante
* @param pagesTotal nombre totale de pages
* @returns intervalles correspondants
*/
function calcPagesExtended(currentPage, pagesTotal) {
if (pagesTotal <= 7) {
return [[1, pagesTotal]];
}
// Début de pagination
if (currentPage <= 4) {
return [
[1, currentPage === 4 ? 5 : 4],
[pagesTotal - (currentPage === 4 ? 1 : 2), pagesTotal],
];
}
// Fin de pagination
if (currentPage >= pagesTotal - 3) {
return [
[1, currentPage === pagesTotal - 3 ? 2 : 3],
[pagesTotal - (currentPage === pagesTotal - 3 ? 4 : 3), pagesTotal],
];
}
// Milieu
return [
[1, 1],
[currentPage - 2, currentPage + 2],
[pagesTotal, pagesTotal],
];
}
class DsfrPaginationComponent {
constructor() {
/**
* Affiche uniquement l'icône d'accès à la page précédement si vrai.
*
* @since 1.6
*/
this.previousOnly = false;
/** Sélection d'une page */
this.pageSelect = new EventEmitter();
/** @deprecated(since 1.6) utiliser `pageSelect` à la place. */
this.pageSelectEvent = new EventEmitter();
/* Evènement page précédente. L'événement initial n'est pas propagé. */
this.backSelect = new EventEmitter();
/** Changement du nombre de lignes à afficher */
this.rowsPerPageChange = new EventEmitter();
/**
* @deprecated (since 1.6) utiliser `backSelect` à la place.
*/
this.backEvent = new EventEmitter();
/* N° de la page active, démarre à : 1 */
this.currentPage = input(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
/** Désactiver la sélection du nombre de lignes à afficher */
this.disabledRowsPerPage = input(false, ...(ngDevMode ? [{ debugName: "disabledRowsPerPage", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
this.pageCount = input.required(...(ngDevMode ? [{ debugName: "pageCount" }] : []));
/** Nombre d'éléments total. Optionnel pour afficher un nombre d'éléments */
this.totalElements = input(...(ngDevMode ? [undefined, { debugName: "totalElements" }] : []));
/** Controle le nombre de numéro de pages affichés (5 en standard par défaut, 7 en extended) */
this.displayedNumbers = input('standard', ...(ngDevMode ? [{ debugName: "displayedNumbers" }] : []));
/** Options pour la sélection du nombre de lignes à afficher par page */
this.rowsPerPageOptions = input([], ...(ngDevMode ? [{ debugName: "rowsPerPageOptions" }] : []));
/** Taille de page courante */
this.rowsPerPage = input(10, ...(ngDevMode ? [{ debugName: "rowsPerPage" }] : []));
/* Affiche les boutons textuels "Page précédente/suivante" */
this.showPrevAndNextBtn = input(true, ...(ngDevMode ? [{ debugName: "showPrevAndNextBtn", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/** Affiche les labels des boutons textuels "Page précédente/suivante" à partir du breakpoint LG */
this.showPrevAndNextLabelLg = input(true, ...(ngDevMode ? [{ debugName: "showPrevAndNextLabelLg", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
/** Identifiant du select du nombre de pages */
this.selectId = '';
this.multipleElements = computed(() => {
const total = this.totalElements();
return total && total > 1;
}, ...(ngDevMode ? [{ debugName: "multipleElements" }] : []));
this.intervals = computed(() => {
return calcPages(this.currentPage(), this.pageCount(), this.displayedNumbers() === 'extended');
}, ...(ngDevMode ? [{ debugName: "intervals" }] : []));
this.isFirstPage = computed(() => {
return this.pageCount() === 0 || this.currentPage() == 1;
}, ...(ngDevMode ? [{ debugName: "isFirstPage" }] : []));
this.isLastPage = computed(() => {
return this.pageCount() === 0 || this.currentPage() == this.pageCount();
}, ...(ngDevMode ? [{ debugName: "isLastPage" }] : []));
}
get prevNum() {
return Math.max(1, this.currentPage() - 1);
}
get nextNum() {
return Math.min(this.currentPage() + 1, this.pageCount());
}
ngOnInit() {
this.selectId = newUniqueId();
}
onBack() {
this.backSelect.emit();
this.backEvent.emit();
}
onPage(pageNum) {
this.pageSelect.emit(pageNum);
this.pageSelectEvent.emit(pageNum);
}
previous() {
if (!this.isFirstPage())
this.onPage(this.currentPage() - 1);
return this.currentPage();
}
next() {
if (!this.isLastPage())
this.onPage(this.currentPage() + 1);
return this.currentPage();
}
pagesFrom(interval) {
const firstPage = interval[0];
const lastPage = interval[1];
const count = lastPage - firstPage + 1;
return Array(count)
.fill(0)
.map((e, i) => i + firstPage);
}
onChangerowsPerPage(value) {
this.rowsPerPageChange.emit(value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrPaginationComponent, isStandalone: true, selector: "dsfr-pagination", inputs: { previousOnly: { classPropertyName: "previousOnly", publicName: "previousOnly", isSignal: false, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: false, isRequired: false, transformFunction: null }, rowsPerPagePlaceholder: { classPropertyName: "rowsPerPagePlaceholder", publicName: "rowsPerPagePlaceholder", isSignal: false, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, disabledRowsPerPage: { classPropertyName: "disabledRowsPerPage", publicName: "disabledRowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, pageCount: { classPropertyName: "pageCount", publicName: "pageCount", isSignal: true, isRequired: true, transformFunction: null }, totalElements: { classPropertyName: "totalElements", publicName: "totalElements", isSignal: true, isRequired: false, transformFunction: null }, displayedNumbers: { classPropertyName: "displayedNumbers", publicName: "displayedNumbers", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPage: { classPropertyName: "rowsPerPage", publicName: "rowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, showPrevAndNextBtn: { classPropertyName: "showPrevAndNextBtn", publicName: "showPrevAndNextBtn", isSignal: true, isRequired: false, transformFunction: null }, showPrevAndNextLabelLg: { classPropertyName: "showPrevAndNextLabelLg", publicName: "showPrevAndNextLabelLg", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageSelect: "pageSelect", pageSelectEvent: "pageSelectEvent", backSelect: "backSelect", rowsPerPageChange: "rowsPerPageChange", backEvent: "backEvent" }, queries: [{ propertyName: "totalElementsTemplate", first: true, predicate: ["totalElementsTemplate"], descendants: true, static: true }, { propertyName: "additionalTemplate", first: true, predicate: ["additionalTemplate"], descendants: true, static: true }], ngImport: i0, template: "@if (totalElements() || rowsPerPageOptions().length > 0) {\n <div class=\"fr-table__footer\">\n <!-- Nombre de r\u00E9sultats total -->\n <div class=\"fr-table__footer--start\">\n @if (totalElements()) {\n <p class=\"fr-table__detail\">\n <!-- Afficher le nombre de r\u00E9sultat -->\n @if (totalElementsTemplate) {\n <ng-container *ngTemplateOutlet=\"totalElementsTemplate\"></ng-container>\n } @else {\n @if (multipleElements()) {\n {{ totalElements() }} {{ 'pagination.row' | dsfrI18n }}s\n } @else {\n {{ totalElements() }} {{ 'pagination.row' | dsfrI18n }}\n }\n }\n </p>\n }\n\n <!-- S\u00E9lection de la taille de page -->\n @if (rowsPerPageOptions().length > 0) {\n <dsfr-form-select\n [inputId]=\"selectId\"\n [ngModel]=\"rowsPerPage()\"\n (ngModelChange)=\"onChangerowsPerPage($event)\"\n [disabled]=\"disabledRowsPerPage()\"\n [placeholder]=\"rowsPerPagePlaceholder ?? 'pagination.rowsPerPageLabel' | dsfrI18n\"\n [options]=\"rowsPerPageOptions()\">\n <ng-container label\n ><span class=\"fr-sr-only\"> {{ 'pagination.rowsPerPageLabel' | dsfrI18n }}</span>\n </ng-container>\n </dsfr-form-select>\n }\n </div>\n <div class=\"fr-table__footer--middle\">\n <ng-container *ngTemplateOutlet=\"paginationTemplate\"></ng-container>\n </div>\n\n <div class=\"fr-table__footer--end\">\n @if (additionalTemplate) {\n <ng-container *ngTemplateOutlet=\"additionalTemplate\"></ng-container>\n }\n </div>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"paginationTemplate\"></ng-container>\n}\n\n<ng-template #paginationTemplate>\n <!-- Page pr\u00E9c\u00E9dente seulement ---------------------------------------------------------------------------------------->\n\n @if (previousOnly) {\n <nav\n class=\"fr-pagination\"\n [attr.role]=\"'pagination.role' | dsfrI18n\"\n [attr.aria-label]=\"ariaLabel ?? ('pagination.ariaLabel' | dsfrI18n)\">\n <ul class=\"fr-pagination__list\">\n <edu-page-link\n customClass=\"fr-pagination__link--prev fr-pagination__link--lg-label\"\n label=\"Page pr\u00E9c\u00E9dente\"\n (pageSelectEvent)=\"onBack()\"></edu-page-link>\n </ul>\n </nav>\n } @else {\n <!-- Pagination ------------------------------------------------------------------------------------------------------->\n <nav class=\"fr-pagination\" [attr.role]=\"'pagination.role' | dsfrI18n\" [attr.aria-label]=\"ariaLabel\">\n <ul class=\"fr-pagination__list\">\n <!-- Premi\u00E8re page -->\n <li>\n <edu-page-link\n customClass=\"fr-pagination__link--first\"\n [label]=\"'pagination.firstPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.firstPage' | dsfrI18n\"\n [num]=\"1\"\n [disabled]=\"isFirstPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n <!-- Lien vers 'Page pr\u00E9c\u00E9dente' -->\n @if (showPrevAndNextBtn()) {\n <li>\n <edu-page-link\n [customClass]=\"\n showPrevAndNextLabelLg()\n ? 'fr-pagination__link--prev fr-pagination__link--lg-label'\n : 'fr-pagination__link--prev'\n \"\n [label]=\"'pagination.previousPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.previousPage' | dsfrI18n\"\n [num]=\"prevNum\"\n [disabled]=\"isFirstPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n <!-- Toutes les pages -->\n @for (interval of intervals(); let i = $index; track i) {\n @if (interval[0] !== 1) {\n <li>\n <span class=\"fr-pagination__link fr-hidden fr-unhidden-lg\"> \u2026 </span>\n </li>\n }\n @for (pageNum of pagesFrom(interval); track $index) {\n <li>\n <edu-page-link\n [num]=\"pageNum\"\n [label]=\"pageNum + ''\"\n [tooltipMessage]=\"'Page ' + pageNum\"\n [active]=\"pageNum === currentPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n }\n <!-- Lien vers 'Page suivante' -->\n @if (showPrevAndNextBtn()) {\n <li>\n <edu-page-link\n [customClass]=\"\n showPrevAndNextLabelLg()\n ? 'fr-pagination__link--next fr-pagination__link--lg-label'\n : 'fr-pagination__link--next'\n \"\n [label]=\"'pagination.nextPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.nextPage' | dsfrI18n\"\n [num]=\"nextNum\"\n [disabled]=\"isLastPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n <!-- Derni\u00E8re page (non affich\u00E9e sur mobile) -->\n <li>\n <edu-page-link\n customClass=\"fr-pagination__link--last\"\n [label]=\"'pagination.lastPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.lastPage' | dsfrI18n\"\n [num]=\"pageCount()\"\n [disabled]=\"isLastPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n </ul>\n </nav>\n }\n</ng-template>\n", styles: ["@media(max-width:36em){.fr-pagination__list{justify-content:space-between}}dsfr-pagination .fr-table__detail{margin-bottom:0}\n/*! media sm et xs */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: EduPageLinkComponent, selector: "edu-page-link", inputs: ["active", "customClass", "disabled", "label", "labelCustomClass", "num", "tooltipMessage"], outputs: ["pageSelectEvent"] }, { kind: "component", type: DsfrFormSelectComponent, selector: "dsfr-form-select", inputs: ["placeholder", "required", "ariaLabel", "ariaInvalid", "labelSrOnly", "options", "placeHolder", "value", "compareWith"], outputs: ["selectChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-pagination', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule, DsfrI18nPipe, EduPageLinkComponent, DsfrFormSelectComponent, FormsModule], template: "@if (totalElements() || rowsPerPageOptions().length > 0) {\n <div class=\"fr-table__footer\">\n <!-- Nombre de r\u00E9sultats total -->\n <div class=\"fr-table__footer--start\">\n @if (totalElements()) {\n <p class=\"fr-table__detail\">\n <!-- Afficher le nombre de r\u00E9sultat -->\n @if (totalElementsTemplate) {\n <ng-container *ngTemplateOutlet=\"totalElementsTemplate\"></ng-container>\n } @else {\n @if (multipleElements()) {\n {{ totalElements() }} {{ 'pagination.row' | dsfrI18n }}s\n } @else {\n {{ totalElements() }} {{ 'pagination.row' | dsfrI18n }}\n }\n }\n </p>\n }\n\n <!-- S\u00E9lection de la taille de page -->\n @if (rowsPerPageOptions().length > 0) {\n <dsfr-form-select\n [inputId]=\"selectId\"\n [ngModel]=\"rowsPerPage()\"\n (ngModelChange)=\"onChangerowsPerPage($event)\"\n [disabled]=\"disabledRowsPerPage()\"\n [placeholder]=\"rowsPerPagePlaceholder ?? 'pagination.rowsPerPageLabel' | dsfrI18n\"\n [options]=\"rowsPerPageOptions()\">\n <ng-container label\n ><span class=\"fr-sr-only\"> {{ 'pagination.rowsPerPageLabel' | dsfrI18n }}</span>\n </ng-container>\n </dsfr-form-select>\n }\n </div>\n <div class=\"fr-table__footer--middle\">\n <ng-container *ngTemplateOutlet=\"paginationTemplate\"></ng-container>\n </div>\n\n <div class=\"fr-table__footer--end\">\n @if (additionalTemplate) {\n <ng-container *ngTemplateOutlet=\"additionalTemplate\"></ng-container>\n }\n </div>\n </div>\n} @else {\n <ng-container *ngTemplateOutlet=\"paginationTemplate\"></ng-container>\n}\n\n<ng-template #paginationTemplate>\n <!-- Page pr\u00E9c\u00E9dente seulement ---------------------------------------------------------------------------------------->\n\n @if (previousOnly) {\n <nav\n class=\"fr-pagination\"\n [attr.role]=\"'pagination.role' | dsfrI18n\"\n [attr.aria-label]=\"ariaLabel ?? ('pagination.ariaLabel' | dsfrI18n)\">\n <ul class=\"fr-pagination__list\">\n <edu-page-link\n customClass=\"fr-pagination__link--prev fr-pagination__link--lg-label\"\n label=\"Page pr\u00E9c\u00E9dente\"\n (pageSelectEvent)=\"onBack()\"></edu-page-link>\n </ul>\n </nav>\n } @else {\n <!-- Pagination ------------------------------------------------------------------------------------------------------->\n <nav class=\"fr-pagination\" [attr.role]=\"'pagination.role' | dsfrI18n\" [attr.aria-label]=\"ariaLabel\">\n <ul class=\"fr-pagination__list\">\n <!-- Premi\u00E8re page -->\n <li>\n <edu-page-link\n customClass=\"fr-pagination__link--first\"\n [label]=\"'pagination.firstPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.firstPage' | dsfrI18n\"\n [num]=\"1\"\n [disabled]=\"isFirstPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n <!-- Lien vers 'Page pr\u00E9c\u00E9dente' -->\n @if (showPrevAndNextBtn()) {\n <li>\n <edu-page-link\n [customClass]=\"\n showPrevAndNextLabelLg()\n ? 'fr-pagination__link--prev fr-pagination__link--lg-label'\n : 'fr-pagination__link--prev'\n \"\n [label]=\"'pagination.previousPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.previousPage' | dsfrI18n\"\n [num]=\"prevNum\"\n [disabled]=\"isFirstPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n <!-- Toutes les pages -->\n @for (interval of intervals(); let i = $index; track i) {\n @if (interval[0] !== 1) {\n <li>\n <span class=\"fr-pagination__link fr-hidden fr-unhidden-lg\"> \u2026 </span>\n </li>\n }\n @for (pageNum of pagesFrom(interval); track $index) {\n <li>\n <edu-page-link\n [num]=\"pageNum\"\n [label]=\"pageNum + ''\"\n [tooltipMessage]=\"'Page ' + pageNum\"\n [active]=\"pageNum === currentPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n }\n <!-- Lien vers 'Page suivante' -->\n @if (showPrevAndNextBtn()) {\n <li>\n <edu-page-link\n [customClass]=\"\n showPrevAndNextLabelLg()\n ? 'fr-pagination__link--next fr-pagination__link--lg-label'\n : 'fr-pagination__link--next'\n \"\n [label]=\"'pagination.nextPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.nextPage' | dsfrI18n\"\n [num]=\"nextNum\"\n [disabled]=\"isLastPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n }\n <!-- Derni\u00E8re page (non affich\u00E9e sur mobile) -->\n <li>\n <edu-page-link\n customClass=\"fr-pagination__link--last\"\n [label]=\"'pagination.lastPage' | dsfrI18n\"\n labelCustomClass=\"label-sm\"\n [tooltipMessage]=\"'pagination.lastPage' | dsfrI18n\"\n [num]=\"pageCount()\"\n [disabled]=\"isLastPage()\"\n (pageSelectEvent)=\"onPage($event)\"></edu-page-link>\n </li>\n </ul>\n </nav>\n }\n</ng-template>\n", styles: ["@media(max-width:36em){.fr-pagination__list{justify-content:space-between}}dsfr-pagination .fr-table__detail{margin-bottom:0}\n/*! media sm et xs */\n"] }]
}], propDecorators: { previousOnly: [{
type: Input
}], ariaLabel: [{
type: Input
}], rowsPerPagePlaceholder: [{
type: Input
}], totalElementsTemplate: [{
type: ContentChild,
args: ['totalElementsTemplate', { static: true }]
}], additionalTemplate: [{
type: ContentChild,
args: ['additionalTemplate', { static: true }]
}], pageSelect: [{
type: Output
}], pageSelectEvent: [{
type: Output
}], backSelect: [{
type: Output
}], rowsPerPageChange: [{
type: Output
}], backEvent: [{
type: Output
}], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }], disabledRowsPerPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledRowsPerPage", required: false }] }], pageCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageCount", required: true }] }], totalElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalElements", required: false }] }], displayedNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayedNumbers", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], rowsPerPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPage", required: false }] }], showPrevAndNextBtn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPrevAndNextBtn", required: false }] }], showPrevAndNextLabelLg: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPrevAndNextLabelLg", required: false }] }] } });
/** @deprecated (since 1.6) utiliser `dsfr-pagination` à la place. */
class DsfrPreviousPageComponent {
constructor() {
/* Evènement page précédente. L'événement initial n'est pas propagé. */
this.backEvent = new EventEmitter();
}
onBack() {
this.backEvent.emit();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPreviousPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrPreviousPageComponent, isStandalone: true, selector: "dsfr-previous-page", outputs: { backEvent: "backEvent" }, ngImport: i0, template: "<nav role=\"navigation\" class=\"fr-pagination\" aria-label=\"Pagination\">\n <ul class=\"fr-pagination__list\">\n <edu-page-link\n customClass=\"fr-pagination__link--prev fr-pagination__link--lg-label\"\n label=\"Page pr\u00E9c\u00E9dente\"\n (pageSelectEvent)=\"onBack()\"></edu-page-link>\n </ul>\n</nav>\n", dependencies: [{ kind: "component", type: EduPageLinkComponent, selector: "edu-page-link", inputs: ["active", "customClass", "disabled", "label", "labelCustomClass", "num", "tooltipMessage"], outputs: ["pageSelectEvent"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPreviousPageComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-previous-page', encapsulation: ViewEncapsulation.None, imports: [EduPageLinkComponent], template: "<nav role=\"navigation\" class=\"fr-pagination\" aria-label=\"Pagination\">\n <ul class=\"fr-pagination__list\">\n <edu-page-link\n customClass=\"fr-pagination__link--prev fr-pagination__link--lg-label\"\n label=\"Page pr\u00E9c\u00E9dente\"\n (pageSelectEvent)=\"onBack()\"></edu-page-link>\n </ul>\n</nav>\n" }]
}], propDecorators: { backEvent: [{
type: Output
}] } });
class DsfrPaginationModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationModule, imports: [CommonModule,
RouterModule,
DsfrI18nPipe,
EduPageLinkComponent,
DsfrPreviousPageComponent,
DsfrPaginationComponent], exports: [DsfrPaginationComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationModule, imports: [CommonModule,
RouterModule,
EduPageLinkComponent,
DsfrPreviousPageComponent,
DsfrPaginationComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPaginationModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrPaginationComponent],
imports: [
CommonModule,
RouterModule,
DsfrI18nPipe,
EduPageLinkComponent,
DsfrPreviousPageComponent,
DsfrPaginationComponent,
],
}]
}] });
class DsfrQuoteComponent {
constructor() {
/**
* Texte de la citation.
*/
this.text = '';
/**
* Taille du texte de la citation. XL par défaut si non précisé.
*/
this.textSize = DsfrTextSizeConst.XL;
/**
* URL pour afficher une image d'illustration.
*/
this.imagePath = '';
/**
* Texte alternatif à utiliser uniquement si l'image à une information à passer.
*/
this.imageAlt = '';
/**
* Auteur de la citation.
*/
this.author = '';
/**
* URL de la source de la citation.
*/
this.sourceUrl = '';
/**
* Tableau des détails de la citation, sous la forme : detail=[{text:string, isTitle?:boolean, sourceURL?:string}]
*
*/
this.details = [];
/**
* Permet de personnaliser la couleur du tag. Il faut donner la class exact (ex : fr-quote--green-emeraude) que vous
* trouverez [ici](https://www.systeme-de-design.gouv.fr/version-courante/fr/fondamentaux/couleurs-palette#couleurs-illustratives)
*/
this.customClass = '';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrQuoteComponent, isStandalone: true, selector: "dsfr-quote", inputs: { text: "text", textSize: "textSize", imagePath: "imagePath", imageAlt: "imageAlt", author: "author", sourceUrl: "sourceUrl", details: "details", customClass: "customClass" }, ngImport: i0, template: "<figure class=\"fr-quote\" [class]=\"customClass\" [ngClass]=\"{ 'fr-quote--column': imagePath ? true : false }\">\n <blockquote [attr.cite]=\"sourceUrl && sourceUrl.length ? sourceUrl : null\">\n <p\n [class.fr-text--lg]=\"textSize === 'LG'\"\n [class.fr-text--md]=\"textSize === 'MD'\"\n [class.fr-text--sm]=\"textSize === 'SM'\"\n [class.fr-text--xs]=\"textSize === 'XS'\"\n [innerHTML]=\"text\"></p>\n </blockquote>\n <figcaption>\n @if (author) {\n <p class=\"fr-quote__author\" [innerHTML]=\"author\"></p>\n }\n <ng-container *ngTemplateOutlet=\"detailTemplate\"></ng-container>\n\n @if (imagePath) {\n <div class=\"fr-quote__image\">\n <img [src]=\"imagePath\" class=\"fr-responsive-img\" [attr.alt]=\"imageAlt\" />\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit rester vide car l\u2019image est illustrative et ne doit pas \u00EAtre restitu\u00E9e aux technologies d\u2019assistance -->\n </div>\n }\n </figcaption>\n</figure>\n\n<ng-template #detailTemplate>\n @if (details.length && details.length > 1) {\n <ul class=\"fr-quote__source\">\n @for (detail of details; track $index) {\n <li>\n @if (!detail.isTitle && !detail.sourceUrl) {\n <!--fixme supprimer innerHTML -->\n <span [innerHTML]=\"detail.text\"></span>\n } @else if (detail.isTitle) {\n <cite>{{ detail.text }}</cite>\n } @else if (detail.sourceUrl && !detail.isTitle) {\n <a\n target=\"_blank\"\n rel=\"noopener external\"\n [title]=\"detail.text + ' - ' + ('link.newWindow' | dsfrI18n)\"\n [href]=\"detail.sourceUrl\"\n >{{ detail.text }}</a\n >\n }\n </li>\n }\n </ul>\n } @else if (details.length && details.length === 1) {\n <div class=\"fr-quote__source\">\n @if (!details[0].isTitle && !details[0].sourceUrl) {\n <span *ngIf=\"!details[0].isTitle && !details[0].sourceUrl\" [innerHTML]=\"details[0].text\"></span>\n } @else if (details[0].isTitle) {\n <cite>{{ details[0].text }}</cite>\n } @else if (details[0].sourceUrl && !details[0].isTitle) {\n <a target=\"_blank\" rel=\"noreferrer\" [href]=\"details[0].sourceUrl\">{{ details[0].text }}</a>\n }\n </div>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-quote', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrI18nPipe], template: "<figure class=\"fr-quote\" [class]=\"customClass\" [ngClass]=\"{ 'fr-quote--column': imagePath ? true : false }\">\n <blockquote [attr.cite]=\"sourceUrl && sourceUrl.length ? sourceUrl : null\">\n <p\n [class.fr-text--lg]=\"textSize === 'LG'\"\n [class.fr-text--md]=\"textSize === 'MD'\"\n [class.fr-text--sm]=\"textSize === 'SM'\"\n [class.fr-text--xs]=\"textSize === 'XS'\"\n [innerHTML]=\"text\"></p>\n </blockquote>\n <figcaption>\n @if (author) {\n <p class=\"fr-quote__author\" [innerHTML]=\"author\"></p>\n }\n <ng-container *ngTemplateOutlet=\"detailTemplate\"></ng-container>\n\n @if (imagePath) {\n <div class=\"fr-quote__image\">\n <img [src]=\"imagePath\" class=\"fr-responsive-img\" [attr.alt]=\"imageAlt\" />\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit rester vide car l\u2019image est illustrative et ne doit pas \u00EAtre restitu\u00E9e aux technologies d\u2019assistance -->\n </div>\n }\n </figcaption>\n</figure>\n\n<ng-template #detailTemplate>\n @if (details.length && details.length > 1) {\n <ul class=\"fr-quote__source\">\n @for (detail of details; track $index) {\n <li>\n @if (!detail.isTitle && !detail.sourceUrl) {\n <!--fixme supprimer innerHTML -->\n <span [innerHTML]=\"detail.text\"></span>\n } @else if (detail.isTitle) {\n <cite>{{ detail.text }}</cite>\n } @else if (detail.sourceUrl && !detail.isTitle) {\n <a\n target=\"_blank\"\n rel=\"noopener external\"\n [title]=\"detail.text + ' - ' + ('link.newWindow' | dsfrI18n)\"\n [href]=\"detail.sourceUrl\"\n >{{ detail.text }}</a\n >\n }\n </li>\n }\n </ul>\n } @else if (details.length && details.length === 1) {\n <div class=\"fr-quote__source\">\n @if (!details[0].isTitle && !details[0].sourceUrl) {\n <span *ngIf=\"!details[0].isTitle && !details[0].sourceUrl\" [innerHTML]=\"details[0].text\"></span>\n } @else if (details[0].isTitle) {\n <cite>{{ details[0].text }}</cite>\n } @else if (details[0].sourceUrl && !details[0].isTitle) {\n <a target=\"_blank\" rel=\"noreferrer\" [href]=\"details[0].sourceUrl\">{{ details[0].text }}</a>\n }\n </div>\n }\n</ng-template>\n" }]
}], propDecorators: { text: [{
type: Input
}], textSize: [{
type: Input
}], imagePath: [{
type: Input
}], imageAlt: [{
type: Input
}], author: [{
type: Input
}], sourceUrl: [{
type: Input
}], details: [{
type: Input
}], customClass: [{
type: Input
}] } });
class DsfrQuoteModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteModule, imports: [CommonModule, DsfrI18nPipe, DsfrQuoteComponent], exports: [DsfrQuoteComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteModule, imports: [CommonModule, DsfrQuoteComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrQuoteModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrQuoteComponent],
imports: [CommonModule, DsfrI18nPipe, DsfrQuoteComponent],
}]
}] });
class DsfrSegmentedControlComponent {
constructor() {
/**
* Cache la légende visuellement.
*/
this.legendSrOnly = false;
/**
* Taille du contrôle segmenté, existe en `SM` et `MD` uniquement. `MD` par défaut.
*/
this.segmentedControlSize = DsfrSizeConst.MD;
/**
* Contrôle segmenté avec légende en ligne
*/
this.inline = false;
/**
* Renvoie la value du choix selectionné.
*/
this.segmentedControlSelect = new EventEmitter();
}
/**
* Taille du contrôle segmenté, existe en `SM` et `MD` uniquement. `MD` par défaut.
*
* @deprecated since(1.15.0) utiliser 'segmentedControlSize' à la place
*/
get size() {
return this.segmentedControlSize;
}
get name() {
return this._name;
}
/**
* Taille du contrôle segmenté, existe en `SM` et `MD` uniquement. `MD` par défaut.
*
* @deprecated since(1.15.0) utiliser 'segmentedControlSize' à la place
*/
set size(value) {
this.segmentedControlSize = value;
}
/**
* Valeur de l'attribut name des inputs du contrôle segmenté.
*/
set name(value) {
if (!value.length) {
this._name = 'segmented-' + newUniqueId();
}
else {
this._name = value;
}
}
/** @internal */
getClasses() {
return {
'fr-segmented': true,
'fr-segmented--sm': this.segmentedControlSize === DsfrSizeConst.SM,
'fr-segmented--no-legend': this.legendSrOnly,
};
}
/** @internal */
getSegmentedControlId(index) {
return `${this._name}_${index + 1}`;
}
/**
* @internal
*/
onSegmentSelect(value) {
this.segmentedControlSelect.emit(value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrSegmentedControlComponent, isStandalone: true, selector: "dsfr-segmented-control", inputs: { segments: "segments", legend: "legend", legendSrOnly: "legendSrOnly", hint: "hint", segmentedControlSize: "segmentedControlSize", inline: "inline", size: "size", name: "name" }, outputs: { segmentedControlSelect: "segmentedControlSelect" }, ngImport: i0, template: "<fieldset [class]=\"getClasses()\">\n @if (legend) {\n <legend class=\"fr-segmented__legend\" [class.fr-segmented__legend--inline]=\"inline && !hint\">\n {{ legend }}\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </legend>\n }\n <div class=\"fr-segmented__elements\">\n @for (segment of segments; track $index; let i = $index) {\n <div class=\"fr-segmented__element\">\n <input\n [value]=\"segment.value\"\n type=\"radio\"\n [attr.checked]=\"segment.checked ? segment.checked : null\"\n [disabled]=\"segment.disabled\"\n [attr.id]=\"getSegmentedControlId(i)\"\n [name]=\"name\"\n (click)=\"onSegmentSelect(segment.value)\" />\n <label class=\"fr-label\" [ngClass]=\"segment.icon\" [for]=\"getSegmentedControlId(i)\">{{ segment.label }}</label>\n </div>\n }\n </div>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-segmented-control', encapsulation: ViewEncapsulation.None, imports: [CommonModule, EduHintComponent], template: "<fieldset [class]=\"getClasses()\">\n @if (legend) {\n <legend class=\"fr-segmented__legend\" [class.fr-segmented__legend--inline]=\"inline && !hint\">\n {{ legend }}\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </legend>\n }\n <div class=\"fr-segmented__elements\">\n @for (segment of segments; track $index; let i = $index) {\n <div class=\"fr-segmented__element\">\n <input\n [value]=\"segment.value\"\n type=\"radio\"\n [attr.checked]=\"segment.checked ? segment.checked : null\"\n [disabled]=\"segment.disabled\"\n [attr.id]=\"getSegmentedControlId(i)\"\n [name]=\"name\"\n (click)=\"onSegmentSelect(segment.value)\" />\n <label class=\"fr-label\" [ngClass]=\"segment.icon\" [for]=\"getSegmentedControlId(i)\">{{ segment.label }}</label>\n </div>\n }\n </div>\n</fieldset>\n" }]
}], propDecorators: { segments: [{
type: Input,
args: [{ required: true }]
}], legend: [{
type: Input,
args: [{ required: true }]
}], legendSrOnly: [{
type: Input
}], hint: [{
type: Input
}], segmentedControlSize: [{
type: Input
}], inline: [{
type: Input
}], segmentedControlSelect: [{
type: Output
}], size: [{
type: Input
}], name: [{
type: Input
}] } });
class DsfrSegmentedControlModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlModule, imports: [CommonModule, DsfrSegmentedControlComponent], exports: [DsfrSegmentedControlComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlModule, imports: [CommonModule, DsfrSegmentedControlComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSegmentedControlModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrSegmentedControlComponent],
imports: [CommonModule, DsfrSegmentedControlComponent],
}]
}] });
/**
* Définit les réseaux sociaux suportés par le composant `Share`.
*/
var DsfrShareNameConst;
(function (DsfrShareNameConst) {
/**
* Facebook.
*/
DsfrShareNameConst["FACEBOOK"] = "facebook";
/**
* Linkedin.
*/
DsfrShareNameConst["LINKEDIN"] = "linkedin";
/**
* Mail
*/
DsfrShareNameConst["MAIL"] = "mail";
/**
* Mastodon
*/
DsfrShareNameConst["MASTODON"] = "mastodon";
/**
* Twitter
*/
DsfrShareNameConst["TWITTER"] = "twitter";
/**
* X (ex-Twitter)
*/
DsfrShareNameConst["X"] = "twitter-x";
})(DsfrShareNameConst || (DsfrShareNameConst = {}));
class DsfrShareLinkComponent {
constructor() {
/** Indique si le composant est disable ou non (le mail est toujours enable). */
this.disabled = false;
}
/** @deprecated (since 1.8.0) use linkTarget instead. */
get target() {
return this.linkTarget;
}
get name() {
return this._name;
}
/** @deprecated (since 1.8.0) use linkTarget instead. */
set target(value) {
this.linkTarget = value;
}
/** La propriété 'name' est obligatoire et doit être sélectionnée dans une liste énumérée. */
set name(value) {
if (value === DsfrShareNameConst.TWITTER) {
this._name = DsfrShareNameConst.X;
}
else {
this._name = value;
}
}
/** @internal */
getClasses() {
return `fr-btn--${this.name} fr-btn`;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.23", type: DsfrShareLinkComponent, isStandalone: true, selector: "dsfr-share-link", inputs: { disabled: ["disabled", "disabled", booleanAttribute], id: "id", linkId: "linkId", label: "label", link: "link", rel: "rel", linkTarget: "linkTarget", target: "target", name: "name" }, ngImport: i0, template: "<li>\n <a\n [class]=\"getClasses()\"\n [title]=\"'share.' + name + '.title' | dsfrI18n\"\n [attr.id]=\"linkId || id || null\"\n [attr.href]=\"!disabled ? link : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.rel]=\"rel || null\">\n {{ 'share.' + name + '.label' | dsfrI18n }}\n </a>\n</li>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-share-link', imports: [CommonModule, RouterModule, DsfrI18nPipe], template: "<li>\n <a\n [class]=\"getClasses()\"\n [title]=\"'share.' + name + '.title' | dsfrI18n\"\n [attr.id]=\"linkId || id || null\"\n [attr.href]=\"!disabled ? link : null\"\n [attr.target]=\"linkTarget || null\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.role]=\"disabled ? 'link' : null\"\n [attr.rel]=\"rel || null\">\n {{ 'share.' + name + '.label' | dsfrI18n }}\n </a>\n</li>\n" }]
}], propDecorators: { disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], id: [{
type: Input
}], linkId: [{
type: Input
}], label: [{
type: Input
}], link: [{
type: Input
}], rel: [{
type: Input
}], linkTarget: [{
type: Input
}], target: [{
type: Input
}], name: [{
type: Input
}] } });
class DsfrShareComponent {
/** @internal */
constructor() {
/** Si route, événement avec la route en paramètre. */
this.consentManagerSelect = new EventEmitter();
/** Permet d'indiquer si l'usage des cookies a été autorisé par l'utilisateur final. */
this.hasCookiePermissions = input(true, ...(ngDevMode ? [{ debugName: "hasCookiePermissions", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
this.cookiesText = 'share.cookiesText';
afterRenderEffect(() => {
this.setDisabled(!this.hasCookiePermissions());
});
}
/** @internal */
onConsentLinkSelect() {
if (this.consentManagerRoute) {
this.consentManagerSelect.emit(this.consentManagerRoute);
}
}
/** @internal */
onCopy(event) {
event.preventDefault();
navigator.clipboard.writeText(window.location.href).then(() => {
this.message = 'Le lien a été copié dans le presse-papier.';
setTimeout(() => (this.message = ''), 3000);
});
}
setDisabled(disabled) {
this.shareLinks?.forEach((shareLink) => {
if (shareLink.name != DsfrShareNameConst.MAIL)
shareLink.disabled = disabled;
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrShareComponent, isStandalone: true, selector: "dsfr-share", inputs: { consentManagerLink: { classPropertyName: "consentManagerLink", publicName: "consentManagerLink", isSignal: false, isRequired: false, transformFunction: null }, consentManagerLinkTarget: { classPropertyName: "consentManagerLinkTarget", publicName: "consentManagerLinkTarget", isSignal: false, isRequired: false, transformFunction: null }, consentManagerRoute: { classPropertyName: "consentManagerRoute", publicName: "consentManagerRoute", isSignal: false, isRequired: false, transformFunction: null }, hasCookiePermissions: { classPropertyName: "hasCookiePermissions", publicName: "hasCookiePermissions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { consentManagerSelect: "consentManagerSelect" }, queries: [{ propertyName: "shareLinks", predicate: DsfrShareLinkComponent }], ngImport: i0, template: "<div class=\"fr-share\">\n <p class=\"fr-share__title\">{{ 'share.heading' | dsfrI18n }}</p>\n @if (!hasCookiePermissions()) {\n <p class=\"fr-share__text\">\n {{ 'share.cookiesText.prelink' | dsfrI18n }}\n <dsfr-link\n customClass=\"fr-share__text\"\n [link]=\"consentManagerLink\"\n [linkTarget]=\"consentManagerLinkTarget\"\n [route]=\"consentManagerRoute\"\n (linkSelect)=\"onConsentLinkSelect()\"\n >{{ 'share.cookiesText.link' | dsfrI18n }}</dsfr-link\n >\n {{ 'share.cookiesText.postlink' | dsfrI18n }}\n </p>\n }\n @if (message) {\n <p class=\"fr-share__text\">{{ message }}</p>\n }\n <ul class=\"fr-btns-group\">\n <ng-content></ng-content>\n <li>\n <button\n type=\"button\"\n class=\"fr-share__link fr-share__link--copy\"\n [title]=\"'share.copy.title' | dsfrI18n\"\n (click)=\"onCopy($event)\">\n {{ 'share.copy.label' | dsfrI18n }}\n </button>\n </li>\n </ul>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-share', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule, DsfrLinkComponent, DsfrI18nPipe], template: "<div class=\"fr-share\">\n <p class=\"fr-share__title\">{{ 'share.heading' | dsfrI18n }}</p>\n @if (!hasCookiePermissions()) {\n <p class=\"fr-share__text\">\n {{ 'share.cookiesText.prelink' | dsfrI18n }}\n <dsfr-link\n customClass=\"fr-share__text\"\n [link]=\"consentManagerLink\"\n [linkTarget]=\"consentManagerLinkTarget\"\n [route]=\"consentManagerRoute\"\n (linkSelect)=\"onConsentLinkSelect()\"\n >{{ 'share.cookiesText.link' | dsfrI18n }}</dsfr-link\n >\n {{ 'share.cookiesText.postlink' | dsfrI18n }}\n </p>\n }\n @if (message) {\n <p class=\"fr-share__text\">{{ message }}</p>\n }\n <ul class=\"fr-btns-group\">\n <ng-content></ng-content>\n <li>\n <button\n type=\"button\"\n class=\"fr-share__link fr-share__link--copy\"\n [title]=\"'share.copy.title' | dsfrI18n\"\n (click)=\"onCopy($event)\">\n {{ 'share.copy.label' | dsfrI18n }}\n </button>\n </li>\n </ul>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { shareLinks: [{
type: ContentChildren,
args: [DsfrShareLinkComponent]
}], consentManagerLink: [{
type: Input
}], consentManagerLinkTarget: [{
type: Input
}], consentManagerRoute: [{
type: Input
}], consentManagerSelect: [{
type: Output
}], hasCookiePermissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasCookiePermissions", required: false }] }] } });
class DsfrShareModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareModule, imports: [CommonModule, RouterModule, DsfrLinkComponent, DsfrI18nPipe, DsfrShareLinkComponent, DsfrShareComponent], exports: [DsfrShareComponent, DsfrShareLinkComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareModule, imports: [CommonModule, RouterModule, DsfrLinkComponent, DsfrShareLinkComponent, DsfrShareComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrShareModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrShareComponent, DsfrShareLinkComponent],
imports: [CommonModule, RouterModule, DsfrLinkComponent, DsfrI18nPipe, DsfrShareLinkComponent, DsfrShareComponent],
}]
}] });
class DsfrSidemenuComponent {
constructor() {
/** Attribut aria-label du menu latéral. */
this.ariaLabel = 'Menu latéral';
/** Affiche le menu latéral en version fixe */
this.sticky = false;
/** Affiche le menu latéral en version fixe sur 100% de la hauteur de la page. */
this.stickyFullHeight = false;
/** Le menu latéral est affiché à gauche par défaut mais il est possible de l'afficher à droite. */
this.position = DsfrPositionConst.LEFT;
/** Activer la selection automatique des items et parents (si absence de routerLink) */
this.autoActive = true;
/** Evénement émis suite à la sélection d'un item avec lien */
this.itemSelect = new EventEmitter();
/** Ne pas afficher d'accordéons pour les éléments de menu */
this.noAccordion = input(false, ...(ngDevMode ? [{ debugName: "noAccordion" }] : []));
/** Modèle de donnée du menu. */
this.menu = input.required(...(ngDevMode ? [{ debugName: "menu" }] : []));
this.menuViewModel = computed(() => {
const menu = this.menu();
if (!menu.items)
return [];
return menu.items.map((item) => this.cloneAndFilterVisibles(item)).filter(Boolean);
}, ...(ngDevMode ? [{ debugName: "menuViewModel" }] : []));
this._btnMenuMobile = viewChild('btnMenuMobile', ...(ngDevMode ? [{ debugName: "_btnMenuMobile" }] : []));
effect(() => {
const menu = this.menuViewModel();
// recalculer l'élément actif en cas de modification de menu
if (this.autoActive && this._initialActiveItem) {
this.selectItem(this._initialActiveItem);
}
});
}
onSelectItem(item) {
if (this.autoActive) {
this.selectItem(item);
}
this.itemSelect.emit(item);
this.closeMenuOnMobile();
}
/**
* Fermer le menu en mode mobile après une navigation
*/
closeMenuOnMobile() {
const element = this._btnMenuMobile();
if (element) {
element.nativeElement.setAttribute('aria-expanded', 'false');
}
}
/** Dupliquer les items de menu et filtres les éléments visibles pour ne pas modifier le modèle en entrée */
cloneAndFilterVisibles(item) {
if (!this.isItemVisible(item)) {
return undefined;
}
let children;
if (item.subItems?.length) {
children = item.subItems.map((sub) => this.cloneAndFilterVisibles(sub)).filter(Boolean);
if (children.length === 0) {
return undefined; // aucun enfant visible
}
}
const clonedItem = {
...item,
active: this.autoActive ? !!(item.active && !item.subItems) : item.active,
controlId: item.controlId ?? newUniqueId(),
subItems: children,
};
if (clonedItem.active && !clonedItem.subItems) {
this._initialActiveItem = clonedItem;
}
return clonedItem;
}
/** A la selection d'un item si autoActive :
* - on passe le précédent item sélectionné _prevItemSelected en désactivé ainsi que ses parents (trois niveaux potentiels)
* - nouvel item et ses parents en actif
* - mise a jour de _prevItemSelected
*/
selectItem(item) {
if (this._prevItemSelected) {
this.activateItem(this._prevItemSelected, false);
}
this.activateItem(item, true);
this._prevItemSelected = item;
}
/** Désactiver ou activer un item et ses noeuds parents */
activateItem(item, activate) {
item.active = activate;
const parent = this.findParent(item, this.menuViewModel());
if (parent) {
this.activateItem(parent, activate);
}
}
/**
* Parcours récursif du menu pour retrouver le parent direct d'un item
*/
findParent(item, items) {
for (const parent of items) {
if (parent.subItems?.includes(item)) {
return parent;
}
const foundParent = this.findParent(item, parent.subItems || []);
if (foundParent) {
return foundParent;
}
}
return undefined;
}
isItemVisible(item) {
const visible = item.isVisible?.();
if (visible !== undefined)
return visible;
if (this.canDisplay) {
return this.canDisplay(item);
}
return true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrSidemenuComponent, isStandalone: true, selector: "dsfr-sidemenu", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: false, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: false, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: false, isRequired: false, transformFunction: null }, stickyFullHeight: { classPropertyName: "stickyFullHeight", publicName: "stickyFullHeight", isSignal: false, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: false, isRequired: false, transformFunction: null }, canDisplay: { classPropertyName: "canDisplay", publicName: "canDisplay", isSignal: false, isRequired: false, transformFunction: null }, autoActive: { classPropertyName: "autoActive", publicName: "autoActive", isSignal: false, isRequired: false, transformFunction: null }, noAccordion: { classPropertyName: "noAccordion", publicName: "noAccordion", isSignal: true, isRequired: false, transformFunction: null }, menu: { classPropertyName: "menu", publicName: "menu", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { itemSelect: "itemSelect" }, queries: [{ propertyName: "labelItemTemplate", first: true, predicate: ["labelItemTemplate"], descendants: true, static: true }], viewQueries: [{ propertyName: "_btnMenuMobile", first: true, predicate: ["btnMenuMobile"], descendants: true, isSignal: true }], ngImport: i0, template: "<nav\n [class]=\"{\n 'fr-sidemenu': true,\n 'fr-sidemenu--sticky': sticky,\n 'fr-sidemenu--sticky-full-height': stickyFullHeight,\n 'fr-sidemenu--right': position === 'right',\n }\"\n role=\"navigation\"\n [attr.aria-labelledBy]=\"menu().title ? 'sidemenu-title-' + controlId : null\"\n [attr.aria-label]=\"menu().title ? null : ariaLabel\">\n <div class=\"fr-sidemenu__inner\" [class.fr-sidemenu__inner--no-accordion]=\"noAccordion()\">\n <button\n #btnMenuMobile\n type=\"button\"\n class=\"fr-sidemenu__btn\"\n [attr.aria-controls]=\"'sidemenu-' + controlId\"\n aria-expanded=\"false\">\n {{ 'sidemenu.menuMobile' | dsfrI18n }}\n </button>\n <div [class]=\"'fr-collapse'\" [id]=\"'sidemenu-' + controlId\">\n <!--Titre de rubrique optionnel-->\n @if (menu() && menu().title) {\n <p class=\"fr-sidemenu__title\" [id]=\"'sidemenu-title-' + controlId\">{{ menu().title }}</p>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item1 of menuViewModel(); track item1) {\n <!--Niveau 1-->\n @if (!item1.subItems) {\n <li class=\"fr-sidemenu__item\">\n <ng-container [ngTemplateOutlet]=\"linkMenu\" [ngTemplateOutletContext]=\"{ item: item1 }\"></ng-container>\n </li>\n } @else {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"noAccordion() ? linkMenu : buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item1 }\"></ng-container>\n <div [class.fr-collapse]=\"!noAccordion()\" [id]=\"item1.controlId\">\n <!--Titre de rubrique optionnel-->\n @if (item1.title) {\n <div class=\"fr-sidemenu__title\">{{ item1.title }}</div>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item2 of item1.subItems; track item2) {\n <!--Niveau 2-->\n\n @if (!item2.subItems) {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"linkMenu\"\n [ngTemplateOutletContext]=\"{ item: item2 }\"></ng-container>\n </li>\n } @else {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"noAccordion() ? linkMenu : buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item2 }\"></ng-container>\n\n <div [class.fr-collapse]=\"!noAccordion()\" [id]=\"item2.controlId\">\n @if (item2.title) {\n <div class=\"fr-sidemenu__title\">{{ item2.title }}</div>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item3 of item2.subItems; track item3) {\n <!--Niveau 3-->\n <li class=\"fr-sidemenu__item\" [attr.aria-current]=\"item3.active ? 'page' : null\">\n <ng-container\n [ngTemplateOutlet]=\"linkMenu\"\n [ngTemplateOutletContext]=\"{ item: item3 }\"></ng-container>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n }\n </ul>\n </div>\n </li>\n }\n }\n </ul>\n </div>\n </div>\n</nav>\n\n<ng-template #buttonMenu let-item=\"item\" let-index=\"index\">\n <button\n #rla=\"routerLinkActive\"\n type=\"button\"\n class=\"fr-sidemenu__btn\"\n [attr.aria-expanded]=\"noAccordion() ? null : item.routerLink ? rla.isActive : item.active\"\n [attr.aria-controls]=\"item.controlId\"\n [disableNavigation]=\"true\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [ariaCurrentWhenActive]=\"true\">\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n <i *ngIf=\"item.icon\" class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n {{ item.label }}\n }\n </button>\n</ng-template>\n\n<ng-template #linkMenu let-item=\"item\" let-index=\"index\">\n @if (!item.route && !item.routerLink && !item.link) {\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n <span [attr.aria-current]=\"item.active ? true : null\" class=\"fr-sidemenu__link\">\n @if (item.iconPosition === 'left') {\n <i class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n {{ item.label }}\n @if (item.iconPosition === 'right') {\n <i class=\"edu-item-icon edu-item-icon--right\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n </span>\n }\n } @else {\n <dsfr-link\n [ariaControls]=\"item.ariaControls\"\n [ariaCurrent]=\"item.active && !item.routerLink ? (item.subItems ? 'true' : 'page') : undefined\"\n [ariaLabel]=\"item.ariaLabel ?? ''\"\n [customClass]=\"'fr-sidemenu__link ' + (item.customClass ?? '')\"\n [disabled]=\"item.disabled\"\n [link]=\"item.link\"\n [linkId]=\"item.linkId\"\n [linkTarget]=\"item.linkTarget\"\n [label]=\"item.icon || labelItemTemplate ? null : item.label\"\n [route]=\"item.route\"\n [routePath]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [routerLinkExtras]=\"item.routerLinkExtras\"\n [tooltipMessage]=\"item.tooltipMessage\"\n (linkSelect)=\"onSelectItem(item)\">\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n @if (item.iconPosition === 'left') {\n <i class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n @if (item.icon) {\n <span label> {{ item.label }}</span>\n }\n @if (item.iconPosition === 'right') {\n <i class=\"edu-item-icon edu-item-icon--right\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n }\n </dsfr-link>\n }\n</ng-template>\n", styles: ["dsfr-sidemenu .edu-item-icon{pointer-events:none}dsfr-sidemenu .edu-item-icon:before{--icon-size: 1rem;margin-right:.5rem}dsfr-sidemenu .edu-item-icon--right:before{margin-left:.5rem;margin-right:0}dsfr-sidemenu .fr-sidemenu__inner--no-accordion .fr-sidemenu__list .fr-sidemenu__list{padding:0 1rem 1rem;margin:0}dsfr-sidemenu .fr-sidemenu__inner--no-accordion .fr-sidemenu__list .fr-sidemenu__list:first-child:last-child:before{content:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: DsfrDisableRouterLinkDirective, selector: "button[routerLink][disableNavigation]", inputs: ["disableNavigation"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-sidemenu', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterModule, DsfrDisableRouterLinkDirective, DsfrLinkComponent, DsfrI18nPipe], template: "<nav\n [class]=\"{\n 'fr-sidemenu': true,\n 'fr-sidemenu--sticky': sticky,\n 'fr-sidemenu--sticky-full-height': stickyFullHeight,\n 'fr-sidemenu--right': position === 'right',\n }\"\n role=\"navigation\"\n [attr.aria-labelledBy]=\"menu().title ? 'sidemenu-title-' + controlId : null\"\n [attr.aria-label]=\"menu().title ? null : ariaLabel\">\n <div class=\"fr-sidemenu__inner\" [class.fr-sidemenu__inner--no-accordion]=\"noAccordion()\">\n <button\n #btnMenuMobile\n type=\"button\"\n class=\"fr-sidemenu__btn\"\n [attr.aria-controls]=\"'sidemenu-' + controlId\"\n aria-expanded=\"false\">\n {{ 'sidemenu.menuMobile' | dsfrI18n }}\n </button>\n <div [class]=\"'fr-collapse'\" [id]=\"'sidemenu-' + controlId\">\n <!--Titre de rubrique optionnel-->\n @if (menu() && menu().title) {\n <p class=\"fr-sidemenu__title\" [id]=\"'sidemenu-title-' + controlId\">{{ menu().title }}</p>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item1 of menuViewModel(); track item1) {\n <!--Niveau 1-->\n @if (!item1.subItems) {\n <li class=\"fr-sidemenu__item\">\n <ng-container [ngTemplateOutlet]=\"linkMenu\" [ngTemplateOutletContext]=\"{ item: item1 }\"></ng-container>\n </li>\n } @else {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"noAccordion() ? linkMenu : buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item1 }\"></ng-container>\n <div [class.fr-collapse]=\"!noAccordion()\" [id]=\"item1.controlId\">\n <!--Titre de rubrique optionnel-->\n @if (item1.title) {\n <div class=\"fr-sidemenu__title\">{{ item1.title }}</div>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item2 of item1.subItems; track item2) {\n <!--Niveau 2-->\n\n @if (!item2.subItems) {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"linkMenu\"\n [ngTemplateOutletContext]=\"{ item: item2 }\"></ng-container>\n </li>\n } @else {\n <li class=\"fr-sidemenu__item\">\n <ng-container\n [ngTemplateOutlet]=\"noAccordion() ? linkMenu : buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item2 }\"></ng-container>\n\n <div [class.fr-collapse]=\"!noAccordion()\" [id]=\"item2.controlId\">\n @if (item2.title) {\n <div class=\"fr-sidemenu__title\">{{ item2.title }}</div>\n }\n <ul class=\"fr-sidemenu__list\">\n @for (item3 of item2.subItems; track item3) {\n <!--Niveau 3-->\n <li class=\"fr-sidemenu__item\" [attr.aria-current]=\"item3.active ? 'page' : null\">\n <ng-container\n [ngTemplateOutlet]=\"linkMenu\"\n [ngTemplateOutletContext]=\"{ item: item3 }\"></ng-container>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n }\n </ul>\n </div>\n </li>\n }\n }\n </ul>\n </div>\n </div>\n</nav>\n\n<ng-template #buttonMenu let-item=\"item\" let-index=\"index\">\n <button\n #rla=\"routerLinkActive\"\n type=\"button\"\n class=\"fr-sidemenu__btn\"\n [attr.aria-expanded]=\"noAccordion() ? null : item.routerLink ? rla.isActive : item.active\"\n [attr.aria-controls]=\"item.controlId\"\n [disableNavigation]=\"true\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [ariaCurrentWhenActive]=\"true\">\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n <i *ngIf=\"item.icon\" class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n {{ item.label }}\n }\n </button>\n</ng-template>\n\n<ng-template #linkMenu let-item=\"item\" let-index=\"index\">\n @if (!item.route && !item.routerLink && !item.link) {\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n <span [attr.aria-current]=\"item.active ? true : null\" class=\"fr-sidemenu__link\">\n @if (item.iconPosition === 'left') {\n <i class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n {{ item.label }}\n @if (item.iconPosition === 'right') {\n <i class=\"edu-item-icon edu-item-icon--right\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n </span>\n }\n } @else {\n <dsfr-link\n [ariaControls]=\"item.ariaControls\"\n [ariaCurrent]=\"item.active && !item.routerLink ? (item.subItems ? 'true' : 'page') : undefined\"\n [ariaLabel]=\"item.ariaLabel ?? ''\"\n [customClass]=\"'fr-sidemenu__link ' + (item.customClass ?? '')\"\n [disabled]=\"item.disabled\"\n [link]=\"item.link\"\n [linkId]=\"item.linkId\"\n [linkTarget]=\"item.linkTarget\"\n [label]=\"item.icon || labelItemTemplate ? null : item.label\"\n [route]=\"item.route\"\n [routePath]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [routerLinkExtras]=\"item.routerLinkExtras\"\n [tooltipMessage]=\"item.tooltipMessage\"\n (linkSelect)=\"onSelectItem(item)\">\n @if (labelItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"labelItemTemplate\" [ngTemplateOutletContext]=\"{ item: item }\"></ng-container>\n } @else {\n @if (item.iconPosition === 'left') {\n <i class=\"edu-item-icon\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n @if (item.icon) {\n <span label> {{ item.label }}</span>\n }\n @if (item.iconPosition === 'right') {\n <i class=\"edu-item-icon edu-item-icon--right\" [class]=\"item.icon\" aria-hidden=\"true\"></i>\n }\n }\n </dsfr-link>\n }\n</ng-template>\n", styles: ["dsfr-sidemenu .edu-item-icon{pointer-events:none}dsfr-sidemenu .edu-item-icon:before{--icon-size: 1rem;margin-right:.5rem}dsfr-sidemenu .edu-item-icon--right:before{margin-left:.5rem;margin-right:0}dsfr-sidemenu .fr-sidemenu__inner--no-accordion .fr-sidemenu__list .fr-sidemenu__list{padding:0 1rem 1rem;margin:0}dsfr-sidemenu .fr-sidemenu__inner--no-accordion .fr-sidemenu__list .fr-sidemenu__list:first-child:last-child:before{content:none}\n"] }]
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{
type: Input
}], controlId: [{
type: Input
}], sticky: [{
type: Input
}], stickyFullHeight: [{
type: Input
}], position: [{
type: Input
}], canDisplay: [{
type: Input
}], autoActive: [{
type: Input
}], itemSelect: [{
type: Output
}], labelItemTemplate: [{
type: ContentChild,
args: ['labelItemTemplate', { static: true }]
}], noAccordion: [{ type: i0.Input, args: [{ isSignal: true, alias: "noAccordion", required: false }] }], menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "menu", required: true }] }], _btnMenuMobile: [{ type: i0.ViewChild, args: ['btnMenuMobile', { isSignal: true }] }] } });
class DsfrSidemenuModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuModule, imports: [CommonModule, RouterModule, DsfrDisableRouterLinkDirective, DsfrLinkComponent, DsfrSidemenuComponent], exports: [DsfrSidemenuComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuModule, imports: [CommonModule, RouterModule, DsfrLinkComponent, DsfrSidemenuComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSidemenuModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrSidemenuComponent],
imports: [CommonModule, RouterModule, DsfrDisableRouterLinkDirective, DsfrLinkComponent, DsfrSidemenuComponent],
}]
}] });
class DsfrSkipLinksComponent {
constructor() {
/**
* Permet la gestion programmatique d'une navigation initiée au click sur le lien si l'input 'route' est valorisé.
* La valeur de la propriété 'route' sera transmise.
*/
this.linkSelect = new EventEmitter();
/** Identifiant du container skipLinks, généré par défaut */
this.skipLinksId = input(newUniqueId(), ...(ngDevMode ? [{ debugName: "skipLinksId" }] : []));
}
/**
* Dans le cas d'une route, un événement `(routeSelect)` est émis avec la valeur de la route et
* l'événement initial n'est pas propagé.
*
* @internal
*/
onSkiplinkSelect(event, route) {
if (route) {
event.preventDefault();
this.linkSelect.emit(route);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkipLinksComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrSkipLinksComponent, isStandalone: true, selector: "dsfr-skiplinks", inputs: { links: { classPropertyName: "links", publicName: "links", isSignal: false, isRequired: true, transformFunction: null }, skipLinksId: { classPropertyName: "skipLinksId", publicName: "skipLinksId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkSelect: "linkSelect" }, ngImport: i0, template: "<div class=\"fr-skiplinks\">\n <nav [id]=\"skipLinksId()\" class=\"fr-container\" role=\"navigation\" aria-label=\"Acc\u00E8s rapide\">\n @if (links.length === 1) {\n @if (!links[0].fragment) {\n <a\n class=\"fr-link\"\n [href]=\"links[0].route || links[0].link\"\n (click)=\"onSkiplinkSelect($event, links[0].route)\"\n >{{ links[0].label }}</a\n >\n } @else {\n <a class=\"fr-link\" [routerLink]=\"'.'\" [fragment]=\"links[0].fragment\"> {{ links[0].label }}</a>\n }\n } @else {\n <ul class=\"fr-skiplinks__list\">\n @for (item of links; track item) {\n <li>\n @if (!item.fragment) {\n <a class=\"fr-link\" [href]=\"item.route || item.link\" (click)=\"onSkiplinkSelect($event, item.route)\">{{\n item.label\n }}</a>\n } @else {\n <a class=\"fr-link\" [routerLink]=\"'.'\" [fragment]=\"item.fragment\"> {{ item.label }}</a>\n }\n </li>\n }\n </ul>\n }\n </nav>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkipLinksComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-skiplinks', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "<div class=\"fr-skiplinks\">\n <nav [id]=\"skipLinksId()\" class=\"fr-container\" role=\"navigation\" aria-label=\"Acc\u00E8s rapide\">\n @if (links.length === 1) {\n @if (!links[0].fragment) {\n <a\n class=\"fr-link\"\n [href]=\"links[0].route || links[0].link\"\n (click)=\"onSkiplinkSelect($event, links[0].route)\"\n >{{ links[0].label }}</a\n >\n } @else {\n <a class=\"fr-link\" [routerLink]=\"'.'\" [fragment]=\"links[0].fragment\"> {{ links[0].label }}</a>\n }\n } @else {\n <ul class=\"fr-skiplinks__list\">\n @for (item of links; track item) {\n <li>\n @if (!item.fragment) {\n <a class=\"fr-link\" [href]=\"item.route || item.link\" (click)=\"onSkiplinkSelect($event, item.route)\">{{\n item.label\n }}</a>\n } @else {\n <a class=\"fr-link\" [routerLink]=\"'.'\" [fragment]=\"item.fragment\"> {{ item.label }}</a>\n }\n </li>\n }\n </ul>\n }\n </nav>\n</div>\n" }]
}], propDecorators: { links: [{
type: Input,
args: [{ required: true }]
}], linkSelect: [{
type: Output
}], skipLinksId: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLinksId", required: false }] }] } });
class DsfrSkiplinksModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkiplinksModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkiplinksModule, imports: [CommonModule, RouterModule, DsfrSkipLinksComponent], exports: [DsfrSkipLinksComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkiplinksModule, imports: [CommonModule, RouterModule, DsfrSkipLinksComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSkiplinksModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrSkipLinksComponent],
imports: [CommonModule, RouterModule, DsfrSkipLinksComponent],
}]
}] });
class DsfrStepperComponent {
/** @internal */
constructor() {
/**
* Le niveau du titre dans la structure, ne change pas l'apparence, `<h2>` par défaut.
*
* @since 1.11.8
*/
this.headingLevel = DsfrHeadingLevelConst.H2;
/**
* Nombre total d'étapes.
*/
this.totalSteps = input(1, ...(ngDevMode ? [{ debugName: "totalSteps" }] : []));
/**
* Index de l'étape courante.
*/
this.currentStep = input(1, ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
this.i18n = inject(DsfrI18nService);
effect(() => {
const totalSteps = this.totalSteps();
if (totalSteps > 8 || totalSteps < 2) {
console.warn('dsfr-stepper : totalSteps must be below or equal to 8');
}
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: DsfrStepperComponent, isStandalone: true, selector: "dsfr-stepper", inputs: { currentStepTitle: { classPropertyName: "currentStepTitle", publicName: "currentStepTitle", isSignal: false, isRequired: true, transformFunction: null }, nextStepTitle: { classPropertyName: "nextStepTitle", publicName: "nextStepTitle", isSignal: false, isRequired: true, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: false, isRequired: false, transformFunction: null }, totalSteps: { classPropertyName: "totalSteps", publicName: "totalSteps", isSignal: true, isRequired: false, transformFunction: null }, currentStep: { classPropertyName: "currentStep", publicName: "currentStep", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"fr-stepper\">\n <edu-heading [level]=\"headingLevel\" defaultLevel=\"H2\" customClass=\"fr-stepper__title\">\n {{ currentStepTitle }}\n <span class=\"fr-stepper__state\">\n {{ 'stepper.step' | dsfrI18n }} {{ currentStep() }} {{ 'stepper.of' | dsfrI18n }} {{ totalSteps() }}\n </span>\n </edu-heading>\n <div class=\"fr-stepper__steps\" [attr.data-fr-current-step]=\"currentStep()\" [attr.data-fr-steps]=\"totalSteps()\"></div>\n <p class=\"fr-stepper__details\">\n <span class=\"fr-text--bold\">{{ 'stepper.nextStep' | dsfrI18n }}</span> {{ nextStepTitle }}\n </p>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-stepper', encapsulation: ViewEncapsulation.None, imports: [CommonModule, HeadingComponent, DsfrI18nPipe], template: "<div class=\"fr-stepper\">\n <edu-heading [level]=\"headingLevel\" defaultLevel=\"H2\" customClass=\"fr-stepper__title\">\n {{ currentStepTitle }}\n <span class=\"fr-stepper__state\">\n {{ 'stepper.step' | dsfrI18n }} {{ currentStep() }} {{ 'stepper.of' | dsfrI18n }} {{ totalSteps() }}\n </span>\n </edu-heading>\n <div class=\"fr-stepper__steps\" [attr.data-fr-current-step]=\"currentStep()\" [attr.data-fr-steps]=\"totalSteps()\"></div>\n <p class=\"fr-stepper__details\">\n <span class=\"fr-text--bold\">{{ 'stepper.nextStep' | dsfrI18n }}</span> {{ nextStepTitle }}\n </p>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { currentStepTitle: [{
type: Input,
args: [{ required: true }]
}], nextStepTitle: [{
type: Input,
args: [{ required: true }]
}], headingLevel: [{
type: Input
}], totalSteps: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalSteps", required: false }] }], currentStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentStep", required: false }] }] } });
class DsfrStepperModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperModule, imports: [CommonModule, HeadingComponent, DsfrI18nPipe, DsfrStepperComponent], exports: [DsfrStepperComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperModule, imports: [CommonModule, HeadingComponent, DsfrStepperComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrStepperModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrStepperComponent],
imports: [CommonModule, HeadingComponent, DsfrI18nPipe, DsfrStepperComponent],
}]
}] });
class DsfrSummaryComponent {
constructor() {
/** Les entrées du sommaire (required) */
this.entries = [];
/** Événement sur lien avec la valeur de l'ancre. */
this.entrySelect = new EventEmitter();
/** @internal */ this.headingId = newUniqueId();
}
/**
* Emission de la valeur de l'ancre (fragment ou link) au clic sur un lien
* @internal
*/
/** @internal */
onLinkSelect(entry) {
// on propage l'output, pas besoin de gérer ici le preventDefault, c'est géré en amont
if (entry.route) {
this.entrySelect.emit(entry.route);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrSummaryComponent, isStandalone: true, selector: "dsfr-summary", inputs: { heading: "heading", headingLevel: "headingLevel", entries: "entries" }, outputs: { entrySelect: "entrySelect" }, ngImport: i0, template: "<nav class=\"fr-summary\" role=\"navigation\" [attr.aria-labelledby]=\"headingId\">\n <edu-heading [headingId]=\"headingId\" customClass=\"fr-summary__title\" [level]=\"headingLevel\" defaultLevel=\"P\">\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-content select=\"[heading]\"></ng-content>\n }\n </edu-heading>\n <!-- TODO defaultLevel=\"H2\" en 2.0 pour \u00EAtre compatible avec le DSFR 1.9, 1.10, ... -->\n <ng-container [ngTemplateOutlet]=\"summaryTemplate\" [ngTemplateOutletContext]=\"{ summary: entries }\"></ng-container>\n</nav>\n\n<ng-template #summaryTemplate let-summary=\"summary\">\n <ol>\n @for (entry of summary; track $index) {\n <li>\n <edu-item-link [item]=\"entry\" customClass=\"fr-summary__link\" (linkSelect)=\"onLinkSelect(entry)\"></edu-item-link>\n @if (entry.subEntries?.length > 0) {\n <ng-container\n [ngTemplateOutlet]=\"summaryTemplate\"\n [ngTemplateOutletContext]=\"{ summary: entry.subEntries }\"></ng-container>\n }\n </li>\n }\n </ol>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: HeadingComponent, selector: "edu-heading", inputs: ["customClass", "allowFirstLevel", "headingId", "level", "defaultLevel"] }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-summary', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule, HeadingComponent, ItemLinkComponent], template: "<nav class=\"fr-summary\" role=\"navigation\" [attr.aria-labelledby]=\"headingId\">\n <edu-heading [headingId]=\"headingId\" customClass=\"fr-summary__title\" [level]=\"headingLevel\" defaultLevel=\"P\">\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-content select=\"[heading]\"></ng-content>\n }\n </edu-heading>\n <!-- TODO defaultLevel=\"H2\" en 2.0 pour \u00EAtre compatible avec le DSFR 1.9, 1.10, ... -->\n <ng-container [ngTemplateOutlet]=\"summaryTemplate\" [ngTemplateOutletContext]=\"{ summary: entries }\"></ng-container>\n</nav>\n\n<ng-template #summaryTemplate let-summary=\"summary\">\n <ol>\n @for (entry of summary; track $index) {\n <li>\n <edu-item-link [item]=\"entry\" customClass=\"fr-summary__link\" (linkSelect)=\"onLinkSelect(entry)\"></edu-item-link>\n @if (entry.subEntries?.length > 0) {\n <ng-container\n [ngTemplateOutlet]=\"summaryTemplate\"\n [ngTemplateOutletContext]=\"{ summary: entry.subEntries }\"></ng-container>\n }\n </li>\n }\n </ol>\n</ng-template>\n" }]
}], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], entries: [{
type: Input
}], entrySelect: [{
type: Output
}] } });
class DsfrSummaryModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryModule, imports: [CommonModule, RouterModule, HeadingComponent, ItemLinkComponent, DsfrSummaryComponent], exports: [DsfrSummaryComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryModule, imports: [CommonModule, RouterModule, HeadingComponent, ItemLinkComponent, DsfrSummaryComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSummaryModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrSummaryComponent],
imports: [CommonModule, RouterModule, HeadingComponent, ItemLinkComponent, DsfrSummaryComponent],
}]
}] });
/**
* Composant pied de page du tableau
* Contient trois éléments optionnel mais dans cet ordre :
* - start: total de lignes, sélection du nombre de lignesn à afficher si pagination
* - middle: composant de pagination commun dsfr-pagination
* - end: template pour afficher des boutons d'actions agissant sur tout le tableau
*/
class DsfrTableFooterComponent {
constructor() {
/** Afficher la pagination */
this.showPagination = false;
/** Afficher le total de lignes */
this.showFooterResult = false;
/** Nombre total d'éléments */
this.totalElements = 0;
/** Options pour la sélection du nombre de lignes à afficher */
this.rowsPerPageOptions = [];
this.rowsPerPageChange = new EventEmitter(); // changement du nombre de lignes à afficher
this.pageSelect = new EventEmitter(); // changement de page
this.rowsPerPage = 10; // current page size selected
}
ngOnInit() {
this.rowsPerPage =
this.initialState?.rowsPerPage ??
(this.rowsPerPageOptions && this.rowsPerPageOptions[0] ? this.rowsPerPageOptions[0].value : 10);
this.currentPage = this.initialState?.page ?? 1;
}
onPageSelect(pageNumber) {
this.pageSelect.emit(pageNumber);
}
onChangeRowsPerPage(rowsPerPage) {
this.rowsPerPage = rowsPerPage;
this.rowsPerPageChange.emit(this.rowsPerPage);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTableFooterComponent, isStandalone: true, selector: "dsfr-table-footer", inputs: { footerActionsTemplate: "footerActionsTemplate", showPagination: ["showPagination", "showPagination", booleanAttribute], showFooterResult: ["showFooterResult", "showFooterResult", booleanAttribute], footerResultsTemplate: "footerResultsTemplate", initialState: "initialState", totalElements: "totalElements", totalPage: "totalPage", currentPage: "currentPage", rowsPerPageOptions: "rowsPerPageOptions", emptyResultsMessage: "emptyResultsMessage", disabledRowsPerPage: "disabledRowsPerPage" }, outputs: { rowsPerPageChange: "rowsPerPageChange", pageSelect: "pageSelect" }, ngImport: i0, template: "@if ((showFooterResult || footerActionsTemplate) && !showPagination) {\n <div class=\"fr-table__footer\">\n <!-- Nombre de r\u00E9sultats total -->\n <div class=\"fr-table__footer--start\">\n @if (showFooterResult) {\n <p class=\"fr-table__detail\">\n <!-- Afficher le nombre de r\u00E9sultat -->\n @if (footerResultsTemplate) {\n <ng-container *ngTemplateOutlet=\"footerResultsTemplate\"></ng-container>\n } @else {\n {{ totalElements }} {{ 'table.row' | dsfrI18n }}<ng-container *ngIf=\"totalElements > 1\">s</ng-container>\n }\n </p>\n }\n <!-- Boutons d'actions -->\n <div class=\"fr-table__footer--end\">\n <ng-container *ngTemplateOutlet=\"footerActionsTemplate\"></ng-container>\n </div>\n </div>\n </div>\n} @else if (showPagination) {\n <dsfr-pagination\n [pageCount]=\"totalPage\"\n [rowsPerPage]=\"rowsPerPage\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [totalElements]=\"totalElements\"\n (rowsPerPageChange)=\"onChangeRowsPerPage($event)\"\n [currentPage]=\"currentPage\"\n (pageSelect)=\"onPageSelect($event)\">\n <ng-template #totalElementsTemplate>\n @if (footerResultsTemplate) {\n <ng-container *ngTemplateOutlet=\"footerResultsTemplate\"></ng-container>\n } @else {\n {{ totalElements }} {{ 'table.row' | dsfrI18n }}<ng-container *ngIf=\"totalElements > 1\">s</ng-container>\n }\n </ng-template>\n <ng-template #additionalTemplate>\n <ng-container *ngTemplateOutlet=\"footerActionsTemplate\"></ng-container>\n </ng-template>\n </dsfr-pagination>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: DsfrPaginationComponent, selector: "dsfr-pagination", inputs: ["previousOnly", "ariaLabel", "rowsPerPagePlaceholder", "currentPage", "disabledRowsPerPage", "pageCount", "totalElements", "displayedNumbers", "rowsPerPageOptions", "rowsPerPage", "showPrevAndNextBtn", "showPrevAndNextLabelLg"], outputs: ["pageSelect", "pageSelectEvent", "backSelect", "rowsPerPageChange", "backEvent"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableFooterComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-table-footer', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrPaginationComponent, DsfrI18nPipe], template: "@if ((showFooterResult || footerActionsTemplate) && !showPagination) {\n <div class=\"fr-table__footer\">\n <!-- Nombre de r\u00E9sultats total -->\n <div class=\"fr-table__footer--start\">\n @if (showFooterResult) {\n <p class=\"fr-table__detail\">\n <!-- Afficher le nombre de r\u00E9sultat -->\n @if (footerResultsTemplate) {\n <ng-container *ngTemplateOutlet=\"footerResultsTemplate\"></ng-container>\n } @else {\n {{ totalElements }} {{ 'table.row' | dsfrI18n }}<ng-container *ngIf=\"totalElements > 1\">s</ng-container>\n }\n </p>\n }\n <!-- Boutons d'actions -->\n <div class=\"fr-table__footer--end\">\n <ng-container *ngTemplateOutlet=\"footerActionsTemplate\"></ng-container>\n </div>\n </div>\n </div>\n} @else if (showPagination) {\n <dsfr-pagination\n [pageCount]=\"totalPage\"\n [rowsPerPage]=\"rowsPerPage\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [totalElements]=\"totalElements\"\n (rowsPerPageChange)=\"onChangeRowsPerPage($event)\"\n [currentPage]=\"currentPage\"\n (pageSelect)=\"onPageSelect($event)\">\n <ng-template #totalElementsTemplate>\n @if (footerResultsTemplate) {\n <ng-container *ngTemplateOutlet=\"footerResultsTemplate\"></ng-container>\n } @else {\n {{ totalElements }} {{ 'table.row' | dsfrI18n }}<ng-container *ngIf=\"totalElements > 1\">s</ng-container>\n }\n </ng-template>\n <ng-template #additionalTemplate>\n <ng-container *ngTemplateOutlet=\"footerActionsTemplate\"></ng-container>\n </ng-template>\n </dsfr-pagination>\n}\n" }]
}], propDecorators: { footerActionsTemplate: [{
type: Input
}], showPagination: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], showFooterResult: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], footerResultsTemplate: [{
type: Input
}], initialState: [{
type: Input
}], totalElements: [{
type: Input
}], totalPage: [{
type: Input,
args: [{ required: true }]
}], currentPage: [{
type: Input
}], rowsPerPageOptions: [{
type: Input
}], emptyResultsMessage: [{
type: Input
}], disabledRowsPerPage: [{
type: Input
}], rowsPerPageChange: [{
type: Output
}], pageSelect: [{
type: Output
}] } });
/** ajouter ou supprimer une ligne d'une liste */
function updateRowByField(row, list, idField = null) {
// recherche de la ligne selon si dataKey est défini ou par l'index sinon
const index = list.findIndex((r) => (idField ? r[idField] === row[idField] : r.index === row.index));
if (index !== -1) {
list.splice(index, 1); // suppression de la ligne
list.push(row); // ajout de la ligne
}
}
/**
* Met à jour ou ajoute une propriété dans un objet.
* @param obj - L'objet à modifier.
* @param key - Le nom de la propriété à mettre à jour ou ajouter.
* @param value - La nouvelle valeur à assigner à la propriété spécifiée.
* @returns - La nouvelle référence de l'objet mis à jour avec la propriété modifiée ou ajoutée.
*/
function updateOrAddProperty(obj, key, value) {
obj[key] = value;
return { ...obj };
}
/** Calcul du positionnement d'une cellule si la colonne est fixe à gauche (défaut) ou à droite */
function setFixedColumnPosition(el, fixedPosition = 'left') {
const row = el.closest('tr');
if (!row)
return false;
const cellsOnRow = Array.from(row.children);
const index = cellsOnRow.indexOf(el);
if (fixedPosition === 'right') {
// Décalage = somme des largeurs des cellules à droite de la cellule courante
const offset = cellsOnRow.slice(index + 1).reduce((acc, cell) => acc + cell.getBoundingClientRect().width, 0);
el.style.right = `${offset}px`;
el.style.left = '';
}
else {
// Décalage = somme des largeurs des cellules à gauche de la cellule courante
const offset = cellsOnRow.slice(0, index).reduce((acc, cell) => acc + cell.getBoundingClientRect().width, 0);
el.style.left = `${offset}px`;
el.style.right = '';
}
// Si dernier élément a etre replacé, on envoie l'evenement pour indiquer qu'il faut recalculer le positionnement de l'en-tête
if (!row.nextElementSibling)
return true;
return false;
}
/**
* Service pour la datatable
* Gestion du rafraichissement des données
*/
class DsfrDataTableService {
constructor() {
/* eslint-disable @typescript-eslint/member-ordering */
/** @internal Etat du tableau (tri, page courante, page size) */
this.tableState = computed(() => {
return {
page: this._currentPage(),
rowsPerPage: this._rowsPerPage(),
sort: this._activeSort(),
};
}, ...(ngDevMode ? [{ debugName: "tableState" }] : []));
/** @internal Mise à jour des données. Appliquer le calcul du rendu des cellules en cas de colonnes fixed */
this.isDataRendered = signal(false, ...(ngDevMode ? [{ debugName: "isDataRendered" }] : []));
/** Page courante */
this._currentPage = signal(1, ...(ngDevMode ? [{ debugName: "_currentPage" }] : []));
/** Tri courant */
this._activeSort = signal(null, ...(ngDevMode ? [{ debugName: "_activeSort" }] : []));
this.activeSort = this._activeSort.asReadonly();
/** Nombre de lignes à afficher par page */
this._rowsPerPage = signal(10, ...(ngDevMode ? [{ debugName: "_rowsPerPage" }] : []));
this.rowsPerPage = this._rowsPerPage.asReadonly();
/** Données de la table */
this._dataSubject = new Subject();
this.data$ = this._dataSubject.asObservable();
this.currentData = undefined;
/** Options de la table */
this._tableOptions = signal({}, ...(ngDevMode ? [{ debugName: "_tableOptions" }] : []));
this.tableOptions = this._tableOptions.asReadonly();
/** Lignes sélectionnées */
this._selectionSubject = new Subject();
this.selection$ = this._selectionSubject.asObservable();
}
/* eslint-enable @typescript-eslint/member-ordering */
/**
* Mise à jour des données avec rafraichissement du tableau
* @param newData nouvelles données du tableau
* @returns void
*/
refreshData(newData) {
this.isDataRendered.set(false);
this.setData(newData);
}
/**
* L'affichage des cellules du tableau (calcul de position) est terminé
* @internal
*/
updateIsDataRendered() {
this.isDataRendered.set(true);
}
/**
* Forcer le rafraichissement du calcul de position des colonnes fixées
* par ex. si l'on souhaite recalculer après un redimensionnement de la fenêtre
*/
forceRenderFixedColums() {
this.isDataRendered.set(false);
}
/**
* Aller à une page donnée
* @param page
*/
goToPage(page) {
this._currentPage.set(page);
}
/**
* Mise à jour des données apres un changement de rowsPerPage
* @param rowsPerPage
* @internal
*/
updatePagination(rowsPerPage) {
this.isDataRendered.set(false);
this._rowsPerPage.set(rowsPerPage);
}
/**
* Trier la table
* @param sort DsfrSortColumn
*/
sortTable(sort) {
this.isDataRendered.set(false);
this._activeSort.set(sort);
}
/**
* Mise à jour de l'état d'affichage de la table (tri, page courante, nombre de lignes par page)
* @param state
* @internal
*/
setState(state) {
this.isDataRendered.set(false);
this._activeSort.set(state.sort ?? null);
this._currentPage.set(state.page ?? 1);
this._rowsPerPage.set(state.rowsPerPage ?? 10);
}
/**
* Met à jour une propriété spécifique de l'objet `tableOptions`.
* @param key Le nom de la propriété de l'interface `tableOptions` à mettre à jour.
* @param newValue La nouvelle valeur de la propriété spécifiée par `key`
* @returns void
*/
updateTableOptions(key, newValue) {
const newTableOptions = updateOrAddProperty(this._tableOptions(), key, newValue);
this.setTableOptions(newTableOptions);
if (key === 'selectable' && !newValue) {
this.setSelectedRows([]);
}
if ((key === 'selectable' || key === 'showSelectAll') && this.currentData) {
this.refreshData(this.currentData);
}
}
/**
* Sélectionner les lignes programmatiquement
* @param selectedRowIds la liste des identifiants des lignes qui seront sélectionnées
**/
setSelectedRows(selectedRowIds) {
this._selectionSubject.next(selectedRowIds);
}
/** @internal */
setTableOptions(tableOptions) {
this._tableOptions.set(tableOptions);
}
setData(data) {
this.isDataRendered.set(false);
this.currentData = data;
this._dataSubject.next(data);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableService, decorators: [{
type: Injectable
}] });
/**
* Classe abstraite gestion de la pagination du tableau
* selon le nombre de lignes à afficher et nombre de pages
*/
class BasePaginatedTableComponent {
constructor() {
/** Afficher la pagination */
this.pagination = false;
/** Activer le mode serveur (pagination, tri et sélection côté serveur) */
this.serverSide = false;
/** Choix de taille de page (par défaut 10, 20, 50, 100). Pas d'affichage du select si non défini */
this.rowsPerPageOptions = [
{ label: '10 lignes par page', value: 10 },
{ label: '20 lignes par page', value: 20 },
{ label: '50 lignes par page', value: 50 },
{ label: '100 lignes par page', value: 100 },
];
/** Changement de pagination (page affichée ou taille de page) */
this.paginationChange = new EventEmitter();
/** Etat modifié */
this.stateChange = new EventEmitter();
/** @internal Total résultats */
this.totalElements = signal(0, ...(ngDevMode ? [{ debugName: "totalElements" }] : []));
/** @internal Total pages */
this.totalPage = computed(() => {
if (this.totalElements() > 0 || !this.serverSide) {
return this.calculateTotalPage();
}
return 0;
}, ...(ngDevMode ? [{ debugName: "totalPage" }] : []));
/** @internal liste des éléments sélectionnables */
this.selectableRows = [];
/**@internal Service datatable pour rafraichissement des données */
this.dataTableService = inject(DsfrDataTableService);
}
/** @internal */
get serverResultsLength() {
return this._serverResultsLength;
}
/** Total du nombre de résultats (utilisé si serverSide) */
set serverResultsLength(value) {
let currentPage = this.dataTableService.tableState().page;
this._serverResultsLength = value;
if (this.serverSide) {
this.totalElements.set(this._serverResultsLength);
}
if (currentPage > this.totalPage() && this.totalPage() > 0) {
// si le nombre de pages est inférieur a la page courante, aller à la dernière page
currentPage = this.totalPage();
this.onPageSelect(currentPage);
}
else if (this.totalPage() === 0 && currentPage !== 1) {
// si une seule page, aller à la premiere page
this.onPageSelect(1);
}
}
/** Accès au service de la table */
getService() {
return this.dataTableService;
}
/**
* Changement du nombre de lignes affichés par page
* calcul des pages et changement des lignes affichées
* @internal
*/
onRowsPerPageChange(rowsPerPage) {
const currentPage = this.dataTableService.tableState().page;
this.dataTableService.updatePagination(rowsPerPage);
if (currentPage > this.totalPage()) {
this.onPageSelect(this.totalPage()); // aller a la derniere page si currentPage > totalPage
}
else {
this.paginationChange.emit({
currentPage: currentPage,
rowsPerPage: this.dataTableService.tableState().rowsPerPage,
});
}
}
/**
* Au changement de page
* @param page numéro de page
* @internal
*/
onPageSelect(page) {
this.dataTableService.goToPage(page);
this.paginationChange.emit({ currentPage: page, rowsPerPage: this.dataTableService.tableState().rowsPerPage });
}
/** Calcul du total de page selon le nombre total de résultats et le nombre de lignes affichées
* @internal
*/
calculateTotalPage() {
const rowsPerPage = this.dataTableService.rowsPerPage();
return Math.ceil(this.totalElements() / rowsPerPage);
}
/**
* Après changement de page
* Mise a jour des données affichées selon l'offset et la taille de page
* @internal
* */
paginateData(rows, state) {
if (!this.serverSide && this.totalElements() !== rows.length) {
this.totalElements.set(rows.length);
}
if (!state.page || !state.rowsPerPage)
return [];
const offset = (state.page - 1) * state.rowsPerPage;
return rows.slice(offset, offset + state.rowsPerPage);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BasePaginatedTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.23", type: BasePaginatedTableComponent, isStandalone: true, selector: "ng-component", inputs: { initialState: "initialState", pagination: ["pagination", "pagination", booleanAttribute], serverSide: ["serverSide", "serverSide", booleanAttribute], rowsPerPageOptions: "rowsPerPageOptions", serverResultsLength: "serverResultsLength" }, outputs: { paginationChange: "paginationChange", stateChange: "stateChange" }, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BasePaginatedTableComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { initialState: [{
type: Input
}], pagination: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], serverSide: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], rowsPerPageOptions: [{
type: Input
}], paginationChange: [{
type: Output
}], stateChange: [{
type: Output
}], serverResultsLength: [{
type: Input
}] } });
/**
* Composant en-tête du tableau.
* Il peut contenir quatre éléments optionnel mais dans cet ordre :
* - le nombre de lignes sélectionnées
* - une barre de recherche
* - des boutons d’actions liés à la sélection de lignes
* - un contrôle segmenté.
*/
class EduTableHeaderComponent {
constructor() {
/** Afficher le changement de type d'affichage (tableau / liste) */
this.showHeaderViews = false;
/** Contrôle segmenté contenant les types d'affichages (tableau / liste par défaut) */
this.headerViewsOptions = [];
/** Affiche la barre de recherche dans le header */
this.showSearch = false;
/** Changement de type de vue */
this.viewSelect = new EventEmitter();
/** Emet le texte lors de l'évènement keyup sur l'input de recherche. */
this.searchChange = new EventEmitter();
/** Emet le texte lors du clic sur le bouton "rechercher". */
this.searchSelect = new EventEmitter();
}
/**
* Permet de positionner la valeur intiale du champ de recherche (si utilisé)
*
* @deprecated (since 1.15) Utiliser `searchInputValue` à la place.
*/
set searchInputInitialValue(value) {
this.searchInputValue = value;
}
/**
* Changement de type d'affichage (liste ou tableau)
* @internal */
onViewSelect(value) {
this.viewSelect.emit(value);
}
/** @internal */
onSearch(e) {
this.searchChange.emit(e);
}
/** @internal */
onSearchClick(e) {
this.searchSelect.emit(e);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduTableHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: EduTableHeaderComponent, isStandalone: true, selector: "edu-table-header", inputs: { headerActionsTemplate: "headerActionsTemplate", countSelectedRowsTemplate: "countSelectedRowsTemplate", countSelectedRows: "countSelectedRows", showHeaderViews: ["showHeaderViews", "showHeaderViews", booleanAttribute], headerViewsOptions: "headerViewsOptions", showSearch: "showSearch", searchInputPlaceholder: "searchInputPlaceholder", searchLabel: "searchLabel", searchButtonTitle: "searchButtonTitle", searchInputValue: "searchInputValue", searchInputInitialValue: "searchInputInitialValue" }, outputs: { viewSelect: "viewSelect", searchChange: "searchChange", searchSelect: "searchSelect" }, ngImport: i0, template: "<div class=\"fr-table__header\">\n <!-- Controle segment\u00E9 pour le changement de type d'affichage-->\n @if (showHeaderViews) {\n <div class=\"edu-table-segmented\">\n <dsfr-segmented-control\n legend=\"Type d'affichage\"\n [legendSrOnly]=\"true\"\n (segmentedControlSelect)=\"onViewSelect($event)\"\n [segments]=\"headerViewsOptions\"></dsfr-segmented-control>\n </div>\n }\n\n <!-- Nombre de lignes s\u00E9lectionn\u00E9es -->\n @if (countSelectedRowsTemplate && countSelectedRows !== undefined) {\n <p class=\"fr-table__detail\">\n <ng-container *ngTemplateOutlet=\"countSelectedRowsTemplate\"></ng-container>\n </p>\n } @else if (countSelectedRows !== undefined) {\n <p class=\"fr-table__detail\">\n @if (countSelectedRows === 0) {\n {{ 'table.noSelectedRows' | dsfrI18n }}\n } @else if (countSelectedRows === 1) {\n {{ countSelectedRows }} {{ 'table.oneSelectedRow' | dsfrI18n }}\n } @else if (countSelectedRows && countSelectedRows > 1) {\n {{ countSelectedRows }} {{ 'table.selectedRows' | dsfrI18n }}\n }\n </p>\n }\n\n <!-- Template barre de recherche -->\n @if (showSearch) {\n <div class=\"edu-table__search\">\n <dsfr-search-bar\n [label]=\"searchLabel\"\n [placeholder]=\"searchInputPlaceholder\"\n [buttonTitle]=\"searchButtonTitle\"\n [value]=\"searchInputValue\"\n (searchSelect)=\"onSearchClick($event)\"\n (searchChange)=\"onSearch($event)\">\n </dsfr-search-bar>\n </div>\n }\n\n <!-- Template actions group\u00E9es -->\n @if (headerActionsTemplate) {\n <ng-container *ngTemplateOutlet=\"headerActionsTemplate\"></ng-container>\n }\n</div>\n", styles: [".fr-table__header{gap:.5rem;align-items:center}.fr-table__header .edu-table-segmented{order:3;flex:0}.fr-table__header dsfr-buttons-group{order:2}.fr-table__header dsfr-buttons-group:has(.fr-btns-group--right){margin-left:auto}.fr-table__header .edu-table__search{margin-bottom:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: DsfrSegmentedControlComponent, selector: "dsfr-segmented-control", inputs: ["segments", "legend", "legendSrOnly", "hint", "segmentedControlSize", "inline", "size", "name"], outputs: ["segmentedControlSelect"] }, { kind: "component", type: DsfrSearchBarComponent, selector: "dsfr-search-bar", inputs: ["inputId", "buttonText", "value", "label", "large", "placeholder", "buttonTitle", "pattern", "triggerSearchOnEnter", "maxLength", "delay", "initialValue", "id"], outputs: ["searchChange", "searchSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: EduTableHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-table-header', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrSegmentedControlComponent, DsfrSearchBarComponent, DsfrI18nPipe], template: "<div class=\"fr-table__header\">\n <!-- Controle segment\u00E9 pour le changement de type d'affichage-->\n @if (showHeaderViews) {\n <div class=\"edu-table-segmented\">\n <dsfr-segmented-control\n legend=\"Type d'affichage\"\n [legendSrOnly]=\"true\"\n (segmentedControlSelect)=\"onViewSelect($event)\"\n [segments]=\"headerViewsOptions\"></dsfr-segmented-control>\n </div>\n }\n\n <!-- Nombre de lignes s\u00E9lectionn\u00E9es -->\n @if (countSelectedRowsTemplate && countSelectedRows !== undefined) {\n <p class=\"fr-table__detail\">\n <ng-container *ngTemplateOutlet=\"countSelectedRowsTemplate\"></ng-container>\n </p>\n } @else if (countSelectedRows !== undefined) {\n <p class=\"fr-table__detail\">\n @if (countSelectedRows === 0) {\n {{ 'table.noSelectedRows' | dsfrI18n }}\n } @else if (countSelectedRows === 1) {\n {{ countSelectedRows }} {{ 'table.oneSelectedRow' | dsfrI18n }}\n } @else if (countSelectedRows && countSelectedRows > 1) {\n {{ countSelectedRows }} {{ 'table.selectedRows' | dsfrI18n }}\n }\n </p>\n }\n\n <!-- Template barre de recherche -->\n @if (showSearch) {\n <div class=\"edu-table__search\">\n <dsfr-search-bar\n [label]=\"searchLabel\"\n [placeholder]=\"searchInputPlaceholder\"\n [buttonTitle]=\"searchButtonTitle\"\n [value]=\"searchInputValue\"\n (searchSelect)=\"onSearchClick($event)\"\n (searchChange)=\"onSearch($event)\">\n </dsfr-search-bar>\n </div>\n }\n\n <!-- Template actions group\u00E9es -->\n @if (headerActionsTemplate) {\n <ng-container *ngTemplateOutlet=\"headerActionsTemplate\"></ng-container>\n }\n</div>\n", styles: [".fr-table__header{gap:.5rem;align-items:center}.fr-table__header .edu-table-segmented{order:3;flex:0}.fr-table__header dsfr-buttons-group{order:2}.fr-table__header dsfr-buttons-group:has(.fr-btns-group--right){margin-left:auto}.fr-table__header .edu-table__search{margin-bottom:1rem}\n"] }]
}], propDecorators: { headerActionsTemplate: [{
type: Input
}], countSelectedRowsTemplate: [{
type: Input
}], countSelectedRows: [{
type: Input
}], showHeaderViews: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], headerViewsOptions: [{
type: Input
}], showSearch: [{
type: Input
}], searchInputPlaceholder: [{
type: Input
}], searchLabel: [{
type: Input
}], searchButtonTitle: [{
type: Input
}], searchInputValue: [{
type: Input
}], viewSelect: [{
type: Output
}], searchChange: [{
type: Output
}], searchSelect: [{
type: Output
}], searchInputInitialValue: [{
type: Input
}] } });
/**
* Directive affichage d'une cellule (alignements)
*/
class DsfrCellDirective {
constructor() {
this.cell = input.required(...(ngDevMode ? [{ debugName: "cell", alias: 'dsfrCell' }] : [{ alias: 'dsfrCell' }]));
this.element = inject(ElementRef);
this.dataTableService = inject(DsfrDataTableService);
this.init = false;
// todo : fix au passage angular 20 avec un seul hook afterEveryRender
afterNextRender(() => {
const cell = this.cell();
const isDataRendered = this.dataTableService.isDataRendered();
this.init = false;
if (cell.fixed && !isDataRendered) {
this.calculateCellRender(true, isDataRendered);
}
});
effect(() => {
const cell = this.cell();
const isDataRendered = this.dataTableService.isDataRendered();
if (cell.fixed && !isDataRendered && this.init) {
this.calculateCellRender(false, isDataRendered);
}
});
}
/**
* Calcul de la position et taille des cellules en cas de colonnes fixes
* Double animation request pour bug webkit refresh
* Relancé à chaque changement de données
*/
calculateCellRender(onInit, isDataRendered) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const lastRowRendered = setFixedColumnPosition(this.element.nativeElement, this.cell().fixedPosition);
if (lastRowRendered && !isDataRendered) {
this.dataTableService.updateIsDataRendered();
}
if (onInit)
this.init = true;
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.23", type: DsfrCellDirective, isStandalone: true, selector: "[dsfrCell]", inputs: { cell: { classPropertyName: "cell", publicName: "dsfrCell", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.fr-cell--multiline": "cell()?.allowWrap", "class.fr-cell--top": "cell()?.verticalAlign === \"top\"", "class.fr-cell--bottom": "cell()?.verticalAlign === \"bottom\"", "class.fr-cell--center": "cell()?.textAlign === \"center\"", "class.fr-cell--right": "cell()?.textAlign === \"right\"", "class.fr-cell--fixed": "cell()?.fixed" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrCellDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrCell]',
standalone: true,
host: {
'[class.fr-cell--multiline]': 'cell()?.allowWrap',
'[class.fr-cell--top]': 'cell()?.verticalAlign === "top"',
'[class.fr-cell--bottom]': 'cell()?.verticalAlign === "bottom"',
'[class.fr-cell--center]': 'cell()?.textAlign === "center"',
'[class.fr-cell--right]': 'cell()?.textAlign === "right"',
'[class.fr-cell--fixed]': 'cell()?.fixed',
},
}]
}], ctorParameters: () => [], propDecorators: { cell: [{ type: i0.Input, args: [{ isSignal: true, alias: "dsfrCell", required: true }] }] } });
const DSFR_TABLE_CONTEXT = new InjectionToken('DSFR_TABLE_CONTEXT');
/**
* Directive ajout de style pour une colonne (taille, alignements)
*/
class DsfrColumnDirective {
constructor() {
this.col = input.required(...(ngDevMode ? [{ debugName: "col", alias: 'dsfrCol' }] : [{ alias: 'dsfrCol' }]));
this.allowWrap = input(false, ...(ngDevMode ? [{ debugName: "allowWrap" }] : []));
this.dataTableService = inject(DsfrDataTableService);
this.element = inject(ElementRef);
this.table = inject(DSFR_TABLE_CONTEXT, { optional: false });
effect(() => {
const col = this.col();
const isDataRendered = this.dataTableService.isDataRendered();
// Calcul de la position des en-têtes en cas de colonne fixe, une fois toutes les cellules du tableau affichées
if (col.fixed && isDataRendered) {
setFixedColumnPosition(this.element.nativeElement, col.fixedPosition);
}
});
}
// Ajout de l'attribut scope si ce n'est pas un tableau complexe (pas de summary)
get scope() {
return this.table.headerSummaryTemplate ? null : 'col';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.23", type: DsfrColumnDirective, isStandalone: true, selector: "[dsfrCol]", inputs: { col: { classPropertyName: "col", publicName: "dsfrCol", isSignal: true, isRequired: true, transformFunction: null }, allowWrap: { classPropertyName: "allowWrap", publicName: "allowWrap", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.fr-cell--fixed": "col().fixed", "class.fr-cell--multiline": "col().allowWrap ?? allowWrap", "class.fr-cell--top": "col().verticalAlign === \"top\"", "class.fr-cell--bottom": "col().verticalAlign === \"bottom\"", "class.fr-cell--center": "col().textAlign === \"center\"", "class.fr-cell--right": "col().textAlign === \"right\"", "class.fr-col--xs": "col().minWidth === \"XS\"", "class.fr-col--sm": "col().minWidth === \"SM\"", "class.fr-col--md": "col().minWidth === \"MD\"", "class.fr-col--lg": "col().minWidth === \"LG\"", "attr.scope": "scope" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrColumnDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrCol]',
standalone: true,
host: {
'[class.fr-cell--fixed]': 'col().fixed',
'[class.fr-cell--multiline]': 'col().allowWrap ?? allowWrap',
'[class.fr-cell--top]': 'col().verticalAlign === "top"',
'[class.fr-cell--bottom]': 'col().verticalAlign === "bottom"',
'[class.fr-cell--center]': 'col().textAlign === "center"',
'[class.fr-cell--right]': 'col().textAlign === "right"',
'[class.fr-col--xs]': 'col().minWidth === "XS"',
'[class.fr-col--sm]': 'col().minWidth === "SM"',
'[class.fr-col--md]': 'col().minWidth === "MD"',
'[class.fr-col--lg]': 'col().minWidth === "LG"',
'[attr.scope]': 'scope',
},
}]
}], ctorParameters: () => [], propDecorators: { col: [{ type: i0.Input, args: [{ isSignal: true, alias: "dsfrCol", required: true }] }], allowWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowWrap", required: false }] }] } });
/**
* Directive pour la checkbox d'en-tête de colonne (tout sélectionner/déselectionner)
*/
class DsfrSelectAllDirective {
constructor() {
this.table = inject(DSFR_TABLE_CONTEXT, { optional: false });
this.element = inject(ElementRef);
this.viewContainer = inject(ViewContainerRef);
this.injector = inject(EnvironmentInjector);
this.i18n = inject(DsfrI18nService);
}
// Ajout de l'attribut scope si ce n'est pas un tableau complexe (pas de summary)
get scope() {
return this.table.headerSummaryTemplate ? null : 'row';
}
ngOnInit() {
this.componentRef = this.createCheckboxComponent();
this.element.nativeElement?.appendChild(this.componentRef.location.nativeElement);
}
createCheckboxComponent() {
return this.viewContainer.createComponent(EduCheckboxLightComponent, {
environmentInjector: this.injector,
bindings: [
inputBinding('labelSrOnly', () => true),
inputBinding('id', () => this.getCheckboxId()),
inputBinding('label', () => this.getCheckboxLabel()),
inputBinding('selected', () => this.table.isAllSelected()),
outputBinding('checkboxClick', () => this.table.toggleSelectAll()),
],
});
}
getCheckboxId() {
return 'table-select-checkbox-' + this.table.tableId + '-select-all';
}
getCheckboxLabel() {
return this.table.isAllSelected() ? this.i18n.t('table.unselectAll') : this.i18n.t('table.selectAll');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSelectAllDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: DsfrSelectAllDirective, isStandalone: true, selector: "[dsfrSelectAll]", host: { properties: { "attr.scope": "scope", "class.fr-cell--fixed": "true" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSelectAllDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrSelectAll]',
standalone: true,
host: {
// ajouter l'attribut scope si ce n'est pas un tableau complexe (pas de summary)
'[attr.scope]': 'scope',
'[class.fr-cell--fixed]': 'true',
},
}]
}] });
/**
* Directive group sur une ligne <tr> à l'intérieur d'un groupe
* Gère les attributs aria nécessaires au treegrid
*/
class DsfrRowGroupDirective {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRowGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: DsfrRowGroupDirective, isStandalone: true, selector: "[dsfrRowGroup]", inputs: { group: "group", index: "index" }, host: { properties: { "class.edu-row--group": "true", "attr.aria-level": "2", "attr.aria-posinset": "(index ?? 0) + 1 ", "attr.aria-setsize": "group?.rows?.length ?? 0" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRowGroupDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrRowGroup]',
standalone: true,
host: {
'[class.edu-row--group]': 'true',
'[attr.aria-level]': '2',
'[attr.aria-posinset]': '(index ?? 0) + 1 ',
'[attr.aria-setsize]': 'group?.rows?.length ?? 0',
},
}]
}], propDecorators: { group: [{
type: Input
}], index: [{
type: Input
}] } });
/**
* Composant d'affichage du bouton de tri pour une colonne (icônes flêche)
*/
class ExpandButtonRowComponent {
constructor() {
this.collapsed = input.required(...(ngDevMode ? [{ debugName: "collapsed" }] : []));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ExpandButtonRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: ExpandButtonRowComponent, isStandalone: true, selector: "edu-expand-button-row", inputs: { collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
<span class="tree-button {{ collapsed() ? 'fr-icon-arrow-right-s-line' : 'fr-icon-arrow-down-s-line' }}"></span>
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: ExpandButtonRowComponent, decorators: [{
type: Component,
args: [{
selector: 'edu-expand-button-row',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<span class="tree-button {{ collapsed() ? 'fr-icon-arrow-right-s-line' : 'fr-icon-arrow-down-s-line' }}"></span>
`,
}]
}], propDecorators: { collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: true }] }] } });
/**
* Directive group sur une ligne de groupe <tr>
* Gère l'etat collapse/expand et les attributs aria nécessaires au treegrid
*/
class DsfrRowGroupHeaderDirective {
constructor() {
this.group = input.required(...(ngDevMode ? [{ debugName: "group" }] : [])); // le groupe de données complet
this._unlisten = [];
this.element = inject(ElementRef);
this.viewContainer = inject(ViewContainerRef);
this.renderer = inject(Renderer2);
effect(() => {
const group = this.group();
if (!group || !group.collapsed)
return;
const collapsed = group.collapsed();
if (collapsed !== undefined && this.componentRef) {
this.updateExpansionButton(group);
}
else if (collapsed !== undefined && !this.componentRef) {
this.createExpansionButton(group);
}
else if (collapsed === undefined && this.componentRef) {
this.destroyButton();
}
});
}
ngOnDestroy() {
this._unlisten.forEach((unlistenFunc) => unlistenFunc());
this.destroyButton();
}
createExpansionButton(group) {
const firstTd = this.element.nativeElement?.querySelector('td');
if (!firstTd || !group || !group.collapsed)
return;
this.firstTd = firstTd;
this.componentRef = this.viewContainer.createComponent(ExpandButtonRowComponent);
this.componentRef.setInput('collapsed', group.collapsed()); // valeur initiale
// Ajout des évènements expand/collapse sur le premier <td>
this._unlisten.push(this.renderer.listen(firstTd, 'click', () => {
group.collapsed.update((c) => !c);
this.updateFirstCellAttributes(group);
}), this.renderer.listen(firstTd, 'keydown', (e) => {
if (e.key === 'Enter') {
group.collapsed.update((c) => !c);
this.updateFirstCellAttributes(group);
}
}));
// Ajoute le bouton au début du premier <td>
const buttonEl = this.componentRef.location.nativeElement;
this.updateFirstCellAttributes(group);
this.renderer.insertBefore(firstTd, buttonEl, firstTd.firstChild);
}
updateExpansionButton(group) {
if (this.componentRef?.instance && group.collapsed) {
this.componentRef.setInput('collapsed', group.collapsed()); // valeur initiale
}
}
updateFirstCellAttributes(group) {
if (!this.firstTd)
return;
this.firstTd.setAttribute('tabindex', '0');
this.firstTd.setAttribute('aria-expanded', group.collapsed() ? 'false' : 'true');
}
destroyButton() {
this.componentRef?.destroy();
this.componentRef = undefined;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRowGroupHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.23", type: DsfrRowGroupHeaderDirective, isStandalone: true, selector: "[dsfrRowGroupHeader]", inputs: { group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.edu-row--group-header": "true", "attr.aria-level": "1", "attr.aria-posinset": "group().ariaPosinset ?? null", "attr.aria-setsize": "group().ariaSetsize ?? null", "attr.tabindex": "group().collapsed ? \"0\" : null" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRowGroupHeaderDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrRowGroupHeader]',
standalone: true,
host: {
'[class.edu-row--group-header]': 'true',
'[attr.aria-level]': '1',
'[attr.aria-posinset]': 'group().ariaPosinset ?? null',
'[attr.aria-setsize]': 'group().ariaSetsize ?? null',
'[attr.tabindex]': 'group().collapsed ? "0" : null',
},
}]
}], ctorParameters: () => [], propDecorators: { group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: true }] }] } });
/**
* Directive checkbox sur la première cellule th (Selectionner/Déselectionner la ligne)
*/
class DsfrSelectRowDirective {
constructor() {
this.row = input(undefined, ...(ngDevMode ? [{ debugName: "row", alias: 'dsfrSelectRow' }] : [{ alias: 'dsfrSelectRow' }]));
this.disableSelect = input(false, ...(ngDevMode ? [{ debugName: "disableSelect" }] : []));
this.element = inject(ElementRef);
this.viewContainer = inject(ViewContainerRef);
this.injector = inject(EnvironmentInjector);
this.i18n = inject(DsfrI18nService);
this.table = inject(DSFR_TABLE_CONTEXT, { optional: false });
// Mise a jour de aria-selected et style de la ligne à la sélection/déselection de la checkbox
afterRenderEffect(() => {
const row = this.row();
if (!row)
return;
const checked = row.selected();
if (this.rowElement) {
this.rowElement.setAttribute('aria-selected', `${!!checked}`);
this.addRowHeightSyle();
}
});
// Création ou suppression du composant checkbox en cas de modification de disabledSelected
effect(() => {
const disabled = this.disableSelect();
if (!this.element.nativeElement)
return;
if (!disabled && !this.componentRef) {
this.createCheckbox();
}
else if (disabled && this.componentRef) {
const checked = this.row().selected();
if (checked)
this.row().selected.set(false);
this.destroyCheckbox();
}
});
}
// Ajout de l'attribut scope si ce n'est pas un tableau complexe (pas de summary)
get scope() {
return this.table.headerSummaryTemplate ? null : 'row';
}
createCheckbox() {
this.componentRef = this.createCheckboxComponent();
this.rowElement = this.element.nativeElement.closest('tr');
if (this.componentRef) {
const host = this.element.nativeElement;
this.rowElement = host.closest('tr');
host.insertBefore(this.componentRef.location.nativeElement, host.firstChild);
if (this.rowElement) {
const checked = !!this.row()?.selected();
this.rowElement.setAttribute('aria-selected', `${checked}`);
if (checked)
this.addRowHeightSyle();
}
}
}
destroyCheckbox() {
this.componentRef.location.nativeElement.remove();
this.componentRef.destroy();
this.componentRef = undefined;
}
createCheckboxComponent() {
return this.viewContainer.createComponent(EduCheckboxLightComponent, {
environmentInjector: this.injector,
bindings: [
inputBinding('labelSrOnly', () => true),
inputBinding('id', () => this.getCheckboxId()),
inputBinding('label', () => this.getCheckboxLabel()),
inputBinding('selected', () => this.row().selected()),
outputBinding('checkboxClick', () => this.table.selectRow(this.row())),
],
});
}
/** Créer manuellement le style ajouté par le script DSFR à la sélection d'une ligne pour avoir les bordures bleues */
addRowHeightSyle() {
const height = this.rowElement.getBoundingClientRect().height + 2;
this.rowElement.style.setProperty('--row-height', `${height}px`);
}
getCheckboxId() {
return 'table-select-checkbox-' + this.table.tableId + '--' + this.row().index;
}
getCheckboxLabel() {
return this.i18n.t('table.selectRow') + ' ' + this.row().index;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSelectRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.23", type: DsfrSelectRowDirective, isStandalone: true, selector: "[dsfrSelectRow]", inputs: { row: { classPropertyName: "row", publicName: "dsfrSelectRow", isSignal: true, isRequired: false, transformFunction: null }, disableSelect: { classPropertyName: "disableSelect", publicName: "disableSelect", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.scope": "scope", "class.fr-cell--fixed": "true" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSelectRowDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrSelectRow]',
standalone: true,
host: {
// ajouter l'attribut scope si ce n'est pas un tableau complexe (pas de summary)
'[attr.scope]': 'scope',
'[class.fr-cell--fixed]': 'true',
},
}]
}], ctorParameters: () => [], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "dsfrSelectRow", required: false }] }], disableSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableSelect", required: false }] }] } });
/**
* Composant d'affichage du bouton de tri pour une colonne (icônes flêche)
*/
class SortButtonHeaderComponent {
constructor() {
this.sortChanged = new EventEmitter();
this.ariaSort = input(...(ngDevMode ? [undefined, { debugName: "ariaSort" }] : []));
}
sortTable() {
this.sortChanged.emit(this.field);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SortButtonHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: SortButtonHeaderComponent, isStandalone: true, selector: "edu-sort-button-header", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: false, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, ariaSort: { classPropertyName: "ariaSort", publicName: "ariaSort", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChanged: "sortChanged" }, ngImport: i0, template: `
<button
(click)="sortTable()"
type="button"
class="fr-btn--sort fr-btn fr-btn--sm"
[class]="{
'fr-btn--sort-asc': ariaSort() === 'ascending',
'fr-btn--sort-desc': ariaSort() === 'descending',
}">
{{ 'table.sortTableBy' | dsfrI18n }} {{ label ?? field }}
</button>
`, isInline: true, styles: [".fr-btn--sort{margin-left:1rem}\n"], dependencies: [{ kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: SortButtonHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'edu-sort-button-header', encapsulation: ViewEncapsulation.None, imports: [DsfrI18nPipe], template: `
<button
(click)="sortTable()"
type="button"
class="fr-btn--sort fr-btn fr-btn--sm"
[class]="{
'fr-btn--sort-asc': ariaSort() === 'ascending',
'fr-btn--sort-desc': ariaSort() === 'descending',
}">
{{ 'table.sortTableBy' | dsfrI18n }} {{ label ?? field }}
</button>
`, styles: [".fr-btn--sort{margin-left:1rem}\n"] }]
}], propDecorators: { field: [{
type: Input,
args: [{ required: true }]
}], label: [{
type: Input
}], sortChanged: [{
type: Output
}], ariaSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaSort", required: false }] }] } });
/**
* Directive ajout du tri sur l'en-tête de colonne (icône et logique)
*/
class DsfrSortColumnDirective extends DsfrColumnDirective {
constructor() {
super();
this.col = input.required(...(ngDevMode ? [{ debugName: "col", alias: 'dsfrColumn' }] : [{ alias: 'dsfrColumn' }]));
/**
* Calcul de la valeur de aria-sort sur la colonne selon le tri courant activeSort()
* @returns null / ascending / descending
*/
this.ariaSort = computed(() => {
const activeSort = this.dataTableService.activeSort();
const col = this.col();
if (activeSort && activeSort.field === col.field) {
return activeSort.order === 'ascending' ? 'ascending' : 'descending';
}
return null;
}, ...(ngDevMode ? [{ debugName: "ariaSort" }] : []));
this.viewContainer = inject(ViewContainerRef);
this.renderer = inject(Renderer2);
effect(() => {
if (this.componentRef && this.componentRef.instance) {
// a la modification de aria-sort sur le th, appliquer également sur le bouton de tri
this.componentRef.setInput('ariaSort', this.ariaSort());
}
});
}
ngOnInit() {
if (this.col() && this.col().sortable) {
this.componentRef = this.viewContainer.createComponent(SortButtonHeaderComponent);
this.componentRef.instance.field = this.col().field;
this.componentRef.instance.label = this.col().label;
this.subscription = this.componentRef.instance.sortChanged.subscribe((field) => {
this.applySort(field);
});
this.addWrappingElement(this.element.nativeElement, this.componentRef.location.nativeElement);
}
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
/**
* Appliquer le tri selon la colonne concernées et mise a jour de activeSort()
**/
applySort(colId) {
const currentSort = this.dataTableService.activeSort();
const activeSort = {
field: colId,
order: currentSort?.field === colId && currentSort?.order === 'ascending' ? 'descending' : 'ascending',
};
this.dataTableService.sortTable(activeSort);
this.table.onSortChange(); // fixme
}
/**
* Ajouter une div wrapper fr-cell--sort autour des éléments pour style DSFR
* @param nativeElement composant <th>
* @param componentElement composant sort-buton
*/
addWrappingElement(nativeElement, componentElement) {
const wrapperDiv = this.renderer.createElement('div');
this.renderer.addClass(wrapperDiv, 'fr-cell--sort');
// Déplacer tous les enfants actuels du th dans le <div>
while (nativeElement.firstChild) {
this.renderer.appendChild(wrapperDiv, nativeElement.firstChild);
}
// Ajouter le <div> au <th>
this.renderer.appendChild(nativeElement, wrapperDiv);
// Ajouter le composant au <th>
this.renderer.appendChild(wrapperDiv, componentElement);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSortColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.23", type: DsfrSortColumnDirective, isStandalone: true, selector: "[dsfrColumn]", inputs: { col: { classPropertyName: "col", publicName: "dsfrColumn", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.aria-sort": "ariaSort()" } }, usesInheritance: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrSortColumnDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrColumn]',
standalone: true,
host: { '[attr.aria-sort]': 'ariaSort()' },
}]
}], ctorParameters: () => [], propDecorators: { col: [{ type: i0.Input, args: [{ isSignal: true, alias: "dsfrColumn", required: true }] }] } });
const FOCUSABLE_HTML_ELEMENTS = [
'a[href]',
'button',
'input',
'select',
'textarea',
'iframe',
'details',
'[tabindex]:not([tabindex="-1"])',
'[contenteditable]',
];
/**
* Index de la ligne contenant la cellule courante dans la liste de lignes de la table
* -1 si aucune tr ou table n'est détecté
*/
function getCurrentRowIndex(target, theadWithInteractiveElt) {
const row = target.closest('tr');
const table = row?.closest('table');
if (!row || !table)
return -1;
const rows = table.querySelectorAll(theadWithInteractiveElt ? 'thead tr, tbody tr' : 'tbody tr');
return Array.from(rows).indexOf(row);
}
/**
* Au changement de ligne
* Positionne le focus sur la prochaine cellule selon la direction (-1 ligne précédente, 1 suivante)
* Si la cellule possède un élément focusable, le focus sera directement sur cet élément
*/
function moveFocusRow(rowIndex, cellIndex, direction, rows, position) {
if (!rows)
return;
const rowsArray = Array.from(rows);
let nextRowIndex = rowIndex + direction;
if (position === 'start') {
cellIndex = 0;
nextRowIndex = 0;
}
else if (position === 'end') {
nextRowIndex = rowsArray.length - 1;
cellIndex = rowsArray[nextRowIndex].children.length - 1;
}
const nextRow = rowsArray[nextRowIndex];
if (nextRow) {
const nextCell = nextRow.children[cellIndex]
? nextRow.children[cellIndex]
: nextRow.children[0];
setFocusCellOrFocusableElement(nextCell);
}
}
/**
* Sur la même ligne
* Positionne le focus sur la prochaine cellule selon la direction (-1 précédent, 1 suivant)
* Si la cellule possède un élément focusable, le focus sera directement sur cet élément
*/
function moveFocusCell(target, direction, position) {
const parentCell = isCellElement(target) ? target : target.closest('td, th');
if (!parentCell)
return;
const cells = parentCell.parentElement.children;
let nextCellIndex = getNextCellIndex(cells, parentCell, direction, position);
if (nextCellIndex !== null && cells[nextCellIndex]) {
const nextCell = cells[nextCellIndex];
setFocusCellOrFocusableElement(nextCell);
}
}
function moveFocusToFirstParentCell(target) {
const cell = target.closest('td, th');
if (cell) {
cell.focus();
cell.setAttribute('tabindex', '0');
}
}
/**
* Si la cellule possède un élément focusable, le focus sera directement sur cet élément
*/
function setFocusCellOrFocusableElement(nextCell) {
if (!nextCell)
return;
const focusableElement = findFirstFocusableElement(nextCell);
if (focusableElement) {
focusableElement.focus();
}
else {
nextCell.focus();
nextCell.setAttribute('tabindex', '0');
}
}
function getNextCellIndex(cells, targetCell, direction, position) {
if (position === 'start')
return 0;
if (position === 'end')
return cells.length - 1;
const currentIndex = Array.prototype.indexOf.call(cells, targetCell);
const nextIndex = currentIndex + direction;
return nextIndex >= 0 && nextIndex < cells.length ? nextIndex : null;
}
function isCellElement(element) {
return element.tagName === 'TD' || element.tagName === 'TH';
}
/**
* Renvoie le premier élément HTML focusable dans l'élément HTML donné. Sinon renvoie null.
* https://developer.mozilla.org/fr/docs/Web/API/Document/querySelector
*/
function findFirstFocusableElement(element) {
const focusableElementsCssSelectors = FOCUSABLE_HTML_ELEMENTS.join(', ');
return element?.querySelector(focusableElementsCssSelectors);
}
/**
* L'élément est un widget interactif qui nécessite l'utilisation des fleches et donc désactiver la nav grid
* - input éditable ou [contentEditable]
* - select
*/
function isEditableElement(element) {
if (element.isContentEditable)
return true;
if (element instanceof HTMLInputElement) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
if (/text|email|number|password|search|tel|url/.test(element.type || '')) {
return !(element.disabled || element.readOnly);
}
}
if (element instanceof HTMLTextAreaElement)
return !(element.disabled || element.readOnly);
if (element instanceof HTMLSelectElement)
return !element.disabled;
return false;
}
/**
* Directive navigation clavier sur la table role treegrid
* https://www.w3.org/WAI/ARIA/apg/patterns/treegrid/examples/treegrid-1/
* => scoTreeGridNavigation de sco-widgets
*/
class DsfrTreeGridNavigationDirective {
constructor() {
this.hasInteractiveTheadElements = false;
this._element = inject(ElementRef);
afterNextRender(() => {
if (this.role !== 'treegrid' && this.role !== 'grid')
return;
this.tableHtml = this._element.nativeElement;
this.setFocusableRow();
this.observer = new MutationObserver(() => {
this.setFocusableRow();
});
if (this.tableHtml) {
this.observer.observe(this.tableHtml, { childList: true, subtree: true });
}
});
}
setFocusableRow() {
if (!this.tableHtml)
return;
// Si le thead contient des éléments interactifs => inclure dans la navigation calvier
const theadRows = this.tableHtml.querySelectorAll('thead tr');
this.hasInteractiveTheadElements = this.hasInteractiveElementsOnHeaderRows(theadRows);
// Récupérer l'ensemble des ligne concernée(inclure le thead si nécessaire) par la navigation clavier
this.tableRows = !this.hasInteractiveTheadElements
? this.tableHtml.querySelector('tbody')?.querySelectorAll('tr')
: this.tableHtml.querySelectorAll('tr');
this.tableRows?.forEach((row, i) => {
row.setAttribute('tabindex', '-1');
row.onkeydown = (e) => this.handleTreeGridKeydown(e); // ajouter event listener
this.setGridCellRole(row, i === 0); // ajouter role=gridcell sur toutes les cellules
});
}
/**
* Les lignes du header de la table possèdent au moins un élément interactif qui doit prendre le focus
* @param theadRows lignes du header
*/
hasInteractiveElementsOnHeaderRows(theadRows) {
if (!theadRows || theadRows.length === 0)
return false;
for (const row of Array.from(theadRows)) {
const cells = Array.from(row.querySelectorAll('th, td'));
const hasFocusable = cells.some((cell) => findFirstFocusableElement(cell));
if (hasFocusable)
return true;
}
return false;
}
/**
* Rendre les cellules focusables
*/
setGridCellRole(row, isFirstRow) {
const cells = row.querySelectorAll('td,th');
cells?.forEach((cell, i) => {
if (!cell.getAttribute('tabindex')) {
// Rendre focusable <td>,<th>. La premiere cellule focusable a tabindex 0
cell.setAttribute('tabindex', i === 0 && isFirstRow ? '0' : '-1');
}
if (cell.tagName === 'TD' && this.role === 'treegrid') {
// Ajout du rôle gridcell uniquement sur les td et en mode treegrid (non nécessaire en grid - WCAG)
cell.setAttribute('role', 'gridcell');
}
});
}
handleTreeGridKeydown(event) {
if (!event.target)
return;
const target = event.target;
const cell = target.closest('td, th');
// index de cette cellule dans sa ligne
const currentCellIndex = cell ? Array.from(cell.parentElement.children).indexOf(cell) : 0;
// index de cette ligne
const currentRowIndex = getCurrentRowIndex(target, this.hasInteractiveTheadElements);
if (isEditableElement(target)) {
this.toggleGridNavigationForEditable(event, target);
return;
}
switch (event.key) {
case 'ArrowDown':
case 'PageDown':
this.hasToMoveFocus(event, cell);
moveFocusRow(currentRowIndex, currentCellIndex, 1, this.tableRows);
break;
case 'ArrowUp':
case 'PageUp':
this.hasToMoveFocus(event, cell);
moveFocusRow(currentRowIndex, currentCellIndex, -1, this.tableRows);
break;
case 'ArrowRight':
this.hasToMoveFocus(event, cell);
moveFocusCell(target, 1);
break;
case 'ArrowLeft':
this.hasToMoveFocus(event, cell);
moveFocusCell(target, -1);
break;
case 'Home':
this.hasToMoveFocus(event, cell);
if (event.ctrlKey) {
moveFocusRow(currentRowIndex, currentCellIndex, 1, this.tableRows, 'start');
}
else {
moveFocusCell(target, -1, 'start'); //Control + Home moves focus to the first cell in the first row.
}
break;
case 'End':
this.hasToMoveFocus(event, cell);
if (event.ctrlKey) {
moveFocusRow(currentRowIndex, currentCellIndex, 1, this.tableRows, 'end');
}
else {
moveFocusCell(target, -1, 'end'); //Control + End moves focus to the last cell in the last row.
}
break;
case 'Escape':
this.hasToMoveFocus(event, cell);
if (target.tagName !== 'TD' && target.tagName !== 'TH') {
moveFocusToFirstParentCell(target);
}
break;
case 'Enter':
case 'Spacebar':
case ' ':
const focusableElement = findFirstFocusableElement(event.target);
if (focusableElement) {
focusableElement?.focus();
// Remise à -1 du tabindex uniquement si la cellule contient un élément focusable
if (cell && cell.getAttribute('tabIndex') === '0') {
cell.setAttribute('tabIndex', '-1');
}
}
break;
default:
break;
}
}
/**
* Le focus change de cellule dans le tableau. Prevent default et la cellule précédente reprendre un tabIndex de -1
* @param event target courante
* @param currentCellFocused
*/
hasToMoveFocus(event, currentCellFocused) {
event.preventDefault();
if (currentCellFocused &&
currentCellFocused.getAttribute('tabIndex') === '0' &&
!currentCellFocused.getAttribute('aria-expanded')) {
currentCellFocused.setAttribute('tabIndex', '-1');
}
}
/**
* En cas d'élément éditable dans une cellule, qui utilise les flèches pour l'édition
* La navigation dans la grid doit être désactivée/activée selon le focus de cet élément
* @param event keyboard event
* @param target element dans la cellule
*/
toggleGridNavigationForEditable(event, target) {
switch (event.key) {
case 'Escape':
case 'Enter':
event.preventDefault();
moveFocusToFirstParentCell(target);
break;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTreeGridNavigationDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: DsfrTreeGridNavigationDirective, isStandalone: true, selector: "[dsfrTreeGridNavigation]", inputs: { role: ["dsfrTreeGridNavigation", "role"] }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTreeGridNavigationDirective, decorators: [{
type: Directive,
args: [{
selector: '[dsfrTreeGridNavigation]',
standalone: true,
}]
}], ctorParameters: () => [], propDecorators: { role: [{
type: Input,
args: ['dsfrTreeGridNavigation']
}] } });
class DsfrTableComponent extends BasePaginatedTableComponent {
constructor() {
super(...arguments);
/** Identifiant de la table */
this.tableId = '';
/** Rend les th sticky en haut du tableau. */
this.thSticky = false;
/** Le composant se bloquera au top de l'écran et prendra toute sa hauteur.*/
this.tableHeaderSticky = false;
/** Données */
this.data = [];
/** Options d'affichage du tableau */
this.tableOptions = {};
/** Afficher le nombre de résultat total dans le footer */
this.showFooterResult = true;
/** Indique que les données sont en cours de chargement */
this.loading = false;
/** Header: affiche la barre de recherche dans le header */
this.showSearch = false;
/** Header: affiche le changement de type de vue (tableau / liste) */
this.showHeaderViews = false;
/** Header: redéfinit les vues du contrôle segmenté (si changeDisplayType). Par défaut tableau/liste */
this.headerViewsOptions = [
{
label: 'Tableau',
value: 'TABLE',
checked: true,
icon: 'fr-icon-table-line',
},
{
label: 'Liste',
value: 'LIST',
icon: 'fr-icon-list-unordered',
},
];
/** Liste des identifiants des lignes initialement sélectionnées */
this.initialSelection = [];
/** Renvoie la colonne triée au changement de tri de la table */
this.sortChange = new EventEmitter();
/** Renvoie la vue sélectionnée au changement de vue (displayModes) */
this.viewSelect = new EventEmitter(); // ou displayList
/** Renvoie la valeur de l'input de la barre de recherche au changement. */
this.searchChange = new EventEmitter();
/** Renvoie la valeur de l'input de la barre de recherche au clic sur rechercher. */
this.searchSelect = new EventEmitter();
/** A la sélection d'une ligne, émet toutes les lignes sélectionnées et la ligne sélectionnée */
this.selectionChange = new EventEmitter();
/** Case à cocher tout sélectionner cochée ou décochée */
this.showSelectAllChange = new EventEmitter();
/** @internal pour permettre la mise a jour du select all en header */
this.isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
/**@internal pas de choix de vue, ou vue table sélectionnée */
this.showTableView = true;
/** @internal Observable de l'état du tableau pour le rafraichissement des données */
this.tableState$ = toObservable(this.dataTableService.tableState);
/** @internal liste des différentes classes de la table */
this.tableClasses = computed(() => {
const tableOptions = this.dataTableService.tableOptions();
if (tableOptions) {
return this.setTableClasses(tableOptions);
}
return [];
}, ...(ngDevMode ? [{ debugName: "tableClasses" }] : []));
this.isSortable = false;
// données en entrée modifiées
this.dataChanged$ = new BehaviorSubject([]);
this.notifyOnDestroy = new Subject();
this.logger = inject(LoggerService);
this._displayedRows = []; // données affichées modifiées par l'état
this._selectedRows = []; // lignes sélectionnées
}
/** Récupérer les données couramment affichées dans le tableau */
get displayedRows() {
return this._displayedRows;
}
/** Récupérer les groupes créés dans le tableau */
get groups() {
if (!this.groupBy)
return [];
return this.displayedRows.filter((r) => r.key);
}
/** Récupérer les données sélectionnées dans le tableau */
get selectedRows() {
return this._selectedRows;
}
ngOnInit() {
if (!this.tableId) {
this.tableId = newUniqueId();
}
this.dataTableService.setTableOptions(this.tableOptions); // application des options d'affichage du tableau
if (!this.serverSide)
this.totalElements.set(this.data.length);
// Initialisation de l'état
if (this.initialState) {
this.dataTableService.setState(this.initialState);
}
else if (this.rowsPerPageOptions[0].value !== 10) {
// La pagination par défaut n'est pas 10, initialisation de l'état sur la pagination correcte
this.initialState = { rowsPerPage: this.rowsPerPageOptions[0].value, sort: null, page: 1 };
this.dataTableService.setState(this.initialState);
}
this.dataTableService.selection$.pipe(takeUntil(this.notifyOnDestroy)).subscribe((selectedRows) => {
this.refreshSelection(selectedRows);
});
// Abonnement à la mise a jour des données
this.registerRefreshData();
// Initialisation des données
this.dataTableService.refreshData(this.data);
this.isSortable = this.hasSortableColumn();
if (!this.caption && !this.tableOptions?.ariaLabelledBy) {
this.logger.warn('dsfr-datatable : Renseigner le titre du tableau est obligatoire (caption ou labelledBy).');
}
if ((this.rowsOptions || (this.pagination && this.tableOptions?.selectable)) && !this.dataKey) {
this.logger.warn("dsfr-datatable : La propriété dataKey n'est pas définie.");
}
if (this.serverSide && this.serverResultsLength === undefined) {
this.logger.warn('dsfr-datatable : En mode serveur serverResultsLength doit être renseigné.');
}
}
/**
* Au changement de data / rowsOptions / tableOptions
* Rafraichir les données et la vue
* @param changes
*/
ngOnChanges(changes) {
// Ignorer le premier changement à l'initialisation (pas de double appel)
if ((changes.data && !changes.data.isFirstChange()) || (!changes.data && changes.rowsOptions)) {
this.dataTableService.refreshData(this.data); // rafraichir les données affichées
}
// Changement de tableOptions dynamiquement
if (changes.tableOptions && !changes.tableOptions.isFirstChange()) {
this.dataTableService.setTableOptions(this.tableOptions);
}
}
ngOnDestroy() {
this.notifyOnDestroy.next();
this.notifyOnDestroy.complete();
}
/**
* Affichage du tableau selon la colonne concernées et mise a jour des données affichées
* @internal
**/
onSortChange() {
this.sortChange.emit(this.dataTableService.activeSort());
}
/**
* Dans le cas ou la directive selectAll n'est pas controlée par les sous directives dsfrSelectCell
* Mettre à jour manuellement la directive isAllSelected (par ex. depuis composant parent TreeTable)
* @param nbSelectedRows nombre de lignes couramment sélectionnées
* @param isAllSelected boolean
*/
manuallySetIsAllSelected(nbSelectedRows, isAllSelected) {
this.countSelectedRows = nbSelectedRows;
if (nbSelectedRows > 0 && !isAllSelected) {
this.isAllSelected.set(undefined);
}
else {
this.isAllSelected.set(isAllSelected);
}
}
trackById(dataKey, index, item) {
return dataKey ? item[dataKey] : index;
}
onviewSelect(view) {
this.showTableView = view === this.headerViewsOptions[0].value;
this.viewSelect.emit(view);
}
onsearchChange(search) {
this.searchChange.emit(search);
}
onsearchSelect(search) {
this.searchSelect.emit(search);
}
/**
* Initialisation de la table si sélection
* - calcul du nombre d'elements selectionnables
* - application de la sélection initiale
* - coche de isAllSelected si toutes les lignes sont sélectionnées
*/
initSelectableTable(rows) {
this.countSelectedRows = this.initialSelection?.length;
this.selectableRows = this.setSelectableRows(rows);
if (!this.dataKey || !this.selectableRows)
return;
const key = this.dataKey;
// Premiere initialisation ou serverside
const isInit = this._selectedRows.length === 0 || this.serverSide;
if (this.countSelectedRows && isInit) {
this._selectedRows = this.selectableRows.filter((row) => this.initialSelection.includes(row[key]));
}
else if (this._selectedRows.length > 0 && !this.serverSide) {
// Lignes déja sélectionnées (rafraichissement de la vue)
const selectableIds = new Set(this.selectableRows.map((r) => r[key]));
this._selectedRows = this._selectedRows.filter((selected) => selectableIds.has(selected[key]));
}
this._selectedRows.forEach((r) => r.selected.set(true));
this.isAllSelected.set(this.checkAllRowsSelected());
}
setSelectableRows(rows) {
return this.rowsOptions ? rows.filter((r) => !r.rowOptions || !r.rowOptions.disableSelect) : rows;
}
/** Sélection ou déselection des lignes à la sélection de la case à cocher en en-tête **/
toggleSelectAll() {
if (!this.dataKey)
return;
if (this.tableOptions.selectionScope === 'currentPage' || this.serverSide) {
this.selectRowsOnCurrentPage(this.dataKey);
}
else {
this.selectAllRows();
}
const isSelected = this.isAllSelected() === true;
for (const row of this.selectableRows) {
row.selected.set(isSelected); // mise a jour visuelle des checkboxes
}
this.countSelectedRows = this._selectedRows.length;
this.selectionChange.emit({ row: null, selectedRows: [...this._selectedRows] });
this.showSelectAllChange.emit(isSelected);
}
/** Sélection ou déselection d'une ligne **/
selectRow(row) {
if (!this.dataKey)
return;
const selectedIndex = this._selectedRows.findIndex((r) => r[this.dataKey] === row[this.dataKey]);
if (selectedIndex !== -1) {
this._selectedRows.splice(selectedIndex, 1); // suppression de la ligne
}
else {
this._selectedRows.push(row); // ajout de la ligne
}
this.countSelectedRows = this._selectedRows.length;
row.selected.update((s) => !s);
this.isAllSelected.set(this.checkAllRowsSelected());
this.selectionChange.emit({ row: row, selectedRows: [...this._selectedRows] });
}
/**
* Sélectionne ou déselectionne toutes les lignes de la page courante
* N'agit pas sur la sélection/déselection des lignes des autres pages
* @param key datakey
*/
selectRowsOnCurrentPage(key) {
const currentSelectedIds = new Set(this._selectedRows.map((r) => r[key]));
const rowsNotSelected = this.selectableRows.filter((r) => !currentSelectedIds.has(r[key]));
const isAllSelectedOnPage = rowsNotSelected.length === 0;
if (isAllSelectedOnPage) {
const idsToRemove = new Set(this.selectableRows.map((r) => r[key]));
this._selectedRows = this._selectedRows.filter((r) => !idsToRemove.has(r[key]));
}
else {
this._selectedRows = [...this._selectedRows, ...rowsNotSelected];
}
this.isAllSelected.set(!isAllSelectedOnPage);
}
/*** Sélectionne ou déselectionne toutes les lignes du tableau **/
selectAllRows() {
const allSelected = this._selectedRows.length === this.selectableRows?.length;
this._selectedRows = allSelected ? [] : [...this.selectableRows];
this.isAllSelected.set(!allSelected);
}
/**
* Détermine l'état de la checkbox de sélection globale selon selectionScope.
* @returns true (tout), false (rien), undefined (partiel)
*/
checkAllRowsSelected() {
if (!this.dataKey || !this.countSelectedRows || this.countSelectedRows === 0)
return false;
// Cas sélection par page
if (this.tableOptions.selectionScope === 'currentPage' || this.serverSide) {
const key = this.dataKey;
const selectedIds = new Set(this._selectedRows.map((r) => r[key]));
const countSelectedOnPage = this.selectableRows.filter((r) => selectedIds.has(r[key])).length;
if (countSelectedOnPage === this.selectableRows.length)
return true;
return countSelectedOnPage === 0 ? false : undefined;
}
// Cas sélection globale
if (this.countSelectedRows >= this.selectableRows?.length)
return true;
return undefined;
}
/** Mise a jour des classes de fr-table selon tableOptions */
setTableClasses(tableOptions) {
const classes = ['fr-table'];
if (tableOptions.bordered)
classes.push('fr-table--bordered');
if (tableOptions.noScroll)
classes.push('fr-table--no-scroll');
if (tableOptions.bottomCaption)
classes.push('fr-table--caption-bottom');
if (this.tableOptions.allowWrap)
classes.push('fr-table--multiline');
if (tableOptions.captionSrOnly)
classes.push('fr-table--no-caption');
if (this.fixedHeight) {
classes.push('edu-fixed--height');
if (Number(this.fixedHeight))
this.fixedHeight = `${this.fixedHeight}px`;
}
if (this.thSticky)
classes.push('edu-th--sticky');
if (this.tableHeaderSticky) {
classes.push('edu-table--sticky');
this.fixedHeight = "'90vh'";
}
// Size class
switch (tableOptions.cellSize) {
case 'SM':
classes.push('fr-table--sm');
break;
case 'LG':
classes.push('fr-table--lg');
break;
default:
break;
}
return classes;
}
/**
* Détermine si le tableau contient au moins une colonne triable
*/
hasSortableColumn() {
return !!this.columns.find((column) => column.sortable);
}
/** Fonction de tri : sortFunction de la colonne si définie ou par défaut sur du contenu texte simple */
sortRows(rows, state) {
if (!state.sort?.field)
return rows;
const { field, order } = state.sort;
const column = this.columns.find((c) => c.field === field);
// tri selon la fonction définie sur la colonne
if (column && column.sortFunction && typeof column.sortFunction === 'function') {
return rows.sort((a, b) => column.sortFunction(a, b, order));
}
// tri par défaut
return rows.sort((a, b) => this.sortByDefault(a, b, field, order));
}
/**
* Abonnement à la mise a jour des données et au changement d'état pour rafraichir le tableau
*/
registerRefreshData() {
if (!this.serverSide) {
// Mise à jour des données lorsque refreshData appelé
this.dataTableService.data$.pipe(takeUntil(this.notifyOnDestroy)).subscribe((data) => {
this.dataChanged$.next(this.mapDataToRows(data));
});
// Mise à jour des lignes affichées si l'état a changé et/ou les données
this.displayedRows$ = combineLatest([this.dataChanged$, this.tableState$]).pipe(map(([rows, state]) => this.applyStateToData(rows, state)));
}
else {
// si serveur side, abonnement uniquement au changement des données (état géré par l'utilisateur en distant)
// et à dataTableService et non dataChanged pour ne pas déclencher deux fois l'event
this.displayedRows$ = this.dataTableService.data$.pipe(takeUntil(this.notifyOnDestroy), map((data) => this.mapDataToRows(data)), map((rows) => this.groupRows(rows)));
this.tableState$.pipe(takeUntil(this.notifyOnDestroy)).subscribe((state) => this.stateChange.emit(state));
}
}
/**
* Application de l'état sur les données affichées apres refresh data ou tri/pagination
* @param data données modifiées
* @param state état (tri, pagination, page size)
* @returns données à jour triées et paginées
*/
applyStateToData(data, state) {
this.dataTableService.isDataRendered.set(false); // forcer le calcul du rendu des cellules en cas de fixed
if (this.groupBy && this.groupBy.key) {
data = data.sort((a, b) => this.sortByDefault(a, b, this.groupBy.key, 'ascending'));
}
if (state) {
// application du tri
if (state.sort) {
data = this.sortRows(data, state);
}
this._displayedRows = data;
if (this.pagination) {
data = this.paginateData(data, state);
}
else {
this.totalElements.set(this.data.length);
}
}
if (this.tableOptions.selectionScope === 'currentPage') {
this.selectableRows = this.setSelectableRows(data);
this.isAllSelected.set(this.checkAllRowsSelected());
}
return this.groupBy ? this.groupRows(data) : data;
}
/**
* Retourne les options de la ligne passée en paramètre, selon l'identifiant ou l'index
* */
getRowOption(row) {
if (!this.rowsOptions || !row)
return undefined;
return this.dataKey ? this.rowsOptions.find((r) => r.id === row[this.dataKey]) : undefined;
}
/**
* Map data au format lignes affichables et application de la sélection
* @param data
* @returns data avec informations d'affichage et index
*/
mapDataToRows(data) {
const rows = data.map((row, i) => ({
...row,
index: i,
selected: signal(!!this._selectedRows.find((r) => r.id === row.id)),
rowOptions: this.getRowOption(row),
}));
if (this.tableOptions?.selectable) {
this.initSelectableTable(rows);
}
this._displayedRows = rows;
return rows;
}
/** Mettre à jour la sélection lors de l'appel a la methode sur le service */
refreshSelection(newSelectedRows) {
this.selectableRows.forEach((row) => {
if (row.selected()) {
row.selected.set(false);
}
});
if (this.dataKey) {
this._selectedRows = this.selectableRows.filter((row) => newSelectedRows.includes(row[this.dataKey]));
this._selectedRows.forEach((row) => {
if (!row.selected()) {
row.selected.set(true);
}
});
}
this.countSelectedRows = this._selectedRows.length;
this.isAllSelected.set(this.checkAllRowsSelected());
}
/** Fonction de tri par défaut insensible à la casse et aux diacritiques */
sortByDefault(a, b, field, order) {
let valueA = a[field] ?? ''; // Remplacer undefined par chaîne vide
let valueB = b[field] ?? '';
// Tri par défaut si data est défini (type de données complexe Node)
if (!valueA && !valueB && a.data && b.data) {
valueA = a.data[field] ?? '';
valueB = b.data[field] ?? '';
}
if (!isNaN(Number(valueA)) && !isNaN(Number(valueB))) {
return (Number(valueA) - Number(valueB)) * (order === 'ascending' ? 1 : -1);
}
return valueA === valueB ? 0 : valueA.localeCompare(valueB) * (order === 'ascending' ? 1 : -1);
}
/**
* Création des groupes selon groupBy
* @returns liste des données par groupes et lignes sans groupes
*/
groupRows(rows) {
if (!this.groupBy)
return rows;
const groups = [];
let countGroups = 1;
rows.forEach((row) => {
const currentKey = this.groupBy && this.groupBy.key ? row[this.groupBy.key] : undefined;
if (currentKey) {
let currentGroup = groups.find((g) => g.key === currentKey);
if (!currentGroup) {
currentGroup = {
key: currentKey,
rows: [],
ariaPosinset: countGroups,
total: 0,
};
if (this.groupBy?.expandable)
currentGroup.collapsed = signal(!this.groupBy.expanded, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
groups.push(currentGroup);
countGroups++;
}
currentGroup.rows.push(row);
}
else {
groups.push(row);
}
});
// Calculer les totaux des groupes sans pagination
const counts = new Map();
for (const row of this.data) {
const currentKey = row[this.groupBy.key];
if (currentKey) {
counts.set(currentKey, (counts.get(currentKey) ?? 0) + 1);
}
}
return groups.map((g) => ({ ...g, ariaSetsize: countGroups, total: counts.get(g.key) ?? 0 }));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTableComponent, isStandalone: true, selector: "dsfr-datatable", inputs: { headerActionsTemplateRef: "headerActionsTemplateRef", headerSummaryTemplateRef: "headerSummaryTemplateRef", headerSelectedRowCountTemplateRef: "headerSelectedRowCountTemplateRef", tableHeaderTemplateRef: "tableHeaderTemplateRef", tableFooterTemplateRef: "tableFooterTemplateRef", footerActionsTemplateRef: "footerActionsTemplateRef", footerResultsTemplateRef: "footerResultsTemplateRef", loadingTemplateRef: "loadingTemplateRef", alternativeViewTemplateRef: "alternativeViewTemplateRef", tableId: "tableId", caption: "caption", fixedHeight: "fixedHeight", thSticky: "thSticky", tableHeaderSticky: "tableHeaderSticky", data: "data", dataKey: "dataKey", tableOptions: "tableOptions", rowsOptions: "rowsOptions", columns: "columns", showFooterResult: "showFooterResult", emptyResultsMessage: "emptyResultsMessage", loading: "loading", showSearch: "showSearch", searchInputPlaceholder: "searchInputPlaceholder", searchLabel: "searchLabel", searchButtonTitle: "searchButtonTitle", searchInputValue: "searchInputValue", showHeaderViews: ["showHeaderViews", "showHeaderViews", booleanAttribute], headerViewsOptions: "headerViewsOptions", initialSelection: "initialSelection", groupBy: "groupBy" }, outputs: { sortChange: "sortChange", viewSelect: "viewSelect", searchChange: "searchChange", searchSelect: "searchSelect", selectionChange: "selectionChange", showSelectAllChange: "showSelectAllChange" }, providers: [
{
provide: DSFR_TABLE_CONTEXT,
useExisting: DsfrTableComponent,
},
{
provide: DsfrDataTableService,
useFactory: (parent = inject(DsfrDataTableService, { optional: true, skipSelf: true })) => parent ?? new DsfrDataTableService(),
},
], queries: [{ propertyName: "headerActionsTemplate", first: true, predicate: ["headerActionsTemplate"], descendants: true, static: true }, { propertyName: "headerSummaryTemplate", first: true, predicate: ["headerSummaryTemplate"], descendants: true, static: true }, { propertyName: "headerSelectedRowCountTemplate", first: true, predicate: ["headerSelectedRowCountTemplate"], descendants: true, static: true }, { propertyName: "tableHeaderTemplate", first: true, predicate: ["tableHeaderTemplate"], descendants: true, static: true }, { propertyName: "tableBodyTemplate", first: true, predicate: ["tableBodyTemplate"], descendants: true, static: true }, { propertyName: "tableFooterTemplate", first: true, predicate: ["tableFooterTemplate"], descendants: true, static: true }, { propertyName: "rowGroupHeaderTemplate", first: true, predicate: ["rowGroupHeaderTemplate"], descendants: true, static: true }, { propertyName: "rowGroupTemplate", first: true, predicate: ["rowGroupTemplate"], descendants: true, static: true }, { propertyName: "footerResultsTemplate", first: true, predicate: ["footerResultsTemplate"], descendants: true, static: true }, { propertyName: "footerActionsTemplate", first: true, predicate: ["footerActionsTemplate"], descendants: true, static: true }, { propertyName: "loadingTemplate", first: true, predicate: ["loadingTemplate"], descendants: true, static: true }, { propertyName: "alternativeViewTemplate", first: true, predicate: ["alternativeViewTemplate"], descendants: true, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div [class]=\"tableClasses()\">\n <edu-table-header\n (viewSelect)=\"onviewSelect($event)\"\n [showHeaderViews]=\"showHeaderViews\"\n [showSearch]=\"showSearch\"\n [searchInputValue]=\"searchInputValue\"\n [searchInputPlaceholder]=\"searchInputPlaceholder\"\n [searchButtonTitle]=\"searchButtonTitle\"\n [searchLabel]=\"searchLabel\"\n (searchChange)=\"onsearchChange($event)\"\n (searchSelect)=\"onsearchSelect($event)\"\n [headerViewsOptions]=\"headerViewsOptions\"\n [countSelectedRowsTemplate]=\"headerSelectedRowCountTemplateRef ?? headerSelectedRowCountTemplate\"\n [headerActionsTemplate]=\"headerActionsTemplateRef ?? headerActionsTemplate\"\n [countSelectedRows]=\"tableOptions.selectable ? countSelectedRows : undefined\">\n </edu-table-header>\n @if (showTableView) {\n <div class=\"fr-table__wrapper\">\n <div class=\"fr-table__container\">\n <div class=\"fr-table__content\" [style.max-height]=\"fixedHeight ? fixedHeight : 'initial'\">\n <!-- prettier-ignore -->\n @if (!caption && totalElements() === 0) {\n <span [id]=\"tableId + '-no-result-description'\" class=\"fr-sr-only\">{{ 'table.noResult' | dsfrI18n }}</span>\n }\n @if (!caption && isSortable) {\n <span [id]=\"tableId + '-sort-explanation'\" class=\"fr-sr-only\">{{\n 'table.sortExplanation' | dsfrI18n\n }}</span>\n }\n <table\n [id]=\"tableId\"\n [attr.role]=\"tableOptions.role ? tableOptions.role : null\"\n [dsfrTreeGridNavigation]=\"tableOptions.role\"\n [attr.aria-labelledby]=\"!caption && tableOptions.ariaLabelledBy ? tableOptions.ariaLabelledBy : null\"\n [attr.aria-describedby]=\"\n !caption\n ? (totalElements() === 0 ? tableId + '-no-result-description ' : '') +\n (isSortable && totalElements() !== 0 ? tableId + '-sort-explanation ' : '') +\n (tableOptions.ariaDescribedBy ?? '')\n : null\n \">\n @let resolveHeaderSummaryTemplate = headerSummaryTemplateRef ?? headerSummaryTemplate;\n @if (caption || resolveHeaderSummaryTemplate) {\n <caption>\n {{\n caption\n }}\n @if (totalElements() === 0) {\n <span class=\"fr-sr-only\">({{ 'table.noResult' | dsfrI18n }})</span>\n }\n @if (isSortable && totalElements() !== 0) {\n <span class=\"fr-sr-only\">({{ 'table.sortExplanation' | dsfrI18n }})</span>\n }\n @if (resolveHeaderSummaryTemplate) {\n <div class=\"fr-table__caption__desc\">\n <ng-container *ngTemplateOutlet=\"resolveHeaderSummaryTemplate\"></ng-container>\n </div>\n }\n </caption>\n }\n <!-- Header ------------------------------------------------------------------------------------------------->\n <thead>\n <!-- Slot -->\n @let resolveTableHeaderTemplate = tableHeaderTemplateRef ?? tableHeaderTemplate;\n\n @if (resolveTableHeaderTemplate) {\n <ng-container *ngTemplateOutlet=\"resolveTableHeaderTemplate\"></ng-container>\n } @else {\n <tr>\n @if (tableOptions.selectable && !tableOptions.showSelectAll) {\n <th class=\"fr-cell--fixed fr-col--xs\" role=\"columnheader\">\n <span class=\"fr-sr-only\"> {{ 'commons.select' | dsfrI18n }}</span>\n </th>\n } @else if (tableOptions.selectable && tableOptions.showSelectAll) {\n <th dsfrSelectAll></th>\n }\n @for (col of columns; track col.field) {\n <th\n [attr.colspan]=\"col.colspan\"\n id=\"{{ tableId }}-header-{{ col.field }}\"\n [dsfrColumn]=\"col\"\n scope=\"col\">\n @if (col.labelSrOnly) {\n <span class=\"fr-sr-only\">{{ col.label ?? col.field }}</span>\n } @else {\n {{ col.label ?? col.field }}\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <!-- Body ------------------------------------------------------------------------------------------------->\n <tbody>\n @if (groupBy) {\n <!-- Affichages des groupes --------------------------------------------------------------------------->\n @let rows = displayedRows$ | async;\n @for (group of rows; let i = $index; track group.key) {\n @if (!group.rows) {\n <!-- Ligne sans groupe -->\n <ng-container\n [ngTemplateOutlet]=\"cellsDefaultTemplate\"\n [ngTemplateOutletContext]=\"{ row: group }\"></ng-container>\n } @else {\n <!-- Affichage de la ligne parent d'un groupe, expandable ou non -->\n <ng-container\n [ngTemplateOutlet]=\"displayRowGroupHeader\"\n [ngTemplateOutletContext]=\"{ group: group, index: i }\"></ng-container>\n\n @if (!groupBy.expandable || !group.collapsed()) {\n @for (row of group.rows; let j = $index; track trackById.bind(this, this.dataKey)(j, row)) {\n <!-- Affichage des lignes d'un groupe -->\n <ng-container\n [ngTemplateOutlet]=\"displayRowGroup\"\n [ngTemplateOutletContext]=\"{ group: group, index: j, row: row }\"></ng-container>\n }\n }\n }\n }\n } @else {\n @for (row of displayedRows$ | async; track trackById.bind(this, this.dataKey)(i, row); let i = $index) {\n <!-- Affichages des lignes simples ------------------------------------------------------------------>\n @if (tableBodyTemplate) {\n <!-- Slot -->\n <ng-container\n *ngTemplateOutlet=\"\n tableBodyTemplate;\n context: { $implicit: row, index: i, rowOptions: row.rowOptions }\n \"></ng-container>\n } @else {\n <tr>\n <!-- Default -->\n <ng-container\n [ngTemplateOutlet]=\"cellsDefaultTemplate\"\n [ngTemplateOutletContext]=\"{ row: row }\"></ng-container>\n </tr>\n }\n }\n }\n @if (!loading && totalElements() === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (tableOptions.selectable ? 1 : 0)\">\n {{ emptyResultsMessage ?? ('table.noResult' | dsfrI18n) }}\n </td>\n </tr>\n }\n </tbody>\n @let resolveTableFooterTemplate = tableFooterTemplateRef ?? tableFooterTemplate;\n\n @if (resolveTableFooterTemplate) {\n <tfoot>\n <ng-container *ngTemplateOutlet=\"resolveTableFooterTemplate\"></ng-container>\n </tfoot>\n }\n </table>\n <!-- Afficher un template de chargement si d\u00E9fini -->\n @let resolveLoadingTemplate = loadingTemplateRef ?? loadingTemplate;\n\n @if (loading && resolveLoadingTemplate) {\n <ng-container [ngTemplateOutlet]=\"resolveLoadingTemplate\"></ng-container>\n }\n </div>\n </div>\n </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"alternativeViewTemplateRef ?? this.alternativeViewTemplate\"></ng-template>\n }\n\n <!-- Footer -->\n @let resolveFooterActionsTemplate = footerActionsTemplateRef ?? footerActionsTemplate;\n @if (showFooterResult || pagination || resolveFooterActionsTemplate) {\n <dsfr-table-footer\n [initialState]=\"initialState\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [currentPage]=\"dataTableService.tableState().page\"\n (rowsPerPageChange)=\"onRowsPerPageChange($event)\"\n (pageSelect)=\"onPageSelect($event)\"\n [showPagination]=\"pagination\"\n [disabledRowsPerPage]=\"totalElements() === 0\"\n [totalPage]=\"totalPage()\"\n [showFooterResult]=\"showFooterResult\"\n [emptyResultsMessage]=\"emptyResultsMessage ?? ('table.noResult' | dsfrI18n)\"\n [footerActionsTemplate]=\"resolveFooterActionsTemplate\"\n [footerResultsTemplate]=\"footerResultsTemplateRef ?? this.footerResultsTemplate\"\n [totalElements]=\"totalElements()\">\n </dsfr-table-footer>\n }\n</div>\n\n<!-- Template de la ligne parent d'un groupe -------------------------------------------------------------------------->\n<ng-template #displayRowGroupHeader let-group=\"group\" let-i=\"index\">\n @if (rowGroupHeaderTemplate) {\n <!-- Slot -->\n <ng-container\n [ngTemplateOutlet]=\"rowGroupHeaderTemplate\"\n [ngTemplateOutletContext]=\"{ group: group, index: i }\"></ng-container>\n } @else {\n <!-- Default -->\n <tr dsfrRowGroupHeader [group]=\"group\">\n <td [attr.colspan]=\"columns.length + (tableOptions.selectable ? 2 : 1)\">\n {{ group.key }}\n </td>\n </tr>\n }\n</ng-template>\n\n<!-- Template de la ligne d'un groupe --------------------------------------------------------------------------------->\n<ng-template #displayRowGroup let-group=\"group\" let-i=\"index\" let-row=\"row\">\n @if (rowGroupTemplate) {\n <!-- Slot -->\n <ng-container\n [ngTemplateOutlet]=\"rowGroupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row, group: group, index: i, row: row }\"></ng-container>\n } @else {\n <!-- Default -->\n <tr dsfrRowGroup [index]=\"i\" [group]=\"group\">\n <ng-container [ngTemplateOutlet]=\"cellsDefaultTemplate\" [ngTemplateOutletContext]=\"{ row: row }\"></ng-container>\n </tr>\n }\n</ng-template>\n\n<!-- Template par d\u00E9faut des cellules -------------------------------------------------------------------------------->\n<ng-template #cellsDefaultTemplate let-row=\"row\">\n <!-- afficher une cellule avec checkbox de selection -->\n @if (tableOptions.selectable) {\n <th [disableSelect]=\"row.rowOptions?.disableSelect\" [dsfrSelectRow]=\"row\">\n @if (row.rowOptions?.disableSelect) {\n <span class=\"fr-sr-only\">\n {{ 'table.unselectable' | dsfrI18n }}\n </span>\n }\n </th>\n }\n @for (col of columns; track col.field) {\n @if (!col.formatFunction) {\n <!-- Cellule non format\u00E9e -->\n @if (row[col.field] !== undefined) {\n <td [dsfrCell]=\"col\">{{ row[col.field] }}</td>\n } @else {\n <!-- Cellule vide non format\u00E9e -->\n <td [dsfrCell]=\"col\">N/A</td>\n }\n } @else {\n <!-- Cellule format\u00E9e -->\n <td [dsfrCell]=\"col\">{{ col.formatFunction(row[col.field]) }}</td>\n }\n }\n</ng-template>\n", styles: [".fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox+label:before{margin-top:-.75rem}.fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox{margin-top:-.75rem}.fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox:indeterminate+.fr-label:after{left:-1rem;margin-top:-.5rem}.fr-table.edu-fixed--height .fr-table__content{overflow:auto}.fr-table.edu-th--sticky .fr-table__container{overflow:initial}.fr-table.edu-th--sticky .fr-table__wrapper:after{z-index:2}.fr-table.edu-th--sticky .fr-table__content table tr:after{z-index:0}.fr-table.edu-th--sticky .fr-table__content table tr:first-child:after{top:1px}.fr-table.edu-th--sticky .fr-table__content table .fr-cell--fixed{z-index:0}.fr-table.edu-th--sticky .fr-table__content table thead{position:sticky;top:0;z-index:2}.fr-table.edu-th--sticky .fr-table__content table thead th.fr-cell--fixed{z-index:2}.fr-table.edu-th--sticky .fr-table__content table thead:before{z-index:2}dsfr-datatable:has(>.edu-table--sticky){display:block;position:sticky;top:0;padding-top:1rem;max-height:100vh;overflow:hidden}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper{position:sticky;top:calc(1rem + 56px)}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container{position:sticky;top:30px}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container .fr-table__content{max-height:calc(90vh - (104px + 1rem));overflow:auto}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container .fr-table__content thead{position:sticky;top:0;z-index:3}.fr-table.edu-th--sticky .fr-table__content table tbody,dsfr-datatable:has(>.edu-table--sticky) .fr-table__content table tbody{position:relative}dsfr-datatable table[role=treegrid] td[tabindex]:focus-visible,dsfr-datatable table[role=treegrid] th[tabindex]:focus-visible,dsfr-datatable table[role=grid] td[tabindex]:focus-visible,dsfr-datatable table[role=grid] th[tabindex]:focus-visible{outline-offset:-2px}dsfr-datatable tr.edu-row--group-header{background-color:var(--background-alt-grey);color:var(--text-default-grey)}dsfr-datatable tr.edu-row--group-header td{font-weight:700;background-color:var(--background-alt-grey);background-image:linear-gradient(0deg,var(--border-plain-grey),var(--border-plain-grey)),linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey));background-position:100% 100%;background-repeat:no-repeat;background-size:100% 1px}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]){cursor:pointer}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):hover{background-color:var(--background-alt-grey-hover)}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):hover td{background-color:var(--background-alt-grey-hover)}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):active{background-color:var(--background-alt-grey-active)}dsfr-datatable tr.edu-row--group td:first-child{padding-left:2.5rem}dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) td,dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) th{background-image:linear-gradient(0deg,var(--border-plain-grey),var(--border-plain-grey)),linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey));background-position:100% 100%;background-repeat:no-repeat;background-size:100% 1px}dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) th{border-right:1px solid}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgTemplateOutlet), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(() => DsfrSortColumnDirective), selector: "[dsfrColumn]", inputs: ["dsfrColumn"] }, { kind: "directive", type: i0.forwardRef(() => DsfrCellDirective), selector: "[dsfrCell]", inputs: ["dsfrCell"] }, { kind: "directive", type: i0.forwardRef(() => DsfrSelectRowDirective), selector: "[dsfrSelectRow]", inputs: ["dsfrSelectRow", "disableSelect"] }, { kind: "directive", type: i0.forwardRef(() => DsfrSelectAllDirective), selector: "[dsfrSelectAll]" }, { kind: "directive", type: i0.forwardRef(() => DsfrTreeGridNavigationDirective), selector: "[dsfrTreeGridNavigation]", inputs: ["dsfrTreeGridNavigation"] }, { kind: "component", type: i0.forwardRef(() => DsfrTableFooterComponent), selector: "dsfr-table-footer", inputs: ["footerActionsTemplate", "showPagination", "showFooterResult", "footerResultsTemplate", "initialState", "totalElements", "totalPage", "currentPage", "rowsPerPageOptions", "emptyResultsMessage", "disabledRowsPerPage"], outputs: ["rowsPerPageChange", "pageSelect"] }, { kind: "directive", type: i0.forwardRef(() => DsfrRowGroupHeaderDirective), selector: "[dsfrRowGroupHeader]", inputs: ["group"] }, { kind: "directive", type: i0.forwardRef(() => DsfrRowGroupDirective), selector: "[dsfrRowGroup]", inputs: ["group", "index"] }, { kind: "component", type: i0.forwardRef(() => EduTableHeaderComponent), selector: "edu-table-header", inputs: ["headerActionsTemplate", "countSelectedRowsTemplate", "countSelectedRows", "showHeaderViews", "headerViewsOptions", "showSearch", "searchInputPlaceholder", "searchLabel", "searchButtonTitle", "searchInputValue", "searchInputInitialValue"], outputs: ["viewSelect", "searchChange", "searchSelect"] }, { kind: "pipe", type: i0.forwardRef(() => i1.AsyncPipe), name: "async" }, { kind: "pipe", type: i0.forwardRef(() => DsfrI18nPipe), name: "dsfrI18n" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-datatable', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
{
provide: DSFR_TABLE_CONTEXT,
useExisting: DsfrTableComponent,
},
{
provide: DsfrDataTableService,
useFactory: (parent = inject(DsfrDataTableService, { optional: true, skipSelf: true })) => parent ?? new DsfrDataTableService(),
},
], imports: [
CommonModule,
forwardRef(() => DsfrColumnDirective),
forwardRef(() => DsfrSortColumnDirective),
forwardRef(() => DsfrCellDirective),
forwardRef(() => DsfrSelectRowDirective),
forwardRef(() => DsfrSelectAllDirective),
DsfrTreeGridNavigationDirective,
DsfrTableFooterComponent,
DsfrRowGroupHeaderDirective,
DsfrRowGroupDirective,
EduTableHeaderComponent,
DsfrI18nPipe,
], template: "<div [class]=\"tableClasses()\">\n <edu-table-header\n (viewSelect)=\"onviewSelect($event)\"\n [showHeaderViews]=\"showHeaderViews\"\n [showSearch]=\"showSearch\"\n [searchInputValue]=\"searchInputValue\"\n [searchInputPlaceholder]=\"searchInputPlaceholder\"\n [searchButtonTitle]=\"searchButtonTitle\"\n [searchLabel]=\"searchLabel\"\n (searchChange)=\"onsearchChange($event)\"\n (searchSelect)=\"onsearchSelect($event)\"\n [headerViewsOptions]=\"headerViewsOptions\"\n [countSelectedRowsTemplate]=\"headerSelectedRowCountTemplateRef ?? headerSelectedRowCountTemplate\"\n [headerActionsTemplate]=\"headerActionsTemplateRef ?? headerActionsTemplate\"\n [countSelectedRows]=\"tableOptions.selectable ? countSelectedRows : undefined\">\n </edu-table-header>\n @if (showTableView) {\n <div class=\"fr-table__wrapper\">\n <div class=\"fr-table__container\">\n <div class=\"fr-table__content\" [style.max-height]=\"fixedHeight ? fixedHeight : 'initial'\">\n <!-- prettier-ignore -->\n @if (!caption && totalElements() === 0) {\n <span [id]=\"tableId + '-no-result-description'\" class=\"fr-sr-only\">{{ 'table.noResult' | dsfrI18n }}</span>\n }\n @if (!caption && isSortable) {\n <span [id]=\"tableId + '-sort-explanation'\" class=\"fr-sr-only\">{{\n 'table.sortExplanation' | dsfrI18n\n }}</span>\n }\n <table\n [id]=\"tableId\"\n [attr.role]=\"tableOptions.role ? tableOptions.role : null\"\n [dsfrTreeGridNavigation]=\"tableOptions.role\"\n [attr.aria-labelledby]=\"!caption && tableOptions.ariaLabelledBy ? tableOptions.ariaLabelledBy : null\"\n [attr.aria-describedby]=\"\n !caption\n ? (totalElements() === 0 ? tableId + '-no-result-description ' : '') +\n (isSortable && totalElements() !== 0 ? tableId + '-sort-explanation ' : '') +\n (tableOptions.ariaDescribedBy ?? '')\n : null\n \">\n @let resolveHeaderSummaryTemplate = headerSummaryTemplateRef ?? headerSummaryTemplate;\n @if (caption || resolveHeaderSummaryTemplate) {\n <caption>\n {{\n caption\n }}\n @if (totalElements() === 0) {\n <span class=\"fr-sr-only\">({{ 'table.noResult' | dsfrI18n }})</span>\n }\n @if (isSortable && totalElements() !== 0) {\n <span class=\"fr-sr-only\">({{ 'table.sortExplanation' | dsfrI18n }})</span>\n }\n @if (resolveHeaderSummaryTemplate) {\n <div class=\"fr-table__caption__desc\">\n <ng-container *ngTemplateOutlet=\"resolveHeaderSummaryTemplate\"></ng-container>\n </div>\n }\n </caption>\n }\n <!-- Header ------------------------------------------------------------------------------------------------->\n <thead>\n <!-- Slot -->\n @let resolveTableHeaderTemplate = tableHeaderTemplateRef ?? tableHeaderTemplate;\n\n @if (resolveTableHeaderTemplate) {\n <ng-container *ngTemplateOutlet=\"resolveTableHeaderTemplate\"></ng-container>\n } @else {\n <tr>\n @if (tableOptions.selectable && !tableOptions.showSelectAll) {\n <th class=\"fr-cell--fixed fr-col--xs\" role=\"columnheader\">\n <span class=\"fr-sr-only\"> {{ 'commons.select' | dsfrI18n }}</span>\n </th>\n } @else if (tableOptions.selectable && tableOptions.showSelectAll) {\n <th dsfrSelectAll></th>\n }\n @for (col of columns; track col.field) {\n <th\n [attr.colspan]=\"col.colspan\"\n id=\"{{ tableId }}-header-{{ col.field }}\"\n [dsfrColumn]=\"col\"\n scope=\"col\">\n @if (col.labelSrOnly) {\n <span class=\"fr-sr-only\">{{ col.label ?? col.field }}</span>\n } @else {\n {{ col.label ?? col.field }}\n }\n </th>\n }\n </tr>\n }\n </thead>\n\n <!-- Body ------------------------------------------------------------------------------------------------->\n <tbody>\n @if (groupBy) {\n <!-- Affichages des groupes --------------------------------------------------------------------------->\n @let rows = displayedRows$ | async;\n @for (group of rows; let i = $index; track group.key) {\n @if (!group.rows) {\n <!-- Ligne sans groupe -->\n <ng-container\n [ngTemplateOutlet]=\"cellsDefaultTemplate\"\n [ngTemplateOutletContext]=\"{ row: group }\"></ng-container>\n } @else {\n <!-- Affichage de la ligne parent d'un groupe, expandable ou non -->\n <ng-container\n [ngTemplateOutlet]=\"displayRowGroupHeader\"\n [ngTemplateOutletContext]=\"{ group: group, index: i }\"></ng-container>\n\n @if (!groupBy.expandable || !group.collapsed()) {\n @for (row of group.rows; let j = $index; track trackById.bind(this, this.dataKey)(j, row)) {\n <!-- Affichage des lignes d'un groupe -->\n <ng-container\n [ngTemplateOutlet]=\"displayRowGroup\"\n [ngTemplateOutletContext]=\"{ group: group, index: j, row: row }\"></ng-container>\n }\n }\n }\n }\n } @else {\n @for (row of displayedRows$ | async; track trackById.bind(this, this.dataKey)(i, row); let i = $index) {\n <!-- Affichages des lignes simples ------------------------------------------------------------------>\n @if (tableBodyTemplate) {\n <!-- Slot -->\n <ng-container\n *ngTemplateOutlet=\"\n tableBodyTemplate;\n context: { $implicit: row, index: i, rowOptions: row.rowOptions }\n \"></ng-container>\n } @else {\n <tr>\n <!-- Default -->\n <ng-container\n [ngTemplateOutlet]=\"cellsDefaultTemplate\"\n [ngTemplateOutletContext]=\"{ row: row }\"></ng-container>\n </tr>\n }\n }\n }\n @if (!loading && totalElements() === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (tableOptions.selectable ? 1 : 0)\">\n {{ emptyResultsMessage ?? ('table.noResult' | dsfrI18n) }}\n </td>\n </tr>\n }\n </tbody>\n @let resolveTableFooterTemplate = tableFooterTemplateRef ?? tableFooterTemplate;\n\n @if (resolveTableFooterTemplate) {\n <tfoot>\n <ng-container *ngTemplateOutlet=\"resolveTableFooterTemplate\"></ng-container>\n </tfoot>\n }\n </table>\n <!-- Afficher un template de chargement si d\u00E9fini -->\n @let resolveLoadingTemplate = loadingTemplateRef ?? loadingTemplate;\n\n @if (loading && resolveLoadingTemplate) {\n <ng-container [ngTemplateOutlet]=\"resolveLoadingTemplate\"></ng-container>\n }\n </div>\n </div>\n </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"alternativeViewTemplateRef ?? this.alternativeViewTemplate\"></ng-template>\n }\n\n <!-- Footer -->\n @let resolveFooterActionsTemplate = footerActionsTemplateRef ?? footerActionsTemplate;\n @if (showFooterResult || pagination || resolveFooterActionsTemplate) {\n <dsfr-table-footer\n [initialState]=\"initialState\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [currentPage]=\"dataTableService.tableState().page\"\n (rowsPerPageChange)=\"onRowsPerPageChange($event)\"\n (pageSelect)=\"onPageSelect($event)\"\n [showPagination]=\"pagination\"\n [disabledRowsPerPage]=\"totalElements() === 0\"\n [totalPage]=\"totalPage()\"\n [showFooterResult]=\"showFooterResult\"\n [emptyResultsMessage]=\"emptyResultsMessage ?? ('table.noResult' | dsfrI18n)\"\n [footerActionsTemplate]=\"resolveFooterActionsTemplate\"\n [footerResultsTemplate]=\"footerResultsTemplateRef ?? this.footerResultsTemplate\"\n [totalElements]=\"totalElements()\">\n </dsfr-table-footer>\n }\n</div>\n\n<!-- Template de la ligne parent d'un groupe -------------------------------------------------------------------------->\n<ng-template #displayRowGroupHeader let-group=\"group\" let-i=\"index\">\n @if (rowGroupHeaderTemplate) {\n <!-- Slot -->\n <ng-container\n [ngTemplateOutlet]=\"rowGroupHeaderTemplate\"\n [ngTemplateOutletContext]=\"{ group: group, index: i }\"></ng-container>\n } @else {\n <!-- Default -->\n <tr dsfrRowGroupHeader [group]=\"group\">\n <td [attr.colspan]=\"columns.length + (tableOptions.selectable ? 2 : 1)\">\n {{ group.key }}\n </td>\n </tr>\n }\n</ng-template>\n\n<!-- Template de la ligne d'un groupe --------------------------------------------------------------------------------->\n<ng-template #displayRowGroup let-group=\"group\" let-i=\"index\" let-row=\"row\">\n @if (rowGroupTemplate) {\n <!-- Slot -->\n <ng-container\n [ngTemplateOutlet]=\"rowGroupTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row, group: group, index: i, row: row }\"></ng-container>\n } @else {\n <!-- Default -->\n <tr dsfrRowGroup [index]=\"i\" [group]=\"group\">\n <ng-container [ngTemplateOutlet]=\"cellsDefaultTemplate\" [ngTemplateOutletContext]=\"{ row: row }\"></ng-container>\n </tr>\n }\n</ng-template>\n\n<!-- Template par d\u00E9faut des cellules -------------------------------------------------------------------------------->\n<ng-template #cellsDefaultTemplate let-row=\"row\">\n <!-- afficher une cellule avec checkbox de selection -->\n @if (tableOptions.selectable) {\n <th [disableSelect]=\"row.rowOptions?.disableSelect\" [dsfrSelectRow]=\"row\">\n @if (row.rowOptions?.disableSelect) {\n <span class=\"fr-sr-only\">\n {{ 'table.unselectable' | dsfrI18n }}\n </span>\n }\n </th>\n }\n @for (col of columns; track col.field) {\n @if (!col.formatFunction) {\n <!-- Cellule non format\u00E9e -->\n @if (row[col.field] !== undefined) {\n <td [dsfrCell]=\"col\">{{ row[col.field] }}</td>\n } @else {\n <!-- Cellule vide non format\u00E9e -->\n <td [dsfrCell]=\"col\">N/A</td>\n }\n } @else {\n <!-- Cellule format\u00E9e -->\n <td [dsfrCell]=\"col\">{{ col.formatFunction(row[col.field]) }}</td>\n }\n }\n</ng-template>\n", styles: [".fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox+label:before{margin-top:-.75rem}.fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox{margin-top:-.75rem}.fr-table__wrapper th.fr-cell--fixed .fr-checkbox-group--sm .edu-table-checkbox:indeterminate+.fr-label:after{left:-1rem;margin-top:-.5rem}.fr-table.edu-fixed--height .fr-table__content{overflow:auto}.fr-table.edu-th--sticky .fr-table__container{overflow:initial}.fr-table.edu-th--sticky .fr-table__wrapper:after{z-index:2}.fr-table.edu-th--sticky .fr-table__content table tr:after{z-index:0}.fr-table.edu-th--sticky .fr-table__content table tr:first-child:after{top:1px}.fr-table.edu-th--sticky .fr-table__content table .fr-cell--fixed{z-index:0}.fr-table.edu-th--sticky .fr-table__content table thead{position:sticky;top:0;z-index:2}.fr-table.edu-th--sticky .fr-table__content table thead th.fr-cell--fixed{z-index:2}.fr-table.edu-th--sticky .fr-table__content table thead:before{z-index:2}dsfr-datatable:has(>.edu-table--sticky){display:block;position:sticky;top:0;padding-top:1rem;max-height:100vh;overflow:hidden}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper{position:sticky;top:calc(1rem + 56px)}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container{position:sticky;top:30px}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container .fr-table__content{max-height:calc(90vh - (104px + 1rem));overflow:auto}dsfr-datatable:has(>.edu-table--sticky) .fr-table[data-fr-js-table=true] .fr-table__wrapper .fr-table__container .fr-table__content thead{position:sticky;top:0;z-index:3}.fr-table.edu-th--sticky .fr-table__content table tbody,dsfr-datatable:has(>.edu-table--sticky) .fr-table__content table tbody{position:relative}dsfr-datatable table[role=treegrid] td[tabindex]:focus-visible,dsfr-datatable table[role=treegrid] th[tabindex]:focus-visible,dsfr-datatable table[role=grid] td[tabindex]:focus-visible,dsfr-datatable table[role=grid] th[tabindex]:focus-visible{outline-offset:-2px}dsfr-datatable tr.edu-row--group-header{background-color:var(--background-alt-grey);color:var(--text-default-grey)}dsfr-datatable tr.edu-row--group-header td{font-weight:700;background-color:var(--background-alt-grey);background-image:linear-gradient(0deg,var(--border-plain-grey),var(--border-plain-grey)),linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey));background-position:100% 100%;background-repeat:no-repeat;background-size:100% 1px}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]){cursor:pointer}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):hover{background-color:var(--background-alt-grey-hover)}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):hover td{background-color:var(--background-alt-grey-hover)}dsfr-datatable tr.edu-row--group-header:has(td[aria-expanded]):active{background-color:var(--background-alt-grey-active)}dsfr-datatable tr.edu-row--group td:first-child{padding-left:2.5rem}dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) td,dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) th{background-image:linear-gradient(0deg,var(--border-plain-grey),var(--border-plain-grey)),linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey));background-position:100% 100%;background-repeat:no-repeat;background-size:100% 1px}dsfr-datatable tr.edu-row--group:has(+tr.edu-row--group-header) th{border-right:1px solid}\n"] }]
}], propDecorators: { headerActionsTemplate: [{
type: ContentChild,
args: ['headerActionsTemplate', { static: true }]
}], headerSummaryTemplate: [{
type: ContentChild,
args: ['headerSummaryTemplate', { static: true }]
}], headerSelectedRowCountTemplate: [{
type: ContentChild,
args: ['headerSelectedRowCountTemplate', { static: true }]
}], tableHeaderTemplate: [{
type: ContentChild,
args: ['tableHeaderTemplate', { static: true }]
}], tableBodyTemplate: [{
type: ContentChild,
args: ['tableBodyTemplate', { static: true }]
}], tableFooterTemplate: [{
type: ContentChild,
args: ['tableFooterTemplate', { static: true }]
}], rowGroupHeaderTemplate: [{
type: ContentChild,
args: ['rowGroupHeaderTemplate', { static: true }]
}], rowGroupTemplate: [{
type: ContentChild,
args: ['rowGroupTemplate', { static: true }]
}], footerResultsTemplate: [{
type: ContentChild,
args: ['footerResultsTemplate', { static: true }]
}], footerActionsTemplate: [{
type: ContentChild,
args: ['footerActionsTemplate', { static: true }]
}], loadingTemplate: [{
type: ContentChild,
args: ['loadingTemplate', { static: true }]
}], alternativeViewTemplate: [{
type: ContentChild,
args: ['alternativeViewTemplate', { static: true }]
}], headerActionsTemplateRef: [{
type: Input
}], headerSummaryTemplateRef: [{
type: Input
}], headerSelectedRowCountTemplateRef: [{
type: Input
}], tableHeaderTemplateRef: [{
type: Input
}], tableFooterTemplateRef: [{
type: Input
}], footerActionsTemplateRef: [{
type: Input
}], footerResultsTemplateRef: [{
type: Input
}], loadingTemplateRef: [{
type: Input
}], alternativeViewTemplateRef: [{
type: Input
}], tableId: [{
type: Input
}], caption: [{
type: Input
}], fixedHeight: [{
type: Input
}], thSticky: [{
type: Input
}], tableHeaderSticky: [{
type: Input
}], data: [{
type: Input,
args: [{ required: true }]
}], dataKey: [{
type: Input
}], tableOptions: [{
type: Input
}], rowsOptions: [{
type: Input
}], columns: [{
type: Input,
args: [{ required: true }]
}], showFooterResult: [{
type: Input
}], emptyResultsMessage: [{
type: Input
}], loading: [{
type: Input
}], showSearch: [{
type: Input
}], searchInputPlaceholder: [{
type: Input
}], searchLabel: [{
type: Input
}], searchButtonTitle: [{
type: Input
}], searchInputValue: [{
type: Input
}], showHeaderViews: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], headerViewsOptions: [{
type: Input
}], initialSelection: [{
type: Input
}], groupBy: [{
type: Input
}], sortChange: [{
type: Output
}], viewSelect: [{
type: Output
}], searchChange: [{
type: Output
}], searchSelect: [{
type: Output
}], selectionChange: [{
type: Output
}], showSelectAllChange: [{
type: Output
}] } });
/**
* Définit les tailles de cellules proposées par le DSFR.
*/
var DsfrCellSizeConst;
(function (DsfrCellSizeConst) {
DsfrCellSizeConst["XS"] = "XS";
DsfrCellSizeConst["SM"] = "SM";
DsfrCellSizeConst["MD"] = "MD";
DsfrCellSizeConst["LG"] = "LG";
})(DsfrCellSizeConst || (DsfrCellSizeConst = {}));
class DsfrDataTableModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableModule, imports: [CommonModule,
SortButtonHeaderComponent,
DsfrColumnDirective,
DsfrSortColumnDirective,
DsfrTableComponent,
DsfrSelectRowDirective,
EduCheckboxLightComponent,
DsfrCellDirective,
DsfrSelectAllDirective,
DsfrRowGroupDirective,
DsfrRowGroupHeaderDirective,
DsfrTableFooterComponent,
EduTableHeaderComponent], exports: [DsfrColumnDirective,
DsfrSortColumnDirective,
DsfrCellDirective,
DsfrSelectRowDirective,
DsfrSelectAllDirective,
DsfrRowGroupDirective,
DsfrRowGroupHeaderDirective,
DsfrTableFooterComponent,
DsfrTableComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableModule, imports: [CommonModule,
DsfrTableComponent,
EduCheckboxLightComponent,
DsfrTableFooterComponent,
EduTableHeaderComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDataTableModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
SortButtonHeaderComponent,
DsfrColumnDirective,
DsfrSortColumnDirective,
DsfrTableComponent,
DsfrSelectRowDirective,
EduCheckboxLightComponent,
DsfrCellDirective,
DsfrSelectAllDirective,
DsfrRowGroupDirective,
DsfrRowGroupHeaderDirective,
DsfrTableFooterComponent,
EduTableHeaderComponent,
],
exports: [
DsfrColumnDirective,
DsfrSortColumnDirective,
DsfrCellDirective,
DsfrSelectRowDirective,
DsfrSelectAllDirective,
DsfrRowGroupDirective,
DsfrRowGroupHeaderDirective,
DsfrTableFooterComponent,
DsfrTableComponent,
],
}]
}] });
class DsfrToolLinkMenuComponent {
constructor() {
/** Désactiver le menu */
this.disabled = false;
/** Liste des liens. Prioritaire sur le slot linksTemplate */
this.links = [];
/** Suppression de la bordure si false */
this.outline = true;
/** Evènement émis au clic sur un lien */
this.linkSelect = new EventEmitter();
/** Signale l'ouverture du menu. */
this.disclose = new EventEmitter();
/** Signale la fermeture du menu. */
this.conceal = new EventEmitter();
this.renderer2 = inject(Renderer2);
this._unlisten = [];
}
ngOnInit() {
this.menuId ??= newUniqueId();
}
/**
* Ecouter les events scripts dsfr conceal/disclose sur le menu
* conceal: uniquement apres une premiere ouverture (présence de fr-collapse--expanded)
*/
ngAfterViewInit() {
this._unlisten.push(this.renderer2.listen(this.collapseMenu?.nativeElement, 'dsfr.conceal', () => {
if (this.collapseMenu?.nativeElement.className.includes('fr-collapse--expanded')) {
this.conceal.emit();
}
}), this.renderer2.listen(this.collapseMenu?.nativeElement, 'dsfr.disclose', () => {
this.disclose.emit();
}));
}
ngOnDestroy() {
this._unlisten.forEach((unlistenFunc) => unlistenFunc());
}
onLinkSelect(link) {
if (!this.collapseMenu)
return;
// déclenchement manuel de l'évènement, celui-ci n'est pas envoyé en cas de balise supplémentaire sur le <a>
dsfr(this.collapseMenu.nativeElement).collapse.conceal();
this.linkSelect.emit(link);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrToolLinkMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrToolLinkMenuComponent, isStandalone: true, selector: "dsfr-tool-link-menu, dsfr-ext-toolsmenu, dsfrx-toolsmenu", inputs: { menuId: "menuId", labelMenu: "labelMenu", ariaLabelMenu: "ariaLabelMenu", disabled: ["disabled", "disabled", booleanAttribute], iconMenu: "iconMenu", links: "links", customClass: "customClass", outline: "outline" }, outputs: { linkSelect: "linkSelect", disclose: "disclose", conceal: "conceal" }, queries: [{ propertyName: "headerTemplate", first: true, predicate: ["headerTemplate"], descendants: true, static: true }, { propertyName: "footerTemplate", first: true, predicate: ["footerTemplate"], descendants: true, static: true }, { propertyName: "linksTemplate", first: true, predicate: ["linksTemplate"], descendants: true, static: true }], viewQueries: [{ propertyName: "collapseMenu", first: true, predicate: ["collapseMenu"], descendants: true }], ngImport: i0, template: "<div class=\"fr-nav fr-nav__item edu-tool-link-menu\">\n <!-- ajouter ariaLabel ?-->\n <button\n #btnMenu\n [id]=\"menuId\"\n type=\"button\"\n class=\"edu-menu-dropdown__btn fr-btn--sm fr-btn--icon-left fr-btn {{ iconMenu ?? '' }}\"\n [class.fr-btn--tertiary]=\"outline\"\n [class.fr-btn--tertiary-no-outline]=\"!outline\"\n [disabled]=\"disabled || null\"\n [attr.aria-controls]=\"menuId + '-menu'\"\n [attr.aria-label]=\"ariaLabelMenu ?? null\"\n [attr.aria-expanded]=\"false\">\n {{ labelMenu }}\n </button>\n\n <div #collapseMenu class=\"fr-collapse fr-menu\" [ngClass]=\"customClass\" [id]=\"menuId + '-menu'\">\n <div class=\"fr-menu__list\">\n <!-- Header ------------------------------------------------------------------------>\n @if (headerTemplate) {\n <div class=\"edu-tool-link-menu__header\">\n <!-- Affichage du slot header -->\n <ng-container *ngTemplateOutlet=\"headerTemplate\"></ng-container>\n </div>\n }\n\n <!-- Liens ------------------------------------------------------------------------->\n @if (links && links.length > 0) {\n <ul>\n @for (link of links; track $index) {\n <li>\n <ng-container [ngTemplateOutlet]=\"linkTemplate\" [ngTemplateOutletContext]=\"{ link: link }\"></ng-container>\n </li>\n }\n </ul>\n } @else if (linksTemplate) {\n <!-- Affichage du slot links -->\n <ng-container *ngTemplateOutlet=\"linksTemplate\"></ng-container>\n }\n\n <!-- Footer ----------------------------------------------------------------------->\n @if (footerTemplate) {\n <div class=\"edu-tool-link-menu__footer\">\n <!-- Affichage du slot footer -->\n <ng-container *ngTemplateOutlet=\"footerTemplate\"></ng-container>\n </div>\n }\n </div>\n </div>\n</div>\n\n<ng-template #linkTemplate let-link=\"link\">\n <dsfr-link\n [tooltipMessage]=\"link.tooltipMessage\"\n [ariaLabel]=\"link.label ? link.ariaLabel : link.label\"\n [ariaControls]=\"link.ariaControls\"\n [link]=\"link.link\"\n [label]=\"link.icon ? undefined : link.label\"\n [linkTarget]=\"link.linkTarget\"\n [route]=\"link.route\"\n [customClass]=\"'fr-nav__link'\"\n [routePath]=\"link.routerLink\"\n [routerLinkActive]=\"link.routerLinkActive ?? ''\"\n [routerLinkExtras]=\"link.routerLinkExtras\"\n [mode]=\"link.mode\"\n [ariaCurrent]=\"link.active\"\n (linkSelect)=\"onLinkSelect(link)\"\n [disabled]=\"disabled\">\n <!-- Pour avoir a la fois un item actif et une icone, surcharger ici avec le slot -->\n @if (link.icon) {\n <i aria-hidden=\"true\" class=\"{{ link.icon }} edu-item-icon\"></i>\n {{ link.label }}\n }\n </dsfr-link>\n</ng-template>\n", styles: [".edu-menu-dropdown__btn,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn{margin:0}.edu-menu-dropdown__btn:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn:after{--data-uri-svg: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 24 24' ><path fill='%23929292' d='M12,13.1l5-4.9l1.4,1.4L12,15.9L5.6,9.5l1.4-1.4L12,13.1z'/></svg>\");flex:0 0 auto;display:inline-block;background-color:currentColor;width:var(--icon-size);height:var(--icon-size);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-image:var(--data-uri-svg);mask-image:var(--data-uri-svg);content:\"\";margin-left:.5rem;margin-right:-.125rem;transition:transform .3s}.edu-menu-dropdown__btn.fr-btn--sm,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm{--icon-size: 1rem}.edu-menu-dropdown__btn.fr-btn--sm:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm:after{--icon-size: 1rem;font-size:.875rem}.edu-menu-dropdown__btn.fr-btn--lg,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--lg{--icon-size: 1.25rem;font-size:1.125rem}.edu-menu-dropdown__btn[aria-expanded=true],.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]{background-color:var(--background-action-low-blue-france)}.edu-menu-dropdown__btn[aria-expanded=true]:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]:after{transform:rotate(-180deg)}\n", ".edu-tool-link-menu{--rows: 8;position:relative;margin-left:.25rem;margin-right:.25rem}.edu-tool-link-menu .edu-menu-dropdown__btn{font-weight:500;padding:.25rem .75rem}.edu-tool-link-menu .fr-menu__list{width:max-content}.edu-tool-link-menu .fr-menu__list ul{width:auto}.edu-tool-link-menu .fr-menu__list a.fr-nav__link,.edu-tool-link-menu .fr-menu__list button.fr-nav__link{border-bottom:1px solid var(--border-default-grey);justify-content:flex-start}.edu-tool-link-menu .fr-menu__list a.fr-nav__link[href]:hover,.edu-tool-link-menu .fr-menu__list button.fr-nav__link[href]:hover{--underline-hover-width: var(--underline-max-width);background-color:var(--hover-tint)}.edu-tool-link-menu .fr-menu__list li:last-child a.fr-link,.edu-tool-link-menu .fr-menu__list li:last-child button.fr-link{border-bottom:none}.edu-tool-link-menu .fr-menu__list .edu-item-icon{pointer-events:none}.edu-tool-link-menu .fr-menu__list .edu-item-icon:before{--icon-size: 1rem;margin-right:.5rem}.edu-tool-link-menu__header{border-bottom:1px solid var(--border-default-grey);text-align:left}.edu-tool-link-menu__header,.edu-tool-link-menu__footer{padding:.75rem 1rem}.fr-btns-group .edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center!important;display:inline-flex;align-items:center;margin:0}@media screen and (max-width:62em){.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu{margin:0;overflow:hidden}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu .edu-menu-dropdown__btn.fr-btn{padding-left:.5rem!important;padding-right:.5rem!important;margin:0}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu .edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center;font-size:.875rem;min-height:auto;padding:.25rem .75rem!important;min-height:auto!important;box-shadow:inset 0 0 0 1px var(--border-default-grey)}.fr-header .fr-container .fr-header__menu-links .fr-btns-group.fr-nav.fr-nav__item:before{box-shadow:none}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list{padding-right:0;width:100%}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list a.fr-link,.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list button.fr-link{border-bottom:none}.edu-usermenu.fr-menu{width:100%;padding-right:0;padding-left:0}.edu-menu-dropdown__btn.fr-btn--tertiary.fr-btn--sm{box-shadow:none;display:inline-flex;align-items:center;min-height:3rem;font-size:1rem;width:100%}.edu-menu-dropdown__btn.fr-btn--tertiary.fr-btn--sm:after{--icon-size: 1rem;margin-left:auto}}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools{flex:initial}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools.edu-header__tools--custom{flex:1 0 auto}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools:not(:last-child){padding-right:0;margin-right:.5rem}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools:not(:first-child){padding-left:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrToolLinkMenuComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tool-link-menu, dsfr-ext-toolsmenu, dsfrx-toolsmenu', imports: [CommonModule, DsfrLinkComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"fr-nav fr-nav__item edu-tool-link-menu\">\n <!-- ajouter ariaLabel ?-->\n <button\n #btnMenu\n [id]=\"menuId\"\n type=\"button\"\n class=\"edu-menu-dropdown__btn fr-btn--sm fr-btn--icon-left fr-btn {{ iconMenu ?? '' }}\"\n [class.fr-btn--tertiary]=\"outline\"\n [class.fr-btn--tertiary-no-outline]=\"!outline\"\n [disabled]=\"disabled || null\"\n [attr.aria-controls]=\"menuId + '-menu'\"\n [attr.aria-label]=\"ariaLabelMenu ?? null\"\n [attr.aria-expanded]=\"false\">\n {{ labelMenu }}\n </button>\n\n <div #collapseMenu class=\"fr-collapse fr-menu\" [ngClass]=\"customClass\" [id]=\"menuId + '-menu'\">\n <div class=\"fr-menu__list\">\n <!-- Header ------------------------------------------------------------------------>\n @if (headerTemplate) {\n <div class=\"edu-tool-link-menu__header\">\n <!-- Affichage du slot header -->\n <ng-container *ngTemplateOutlet=\"headerTemplate\"></ng-container>\n </div>\n }\n\n <!-- Liens ------------------------------------------------------------------------->\n @if (links && links.length > 0) {\n <ul>\n @for (link of links; track $index) {\n <li>\n <ng-container [ngTemplateOutlet]=\"linkTemplate\" [ngTemplateOutletContext]=\"{ link: link }\"></ng-container>\n </li>\n }\n </ul>\n } @else if (linksTemplate) {\n <!-- Affichage du slot links -->\n <ng-container *ngTemplateOutlet=\"linksTemplate\"></ng-container>\n }\n\n <!-- Footer ----------------------------------------------------------------------->\n @if (footerTemplate) {\n <div class=\"edu-tool-link-menu__footer\">\n <!-- Affichage du slot footer -->\n <ng-container *ngTemplateOutlet=\"footerTemplate\"></ng-container>\n </div>\n }\n </div>\n </div>\n</div>\n\n<ng-template #linkTemplate let-link=\"link\">\n <dsfr-link\n [tooltipMessage]=\"link.tooltipMessage\"\n [ariaLabel]=\"link.label ? link.ariaLabel : link.label\"\n [ariaControls]=\"link.ariaControls\"\n [link]=\"link.link\"\n [label]=\"link.icon ? undefined : link.label\"\n [linkTarget]=\"link.linkTarget\"\n [route]=\"link.route\"\n [customClass]=\"'fr-nav__link'\"\n [routePath]=\"link.routerLink\"\n [routerLinkActive]=\"link.routerLinkActive ?? ''\"\n [routerLinkExtras]=\"link.routerLinkExtras\"\n [mode]=\"link.mode\"\n [ariaCurrent]=\"link.active\"\n (linkSelect)=\"onLinkSelect(link)\"\n [disabled]=\"disabled\">\n <!-- Pour avoir a la fois un item actif et une icone, surcharger ici avec le slot -->\n @if (link.icon) {\n <i aria-hidden=\"true\" class=\"{{ link.icon }} edu-item-icon\"></i>\n {{ link.label }}\n }\n </dsfr-link>\n</ng-template>\n", styles: [".edu-menu-dropdown__btn,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn{margin:0}.edu-menu-dropdown__btn:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn:after{--data-uri-svg: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 24 24' ><path fill='%23929292' d='M12,13.1l5-4.9l1.4,1.4L12,15.9L5.6,9.5l1.4-1.4L12,13.1z'/></svg>\");flex:0 0 auto;display:inline-block;background-color:currentColor;width:var(--icon-size);height:var(--icon-size);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-image:var(--data-uri-svg);mask-image:var(--data-uri-svg);content:\"\";margin-left:.5rem;margin-right:-.125rem;transition:transform .3s}.edu-menu-dropdown__btn.fr-btn--sm,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm{--icon-size: 1rem}.edu-menu-dropdown__btn.fr-btn--sm:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm:after{--icon-size: 1rem;font-size:.875rem}.edu-menu-dropdown__btn.fr-btn--lg,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--lg{--icon-size: 1.25rem;font-size:1.125rem}.edu-menu-dropdown__btn[aria-expanded=true],.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]{background-color:var(--background-action-low-blue-france)}.edu-menu-dropdown__btn[aria-expanded=true]:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]:after{transform:rotate(-180deg)}\n", ".edu-tool-link-menu{--rows: 8;position:relative;margin-left:.25rem;margin-right:.25rem}.edu-tool-link-menu .edu-menu-dropdown__btn{font-weight:500;padding:.25rem .75rem}.edu-tool-link-menu .fr-menu__list{width:max-content}.edu-tool-link-menu .fr-menu__list ul{width:auto}.edu-tool-link-menu .fr-menu__list a.fr-nav__link,.edu-tool-link-menu .fr-menu__list button.fr-nav__link{border-bottom:1px solid var(--border-default-grey);justify-content:flex-start}.edu-tool-link-menu .fr-menu__list a.fr-nav__link[href]:hover,.edu-tool-link-menu .fr-menu__list button.fr-nav__link[href]:hover{--underline-hover-width: var(--underline-max-width);background-color:var(--hover-tint)}.edu-tool-link-menu .fr-menu__list li:last-child a.fr-link,.edu-tool-link-menu .fr-menu__list li:last-child button.fr-link{border-bottom:none}.edu-tool-link-menu .fr-menu__list .edu-item-icon{pointer-events:none}.edu-tool-link-menu .fr-menu__list .edu-item-icon:before{--icon-size: 1rem;margin-right:.5rem}.edu-tool-link-menu__header{border-bottom:1px solid var(--border-default-grey);text-align:left}.edu-tool-link-menu__header,.edu-tool-link-menu__footer{padding:.75rem 1rem}.fr-btns-group .edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center!important;display:inline-flex;align-items:center;margin:0}@media screen and (max-width:62em){.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu{margin:0;overflow:hidden}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu .edu-menu-dropdown__btn.fr-btn{padding-left:.5rem!important;padding-right:.5rem!important;margin:0}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .edu-tool-link-menu .edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center;font-size:.875rem;min-height:auto;padding:.25rem .75rem!important;min-height:auto!important;box-shadow:inset 0 0 0 1px var(--border-default-grey)}.fr-header .fr-container .fr-header__menu-links .fr-btns-group.fr-nav.fr-nav__item:before{box-shadow:none}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list{padding-right:0;width:100%}.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list a.fr-link,.fr-header .fr-container .fr-header__menu-links .fr-btns-group .fr-menu__list button.fr-link{border-bottom:none}.edu-usermenu.fr-menu{width:100%;padding-right:0;padding-left:0}.edu-menu-dropdown__btn.fr-btn--tertiary.fr-btn--sm{box-shadow:none;display:inline-flex;align-items:center;min-height:3rem;font-size:1rem;width:100%}.edu-menu-dropdown__btn.fr-btn--tertiary.fr-btn--sm:after{--icon-size: 1rem;margin-left:auto}}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools{flex:initial}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools.edu-header__tools--custom{flex:1 0 auto}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools:not(:last-child){padding-right:0;margin-right:.5rem}.fr-header__body-row:has(.edu-header__tools--custom) .fr-header__tools:not(:first-child){padding-left:0}\n"] }]
}], propDecorators: { menuId: [{
type: Input
}], labelMenu: [{
type: Input
}], ariaLabelMenu: [{
type: Input
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], iconMenu: [{
type: Input
}], links: [{
type: Input
}], customClass: [{
type: Input
}], outline: [{
type: Input
}], headerTemplate: [{
type: ContentChild,
args: ['headerTemplate', { static: true }]
}], footerTemplate: [{
type: ContentChild,
args: ['footerTemplate', { static: true }]
}], linksTemplate: [{
type: ContentChild,
args: ['linksTemplate', { static: true }]
}], linkSelect: [{
type: Output
}], disclose: [{
type: Output
}], conceal: [{
type: Output
}], collapseMenu: [{
type: ViewChild,
args: ['collapseMenu']
}] } });
class DsfrUserMenuComponent {
constructor() {
/** Libellé du bouton du menu déroulant */
this.labelMenu = 'Mon espace';
/** Désactiver le menu */
this.disabled = false;
/** Liste des liens. Prioritaire sur le slot linksUserTemplate */
this.links = [];
/** Afficher un bouton de déconnexion en footer */
this.showLogout = true;
/** Suppression de la bordure si false */
this.outline = true;
/** Evènement émis au clic sur un lien */
this.linkSelect = new EventEmitter();
/** Signale l'ouverture du menu. */
this.disclose = new EventEmitter();
/** Signale la fermeture du menu. */
this.conceal = new EventEmitter();
/** Au clic sur le bouton de déconnexion */
this.logoutSelect = new EventEmitter();
}
get resLinksTemplate() {
return this.linksUserTemplate ?? this.linksProfileTemplate;
}
get resFooterTemplate() {
return this.footerUserTemplate ?? this.footerProfileTemplate;
}
get resHeaderTemplate() {
return this.headerUserTemplate ?? this.headerProfileTemplate;
}
/**
* Nom de l'utilisateur connecté affiché dans le header
* @deprecated utiliser userName
*/
set profileName(value) {
if (value) {
this.userName = value;
}
}
/**
* Détail d'information sur l'utilisateur connecté
* @deprecated utiliser userDetails
*/
set profileDetails(value) {
if (value) {
this.userName = value;
}
}
onLogout() {
this.logoutSelect.emit();
}
onDisclose() {
this.disclose.emit();
}
onConceal() {
this.conceal.emit();
}
onLinkSelect(link) {
this.linkSelect.emit(link);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrUserMenuComponent, isStandalone: true, selector: "dsfr-usermenu, dsfr-ext-profilemenu, dsfrx-profilemenu", inputs: { menuId: "menuId", labelMenu: "labelMenu", ariaLabelMenu: "ariaLabelMenu", disabled: ["disabled", "disabled", booleanAttribute], links: "links", userName: "userName", userDetails: "userDetails", showLogout: "showLogout", outline: "outline", profileName: "profileName", profileDetails: "profileDetails" }, outputs: { linkSelect: "linkSelect", disclose: "disclose", conceal: "conceal", logoutSelect: "logoutSelect" }, queries: [{ propertyName: "headerUserTemplate", first: true, predicate: ["headerUserTemplate"], descendants: true, static: true }, { propertyName: "footerUserTemplate", first: true, predicate: ["footerUserTemplate"], descendants: true, static: true }, { propertyName: "linksUserTemplate", first: true, predicate: ["linksUserTemplate"], descendants: true, static: true }, { propertyName: "linksProfileTemplate", first: true, predicate: ["linksProfileTemplate"], descendants: true, static: true }, { propertyName: "footerProfileTemplate", first: true, predicate: ["footerProfileTemplate"], descendants: true, static: true }, { propertyName: "headerProfileTemplate", first: true, predicate: ["headerProfileTemplate"], descendants: true, static: true }], ngImport: i0, template: "<dsfr-tool-link-menu\n [labelMenu]=\"labelMenu\"\n [links]=\"links\"\n [outline]=\"outline\"\n [disabled]=\"disabled\"\n [ariaLabelMenu]=\"ariaLabelMenu\"\n (conceal)=\"onConceal()\"\n (disclose)=\"onDisclose()\"\n customClass=\"edu-usermenu\"\n (linkSelect)=\"onLinkSelect($event)\"\n iconMenu=\"fr-icon-account-circle-fill\"\n [menuId]=\"menuId\">\n <ng-template #headerTemplate>\n @if (userName || userDetails) {\n <p>\n <span class=\"edu-tool-link-menu__desc\">{{ userName }}</span>\n <br />\n <span class=\"edu-tool-link-menu__detail\">{{ userDetails }}</span>\n </p>\n } @else if (headerUserTemplate) {\n <!-- Affichage du slot header -->\n <ng-container *ngTemplateOutlet=\"resHeaderTemplate\"></ng-container>\n }\n </ng-template>\n <ng-template #linksTemplate>\n <ng-container *ngTemplateOutlet=\"resLinksTemplate\"></ng-container>\n </ng-template>\n <ng-template #footerTemplate>\n @if (showLogout && !footerUserTemplate) {\n <dsfr-button\n label=\"Se d\u00E9connecter\"\n icon=\"fr-icon-logout-box-r-line\"\n iconPosition=\"left\"\n (click)=\"onLogout()\"\n variant=\"tertiary\"\n buttonSize=\"SM\"></dsfr-button>\n } @else if (footerUserTemplate) {\n <!-- Affichage du slot footer -->\n <ng-container *ngTemplateOutlet=\"resFooterTemplate\"></ng-container>\n }\n </ng-template>\n</dsfr-tool-link-menu>\n", styles: [".edu-menu-dropdown__btn,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn{margin:0}.edu-menu-dropdown__btn:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn:after{--data-uri-svg: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 24 24' ><path fill='%23929292' d='M12,13.1l5-4.9l1.4,1.4L12,15.9L5.6,9.5l1.4-1.4L12,13.1z'/></svg>\");flex:0 0 auto;display:inline-block;background-color:currentColor;width:var(--icon-size);height:var(--icon-size);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-image:var(--data-uri-svg);mask-image:var(--data-uri-svg);content:\"\";margin-left:.5rem;margin-right:-.125rem;transition:transform .3s}.edu-menu-dropdown__btn.fr-btn--sm,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm{--icon-size: 1rem}.edu-menu-dropdown__btn.fr-btn--sm:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm:after{--icon-size: 1rem;font-size:.875rem}.edu-menu-dropdown__btn.fr-btn--lg,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--lg{--icon-size: 1.25rem;font-size:1.125rem}.edu-menu-dropdown__btn[aria-expanded=true],.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]{background-color:var(--background-action-low-blue-france)}.edu-menu-dropdown__btn[aria-expanded=true]:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]:after{transform:rotate(-180deg)}\n", ".edu-tool-link-menu__desc{font-weight:700}.edu-tool-link-menu__detail{color:var(--text-mention-grey);font-size:.875rem}.edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center}.edu-tool-link-menu__footer:empty,.edu-tool-link-menu__header:empty{height:0;padding:0;border:none}.edu-usermenu.fr-menu{width:20rem}.edu-usermenu.fr-menu .fr-menu__list{width:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: DsfrToolLinkMenuComponent, selector: "dsfr-tool-link-menu, dsfr-ext-toolsmenu, dsfrx-toolsmenu", inputs: ["menuId", "labelMenu", "ariaLabelMenu", "disabled", "iconMenu", "links", "customClass", "outline"], outputs: ["linkSelect", "disclose", "conceal"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUserMenuComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-usermenu, dsfr-ext-profilemenu, dsfrx-profilemenu', imports: [CommonModule, DsfrButtonComponent, DsfrToolLinkMenuComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<dsfr-tool-link-menu\n [labelMenu]=\"labelMenu\"\n [links]=\"links\"\n [outline]=\"outline\"\n [disabled]=\"disabled\"\n [ariaLabelMenu]=\"ariaLabelMenu\"\n (conceal)=\"onConceal()\"\n (disclose)=\"onDisclose()\"\n customClass=\"edu-usermenu\"\n (linkSelect)=\"onLinkSelect($event)\"\n iconMenu=\"fr-icon-account-circle-fill\"\n [menuId]=\"menuId\">\n <ng-template #headerTemplate>\n @if (userName || userDetails) {\n <p>\n <span class=\"edu-tool-link-menu__desc\">{{ userName }}</span>\n <br />\n <span class=\"edu-tool-link-menu__detail\">{{ userDetails }}</span>\n </p>\n } @else if (headerUserTemplate) {\n <!-- Affichage du slot header -->\n <ng-container *ngTemplateOutlet=\"resHeaderTemplate\"></ng-container>\n }\n </ng-template>\n <ng-template #linksTemplate>\n <ng-container *ngTemplateOutlet=\"resLinksTemplate\"></ng-container>\n </ng-template>\n <ng-template #footerTemplate>\n @if (showLogout && !footerUserTemplate) {\n <dsfr-button\n label=\"Se d\u00E9connecter\"\n icon=\"fr-icon-logout-box-r-line\"\n iconPosition=\"left\"\n (click)=\"onLogout()\"\n variant=\"tertiary\"\n buttonSize=\"SM\"></dsfr-button>\n } @else if (footerUserTemplate) {\n <!-- Affichage du slot footer -->\n <ng-container *ngTemplateOutlet=\"resFooterTemplate\"></ng-container>\n }\n </ng-template>\n</dsfr-tool-link-menu>\n", styles: [".edu-menu-dropdown__btn,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn{margin:0}.edu-menu-dropdown__btn:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn:after{--data-uri-svg: url(\"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 24 24' ><path fill='%23929292' d='M12,13.1l5-4.9l1.4,1.4L12,15.9L5.6,9.5l1.4-1.4L12,13.1z'/></svg>\");flex:0 0 auto;display:inline-block;background-color:currentColor;width:var(--icon-size);height:var(--icon-size);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-image:var(--data-uri-svg);mask-image:var(--data-uri-svg);content:\"\";margin-left:.5rem;margin-right:-.125rem;transition:transform .3s}.edu-menu-dropdown__btn.fr-btn--sm,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm{--icon-size: 1rem}.edu-menu-dropdown__btn.fr-btn--sm:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--sm:after{--icon-size: 1rem;font-size:.875rem}.edu-menu-dropdown__btn.fr-btn--lg,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn.fr-btn--lg{--icon-size: 1.25rem;font-size:1.125rem}.edu-menu-dropdown__btn[aria-expanded=true],.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]{background-color:var(--background-action-low-blue-france)}.edu-menu-dropdown__btn[aria-expanded=true]:after,.fr-header__tools-links .fr-btns-group .edu-menu-dropdown__btn[aria-expanded=true]:after{transform:rotate(-180deg)}\n", ".edu-tool-link-menu__desc{font-weight:700}.edu-tool-link-menu__detail{color:var(--text-mention-grey);font-size:.875rem}.edu-tool-link-menu__footer .fr-btn{width:100%;justify-content:center}.edu-tool-link-menu__footer:empty,.edu-tool-link-menu__header:empty{height:0;padding:0;border:none}.edu-usermenu.fr-menu{width:20rem}.edu-usermenu.fr-menu .fr-menu__list{width:auto}\n"] }]
}], propDecorators: { menuId: [{
type: Input
}], labelMenu: [{
type: Input
}], ariaLabelMenu: [{
type: Input
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], links: [{
type: Input
}], userName: [{
type: Input
}], userDetails: [{
type: Input
}], showLogout: [{
type: Input
}], outline: [{
type: Input
}], headerUserTemplate: [{
type: ContentChild,
args: ['headerUserTemplate', { static: true }]
}], footerUserTemplate: [{
type: ContentChild,
args: ['footerUserTemplate', { static: true }]
}], linksUserTemplate: [{
type: ContentChild,
args: ['linksUserTemplate', { static: true }]
}], linkSelect: [{
type: Output
}], disclose: [{
type: Output
}], conceal: [{
type: Output
}], logoutSelect: [{
type: Output
}], linksProfileTemplate: [{
type: ContentChild,
args: ['linksProfileTemplate', { static: true }]
}], footerProfileTemplate: [{
type: ContentChild,
args: ['footerProfileTemplate', { static: true }]
}], headerProfileTemplate: [{
type: ContentChild,
args: ['headerProfileTemplate', { static: true }]
}], profileName: [{
type: Input
}], profileDetails: [{
type: Input
}] } });
/**
* Modèle de présentation INTERNE du composant table.
*
* @internal
* @deprecated utiliser le nouveau composant de table
*/
class EduTableModel {
constructor(data, keyValuePipe) {
this.rows = data.rows;
if (data.columns) {
this.columns = data.columns;
}
else {
this.columns = keyValuePipe.transform(new Map(Object.entries(data.titles ?? {}))).map((t) => ({
key: t.key,
heading: t.value,
}));
}
}
}
/**
* @deprecated utiliser le nouveau composant de table
*/
class DsfrTableLegacyComponent {
constructor() {
/**
* Permet d'empêcher le scroll horizontal. Si le tableau no-scroll comporte trop de colonnes le tableau est coupé
* sur la droite (overflow hidden).
*/
this.noScroll = false;
/**
* Permet de désactiver le scroll en fixant la largeur des colonnes du tableau. Les colonnes sont alors toutes de
* la même taille quelque soit leur contenu.
*/
this.fixedLayout = false;
/** Permet de masquer (visuellement) le caption afin de l’utiliser uniquement dans le cadre de l’accessibilité. */
this.noCaption = false;
/** Permet de placer le caption en dessous du tableau plutôt qu’en haut. */
this.captionBottom = false;
/**
* Classes de personnalisation du tableau (ex : `fr-table--green-emeraude`).
*/
this.customClass = '';
/** Permet d'ajouter des bordures aux lignes du tableau. */
this.bordered = false;
/** @internal */
this.dataModel = { columns: [], rows: [] };
this.i18n = inject(DsfrI18nService);
this.keyValuePipe = inject(KeyValuePipe);
this.noDataMessage = this.i18n.t('table.noData');
}
/** Modèle de données. */
set data(data) {
this.dataModel = new EduTableModel(data, this.keyValuePipe);
}
/** @internal */
hasRows() {
return !!this.dataModel && !!this.dataModel.rows && this.dataModel.rows.length > 0;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableLegacyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTableLegacyComponent, isStandalone: true, selector: "dsfr-table", inputs: { noDataMessage: "noDataMessage", noScroll: "noScroll", fixedLayout: "fixedLayout", caption: "caption", noCaption: "noCaption", captionBottom: "captionBottom", customClass: "customClass", headerCustomClass: "headerCustomClass", bordered: "bordered", data: "data" }, providers: [KeyValuePipe], ngImport: i0, template: "@if (dataModel.columns !== [] && dataModel.rows !== []) {\n <div\n #dsfrTable\n [class]=\"customClass\"\n [ngClass]=\"{\n 'fr-table': true,\n 'fr-table--bordered': bordered,\n 'fr-table--no-scroll': noScroll,\n 'fr-table--layout-fixed': fixedLayout,\n 'fr-table--no-caption': noCaption,\n 'fr-table--caption-bottom': captionBottom,\n }\">\n <table>\n @if (caption) {\n <caption>\n {{\n caption\n }}\n </caption>\n }\n @if (!caption) {\n <caption>\n <ng-content select=\"[caption]\"></ng-content>\n </caption>\n }\n <thead [ngClass]=\"headerCustomClass ? headerCustomClass : null\">\n <tr>\n @for (column of dataModel.columns; track column) {\n <th scope=\"col\">{{ column.heading }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of dataModel.rows; track row) {\n <tr>\n @for (column of dataModel.columns; track column) {\n <td>{{ row[column.key] }}</td>\n }\n </tr>\n }\n @if (!hasRows() && noDataMessage) {\n <tr>\n <td class=\"edu-td__nodata\" [attr.colspan]=\"dataModel.columns.length\">{{ noDataMessage }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n}\n", styles: ["td.edu-td__nodata{text-align:center;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableLegacyComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-table', encapsulation: ViewEncapsulation.None, imports: [CommonModule], providers: [KeyValuePipe], template: "@if (dataModel.columns !== [] && dataModel.rows !== []) {\n <div\n #dsfrTable\n [class]=\"customClass\"\n [ngClass]=\"{\n 'fr-table': true,\n 'fr-table--bordered': bordered,\n 'fr-table--no-scroll': noScroll,\n 'fr-table--layout-fixed': fixedLayout,\n 'fr-table--no-caption': noCaption,\n 'fr-table--caption-bottom': captionBottom,\n }\">\n <table>\n @if (caption) {\n <caption>\n {{\n caption\n }}\n </caption>\n }\n @if (!caption) {\n <caption>\n <ng-content select=\"[caption]\"></ng-content>\n </caption>\n }\n <thead [ngClass]=\"headerCustomClass ? headerCustomClass : null\">\n <tr>\n @for (column of dataModel.columns; track column) {\n <th scope=\"col\">{{ column.heading }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of dataModel.rows; track row) {\n <tr>\n @for (column of dataModel.columns; track column) {\n <td>{{ row[column.key] }}</td>\n }\n </tr>\n }\n @if (!hasRows() && noDataMessage) {\n <tr>\n <td class=\"edu-td__nodata\" [attr.colspan]=\"dataModel.columns.length\">{{ noDataMessage }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n}\n", styles: ["td.edu-td__nodata{text-align:center;font-style:italic}\n"] }]
}], ctorParameters: () => [], propDecorators: { noDataMessage: [{
type: Input
}], noScroll: [{
type: Input
}], fixedLayout: [{
type: Input
}], caption: [{
type: Input
}], noCaption: [{
type: Input
}], captionBottom: [{
type: Input
}], customClass: [{
type: Input
}], headerCustomClass: [{
type: Input
}], bordered: [{
type: Input
}], data: [{
type: Input
}] } });
/**
* @deprecated utiliser le nouveau composant de table
*/
class DsfrTableModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableModule, imports: [CommonModule, DsfrTableLegacyComponent], exports: [DsfrTableLegacyComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableModule, providers: [KeyValuePipe], imports: [CommonModule, DsfrTableLegacyComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTableModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrTableLegacyComponent],
imports: [CommonModule, DsfrTableLegacyComponent],
providers: [KeyValuePipe],
}]
}] });
class DsfrTabnavComponent {
constructor() {
/** Evénement émis suite à la sélection d'un lien */
this.itemSelect = new EventEmitter();
/** Activer la selection automatique des items (si absence de routerLink) */
this.autoActive = true;
/** 👓 Ajoute un role navigation sur le container. Nécessite de spécifier ariaLabel ou ariaLabelledBy. */
this.roleNavigation = false;
/** En mode link/route, le menuActive est défini a partir de la propriété active */
this.activeMenu = computed(() => {
return this.cloneMenu().find((m) => m.active);
}, ...(ngDevMode ? [{ debugName: "activeMenu" }] : []));
/** Copie du menu */
this.cloneMenu = signal([], ...(ngDevMode ? [{ debugName: "cloneMenu" }] : []));
this.menuId = `${newUniqueId()}--collapse`;
/** Référence vers le container du menu (element collapsable)*/
this._menuContainer = viewChild('menuContainer', ...(ngDevMode ? [{ debugName: "_menuContainer" }] : []));
this._menu = [];
}
get menu() {
return this._menu;
}
/** Menu contenant la liste de liens */
set menu(value) {
this._menu = value;
if (this._menu) {
this.cloneMenu.set(this._menu);
}
}
/**
* Au clic sur un lien du menu
* - Mise a jour du lien actif en cas de autoactive
* - Fermeture du menu et émission de itemSelect
* */
onSelectItem(currentItem) {
if (this.autoActive && currentItem.route) {
const menu = this.cloneMenu().map((i) => ({
...i,
active: currentItem.route === i.route,
}));
this.cloneMenu.set(menu);
}
this.closeMenuOnMobile();
this.itemSelect.emit(currentItem);
}
/**
* Force la fermeture du menu en mode mobile après une navigation
*/
closeMenuOnMobile() {
const el = this._menuContainer()?.nativeElement;
if (el)
dsfr(el).collapse.conceal();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabnavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTabnavComponent, isStandalone: true, selector: "dsfr-tabnav", inputs: { autoActive: "autoActive", roleNavigation: "roleNavigation", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", menu: "menu" }, outputs: { itemSelect: "itemSelect" }, viewQueries: [{ propertyName: "_menuContainer", first: true, predicate: ["menuContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n [attr.aria-label]=\"ariaLabel ?? null\"\n [attr.role]=\"roleNavigation ? 'navigation' : null\"\n class=\"fr-sidemenu edu-tabnav\">\n <div class=\"fr-sidemenu__inner\">\n @if (activeMenu()) {\n <!-- Affichage du bouton en mobile selon la prop active en cas de route -->\n <button aria-expanded=\"false\" [attr.aria-controls]=\"menuId\" type=\"button\" class=\"fr-sidemenu__btn\">\n {{ activeMenu()?.label }}\n </button>\n } @else {\n <!-- Affichage du bouton en mobile selon routerLinkActive en cas de routerLink -->\n @for (link of cloneMenu(); track $index) {\n <div\n class=\"fr-sidemenu__inner\"\n [routerLink]=\"link.routerLink\"\n #rla=\"routerLinkActive\"\n routerLinkActive\n [routerLinkActiveOptions]=\"link.routerLinkActiveOptions ?? { exact: true }\">\n @if (rla.isActive) {\n <button aria-expanded=\"false\" [attr.aria-controls]=\"menuId\" type=\"button\" class=\"fr-sidemenu__btn\">\n {{ link.label }}\n </button>\n }\n </div>\n }\n }\n <div #menuContainer class=\"fr-collapse\" [attr.id]=\"menuId\">\n <ul>\n @for (link of cloneMenu(); track $index) {\n <li>\n <edu-item-link\n [item]=\"link\"\n [customClass]=\"'edu-tabnav__tab ' + (link.customClass ?? '')\"\n (linkSelect)=\"onSelectItem(link)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </div>\n</div>\n", styles: ["dsfr-tabnav .edu-tabnav.fr-sidemenu{margin-bottom:2rem;padding-right:0}dsfr-tabnav .edu-tabnav.fr-sidemenu .fr-sidemenu__inner>.fr-sidemenu__btn{font-weight:500;outline-offset:-2px;width:100%}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 1px;text-align:left;width:100%;color:var(--text-default-grey);border:0;padding:.75rem .5rem;font-size:1rem;font-weight:700;line-height:1.5;text-decoration:none;display:block}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab[aria-current=page]{color:var(--text-active-blue-france);display:none}dsfr-tabnav .edu-tabnav.fr-sidemenu ul{padding-top:1rem}@media(min-width:48em){dsfr-tabnav .edu-tabnav.fr-sidemenu{padding-top:2rem}dsfr-tabnav .edu-tabnav.fr-sidemenu .fr-sidemenu__inner{box-shadow:none;padding:0}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 2px;padding:.75rem;font-size:.875rem;font-weight:400}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab[aria-current=page]{background:linear-gradient(0deg,var(--border-active-blue-france),var(--border-active-blue-france)) no-repeat 0 100%/100% 2px;display:block}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab:hover:not([aria-current=page]){background:linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey)) no-repeat 0 100%/100% 2px;background-color:var(--background-default-grey-hover)}dsfr-tabnav .edu-tabnav.fr-sidemenu ul{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 2px;flex-direction:row;padding-top:0;display:flex}dsfr-tabnav .fr-sidemenu__inner>.fr-sidemenu__btn{display:none}dsfr-tabnav .fr-sidemenu__inner>.fr-sidemenu__btn[aria-expanded]{padding:.75rem 2.5rem .75rem 1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabnavComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tabnav', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterModule, ItemLinkComponent], template: "<div\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n [attr.aria-label]=\"ariaLabel ?? null\"\n [attr.role]=\"roleNavigation ? 'navigation' : null\"\n class=\"fr-sidemenu edu-tabnav\">\n <div class=\"fr-sidemenu__inner\">\n @if (activeMenu()) {\n <!-- Affichage du bouton en mobile selon la prop active en cas de route -->\n <button aria-expanded=\"false\" [attr.aria-controls]=\"menuId\" type=\"button\" class=\"fr-sidemenu__btn\">\n {{ activeMenu()?.label }}\n </button>\n } @else {\n <!-- Affichage du bouton en mobile selon routerLinkActive en cas de routerLink -->\n @for (link of cloneMenu(); track $index) {\n <div\n class=\"fr-sidemenu__inner\"\n [routerLink]=\"link.routerLink\"\n #rla=\"routerLinkActive\"\n routerLinkActive\n [routerLinkActiveOptions]=\"link.routerLinkActiveOptions ?? { exact: true }\">\n @if (rla.isActive) {\n <button aria-expanded=\"false\" [attr.aria-controls]=\"menuId\" type=\"button\" class=\"fr-sidemenu__btn\">\n {{ link.label }}\n </button>\n }\n </div>\n }\n }\n <div #menuContainer class=\"fr-collapse\" [attr.id]=\"menuId\">\n <ul>\n @for (link of cloneMenu(); track $index) {\n <li>\n <edu-item-link\n [item]=\"link\"\n [customClass]=\"'edu-tabnav__tab ' + (link.customClass ?? '')\"\n (linkSelect)=\"onSelectItem(link)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </div>\n</div>\n", styles: ["dsfr-tabnav .edu-tabnav.fr-sidemenu{margin-bottom:2rem;padding-right:0}dsfr-tabnav .edu-tabnav.fr-sidemenu .fr-sidemenu__inner>.fr-sidemenu__btn{font-weight:500;outline-offset:-2px;width:100%}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 1px;text-align:left;width:100%;color:var(--text-default-grey);border:0;padding:.75rem .5rem;font-size:1rem;font-weight:700;line-height:1.5;text-decoration:none;display:block}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab[aria-current=page]{color:var(--text-active-blue-france);display:none}dsfr-tabnav .edu-tabnav.fr-sidemenu ul{padding-top:1rem}@media(min-width:48em){dsfr-tabnav .edu-tabnav.fr-sidemenu{padding-top:2rem}dsfr-tabnav .edu-tabnav.fr-sidemenu .fr-sidemenu__inner{box-shadow:none;padding:0}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 2px;padding:.75rem;font-size:.875rem;font-weight:400}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab[aria-current=page]{background:linear-gradient(0deg,var(--border-active-blue-france),var(--border-active-blue-france)) no-repeat 0 100%/100% 2px;display:block}dsfr-tabnav .edu-tabnav.fr-sidemenu .edu-tabnav__tab:hover:not([aria-current=page]){background:linear-gradient(0deg,var(--border-contrast-grey),var(--border-contrast-grey)) no-repeat 0 100%/100% 2px;background-color:var(--background-default-grey-hover)}dsfr-tabnav .edu-tabnav.fr-sidemenu ul{background:linear-gradient(0deg,var(--border-default-grey),var(--border-default-grey)) no-repeat 0 100%/100% 2px;flex-direction:row;padding-top:0;display:flex}dsfr-tabnav .fr-sidemenu__inner>.fr-sidemenu__btn{display:none}dsfr-tabnav .fr-sidemenu__inner>.fr-sidemenu__btn[aria-expanded]{padding:.75rem 2.5rem .75rem 1rem}}\n"] }]
}], propDecorators: { itemSelect: [{
type: Output
}], autoActive: [{
type: Input
}], roleNavigation: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], _menuContainer: [{ type: i0.ViewChild, args: ['menuContainer', { isSignal: true }] }], menu: [{
type: Input
}] } });
const BUTTON_TAB_ID_PREFIX = 'buttontab-';
const DSFR_TAB_SELECTORS = 'dsfr-tab-panel, dsfr-tab';
class DsfrTabComponent {
constructor() {
/** Identifiant de l'onglet, obligatoire, généré si non fourni. */
this.tabId = '';
/** Nom de l'icône, l'icône est à gauche du libellé, optionnel. */
this.icon = '';
/** Permet de désactiver l'onglet. */
this.disabled = false;
}
get id() {
return this.tabId;
}
get attrRole() {
return 'tabpanel';
}
get attrLabelledBy() {
return this.buttonId;
}
get attrTabindex() {
return '0';
}
get attrTabId() {
return this.tabId;
}
get attrLabel() {
return this.label;
}
get attrIconClass() {
return this.icon || null;
}
get class() {
return 'fr-tabs__panel';
}
get attrDisabled() {
return this.disabled;
}
/** @internal */
ngOnInit() {
if (!this.tabId) {
this.tabId = newUniqueId();
}
this.buttonId = BUTTON_TAB_ID_PREFIX + this.tabId;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.23", type: DsfrTabComponent, isStandalone: true, selector: "dsfr-tab-panel, dsfr-tab", inputs: { tabId: "tabId", label: "label", icon: "icon", disabled: ["disabled", "disabled", booleanAttribute] }, host: { properties: { "id": "this.id", "attr.role": "this.attrRole", "attr.aria-labelledby": "this.attrLabelledBy", "attr.tabindex": "this.attrTabindex", "attr.data-tabid": "this.attrTabId", "attr.data-label": "this.attrLabel", "attr.data-iconClass": "this.attrIconClass", "class": "this.class", "attr.disabled": "this.attrDisabled" } }, ngImport: i0, template: "<ng-content></ng-content>\n", encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabComponent, decorators: [{
type: Component,
args: [{ selector: DSFR_TAB_SELECTORS, encapsulation: ViewEncapsulation.None, template: "<ng-content></ng-content>\n" }]
}], propDecorators: { tabId: [{
type: Input
}], label: [{
type: Input,
args: [{ required: true }]
}], icon: [{
type: Input
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], id: [{
type: HostBinding,
args: ['id']
}], attrRole: [{
type: HostBinding,
args: ['attr.role']
}], attrLabelledBy: [{
type: HostBinding,
args: ['attr.aria-labelledby']
}], attrTabindex: [{
type: HostBinding,
args: ['attr.tabindex']
}], attrTabId: [{
type: HostBinding,
args: ['attr.data-tabid']
}], attrLabel: [{
type: HostBinding,
args: ['attr.data-label']
}], attrIconClass: [{
type: HostBinding,
args: ['attr.data-iconClass']
}], class: [{
type: HostBinding,
args: ['class']
}], attrDisabled: [{
type: HostBinding,
args: ['attr.disabled']
}] } });
class DsfrTabsComponent {
constructor() {
/**
* Attribut aria-label de la liste d'onglets.
*/
this.tabsAriaLabel = "Système d'onglets";
/**
* Onglet sélectionné (commence à 0).
* Ne peut pas être utilisé quand on utilise l'input `routes`.
*/
this.selectedTabIndex = 0;
/**
* Permet de positionner la largeur du viewport à 100% en mobile.
*/
this.fullViewport = false;
/**
* Émis lors de la sélection d'un onglet. La valeur émise correspond au `tabId` de l'onglet sélectionné.
*/
this.tabSelect = new EventEmitter();
this.router = inject(Router, { optional: true });
this.activatedRoute = inject(ActivatedRoute, { optional: true });
this.elRef = inject(ElementRef);
this.routerTabId = newUniqueId();
}
get headers() {
return this.routes || this.tabComponents?.toArray();
}
get tabsCount() {
return this.routes ? this.routes.length : this.tabComponents ? this.tabComponents.length : 0;
}
/** @internal */
getHeaderValue(header, key) {
return header[key];
}
/** @internal */
asRoute(header) {
return header;
}
/** @internal */
getIconClasses(tabHeader) {
return !tabHeader.icon ? '' : `${tabHeader.icon} fr-tabs__tab--icon-left`;
}
/**
* [mode routes] Retourne l'identifiant unique du bouton de contrôle de l'onglet passé en paramètre.
*
* @internal
*/
getButtonTabId(header) {
return BUTTON_TAB_ID_PREFIX + header.tabId;
}
/**
* [mode routes] Retourne l'identifiant unique du bouton de contrôle de l'onglet courant.
*
* @internal
*/
getCurrentButtonTabId() {
return this.selectedTabIndex >= 0 ? BUTTON_TAB_ID_PREFIX + this.routes[this.selectedTabIndex].tabId : null;
}
/** @internal */
onSelect(event, index) {
event.preventDefault();
event.stopPropagation();
this.selectedTabIndex = index;
this.tabSelect.emit(this.headers[this.selectedTabIndex].tabId);
}
/**
* Gestion de la navigation au clavier sans routes : emission de tabSelect
* @internal
*/
onRoutlessTabKeydown(event, index) {
const newIndex = this.calculateNewIndex(event, index);
if (newIndex !== -1) {
this.tabSelect.emit(this.headers[newIndex].tabId);
}
}
/** @internal */
onRouterLinkActive(isActive, tabIndex) {
if (isActive) {
this.selectedTabIndex = tabIndex;
}
}
/**
* Gestion de la navigation au clavier en mode routes.
*
* @internal
*/
onButtonTabKeydown(event, index) {
// just in case
if (this.selectedTabIndex !== index)
return;
const newIndex = this.calculateNewIndex(event, index);
if (newIndex > -1) {
this.performNavigation(newIndex);
}
}
isFirstTab(index) {
return index === 0;
}
isLastTab(index) {
return index === this.tabsCount - 1;
}
performNavigation(newIndex) {
const route = this.routes[newIndex];
const extras = route.routerLinkExtras || { relativeTo: this.activatedRoute };
this.router?.navigate([route.path], extras);
this.elRef.nativeElement.querySelector('#' + this.getButtonTabId(route))?.focus();
}
calculateNewIndex(event, index) {
let newIndex = -1;
switch (event.code) {
case 'ArrowLeft':
newIndex = this.isFirstTab(index) ? this.tabsCount - 1 : index - 1;
break;
case 'ArrowRight':
newIndex = this.isLastTab(index) ? 0 : index + 1;
break;
case 'Home':
newIndex = 0;
break;
case 'End':
newIndex = this.tabsCount - 1;
break;
default:
break;
}
return newIndex;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTabsComponent, isStandalone: true, selector: "dsfr-tabs", inputs: { tabsAriaLabel: "tabsAriaLabel", selectedTabIndex: "selectedTabIndex", fullViewport: "fullViewport", routes: "routes" }, outputs: { tabSelect: "tabSelect" }, queries: [{ propertyName: "tabComponents", predicate: DsfrTabComponent }], ngImport: i0, template: "<div class=\"fr-tabs\" [class.fr-tabs--viewport-width]=\"fullViewport\">\n <ul class=\"fr-tabs__list\" role=\"tablist\" [attr.aria-label]=\"tabsAriaLabel\">\n @for (header of headers; track $index; let index = $index) {\n <li role=\"presentation\">\n @if (!routes) {\n <button\n [attr.id]=\"getHeaderValue(header, 'buttonId') || null\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"index === selectedTabIndex ? 0 : -1\"\n [attr.aria-controls]=\"header?.tabId || null\"\n [attr.aria-selected]=\"index === selectedTabIndex\"\n [disabled]=\"header?.disabled || null\"\n (keydown)=\"onRoutlessTabKeydown($event, index)\"\n (click)=\"onSelect($event, index)\">\n {{ header.label }}\n </button>\n } @else {\n <button\n [attr.id]=\"getButtonTabId(header)\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"rla.isActive ? 0 : -1\"\n [attr.aria-controls]=\"rla.isActive ? routerTabId : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [disabled]=\"header?.disabled || null\"\n [routerLink]=\"asRoute(header).path || null\"\n routerLinkActive\n [routerLinkActiveOptions]=\"asRoute(header).routerLinkActiveOptions ?? { exact: false }\"\n [queryParams]=\"asRoute(header).routerLinkExtras?.queryParams\"\n [fragment]=\"asRoute(header).routerLinkExtras?.fragment\"\n [queryParamsHandling]=\"asRoute(header).routerLinkExtras?.queryParamsHandling\"\n [preserveFragment]=\"asRoute(header).routerLinkExtras?.preserveFragment\"\n [skipLocationChange]=\"asRoute(header).routerLinkExtras?.skipLocationChange\"\n [replaceUrl]=\"asRoute(header).routerLinkExtras?.replaceUrl\"\n [state]=\"asRoute(header).routerLinkExtras?.state\"\n (isActiveChange)=\"onRouterLinkActive($event, index)\"\n #rla=\"routerLinkActive\"\n (click)=\"onSelect($event, index)\"\n (keydown)=\"onButtonTabKeydown($event, index)\">\n {{ header.label }}\n </button>\n }\n </li>\n }\n </ul>\n @if (!routes) {\n <ng-content></ng-content>\n } @else {\n <div\n [attr.id]=\"routerTabId\"\n role=\"tabpanel\"\n class=\"fr-tabs__panel fr-tabs__panel--selected\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"getCurrentButtonTabId()\">\n <router-outlet></router-outlet>\n </div>\n @for (header of headers; track $index) {\n <div [attr.aria-labelledby]=\"getCurrentButtonTabId()\"></div>\n }\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tabs', encapsulation: ViewEncapsulation.None, imports: [CommonModule, RouterModule], template: "<div class=\"fr-tabs\" [class.fr-tabs--viewport-width]=\"fullViewport\">\n <ul class=\"fr-tabs__list\" role=\"tablist\" [attr.aria-label]=\"tabsAriaLabel\">\n @for (header of headers; track $index; let index = $index) {\n <li role=\"presentation\">\n @if (!routes) {\n <button\n [attr.id]=\"getHeaderValue(header, 'buttonId') || null\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"index === selectedTabIndex ? 0 : -1\"\n [attr.aria-controls]=\"header?.tabId || null\"\n [attr.aria-selected]=\"index === selectedTabIndex\"\n [disabled]=\"header?.disabled || null\"\n (keydown)=\"onRoutlessTabKeydown($event, index)\"\n (click)=\"onSelect($event, index)\">\n {{ header.label }}\n </button>\n } @else {\n <button\n [attr.id]=\"getButtonTabId(header)\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"rla.isActive ? 0 : -1\"\n [attr.aria-controls]=\"rla.isActive ? routerTabId : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [disabled]=\"header?.disabled || null\"\n [routerLink]=\"asRoute(header).path || null\"\n routerLinkActive\n [routerLinkActiveOptions]=\"asRoute(header).routerLinkActiveOptions ?? { exact: false }\"\n [queryParams]=\"asRoute(header).routerLinkExtras?.queryParams\"\n [fragment]=\"asRoute(header).routerLinkExtras?.fragment\"\n [queryParamsHandling]=\"asRoute(header).routerLinkExtras?.queryParamsHandling\"\n [preserveFragment]=\"asRoute(header).routerLinkExtras?.preserveFragment\"\n [skipLocationChange]=\"asRoute(header).routerLinkExtras?.skipLocationChange\"\n [replaceUrl]=\"asRoute(header).routerLinkExtras?.replaceUrl\"\n [state]=\"asRoute(header).routerLinkExtras?.state\"\n (isActiveChange)=\"onRouterLinkActive($event, index)\"\n #rla=\"routerLinkActive\"\n (click)=\"onSelect($event, index)\"\n (keydown)=\"onButtonTabKeydown($event, index)\">\n {{ header.label }}\n </button>\n }\n </li>\n }\n </ul>\n @if (!routes) {\n <ng-content></ng-content>\n } @else {\n <div\n [attr.id]=\"routerTabId\"\n role=\"tabpanel\"\n class=\"fr-tabs__panel fr-tabs__panel--selected\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"getCurrentButtonTabId()\">\n <router-outlet></router-outlet>\n </div>\n @for (header of headers; track $index) {\n <div [attr.aria-labelledby]=\"getCurrentButtonTabId()\"></div>\n }\n }\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { tabsAriaLabel: [{
type: Input
}], selectedTabIndex: [{
type: Input
}], fullViewport: [{
type: Input
}], routes: [{
type: Input
}], tabSelect: [{
type: Output
}], tabComponents: [{
type: ContentChildren,
args: [DsfrTabComponent]
}] } });
class DsfrTabsModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsModule, imports: [CommonModule, RouterModule, DsfrTabComponent, DsfrTabsComponent], exports: [DsfrTabComponent, DsfrTabsComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsModule, imports: [CommonModule, RouterModule, DsfrTabsComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTabsModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule, RouterModule, DsfrTabComponent, DsfrTabsComponent],
exports: [DsfrTabComponent, DsfrTabsComponent],
}]
}] });
class DsfrTileComponent extends BasePanelComponent {
constructor() {
super();
/**
* Ce boolean permet de ne pas afficher d'icône (flèche).
*/
this.noIcon = false;
/**
* Rotation d'une tuile sur breakpoint 'MD' ou 'LG' (au redimensionnement du contenu).
* 'fr-tile--horizontal@md|lg' si la tuile est verticale
* 'fr-tile--vertical@md|lg' si la tuile est horizontale
*/
this.rotateOn = undefined;
/** @deprecated utiliser `customBackground`. Active le fond de la tuile en gris clair. */
this.useGreyBackground = false;
/** Remplace le lien du bouton de download par un markup de bouton */
this.isDownloadButton = false;
/**
* Permet la gestion programmatique d'une navigation initiée au click sur le tile si l'input 'route' est valorisé.
* La valeur de la propriété 'route' sera transmise.
*/
this.tileSelect = new EventEmitter();
this.DsfrPanelBackground = DsfrPanelBackgroundConst;
this.DsfrPanelBorder = DsfrPanelBorderConst;
this.config = inject(DSFR_CONFIG_TOKEN);
this._elementRef = inject(ElementRef);
}
get tileClasses() {
return {
'fr-enlarge-link': this.enlargeLink && this.hasLink(),
'fr-enlarge-button': this.enlargeButton,
'fr-tile--download': this.download,
'fr-tile--grey': this.customBackground === this.DsfrPanelBackground.GREY || this.useGreyBackground,
'fr-tile--no-background': this.customBackground === this.DsfrPanelBackground.TRANSPARENT,
'fr-tile--horizontal': this.horizontal,
'fr-tile--no-border': this.customBorder === this.DsfrPanelBorder.NO_BORDER,
'fr-tile--shadow': this.customBorder === this.DsfrPanelBorder.SHADOW,
'fr-tile--no-icon': this.noIcon,
'fr-tile--vertical@md': this.horizontal && this.rotateOn === 'MD',
'fr-tile--vertical@lg': this.horizontal && this.rotateOn === 'LG',
};
}
/** @deprecated (since 1.9.0) utiliser `detailBottom` à la place */
set detail(value) {
this.detailBottom = value;
}
ngOnInit() {
if (this.artworkDirPath === undefined && this.config.artworkDirPath) {
this.artworkDirPath = this.config.artworkDirPath;
}
}
ngAfterViewInit() {
// Supprimer les <p> detail et description si ils sont vides
// fixme : problème pour détecter que le slot est vide dans le template et ne pas afficher le <p>
this.removeEmptyElement(this._elementRef.nativeElement?.querySelector('.fr-tile__detail'));
this.removeEmptyElement(this._elementRef.nativeElement?.querySelector('.fr-tile__desc'));
}
/**
* Dans le cas d'une route, un événement `(routeSelect)` est émis avec la valeur de la route et
* l'événement initial n'est pas propagé.
*
* @internal
*/
onLinkSelect() {
// on propage l'output, pas besoin de gérer ici un éventuel preventDefault si usage de route, c'est géré en amont
if (this.route) {
this.tileSelect.emit(this.route);
}
}
removeEmptyElement(el) {
if (el && !el.textContent && el.children?.length === 0) {
el.remove();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrTileComponent, isStandalone: true, selector: "dsfr-tile", inputs: { artworkDirPath: "artworkDirPath", artworkFilePath: "artworkFilePath", imageAlt: "imageAlt", imagePath: "imagePath", noIcon: "noIcon", rotateOn: "rotateOn", useGreyBackground: "useGreyBackground", isDownloadButton: "isDownloadButton", detail: "detail" }, outputs: { tileSelect: "tileSelect" }, usesInheritance: true, ngImport: i0, template: "<div class=\"fr-tile\" [ngClass]=\"tileClasses\">\n <!-- Body -->\n <div class=\"fr-tile__body\">\n <div class=\"fr-tile__content\">\n <ng-container *ngTemplateOutlet=\"tileHeadingTemplate\"></ng-container>\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-tile__desc\">\n @if (description) {\n {{ description }}\n } @else {\n <ng-content select=\"[desc]\"></ng-content>\n }\n </p>\n } @else {\n <div class=\"fr-tile__desc\">\n @if (description) {\n {{ description }}\n } @else {\n <ng-content select=\"[desc]\"></ng-content>\n }\n </div>\n }\n\n <p class=\"fr-tile__detail\">\n @if (detailBottom) {\n {{ detailBottom }}\n } @else {\n <ng-content select=\"[detail]\"></ng-content>\n }\n </p>\n\n @if ((badges && badges.length) || (tags && tags.length)) {\n <div class=\"fr-tile__start\">\n @if (badges) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n @if (tags && !badges.length) {\n <dsfr-tags-group [tags]=\"tags\"></dsfr-tags-group>\n }\n </div>\n }\n </div>\n </div>\n\n <!--TODO : Deprecated image \u00E0 retirer en 2.0 -->\n @if (imagePath && !artworkFilePath && !download) {\n <div class=\"fr-tile__img\">\n <img [src]=\"imagePath\" class=\"fr-responsive-img\" [attr.alt]=\"imageAlt\" />\n <!-- L'alternative de l'image (attribut alt) doit \u00E0 priori rester vide car l'image est illustrative et ne doit pas \u00EAtre restitu\u00E9e aux technologies d\u2019assistance. Vous pouvez toutefois remplir l'alternative si vous estimer qu'elle apporte une information essentielle \u00E0 la compr\u00E9hension du contenu non pr\u00E9sente dans le texte -->\n </div>\n }\n\n <!-- Header --------------------------------------------------------------------------------------------------------->\n @if (artworkFilePath || download) {\n <div class=\"fr-tile__header\">\n <div class=\"fr-tile__pictogram\">\n <edu-pictogram\n [artworkDirPath]=\"artworkDirPath ?? ''\"\n [artworkFilePath]=\"artworkFilePath ?? ''\"\n [download]=\"download\"></edu-pictogram>\n </div>\n </div>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #tileHeadingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @default {\n <h3 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n @case ('H4') {\n <h4 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n }\n</ng-template>\n\n<ng-template #headingTemplate>\n <!-- Definition des templates directement ici pour une projection du slot correcte (pas de else)-->\n\n <!-- Titre simple -------------------------------------------------------->\n @if (!hasLink()) {\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n }\n\n <!-- Lien --------------------------------------------------------------->\n @if (hasLink()) {\n <!-- Lien sans t\u00E9l\u00E9chargement -->\n @if (!download) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [ariaLabel]=\"ariaLabel ?? ''\"\n [disabled]=\"disabled\"\n [label]=\"heading ?? ''\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [mode]=\"enlargeButton ? 'button' : 'link'\"\n (linkSelect)=\"onLinkSelect()\">\n @if (!heading) {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n </dsfr-link>\n } @else {\n <!-- Lien de t\u00E9l\u00E9chargement -->\n <edu-link-download\n [item]=\"itemLink\"\n [downloadDirect]=\"downloadDirect\"\n [downloadAssessFile]=\"downloadAssessFile\"\n [linkTarget]=\"linkTarget\"\n [langCode]=\"downloadLangCode ?? ''\"\n [isButton]=\"isDownloadButton\"\n (linkSelect)=\"onLinkSelect()\"></edu-link-download>\n }\n }\n</ng-template>\n\n<!-- Template s\u00E9par\u00E9 pour avoir une seule d\u00E9finition du select heading (DO NOT DELETE) -->\n<ng-template #headingTemplateSlot>\n <ng-content select=\"[heading]\"></ng-content>\n</ng-template>\n", styles: [".fr-tile{height:100%}.fr-tile__start dsfr-badges-group .fr-badges-group,.fr-tile__start dsfr-badges-group .fr-tags-group,.fr-tile__start dsfr-tags-group .fr-badges-group,.fr-tile__start dsfr-tags-group .fr-tags-group{justify-content:center}.fr-tile__pictogram svg{width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsfrBadgesGroupComponent, selector: "dsfr-badges-group", inputs: ["badges", "small"] }, { kind: "component", type: DsfrTagsGroupComponent, selector: "dsfr-tags-group", inputs: ["tags", "mode"], outputs: ["tagSelect"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: LinkDownloadComponent, selector: "edu-link-download", inputs: ["customClass", "downloadAssessFile", "langCode", "linkTarget", "isButton", "downloadDirect", "item"], outputs: ["linkSelect"] }, { kind: "component", type: PictogramComponent, selector: "edu-pictogram", inputs: ["artworkDirPath", "artworkFilePath", "download"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tile', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
DsfrBadgesGroupComponent,
DsfrTagsGroupComponent,
DsfrLinkComponent,
LinkDownloadComponent,
PictogramComponent,
], template: "<div class=\"fr-tile\" [ngClass]=\"tileClasses\">\n <!-- Body -->\n <div class=\"fr-tile__body\">\n <div class=\"fr-tile__content\">\n <ng-container *ngTemplateOutlet=\"tileHeadingTemplate\"></ng-container>\n @if (descriptionMarkup === 'p') {\n <p class=\"fr-tile__desc\">\n @if (description) {\n {{ description }}\n } @else {\n <ng-content select=\"[desc]\"></ng-content>\n }\n </p>\n } @else {\n <div class=\"fr-tile__desc\">\n @if (description) {\n {{ description }}\n } @else {\n <ng-content select=\"[desc]\"></ng-content>\n }\n </div>\n }\n\n <p class=\"fr-tile__detail\">\n @if (detailBottom) {\n {{ detailBottom }}\n } @else {\n <ng-content select=\"[detail]\"></ng-content>\n }\n </p>\n\n @if ((badges && badges.length) || (tags && tags.length)) {\n <div class=\"fr-tile__start\">\n @if (badges) {\n <dsfr-badges-group [badges]=\"badges\"></dsfr-badges-group>\n }\n @if (tags && !badges.length) {\n <dsfr-tags-group [tags]=\"tags\"></dsfr-tags-group>\n }\n </div>\n }\n </div>\n </div>\n\n <!--TODO : Deprecated image \u00E0 retirer en 2.0 -->\n @if (imagePath && !artworkFilePath && !download) {\n <div class=\"fr-tile__img\">\n <img [src]=\"imagePath\" class=\"fr-responsive-img\" [attr.alt]=\"imageAlt\" />\n <!-- L'alternative de l'image (attribut alt) doit \u00E0 priori rester vide car l'image est illustrative et ne doit pas \u00EAtre restitu\u00E9e aux technologies d\u2019assistance. Vous pouvez toutefois remplir l'alternative si vous estimer qu'elle apporte une information essentielle \u00E0 la compr\u00E9hension du contenu non pr\u00E9sente dans le texte -->\n </div>\n }\n\n <!-- Header --------------------------------------------------------------------------------------------------------->\n @if (artworkFilePath || download) {\n <div class=\"fr-tile__header\">\n <div class=\"fr-tile__pictogram\">\n <edu-pictogram\n [artworkDirPath]=\"artworkDirPath ?? ''\"\n [artworkFilePath]=\"artworkFilePath ?? ''\"\n [download]=\"download\"></edu-pictogram>\n </div>\n </div>\n }\n</div>\n\n<!-- Templates ----------------------------------------------------------------------------------------------------- -->\n\n<ng-template #tileHeadingTemplate>\n @switch (headingLevel) {\n @case ('H2') {\n <h2 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h2>\n }\n @default {\n <h3 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h3>\n }\n @case ('H4') {\n <h4 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h4>\n }\n @case ('H5') {\n <h5 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h5>\n }\n @case ('H6') {\n <h6 class=\"fr-tile__title\">\n <ng-container *ngTemplateOutlet=\"headingTemplate\"></ng-container>\n </h6>\n }\n }\n</ng-template>\n\n<ng-template #headingTemplate>\n <!-- Definition des templates directement ici pour une projection du slot correcte (pas de else)-->\n\n <!-- Titre simple -------------------------------------------------------->\n @if (!hasLink()) {\n @if (heading) {\n {{ heading }}\n } @else {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n }\n\n <!-- Lien --------------------------------------------------------------->\n @if (hasLink()) {\n <!-- Lien sans t\u00E9l\u00E9chargement -->\n @if (!download) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [ariaLabel]=\"ariaLabel ?? ''\"\n [disabled]=\"disabled\"\n [label]=\"heading ?? ''\"\n [link]=\"link\"\n [linkTarget]=\"linkTarget\"\n [route]=\"route\"\n [routePath]=\"routePath\"\n [routerLinkActive]=\"routerLinkActive ?? ''\"\n [routerLinkExtras]=\"routerLinkExtras\"\n [mode]=\"enlargeButton ? 'button' : 'link'\"\n (linkSelect)=\"onLinkSelect()\">\n @if (!heading) {\n <ng-container *ngTemplateOutlet=\"headingTemplateSlot\"></ng-container>\n }\n </dsfr-link>\n } @else {\n <!-- Lien de t\u00E9l\u00E9chargement -->\n <edu-link-download\n [item]=\"itemLink\"\n [downloadDirect]=\"downloadDirect\"\n [downloadAssessFile]=\"downloadAssessFile\"\n [linkTarget]=\"linkTarget\"\n [langCode]=\"downloadLangCode ?? ''\"\n [isButton]=\"isDownloadButton\"\n (linkSelect)=\"onLinkSelect()\"></edu-link-download>\n }\n }\n</ng-template>\n\n<!-- Template s\u00E9par\u00E9 pour avoir une seule d\u00E9finition du select heading (DO NOT DELETE) -->\n<ng-template #headingTemplateSlot>\n <ng-content select=\"[heading]\"></ng-content>\n</ng-template>\n", styles: [".fr-tile{height:100%}.fr-tile__start dsfr-badges-group .fr-badges-group,.fr-tile__start dsfr-badges-group .fr-tags-group,.fr-tile__start dsfr-tags-group .fr-badges-group,.fr-tile__start dsfr-tags-group .fr-tags-group{justify-content:center}.fr-tile__pictogram svg{width:100%;height:100%}\n"] }]
}], ctorParameters: () => [], propDecorators: { artworkDirPath: [{
type: Input
}], artworkFilePath: [{
type: Input
}], imageAlt: [{
type: Input
}], imagePath: [{
type: Input
}], noIcon: [{
type: Input
}], rotateOn: [{
type: Input
}], useGreyBackground: [{
type: Input
}], isDownloadButton: [{
type: Input
}], tileSelect: [{
type: Output
}], detail: [{
type: Input
}] } });
class DsfrTileModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileModule, imports: [CommonModule,
DsfrBadgesGroupComponent,
DsfrTagsGroupComponent,
DsfrLinkComponent,
LinkDownloadComponent,
PictogramComponent,
DsfrTileComponent], exports: [DsfrTileComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileModule, imports: [CommonModule,
DsfrBadgesGroupComponent,
DsfrTagsGroupComponent,
DsfrLinkComponent,
LinkDownloadComponent,
PictogramComponent,
DsfrTileComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTileModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrTileComponent],
imports: [
CommonModule,
DsfrBadgesGroupComponent,
DsfrTagsGroupComponent,
DsfrLinkComponent,
LinkDownloadComponent,
PictogramComponent,
DsfrTileComponent,
],
}]
}] });
class TooltipComponent {
constructor() {
this.tooltipId = newUniqueId();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: TooltipComponent, isStandalone: true, selector: "edu-tooltip", ngImport: i0, template: `<span class="fr-tooltip fr-placement" [id]="tooltipId" role="tooltip" [innerHTML]="message"></span>`, isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: TooltipComponent, decorators: [{
type: Component,
args: [{
selector: 'edu-tooltip',
template: `<span class="fr-tooltip fr-placement" [id]="tooltipId" role="tooltip" [innerHTML]="message"></span>`,
imports: [],
}]
}] });
class DsfrTooltipDirective {
constructor() {
this.elementRef = inject((ElementRef));
this.viewContainer = inject(ViewContainerRef);
const tooltipRef = this.viewContainer.createComponent(TooltipComponent);
// @ts-ignore
this.tooltipComponent = tooltipRef.instance;
// Tooltip id
const nativeElt = this.elementRef.nativeElement;
nativeElt.setAttribute('aria-describedby', this.tooltipComponent.tooltipId);
// Message textuel
const message = nativeElt.getAttribute('tooltip');
if (message)
this.tooltip = message;
}
set tooltip(value) {
this.tooltipComponent.message = value;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.23", type: DsfrTooltipDirective, isStandalone: true, selector: "[tooltip]:not(dsfr-tooltip-button)", inputs: { tooltip: "tooltip" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTooltipDirective, decorators: [{
type: Directive,
args: [{
selector: '[tooltip]:not(dsfr-tooltip-button)',
standalone: true,
}]
}], ctorParameters: () => [], propDecorators: { tooltip: [{
type: Input
}] } });
class DsfrTooltipButtonComponent {
constructor() {
/** @internal */
this.tooltipId = newUniqueId();
this.i18n = inject(DsfrI18nService);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTooltipButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrTooltipButtonComponent, isStandalone: true, selector: "dsfr-tooltip-button", inputs: { tooltip: "tooltip" }, ngImport: i0, template: "<button type=\"button\" class=\"fr-btn--tooltip fr-btn\" [attr.aria-describedby]=\"tooltipId\">\n {{ 'tooltip.button.label' | dsfrI18n }}\n</button>\n<span class=\"fr-tooltip fr-placement\" [id]=\"tooltipId\" role=\"tooltip\" [innerHTML]=\"tooltip\"></span>\n", dependencies: [{ kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrTooltipButtonComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tooltip-button', encapsulation: ViewEncapsulation.None, imports: [DsfrI18nPipe], template: "<button type=\"button\" class=\"fr-btn--tooltip fr-btn\" [attr.aria-describedby]=\"tooltipId\">\n {{ 'tooltip.button.label' | dsfrI18n }}\n</button>\n<span class=\"fr-tooltip fr-placement\" [id]=\"tooltipId\" role=\"tooltip\" [innerHTML]=\"tooltip\"></span>\n" }]
}], propDecorators: { tooltip: [{
type: Input,
args: [{ required: true }]
}] } });
/**
* Définit les types des inputs supportés par le DSFR.
*/
var DsfrInputTypeConst;
(function (DsfrInputTypeConst) {
DsfrInputTypeConst["DATE"] = "date";
DsfrInputTypeConst["DATE_TIME_LOCAL"] = "datetime-local";
DsfrInputTypeConst["EMAIL"] = "email";
DsfrInputTypeConst["NUMBER"] = "number";
DsfrInputTypeConst["PASSWORD"] = "password";
DsfrInputTypeConst["SEARCH"] = "search";
DsfrInputTypeConst["TEL"] = "tel";
DsfrInputTypeConst["TEXT"] = "text";
DsfrInputTypeConst["TEXTAREA"] = "textarea";
DsfrInputTypeConst["TIME"] = "time";
})(DsfrInputTypeConst || (DsfrInputTypeConst = {}));
/**
* L'attribut universel 'inputmode' est un attribut à valeur contrainte qui fournit une indication au navigateur quant
* au type de donnée qui peut être saisi par l'utilisateur lors de l'édition de l'élément ou de son contenu.
* Lorsque cet attribut n'est pas explicitement défini, sa valeur par défaut est "text", ce qui indique que c'est du
* texte qui sera saisi et qu'un clavier standard devrait être utilisé.
* Cf. https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/inputmode
*/
var DsfrInputModeConst;
(function (DsfrInputModeConst) {
/** Aucun clavier virtuel ne doit être affiché. Cette valeur s'avère utile lorsque l'application ou le site web implémente son propre outil de saisie. */
DsfrInputModeConst["NONE"] = "none";
/** C'est du texte qui sera saisi et un clavier dans la locale de l'utilisateur pourra être affiché. */
DsfrInputModeConst["TEXT"] = "text";
/**
* C'est un nombre décimal qui sera saisi. Le clavier affiché peut contenir des chiffres et le séparateur décimal
* de la locale de l'utilisateur. Attention, selon les appareils, le signe moins (-) peut ne pas être présent.
*/
DsfrInputModeConst["DECIMAL"] = "decimal";
/**
* C'est un nombre entier qui sera saisi. Le clavier affiché peut contenir les chiffres de 0 à 9. Attention, selon
* les appareils, le signe moins (-) peut ne pas être présent.
*/
DsfrInputModeConst["NUMERIC"] = "numeric";
/**
* C'est un numéro de téléphone qui sera saisi. Le clavier affiché pourra être celui d'un téléphone avec les chiffres
* allant de 0 à 9, l'astérisque et le dièse. Pour les champs de formulaire où il faut saisir un numéro de téléphone,
* on utilisera plutôt <input type="tel">.
*/
DsfrInputModeConst["TEL"] = "tel";
/**
* L'élément éditable sert à la recherche. Le clavier affiché sera optimisé pour une recherche (par exemple, la
* touche Entrée pourra être indiquée avec le mot-clé « Rechercher »).
*/
DsfrInputModeConst["SEARCH"] = "search";
/**
* C'est une adresse électronique qui sera saisie. Le clavier affiché pourra être optimisé pour la saisie d'adresses
* email (généralement, on aura le caractère @ et d'autres éléments). Pour les champs de formulaire où il faut saisir
* une adresse électronique, on utilisera plutôt <input type="email">.
*/
DsfrInputModeConst["EMAIL"] = "email";
/**
* C'est une URL qui sera saisie. Le clavier affiché pourra être optimisé pour la saisie d'URL. Ainsi, la touche pour
* la barre oblique pourra être plus accessible, le clavier pourra proposer un accès à l'historique des URL utilisées,
* etc. Pour les champs de formulaire où il faut saisir une URL, on utilisera plutôt <input type="url">.
*/
DsfrInputModeConst["URL"] = "url";
})(DsfrInputModeConst || (DsfrInputModeConst = {}));
const INPUT_TYPE_TO_MODE = {
'date': '',
'datetime-local': '',
'email': 'email',
'number': 'numeric',
'password': '',
'search': 'search',
'tel': 'tel',
'text': '',
'textarea': '',
'time': '',
};
/**
* BaseInputComponent n'embarque que les propriétés d'un input Html
*/
class BaseInputComponent extends DefaultControlComponent {
constructor() {
super(...arguments);
/**
* Une chaîne de caractères, on ou off, qui indique si la correction automatique est activée.
* [Safari uniquement](https://developer.mozilla.org/fr/docs/Web/HTML/Element/input#autocorrect).
*
* @since 1.6
*/
this.autoCorrect = true;
/**
* Indique si le champ est obligatoire ou non, faux par défaut.
*/
this.required = false;
/**
* Change la mise en page d'un input accompagné d'un bouton.
*/
this.inputWrapMode = 'addon';
/**
* Émet un événement, si la propriété `pattern` est définie, à chaque changement de la valeur de l'input avec
* une valeur booléenne indiquant si le `pattern` est validé ou non.
*/
this.patternValueChange = new EventEmitter();
/**
* Représente la sévérité du message.
* @deprecated (since 1.12.0) utiliser messageSeverity à la place
* @internal
*/
this.severity = input(...(ngDevMode ? [undefined, { debugName: "severity" }] : []));
/**
* Surcharge severityViewModel de DefaultControlComponent pour garder rétrocompatibilité avec
* la propriété dépréciée severity
* V2 supprimer
* @internal */
this.severityViewModel = computed(() => {
const message = this.messageViewModel();
if (!message?.length)
return undefined;
if (this.error())
return DsfrSeverityConst.ERROR;
if (this.valid())
return DsfrSeverityConst.VALID;
if (this.severity())
return this.severity();
return this.messageSeverity();
}, ...(ngDevMode ? [{ debugName: "severityViewModel" }] : []));
/**
* Attribut utilisés par les composants héritant de la class BaseInput afin d'afficher des messages d'erreurs relatif
* à une validation interne spécifique au composant.
*/
this.internalError = undefined;
this._type = DsfrInputTypeConst.TEXT;
}
get type() {
return this._type;
}
/** @deprecated (since 1.11.0) use placeholder instead (all lowercase) */
get placeHolder() {
return this.placeholder;
}
/**
* Type de l'input, 'text' par défaut.
*/
set type(value) {
this._type = value;
this.inputMode = INPUT_TYPE_TO_MODE[this._type] || undefined;
}
/**
* 👓 Correspond à l'attribut `role`, *
* @deprecated (since 1.15.1) utiliser inputRole à la place
*/
set role(value) {
if (value)
this.inputRole = value;
}
/** @deprecated (since 1.11.0) use placeholder instead (all lowercase) */
set placeHolder(value) {
this.placeholder = value;
}
/**
* À l'écoute de la valeur afin d'émettre un événement dans le cas où on a un pattern.
* @internal
*/
onPatternValueChange() {
if (!this.pattern)
return;
const isValid = new RegExp('^' + this.pattern + '$').test(this.value);
this.patternValueChange.emit(isValid);
}
/** @internal */
isNumber() {
return this.type === DsfrInputTypeConst.NUMBER;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.23", type: BaseInputComponent, isStandalone: true, selector: "ng-component", inputs: { autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: false, isRequired: false, transformFunction: null }, ariaAutocomplete: { classPropertyName: "ariaAutocomplete", publicName: "ariaAutocomplete", isSignal: false, isRequired: false, transformFunction: null }, ariaExpanded: { classPropertyName: "ariaExpanded", publicName: "ariaExpanded", isSignal: false, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: false, isRequired: false, transformFunction: null }, ariaInvalid: { classPropertyName: "ariaInvalid", publicName: "ariaInvalid", isSignal: false, isRequired: false, transformFunction: null }, inputRole: { classPropertyName: "inputRole", publicName: "inputRole", isSignal: false, isRequired: false, transformFunction: null }, autoCorrect: { classPropertyName: "autoCorrect", publicName: "autoCorrect", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: false, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: false, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: false, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: false, isRequired: false, transformFunction: null }, spellCheck: { classPropertyName: "spellCheck", publicName: "spellCheck", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: false, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: false, isRequired: false, transformFunction: null }, customClass: { classPropertyName: "customClass", publicName: "customClass", isSignal: false, isRequired: false, transformFunction: null }, inputWrapMode: { classPropertyName: "inputWrapMode", publicName: "inputWrapMode", isSignal: false, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: false, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: false, isRequired: false, transformFunction: null }, placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { patternValueChange: "patternValueChange" }, host: { listeners: { "input": "onPatternValueChange()" } }, usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseInputComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { autocomplete: [{
type: Input
}], ariaAutocomplete: [{
type: Input
}], ariaExpanded: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaInvalid: [{
type: Input
}], inputRole: [{
type: Input
}], autoCorrect: [{
type: Input
}], required: [{
type: Input
}], placeholder: [{
type: Input
}], min: [{
type: Input
}], max: [{
type: Input
}], minLength: [{
type: Input
}], maxLength: [{
type: Input
}], spellCheck: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], icon: [{
type: Input
}], pattern: [{
type: Input
}], customClass: [{
type: Input
}], inputWrapMode: [{
type: Input
}], readonly: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], patternValueChange: [{
type: Output
}], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], type: [{
type: Input
}], role: [{
type: Input
}], placeHolder: [{
type: Input
}], onPatternValueChange: [{
type: HostListener,
args: ['input']
}] } });
/**
* BaseInputComboComponent ajoute la possibilité d'avoir un bouton sur le côté de l'input
*/
class BaseInputComboComponent extends BaseInputComponent {
constructor() {
super(...arguments);
/**
* Permet de désactiver le bouton d'action, 'false' par défaut.
*/
this.buttonDisabled = false;
/**
* Type du button,'button' par défaut.
*/
this.buttonType = 'button';
/** Style du bouton, 'primary' par défaut. */
this.buttonVariant = 'primary';
/**
* [accessibilité] Permet de masquer visuellement le libellé du bouton en le laissant accessible aux dispositifs d'assistance.
*
* A utiliser pour le bouton si icône seule.
*/
this.buttonLabelSrOnly = false;
/**
* Emission de l'événement si le type du bouton est != de `submit`.
*/
this.buttonSelect = new EventEmitter();
}
/** @internal */
hasButton() {
return !isStringEmptyOrNull(this.buttonLabel) || this.buttonIcon !== undefined;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseInputComboComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: BaseInputComboComponent, isStandalone: true, selector: "ng-component", inputs: { buttonAriaLabel: "buttonAriaLabel", buttonDisabled: "buttonDisabled", buttonIcon: "buttonIcon", buttonLabel: "buttonLabel", buttonTooltipMessage: "buttonTooltipMessage", buttonType: "buttonType", buttonVariant: "buttonVariant", buttonLabelSrOnly: "buttonLabelSrOnly" }, outputs: { buttonSelect: "buttonSelect" }, usesInheritance: true, ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: BaseInputComboComponent, decorators: [{
type: Component,
args: [{
template: '',
}]
}], propDecorators: { buttonAriaLabel: [{
type: Input
}], buttonDisabled: [{
type: Input
}], buttonIcon: [{
type: Input
}], buttonLabel: [{
type: Input
}], buttonTooltipMessage: [{
type: Input
}], buttonType: [{
type: Input
}], buttonVariant: [{
type: Input
}], buttonLabelSrOnly: [{
type: Input
}], buttonSelect: [{
type: Output
}] } });
class DsfrFormInputComponent extends BaseInputComboComponent {
constructor() {
super(...arguments);
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
}
/** @deprecated since 1.2 use `type` instead. */
get textarea() {
return this.isTextArea();
}
/** @deprecated (@since 1.2) utiliser `type` à la place. */
set textarea(value) {
this.type = value ? DsfrInputTypeConst.TEXTAREA : DsfrInputTypeConst.TEXT;
}
isTextArea() {
return this.type === DsfrInputTypeConst.TEXTAREA;
}
hasInputWrap() {
return this.hasButton() || !isStringEmptyOrNull(this.icon);
}
getWrapClasses() {
const hasButton = this.hasButton();
const classes = {
'fr-input-wrap': true,
'fr-input-wrap--addon': hasButton && this.inputWrapMode === 'addon',
'fr-input-wrap--action': hasButton && this.inputWrapMode === 'action',
};
if (this.icon) {
classes[this.icon] = true;
}
return classes;
}
onButtonClick(event) {
if (this.buttonType != 'submit')
this.buttonSelect.emit(event);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormInputComponent, isStandalone: true, selector: "dsfr-form-input", inputs: { rows: "rows", labelSrOnly: "labelSrOnly", width: "width", textarea: "textarea" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormInputComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n", styles: [".fr-label.fr-sr-only+.fr-input,.fr-label.fr-sr-only+.fr-input-wrap{margin-top:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: InputGroupComponent, selector: "edu-input-group", inputs: ["disabled", "messagesGroupId", "message", "messageSeverity", "internalError"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-input', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormInputComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, DsfrButtonComponent, InputGroupComponent, EduHintComponent], template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n", styles: [".fr-label.fr-sr-only+.fr-input,.fr-label.fr-sr-only+.fr-input-wrap{margin-top:0}\n"] }]
}], propDecorators: { rows: [{
type: Input
}], labelSrOnly: [{
type: Input
}], width: [{
type: Input
}], textarea: [{
type: Input
}] } });
class DsfrFormInputModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputModule, imports: [CommonModule, FormsModule, DsfrButtonComponent, InputGroupComponent, DsfrFormInputComponent], exports: [DsfrFormInputComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputModule, imports: [CommonModule, FormsModule, DsfrButtonComponent, InputGroupComponent, DsfrFormInputComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormInputModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormInputComponent],
imports: [CommonModule, FormsModule, DsfrButtonComponent, InputGroupComponent, DsfrFormInputComponent],
}]
}] });
const PATTERN_EMAIL = '^[\\w\\d._%+-]+@[\\w\\d.-]+\\.[\\w]{2,4}$'; // https://www.abstractapi.com/guides/angular-email-validation + majuscules
function isEmailValid(value, pattern) {
if (!value)
return false;
const regexp = new RegExp(pattern ? pattern : PATTERN_EMAIL);
return regexp.test(value);
}
/**
* Saisie d'une adresse email, si la valeur saisie est validée, elle est transformée en minuscules.
*/
class DsfrFormEmailComponent extends DsfrFormInputComponent {
constructor() {
super(...arguments);
this._invalidMessage = '';
this.elementRef = inject(ElementRef);
this.i18n = inject(DsfrI18nService);
this.hasInternalError = false;
}
/**
* La mise à jour du modèle interne n'est effectuée que lorsque l'utilisateur quitte le champ, de même pour la mise
* en erreur le cas échéant.
*
* @internal
*/
onFocusOut() {
if (!this.isValid()) {
this.showInternalErrorMessage();
}
else {
this.clearInternalErrorMessage();
}
}
/**
* Vrai si l'email est valide.
*
* @internal
*/
isValid() {
if (this.value === '' || this.value === null || this.value === undefined) {
return true;
}
return isEmailValid(this.value, this.pattern);
}
/** @internal*/
ngOnInit() {
super.ngOnInit();
this._invalidMessage = this.i18n.t('email.error');
this.type = DsfrInputTypeConst.EMAIL;
this.spellCheck = false;
this.hint ??= this.i18n.t('email.hint');
this.autocomplete = 'email'; // @since 1.6
this.autoCorrect = false; // @since 1.6
}
/**
* Fix: appliquer le label par défaut uniquement si le slot label n'est pas renseigné
* slot label non renseigné => le label 'fr-label' ne possede aucun enfant ou seulement la description additionnelle
*/
ngAfterViewInit() {
const firstElementChild = this.elementRef.nativeElement?.querySelector('.fr-label').firstElementChild;
if (!firstElementChild || [...firstElementChild.classList].includes('fr-hint-text')) {
this.label ??= this.i18n.t('email.label');
}
}
validate(control) {
if (!control.touched) {
return null;
}
else if (this.isValid()) {
return this.clearInternalErrorMessage();
}
else {
return this.showInternalErrorMessage();
}
}
/**
* Affiche le message d'erreur interne.
*/
showInternalErrorMessage() {
if (this.hasInternalError) {
return { isEmailInvalid: true };
}
this.internalError = [this._invalidMessage];
this.hasInternalError = true;
return { isEmailInvalid: true };
}
/**
* Reset du message d'erreur interne.
*/
clearInternalErrorMessage() {
this.internalError = undefined;
this.hasInternalError = false;
return null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormEmailComponent, isStandalone: true, selector: "dsfr-form-email", host: { listeners: { "change": "onFocusOut()" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormEmailComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DsfrFormEmailComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputGroupComponent, selector: "edu-input-group", inputs: ["disabled", "messagesGroupId", "message", "messageSeverity", "internalError"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-email', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormEmailComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DsfrFormEmailComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, InputGroupComponent, DsfrButtonComponent, EduHintComponent], template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n" }]
}], propDecorators: { onFocusOut: [{
type: HostListener,
args: ['change']
}] } });
class DsfrFormEmailModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailModule, imports: [CommonModule, FormsModule, InputGroupComponent, DsfrButtonComponent, DsfrFormEmailComponent], exports: [DsfrFormEmailComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailModule, imports: [CommonModule, FormsModule, InputGroupComponent, DsfrButtonComponent, DsfrFormEmailComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormEmailModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormEmailComponent],
imports: [CommonModule, FormsModule, InputGroupComponent, DsfrButtonComponent, DsfrFormEmailComponent],
}]
}] });
/**
* Définit les réseaux sociaux supportés par le composant `Follow`.
*/
var DsfrFollowNameConst;
(function (DsfrFollowNameConst) {
DsfrFollowNameConst["DAILYMOTION"] = "dailymotion";
DsfrFollowNameConst["FACEBOOK"] = "facebook";
DsfrFollowNameConst["GITHUB"] = "github";
DsfrFollowNameConst["INSTAGRAM"] = "instagram";
DsfrFollowNameConst["LINKEDIN"] = "linkedin";
DsfrFollowNameConst["MASTODON"] = "mastodon";
DsfrFollowNameConst["SNAPCHAT"] = "snapchat";
DsfrFollowNameConst["TELEGRAM"] = "telegram";
DsfrFollowNameConst["TIKTOK"] = "tiktok";
DsfrFollowNameConst["TWITCH"] = "twitch";
DsfrFollowNameConst["TWITTER"] = "twitter";
DsfrFollowNameConst["X"] = "twitter-x";
DsfrFollowNameConst["VIMEO"] = "vimeo";
DsfrFollowNameConst["YOUTUBE"] = "youtube";
DsfrFollowNameConst["BLUESKY"] = "bluesky";
})(DsfrFollowNameConst || (DsfrFollowNameConst = {}));
/**
* Définit les modalités de communication supportés par le composant `Follow`.
*/
var DsfrFollowTypeConst;
(function (DsfrFollowTypeConst) {
DsfrFollowTypeConst["NETWORKS"] = "networks";
DsfrFollowTypeConst["NEWSLETTER"] = "newsletter";
DsfrFollowTypeConst["NEWSLETTER_MAIL"] = "newsletter-mail";
DsfrFollowTypeConst["NEWSLETTER_NETWORKS"] = "newsletter-networks";
DsfrFollowTypeConst["NEWSLETTER_NETWORKS_MAIL"] = "newsletter-networks-mail";
})(DsfrFollowTypeConst || (DsfrFollowTypeConst = {}));
class DsfrFollowLinkComponent {
get name() {
return this._name;
}
/** La propriété 'name' est obligatoire et doit être sélectionnée dans une liste énumérée. */
set name(value) {
if (value === DsfrFollowNameConst.TWITTER) {
this._name = DsfrFollowNameConst.X;
}
else {
this._name = value;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.23", type: DsfrFollowLinkComponent, isStandalone: true, selector: "dsfr-follow-link", inputs: { link: "link", target: "target", name: "name" }, ngImport: i0, template: "<a\n [class]=\"'fr-btn--' + name.toLowerCase() + ' fr-btn'\"\n [href]=\"link\"\n [attr.target]=\"target || null\"\n [title]=\"(name + ' - ' + ('follow.newWindow' | dsfrI18n)).toLowerCase()\">\n {{ name }}\n</a>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowLinkComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-follow-link', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrI18nPipe], template: "<a\n [class]=\"'fr-btn--' + name.toLowerCase() + ' fr-btn'\"\n [href]=\"link\"\n [attr.target]=\"target || null\"\n [title]=\"(name + ' - ' + ('follow.newWindow' | dsfrI18n)).toLowerCase()\">\n {{ name }}\n</a>\n" }]
}], propDecorators: { link: [{
type: Input,
args: [{ required: true }]
}], target: [{
type: Input
}], name: [{
type: Input
}] } });
class DsfrFollowComponent {
/** @internal */
constructor() {
/**
* Niveau du titre, h2 par défaut
*/
this.headingLevel = DsfrHeadingLevelConst.H2;
/** Description (pour certains types seulement) optionnelle. */
this.description = '';
/** Email par défaut. */
this.email = '';
/** Message d'erreur concernant l'email. */
this.emailError = '';
/** Indique que l'inscription a bien été prise en compte. */
this.registered = false;
/** Type du composant, réseaux sociaux seuls par défaut */
this.type = DsfrFollowTypeConst.NETWORKS;
/** Abonnement si l'adresse est valide. */
this.subscribe = new EventEmitter();
/* Tous les types possibles du composant. */
this.followType = DsfrFollowTypeConst;
this.i18n = inject(DsfrI18nService);
this._elementRef = inject(ElementRef);
this.followLinksElements = contentChildren(DsfrFollowLinkComponent, ...(ngDevMode ? [{ debugName: "followLinksElements", read: ElementRef }] : [{ read: ElementRef }]));
this.type = DsfrFollowTypeConst.NETWORKS;
afterRenderEffect(() => {
this.surroundLinksWithLi();
});
}
/** @internal */
onSubscribe(email) {
if (this.emailComponent?.isValid()) {
this.subscribe.emit(email);
}
}
/* Entoure chaque composant follow par une balise <li> si nécessaire */
surroundLinksWithLi() {
DomUtils.surroundElementsWithLi(this._elementRef, this.followLinksElements());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFollowComponent, isStandalone: true, selector: "dsfr-follow", inputs: { headingLevel: "headingLevel", description: "description", email: "email", emailError: "emailError", registered: "registered", type: "type" }, outputs: { subscribe: "subscribe" }, queries: [{ propertyName: "followLinksElements", predicate: DsfrFollowLinkComponent, read: ElementRef, isSignal: true }], viewQueries: [{ propertyName: "emailComponent", first: true, predicate: DsfrFormEmailComponent, descendants: true }], ngImport: i0, template: "<div class=\"fr-follow\">\n <div class=\"fr-container\">\n <div class=\"fr-grid-row\">\n @switch (type) {\n @case (followType.NETWORKS) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"networksTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"newsletterTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER_MAIL) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"newsletterWithMailTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER_NETWORKS) {\n <ng-container [ngTemplateOutlet]=\"newsletterAndNetworksTemplate\"></ng-container>\n }\n @case (followType.NEWSLETTER_NETWORKS_MAIL) {\n <ng-container [ngTemplateOutlet]=\"networksAndNewsletterWithMailTemplate\"></ng-container>\n }\n }\n </div>\n </div>\n</div>\n\n<!-- R\u00E9seaux sociaux seuls -->\n<ng-template #networksTemplate>\n <div class=\"fr-follow__social\">\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.networks.heading' | dsfrI18n }\"></ng-container>\n <ul class=\"fr-btns-group\">\n <ng-content></ng-content>\n </ul>\n </div>\n</ng-template>\n\n<!-- Lettre d'info seule -->\n<ng-template #newsletterTemplate>\n <div class=\"fr-follow__newsletter\">\n <div>\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.newsletter.heading' | dsfrI18n }\"></ng-container>\n @if (description) {\n <p class=\"fr-text--sm\" [innerHTML]=\"description\"></p>\n }\n </div>\n <div>\n <ul class=\"fr-btns-group fr-btns-group--inline-md\">\n <li>\n <button type=\"button\" class=\"fr-btn\" [title]=\"'follow.newsletter.button.title' | dsfrI18n\">\n {{ 'follow.newsletter.button.label' | dsfrI18n }}\n </button>\n </li>\n </ul>\n </div>\n </div>\n</ng-template>\n\n<!-- Lettre d'info seule avec formulaire -->\n<ng-template #newsletterWithMailTemplate>\n <div class=\"fr-follow__newsletter\">\n <div>\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.newsletter.heading' }\"></ng-container>\n @if (description) {\n <p class=\"fr-text--sm\" [innerHTML]=\"description\"></p>\n }\n </div>\n <div>\n @if (registered) {\n <div class=\"fr-alert fr-alert--success\">\n <p>{{ 'follow.newsletter.registration.success' | dsfrI18n }}</p>\n </div>\n } @else {\n <dsfr-form-email\n [value]=\"email\"\n [buttonLabel]=\"'follow.newsletter.button.label' | dsfrI18n\"\n [buttonTooltipMessage]=\"'follow.newsletter.button.title' | dsfrI18n\"\n buttonType=\"button\"\n [placeholder]=\"'follow.newsletter.input.placeholder' | dsfrI18n\"\n [title]=\"'follow.newsletter.input.placeholder' | dsfrI18n\"\n [error]=\"emailError\"\n (buttonSelect)=\"onSubscribe(email)\"></dsfr-form-email>\n <p class=\"fr-hint-text\" [innerHTML]=\"'follow.newsletter.input.hint' | dsfrI18n\"></p>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- R\u00E9seaux sociaux et Lettre d'info mise en avant -->\n<ng-template #newsletterAndNetworksTemplate>\n <div class=\"fr-col-12 fr-col-md-8\">\n <ng-container *ngTemplateOutlet=\"newsletterTemplate\"></ng-container>\n </div>\n <div class=\"fr-col-12 fr-col-md-4\">\n <ng-container *ngTemplateOutlet=\"networksTemplate\"></ng-container>\n </div>\n</ng-template>\n\n<!-- R\u00E9seaux sociaux et Lettre d'info avec formulaire -->\n<ng-template #networksAndNewsletterWithMailTemplate>\n <div class=\"fr-col-12 fr-col-md-8\">\n <ng-container *ngTemplateOutlet=\"newsletterWithMailTemplate\"></ng-container>\n </div>\n <div class=\"fr-col-12 fr-col-md-4\">\n <ng-container *ngTemplateOutlet=\"networksTemplate\"></ng-container>\n </div>\n</ng-template>\n\n<!-- Heading -->\n<ng-template #heading let-heading=\"heading\">\n @switch (headingLevel) {\n @case ('H3') {\n <h3 class=\"fr-h5\" [innerHtml]=\"heading\"></h3>\n }\n @case ('H4') {\n <h4 class=\"fr-h5\" [innerHtml]=\"heading\"></h4>\n }\n @case ('H5') {\n <h5 class=\"fr-h5\" [innerHtml]=\"heading\"></h5>\n }\n @case ('H6') {\n <h6 class=\"fr-h5\" [innerHtml]=\"heading\"></h6>\n }\n @default {\n <h2 class=\"fr-h5\" [innerHtml]=\"heading\"></h2>\n }\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: DsfrFormEmailComponent, selector: "dsfr-form-email" }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-follow', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, DsfrFormEmailComponent, DsfrI18nPipe], template: "<div class=\"fr-follow\">\n <div class=\"fr-container\">\n <div class=\"fr-grid-row\">\n @switch (type) {\n @case (followType.NETWORKS) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"networksTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"newsletterTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER_MAIL) {\n <div class=\"fr-col-12\">\n <ng-container [ngTemplateOutlet]=\"newsletterWithMailTemplate\"></ng-container>\n </div>\n }\n @case (followType.NEWSLETTER_NETWORKS) {\n <ng-container [ngTemplateOutlet]=\"newsletterAndNetworksTemplate\"></ng-container>\n }\n @case (followType.NEWSLETTER_NETWORKS_MAIL) {\n <ng-container [ngTemplateOutlet]=\"networksAndNewsletterWithMailTemplate\"></ng-container>\n }\n }\n </div>\n </div>\n</div>\n\n<!-- R\u00E9seaux sociaux seuls -->\n<ng-template #networksTemplate>\n <div class=\"fr-follow__social\">\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.networks.heading' | dsfrI18n }\"></ng-container>\n <ul class=\"fr-btns-group\">\n <ng-content></ng-content>\n </ul>\n </div>\n</ng-template>\n\n<!-- Lettre d'info seule -->\n<ng-template #newsletterTemplate>\n <div class=\"fr-follow__newsletter\">\n <div>\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.newsletter.heading' | dsfrI18n }\"></ng-container>\n @if (description) {\n <p class=\"fr-text--sm\" [innerHTML]=\"description\"></p>\n }\n </div>\n <div>\n <ul class=\"fr-btns-group fr-btns-group--inline-md\">\n <li>\n <button type=\"button\" class=\"fr-btn\" [title]=\"'follow.newsletter.button.title' | dsfrI18n\">\n {{ 'follow.newsletter.button.label' | dsfrI18n }}\n </button>\n </li>\n </ul>\n </div>\n </div>\n</ng-template>\n\n<!-- Lettre d'info seule avec formulaire -->\n<ng-template #newsletterWithMailTemplate>\n <div class=\"fr-follow__newsletter\">\n <div>\n <ng-container\n [ngTemplateOutlet]=\"heading\"\n [ngTemplateOutletContext]=\"{ heading: 'follow.newsletter.heading' }\"></ng-container>\n @if (description) {\n <p class=\"fr-text--sm\" [innerHTML]=\"description\"></p>\n }\n </div>\n <div>\n @if (registered) {\n <div class=\"fr-alert fr-alert--success\">\n <p>{{ 'follow.newsletter.registration.success' | dsfrI18n }}</p>\n </div>\n } @else {\n <dsfr-form-email\n [value]=\"email\"\n [buttonLabel]=\"'follow.newsletter.button.label' | dsfrI18n\"\n [buttonTooltipMessage]=\"'follow.newsletter.button.title' | dsfrI18n\"\n buttonType=\"button\"\n [placeholder]=\"'follow.newsletter.input.placeholder' | dsfrI18n\"\n [title]=\"'follow.newsletter.input.placeholder' | dsfrI18n\"\n [error]=\"emailError\"\n (buttonSelect)=\"onSubscribe(email)\"></dsfr-form-email>\n <p class=\"fr-hint-text\" [innerHTML]=\"'follow.newsletter.input.hint' | dsfrI18n\"></p>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- R\u00E9seaux sociaux et Lettre d'info mise en avant -->\n<ng-template #newsletterAndNetworksTemplate>\n <div class=\"fr-col-12 fr-col-md-8\">\n <ng-container *ngTemplateOutlet=\"newsletterTemplate\"></ng-container>\n </div>\n <div class=\"fr-col-12 fr-col-md-4\">\n <ng-container *ngTemplateOutlet=\"networksTemplate\"></ng-container>\n </div>\n</ng-template>\n\n<!-- R\u00E9seaux sociaux et Lettre d'info avec formulaire -->\n<ng-template #networksAndNewsletterWithMailTemplate>\n <div class=\"fr-col-12 fr-col-md-8\">\n <ng-container *ngTemplateOutlet=\"newsletterWithMailTemplate\"></ng-container>\n </div>\n <div class=\"fr-col-12 fr-col-md-4\">\n <ng-container *ngTemplateOutlet=\"networksTemplate\"></ng-container>\n </div>\n</ng-template>\n\n<!-- Heading -->\n<ng-template #heading let-heading=\"heading\">\n @switch (headingLevel) {\n @case ('H3') {\n <h3 class=\"fr-h5\" [innerHtml]=\"heading\"></h3>\n }\n @case ('H4') {\n <h4 class=\"fr-h5\" [innerHtml]=\"heading\"></h4>\n }\n @case ('H5') {\n <h5 class=\"fr-h5\" [innerHtml]=\"heading\"></h5>\n }\n @case ('H6') {\n <h6 class=\"fr-h5\" [innerHtml]=\"heading\"></h6>\n }\n @default {\n <h2 class=\"fr-h5\" [innerHtml]=\"heading\"></h2>\n }\n }\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { emailComponent: [{
type: ViewChild,
args: [DsfrFormEmailComponent]
}], headingLevel: [{
type: Input
}], description: [{
type: Input
}], email: [{
type: Input
}], emailError: [{
type: Input
}], registered: [{
type: Input
}], type: [{
type: Input
}], subscribe: [{
type: Output
}], followLinksElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrFollowLinkComponent), { ...{ read: ElementRef }, isSignal: true }] }] } });
class DsfrFollowModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowModule, imports: [CommonModule,
FormsModule,
DsfrFormEmailComponent,
InputGroupComponent,
DsfrI18nPipe,
DsfrFollowComponent,
DsfrFollowLinkComponent], exports: [DsfrFollowComponent, DsfrFollowLinkComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowModule, imports: [CommonModule,
FormsModule,
DsfrFormEmailComponent,
InputGroupComponent,
DsfrFollowComponent,
DsfrFollowLinkComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFollowModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFollowComponent, DsfrFollowLinkComponent],
imports: [
CommonModule,
FormsModule,
DsfrFormEmailComponent,
InputGroupComponent,
DsfrI18nPipe,
DsfrFollowComponent,
DsfrFollowLinkComponent,
],
}]
}] });
/*
* - fix 1.6 : La div messages est toujours présente même dans un groupe (cf. DSFR 1.9)
*/
class DsfrFormCheckboxComponent extends DefaultControlComponent {
constructor() {
super();
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
/**
* Donne une taille de 16px au lieu de 24px.
*/
this.small = false;
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-checkbox-group--error': message && severity === DsfrSeverityConst.ERROR,
'fr-checkbox-group--valid': message && (severity === DsfrSeverityConst.VALID || severity === DsfrSeverityConst.SUCCESS),
'fr-checkbox-group--sm': this.small,
'ngx-checkbox-group--sr-only': this.labelSrOnly,
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
this.elementRef = inject(ElementRef);
this.value = false;
}
/** @since 1.5 */
get checked() {
return this.value ?? false;
}
/** @since 1.5 */
get indeterminateState() {
const nativeElt = this.elementRef.nativeElement;
const checkbox = nativeElt.querySelector('input');
return checkbox.indeterminate;
}
/** @since 1.4 */
set indeterminate(value) {
const nativeElt = this.elementRef.nativeElement;
const checkbox = nativeElt.querySelector('input');
if (value === true || value === false) {
checkbox.indeterminate = value;
}
}
/** @internal */
ngOnInit() {
super.ngOnInit();
this.defaultChecked = super.value ?? false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormCheckboxComponent, isStandalone: true, selector: "dsfr-form-checkbox", inputs: { labelSrOnly: "labelSrOnly", ariaInvalid: "ariaInvalid", small: "small", indeterminate: "indeterminate" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormCheckboxComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<div class=\"fr-checkbox-group\" [ngClass]=\"groupClasses()\">\n <input\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n type=\"checkbox\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n (blur)=\"onBlur()\"\n [disabled]=\"disabled\"\n [attr.checked]=\"defaultChecked || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [(ngModel)]=\"value\" />\n <label class=\"fr-label\" [for]=\"inputId\">\n @if (labelSrOnly) {\n <span class=\"fr-sr-only\">\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n </span>\n } @else {\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n }\n\n @if (hint) {\n <span class=\"fr-hint-text\" [ngClass]=\"{ 'fr-sr-only': labelSrOnly }\">{{ hint }}</span>\n }\n </label>\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n\n<ng-template #labelTemplate>\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n</ng-template>\n", styles: [".fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:before{background-color:var(--background-active-blue-france);background-image:none}.fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.7rem;left:-1.65rem;width:.75rem;height:.1rem;background:var(--background-contrast-blue-france)}.fr-checkbox-group input[type=checkbox]:indeterminate:disabled+.fr-label:before{background-color:var(--background-disabled-grey)}.fr-checkbox-group input[type=checkbox]:indeterminate:disabled+.fr-label:after{background:var(--text-disabled-grey)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.675rem;left:-1.25rem;width:.5rem;height:.125rem;background:var(--background-contrast-blue-france)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate:disabled+.fr-label:after{background:var(--text-disabled-grey)}.fr-checkbox-group.edu-checkbox-group--info:before,.fr-checkbox-group.edu-checkbox-group--warning:before{content:\"\";height:100%;left:-.75rem;position:absolute;top:0;width:2px}.fr-checkbox-group.edu-checkbox-group--info:before{--idle: transparent;--hover: var(--border-plain-info-hover);--active: var(--border-plain-info-active);background-color:var(--border-plain-info)}.fr-checkbox-group.edu-checkbox-group--warning:before{--idle: transparent;--hover: var(--border-plain-warning-hover);--active: var(--border-plain-warning-active);background-color:var(--border-plain-warning)}.ngx-checkbox-group--sr-only{height:1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-checkbox', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormCheckboxComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, EduMessageSeverityDirective], template: "<div class=\"fr-checkbox-group\" [ngClass]=\"groupClasses()\">\n <input\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n type=\"checkbox\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n (blur)=\"onBlur()\"\n [disabled]=\"disabled\"\n [attr.checked]=\"defaultChecked || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [(ngModel)]=\"value\" />\n <label class=\"fr-label\" [for]=\"inputId\">\n @if (labelSrOnly) {\n <span class=\"fr-sr-only\">\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n </span>\n } @else {\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n }\n\n @if (hint) {\n <span class=\"fr-hint-text\" [ngClass]=\"{ 'fr-sr-only': labelSrOnly }\">{{ hint }}</span>\n }\n </label>\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n\n<ng-template #labelTemplate>\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n</ng-template>\n", styles: [".fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:before{background-color:var(--background-active-blue-france);background-image:none}.fr-checkbox-group input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.7rem;left:-1.65rem;width:.75rem;height:.1rem;background:var(--background-contrast-blue-france)}.fr-checkbox-group input[type=checkbox]:indeterminate:disabled+.fr-label:before{background-color:var(--background-disabled-grey)}.fr-checkbox-group input[type=checkbox]:indeterminate:disabled+.fr-label:after{background:var(--text-disabled-grey)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate+.fr-label:after{content:\"\";display:block;margin-right:.5rem;border-radius:.25rem;position:absolute;top:.675rem;left:-1.25rem;width:.5rem;height:.125rem;background:var(--background-contrast-blue-france)}.fr-checkbox-group.fr-checkbox-group--sm input[type=checkbox]:indeterminate:disabled+.fr-label:after{background:var(--text-disabled-grey)}.fr-checkbox-group.edu-checkbox-group--info:before,.fr-checkbox-group.edu-checkbox-group--warning:before{content:\"\";height:100%;left:-.75rem;position:absolute;top:0;width:2px}.fr-checkbox-group.edu-checkbox-group--info:before{--idle: transparent;--hover: var(--border-plain-info-hover);--active: var(--border-plain-info-active);background-color:var(--border-plain-info)}.fr-checkbox-group.edu-checkbox-group--warning:before{--idle: transparent;--hover: var(--border-plain-warning-hover);--active: var(--border-plain-warning-active);background-color:var(--border-plain-warning)}.ngx-checkbox-group--sr-only{height:1.5rem}\n"] }]
}], ctorParameters: () => [], propDecorators: { labelSrOnly: [{
type: Input
}], ariaInvalid: [{
type: Input
}], small: [{
type: Input
}], indeterminate: [{
type: Input
}] } });
class DsfrFormCheckboxModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxModule, imports: [CommonModule, FormsModule, DsfrFormCheckboxComponent], exports: [DsfrFormCheckboxComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxModule, imports: [CommonModule, FormsModule, DsfrFormCheckboxComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormCheckboxModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormCheckboxComponent],
imports: [CommonModule, FormsModule, DsfrFormCheckboxComponent],
}]
}] });
class DsfrFormRadioComponent extends DefaultRadioComponent {
constructor() {
super(...arguments);
/**
* Réduit la taille du radio à 16px (au lieu de 24px).
*/
this.small = false;
/**
* Cache la legend visuellement en la laissant disponible aux lecteurs d'écran.
*/
this.legendSrOnly = false;
}
/**
* @deprecated (since 1.1.0), utiliser la propriété options à la place.
*/
set radios(options) {
this.options = options;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormRadioComponent, isStandalone: true, selector: "dsfr-form-radio", inputs: { options: "options", small: "small", legendSrOnly: "legendSrOnly", radios: "radios" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormRadioComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<dsfr-fieldset\n [fieldsetId]=\"inputId\"\n [legend]=\"label\"\n [legendRegular]=\"legendRegular\"\n [legendSrOnly]=\"legendSrOnly\"\n [hint]=\"hint ?? ''\"\n [inline]=\"inline\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\">\n @if (!label) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n @for (radio of options; track i; let i = $index) {\n <div *fieldsetElement class=\"fr-radio-group\" [ngClass]=\"{ 'fr-radio-group--sm': small }\">\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n type=\"radio\"\n [attr.id]=\"getRadioId(i)\"\n [name]=\"name!\"\n [value]=\"radio.value\"\n [(ngModel)]=\"value\"\n [disabled]=\"disabled || !!radio.disabled\"\n (blur)=\"onBlur()\"\n [attr.name]=\"name\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [required]=\"required\" />\n <label\n class=\"fr-label\"\n [for]=\"getRadioId(i)\"\n [innerHTML]=\"radio.label + (radio.hint ? '<span class=\\'fr-hint-text\\'>' + radio.hint + '</span>' : '')\">\n </label>\n </div>\n }\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-radio', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormRadioComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective], template: "<dsfr-fieldset\n [fieldsetId]=\"inputId\"\n [legend]=\"label\"\n [legendRegular]=\"legendRegular\"\n [legendSrOnly]=\"legendSrOnly\"\n [hint]=\"hint ?? ''\"\n [inline]=\"inline\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\">\n @if (!label) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n @for (radio of options; track i; let i = $index) {\n <div *fieldsetElement class=\"fr-radio-group\" [ngClass]=\"{ 'fr-radio-group--sm': small }\">\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n type=\"radio\"\n [attr.id]=\"getRadioId(i)\"\n [name]=\"name!\"\n [value]=\"radio.value\"\n [(ngModel)]=\"value\"\n [disabled]=\"disabled || !!radio.disabled\"\n (blur)=\"onBlur()\"\n [attr.name]=\"name\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [required]=\"required\" />\n <label\n class=\"fr-label\"\n [for]=\"getRadioId(i)\"\n [innerHTML]=\"radio.label + (radio.hint ? '<span class=\\'fr-hint-text\\'>' + radio.hint + '</span>' : '')\">\n </label>\n </div>\n }\n</dsfr-fieldset>\n" }]
}], propDecorators: { options: [{
type: Input
}], small: [{
type: Input
}], legendSrOnly: [{
type: Input
}], radios: [{
type: Input
}] } });
class DsfrFormRadioModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioModule, imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, DsfrFormRadioComponent], exports: [DsfrFormRadioComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioModule, imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, DsfrFormRadioComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormRadioModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormRadioComponent],
imports: [CommonModule, FormsModule, DsfrFormFieldsetComponent, DsfrFormRadioComponent],
}]
}] });
class DsfrRangeComponent extends DefaultControlComponent {
constructor() {
super();
/** Valeur minimale possible */
this.min = 0;
/** Valeur max possible */
this.max = 100;
/** Ne pas afficher le minimum et le maximum */
this.hideMinMax = false;
/**
* Taille du curseur (SM et MD possibles, MD par défaut)
*/
this.rangeSize = DsfrSizeConst.MD;
/** Désactiver le curseur */
this.disabled = false;
/** @internal Curseur double */
this.double = false;
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-range-group--disabled': this.disabled,
'fr-range-group--error': message && severity === DsfrSeverityConst.ERROR,
'fr-range-group--valid': message && (severity === DsfrSeverityConst.SUCCESS || severity === DsfrSeverityConst.VALID),
'fr-range-group--info': message && severity === DsfrSeverityConst.INFO,
'edu-fieldset--warning': message && severity === DsfrSeverityConst.WARNING,
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
}
/**
* Taille du curseur (SM et MD possibles, MD par défaut)
*
* @deprecated since(1.15.0) utiliser 'rangeSize' à la place
*/
get size() {
return this.rangeSize;
}
get hintId() {
return this.inputId + '-hint-text';
}
/**
* Taille du curseur (SM et MD possibles, MD par défaut)
*
* @deprecated since(1.15.0) utiliser 'rangeSize' à la place
*/
set size(value) {
this.rangeSize = value;
}
/**@internal */
isDouble() {
return Array.isArray(this.value);
}
/** @internal */
isSmall() {
return this.size == DsfrSizeConst.SM;
}
/** @internal */
getValue(index) {
return Array.isArray(this.value) ? this.value[index] : undefined;
}
/** @internal */
onInputChange(index, value) {
if (Array.isArray(this.value)) {
this.value[index] = value;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRangeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrRangeComponent, isStandalone: true, selector: "dsfr-range, dsfr-form-range", inputs: { min: "min", max: "max", hideMinMax: "hideMinMax", step: "step", rangeSize: "rangeSize", prefix: "prefix", suffix: "suffix", disabled: "disabled", ariaLabelledBy: "ariaLabelledBy", size: "size" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrRangeComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<div\n class=\"fr-range-group\"\n id=\"{{ inputId }}-group\"\n [ngClass]=\"groupClasses()\"\n [attr.role]=\"isDouble() ? 'group' : null\"\n [attr.aria-labelledby]=\"inputId + '-label'\">\n <label id=\"{{ inputId }}-label\" class=\"fr-label\" [for]=\"isDouble() ? null : inputId\">\n <!-- Label obligatoire -->\n @if (label) {\n {{ label }}\n } @else {\n <ng-content></ng-content>\n }\n\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <div\n class=\"fr-range\"\n [attr.data-fr-prefix]=\"prefix\"\n [attr.data-fr-suffix]=\"suffix\"\n [ngClass]=\"{ 'fr-range--double': isDouble(), 'fr-range--sm': isSmall(), 'fr-range--step': step }\">\n <span class=\"fr-range__output\">{{ value }}</span>\n\n @if (!isDouble()) {\n <ng-container *ngTemplateOutlet=\"singleCursor\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"doubleCursor\"></ng-container>\n }\n\n @if (!hideMinMax) {\n <span class=\"fr-range__min\" aria-hidden=\"true\">\n {{ min }}\n </span>\n <span class=\"fr-range__max\" aria-hidden=\"true\">\n {{ max }}\n </span>\n }\n </div>\n\n <div class=\"fr-messages-group\" id=\"{{ inputId }}-messages\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n\n<ng-template #singleCursor>\n <input\n id=\"{{ inputId }}\"\n name=\"{{ inputId }}\"\n type=\"range\"\n min=\"{{ min }}\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.value]=\"value\"\n [(ngModel)]=\"value\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\"\n [attr.step]=\"step\" />\n</ng-template>\n\n<ng-template #doubleCursor>\n <input\n id=\"{{ inputId }}\"\n name=\"{{ inputId }}\"\n type=\"range\"\n [attr.aria-label]=\"ariaLabelledBy ? null : ('range.labelMin' | dsfrI18n)\"\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n min=\"{{ min }}\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.value]=\"getValue(0)\"\n [ngModel]=\"getValue(0)\"\n (ngModelChange)=\"onInputChange(0, $event)\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\" />\n\n <input\n id=\"{{ inputId }}-2\"\n name=\"{{ inputId }}-2\"\n type=\"range\"\n [attr.aria-label]=\"ariaLabelledBy ? null : ('range.labelMax' | dsfrI18n)\"\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n [attr.value]=\"getValue(1)\"\n [ngModel]=\"getValue(1)\"\n (blur)=\"onBlur()\"\n (ngModelChange)=\"onInputChange(1, $event)\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\" />\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrRangeComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-range, dsfr-form-range', encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, EduHintComponent, DsfrI18nPipe, EduMessageSeverityDirective], providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrRangeComponent),
multi: true,
},
], template: "<div\n class=\"fr-range-group\"\n id=\"{{ inputId }}-group\"\n [ngClass]=\"groupClasses()\"\n [attr.role]=\"isDouble() ? 'group' : null\"\n [attr.aria-labelledby]=\"inputId + '-label'\">\n <label id=\"{{ inputId }}-label\" class=\"fr-label\" [for]=\"isDouble() ? null : inputId\">\n <!-- Label obligatoire -->\n @if (label) {\n {{ label }}\n } @else {\n <ng-content></ng-content>\n }\n\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <div\n class=\"fr-range\"\n [attr.data-fr-prefix]=\"prefix\"\n [attr.data-fr-suffix]=\"suffix\"\n [ngClass]=\"{ 'fr-range--double': isDouble(), 'fr-range--sm': isSmall(), 'fr-range--step': step }\">\n <span class=\"fr-range__output\">{{ value }}</span>\n\n @if (!isDouble()) {\n <ng-container *ngTemplateOutlet=\"singleCursor\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"doubleCursor\"></ng-container>\n }\n\n @if (!hideMinMax) {\n <span class=\"fr-range__min\" aria-hidden=\"true\">\n {{ min }}\n </span>\n <span class=\"fr-range__max\" aria-hidden=\"true\">\n {{ max }}\n </span>\n }\n </div>\n\n <div class=\"fr-messages-group\" id=\"{{ inputId }}-messages\" aria-live=\"polite\">\n @if (messageViewModel()) {\n <p class=\"fr-message\" [eduMessageSeverity]=\"severityViewModel()\">\n {{ messageViewModel() }}\n </p>\n }\n </div>\n</div>\n\n<ng-template #singleCursor>\n <input\n id=\"{{ inputId }}\"\n name=\"{{ inputId }}\"\n type=\"range\"\n min=\"{{ min }}\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.value]=\"value\"\n [(ngModel)]=\"value\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\"\n [attr.step]=\"step\" />\n</ng-template>\n\n<ng-template #doubleCursor>\n <input\n id=\"{{ inputId }}\"\n name=\"{{ inputId }}\"\n type=\"range\"\n [attr.aria-label]=\"ariaLabelledBy ? null : ('range.labelMin' | dsfrI18n)\"\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n min=\"{{ min }}\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.value]=\"getValue(0)\"\n [ngModel]=\"getValue(0)\"\n (ngModelChange)=\"onInputChange(0, $event)\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\" />\n\n <input\n id=\"{{ inputId }}-2\"\n name=\"{{ inputId }}-2\"\n type=\"range\"\n [attr.aria-label]=\"ariaLabelledBy ? null : ('range.labelMax' | dsfrI18n)\"\n [attr.aria-labelledby]=\"ariaLabelledBy ?? null\"\n max=\"{{ max }}\"\n [disabled]=\"disabled\"\n [attr.value]=\"getValue(1)\"\n [ngModel]=\"getValue(1)\"\n (blur)=\"onBlur()\"\n (ngModelChange)=\"onInputChange(1, $event)\"\n [attr.aria-describedby]=\"messageViewModel() ? inputId + '-messages' : null\" />\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { min: [{
type: Input
}], max: [{
type: Input
}], hideMinMax: [{
type: Input
}], step: [{
type: Input
}], rangeSize: [{
type: Input
}], prefix: [{
type: Input
}], suffix: [{
type: Input
}], disabled: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], size: [{
type: Input
}] } });
const REGEXP_PHONE_NUMBER = /^\(?(\+\d{2})?\)?\s?0?(\d(\s?\d{2}){4})$/;
/**
* Teste si le numéro de téléphone correspond au pattern (soit celui de l'input, soit un pattern par défaut)
*/
function isPhoneNumberValid(value, pattern) {
if (!value)
return false;
const regexp = pattern ? new RegExp(pattern) : REGEXP_PHONE_NUMBER;
const matchArr = regexp.exec(value);
return matchArr ? true : false;
}
/**
* Formatage du n° de téléphone.
* Le formatage ne se fait que dans le cas où on utilise le pattern par défaut
*/
function formatPhoneNumber(value, pattern) {
if (!value)
return '';
if (pattern)
return value;
const regexp = REGEXP_PHONE_NUMBER;
const matchArr = regexp.exec(value);
if (!matchArr)
return value;
let num = value;
let country = matchArr[1];
num = matchArr[2].replace(/ /g, '');
num = num.replace(/(\d)(\d{2})(\d{2})(\d{2})(\d{2})/, '$1 $2 $3 $4 $5');
num = country ? `(${country}) ${num}` : `0${num}`;
return num;
}
/**
* Saisie d'un n° de téléphone et reformatage (sauf dans le cas d'utilisation d'un pattern)
* @since 1.3.0
*/
class DsfrFormTelComponent extends DsfrFormInputComponent {
constructor() {
super(...arguments);
this.errorMessage = '';
this.elementRef = inject(ElementRef);
this.i18n = inject(DsfrI18nService);
}
/**
* Au fil de la saisie, on ne vérifie le n° de téléphone que s'il y avait déjà une erreur.
* @internal */
onInput() {
if (this.internalError && this.internalError?.length > 0) {
this.handleValidation();
}
}
/**
* Vérification au commit de la valeur.
*
* cf. https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event
* @internal */
onChange() {
this.validAndFormatPattern();
}
/** @internal*/
ngOnInit() {
super.ngOnInit();
this.errorMessage = this.i18n.t('tel.error');
this.type = DsfrInputTypeConst.TEL;
this.spellCheck = false;
this.hint ??= this.i18n.t('tel.hint');
this.validAndFormatPattern();
}
/**
* Fix: appliquer le label par défaut uniquement si le slot label n'est pas renseigné
* slot label non renseigné => le label 'fr-label' ne possede aucun enfant ou seulement la description additionnelle
*/
ngAfterViewInit() {
const firstElementChild = this.elementRef.nativeElement?.querySelector('.fr-label').firstElementChild;
if (!firstElementChild || [...firstElementChild.classList].includes('fr-hint-text')) {
this.label ??= this.i18n.t('tel.label');
}
}
/** @internal */
writeValue(value) {
super.writeValue(value);
this.validAndFormatPattern();
}
validAndFormatPattern() {
const isValid = this.handleValidation();
if (isValid && !this.pattern) {
this.value = formatPhoneNumber(this.value);
}
}
/**
* Méthode validation, positionne la propriété error s'il y a lieu.
*/
validate() {
if (this.value === undefined || this.value === null || this.value === '') {
return true;
}
return isPhoneNumberValid(this.value, this.pattern);
}
/**
* Vérifie la validité de l'input en fonction de l'erreur interne possible
*
* @returns un booléen en fonction de l'absence d'erreur interne
*/
handleValidation() {
const isValid = this.validate();
this.internalError = isValid ? undefined : [this.errorMessage];
return isValid;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTelComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormTelComponent, isStandalone: true, selector: "dsfr-form-tel", host: { listeners: { "input": "onInput()", "change": "onChange()" } }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormTelComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputGroupComponent, selector: "edu-input-group", inputs: ["disabled", "messagesGroupId", "message", "messageSeverity", "internalError"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTelComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-tel', imports: [CommonModule, FormsModule, InputGroupComponent, DsfrButtonComponent, EduHintComponent], providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormTelComponent),
multi: true,
},
], template: "<edu-input-group\n [messagesGroupId]=\"messagesGroupId\"\n [disabled]=\"disabled\"\n [message]=\"messageViewModel()\"\n [messageSeverity]=\"severityViewModel()\"\n [internalError]=\"internalError\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n @if (!isTextArea()) {\n @if (!hasInputWrap()) {\n <ng-container [ngTemplateOutlet]=\"inputTemplate\"></ng-container>\n } @else {\n <div [class]=\"getWrapClasses()\">\n <ng-container *ngTemplateOutlet=\"inputTemplate\"></ng-container>\n @if (hasButton()) {\n <dsfr-button\n [ariaLabel]=\"buttonAriaLabel\"\n [disabled]=\"buttonDisabled\"\n [icon]=\"buttonIcon\"\n [label]=\"buttonLabel\"\n [tooltipMessage]=\"buttonTooltipMessage\"\n [type]=\"buttonType\"\n [variant]=\"buttonVariant\"\n [labelSrOnly]=\"buttonLabelSrOnly\"\n (click)=\"onButtonClick($event)\"></dsfr-button>\n }\n </div>\n }\n } @else {\n <textarea\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.maxlength]=\"maxLength || null\"\n [attr.minlength]=\"minLength || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [readOnly]=\"readonly\"\n [(ngModel)]=\"value\"\n [ngStyle]=\"width ? { width: width } : {}\"\n (blur)=\"onBlur()\"\n [attr.rows]=\"rows || null\"></textarea>\n }\n</edu-input-group>\n\n<!-- Templates -------------------------------------------------------------------------------------------------------->\n\n<!-- Template input -->\n<ng-template #inputTemplate>\n <input\n class=\"fr-input\"\n [ngClass]=\"customClass || null\"\n [attr.autocomplete]=\"autocomplete || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete || null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-disabled]=\"disabled || null\"\n [attr.aria-expanded]=\"ariaExpanded !== undefined ? ariaExpanded : null\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [readOnly]=\"readonly\"\n [attr.aria-required]=\"required || null\"\n [attr.role]=\"inputRole || null\"\n [attr.inputmode]=\"inputMode || null\"\n [attr.autocorrect]=\"autoCorrect ? null : 'off'\"\n [(ngModel)]=\"value\"\n [id]=\"inputId\"\n [attr.name]=\"name || null\"\n [attr.type]=\"type || null\"\n [disabled]=\"disabled\"\n [required]=\"required\"\n [attr.pattern]=\"pattern || null\"\n [attr.placeholder]=\"placeholder || null\"\n [attr.min]=\"min || null\"\n [attr.max]=\"max || null\"\n [attr.maxlength]=\"maxLength || null\"\n (blur)=\"onBlur()\"\n [attr.minLength]=\"minLength || null\"\n [attr.spellcheck]=\"spellCheck || null\"\n [ngStyle]=\"width ? { width: width } : {}\" />\n</ng-template>\n" }]
}], propDecorators: { onInput: [{
type: HostListener,
args: ['input']
}], onChange: [{
type: HostListener,
args: ['change']
}] } });
class DsfrFormToggleComponent extends DefaultControlComponent {
constructor() {
super();
/** Position du libellé, à droite par défaut.
* @deprecated 1.14.1 La version avec libellé à gauche est dépréciée et ne doit plus être utilisée (accessibilité)
*/
this.labelPosition = DsfrPositionConst.RIGHT;
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
/**
* Permet d'afficher le libellé, court, décrivant l'état de l’interrupteur (activé / désactivé), placé en dessous du
* switch. Il est affiché par défaut, il est conseillé de le mettre afin de faciliter la compréhension de
* l’utilisateur - Optionnel.
*/
this.showStatusLabel = true;
/**
* Affiche un séparateur horizontal sous le composant.
* Ne devrait être utilisé que dans le cadre d'un toggles-group.
*/
this.showSeparator = false;
/** @internal */
this.combinedAriaDescribedBy = computed(() => {
const ids = [];
if (this.ariaDescribedBy())
ids.push(this.ariaDescribedBy());
if (this.hint)
ids.push(this.hintId);
ids.push(this.messagesGroupId); // toujours présent meme si aucun message
return ids.length ? ids.join(' ') : null;
}, ...(ngDevMode ? [{ debugName: "combinedAriaDescribedBy" }] : []));
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-toggle--label-left': this.labelPosition === 'left',
'fr-toggle--border-bottom': this.showSeparator,
'fr-toggle--error': message && severity === DsfrSeverityConst.ERROR,
'fr-toggle--valid': message && (severity === DsfrSeverityConst.VALID || severity === DsfrSeverityConst.SUCCESS),
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
this.i18n = inject(DsfrI18nService);
}
get hintId() {
return this.inputId + '-hint';
}
/** @deprecated since 1.12 use showStatus instead */
get showCheckedHint() {
return this.showStatusLabel;
}
/** @deprecated since 1.12 use checkedLabel instead */
get checkedHintLabel() {
return this.checkedStatusLabel;
}
/** @deprecated since 1.12 use uncheckedLabel instead */
get uncheckedHintLabel() {
return this.uncheckedStatusLabel;
}
/** @deprecated since 1.12 use showStatus instead */
set showCheckedHint(value) {
this.showStatusLabel = value;
}
/** @deprecated since 1.12 use checkedLabel instead */
set checkedHintLabel(value) {
this.checkedStatusLabel = value;
}
/** @deprecated since 1.12 use uncheckedLabel instead */
set uncheckedHintLabel(value) {
this.uncheckedStatusLabel = value;
}
/**
* Permet d'initialiser de forcer la valeur initiale à une valeur booléenne
* @internal
*/
writeValue(value) {
super.writeValue(value ?? false);
}
/**
* @ignore
*/
getDataLabelChecked() {
return this.showStatusLabel ? this.checkedStatusLabel || this.i18n.t('toggle.dataLabelChecked') : null;
}
/**
* @ignore
*/
getDataLabelUnchecked() {
return this.showStatusLabel ? this.uncheckedStatusLabel || this.i18n.t('toggle.dataLabelUnchecked') : null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormToggleComponent, isStandalone: true, selector: "dsfr-form-toggle", inputs: { labelPosition: "labelPosition", labelSrOnly: "labelSrOnly", ariaInvalid: "ariaInvalid", showStatusLabel: "showStatusLabel", checkedStatusLabel: "checkedStatusLabel", uncheckedStatusLabel: "uncheckedStatusLabel", showSeparator: ["showSeparator", "showSeparator", booleanAttribute], showCheckedHint: "showCheckedHint", checkedHintLabel: "checkedHintLabel", uncheckedHintLabel: "uncheckedHintLabel" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormToggleComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<div class=\"fr-toggle\" [ngClass]=\"groupClasses()\">\n <input\n type=\"checkbox\"\n class=\"fr-toggle__input\"\n [id]=\"inputId\"\n [attr.name]=\"name ? name : null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [disabled]=\"disabled\"\n [attr.checked]=\"value ? true : null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n (blur)=\"onBlur()\"\n [(ngModel)]=\"value\" />\n <label\n class=\"fr-toggle__label\"\n [for]=\"inputId\"\n [attr.data-fr-checked-label]=\"getDataLabelChecked()\"\n [attr.data-fr-unchecked-label]=\"getDataLabelUnchecked()\">\n @if (labelSrOnly) {\n <span class=\"fr-sr-only\">\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n </span>\n } @else {\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n }\n </label>\n @if (hint) {\n <p [id]=\"hintId\" class=\"fr-hint-text\">{{ hint }}</p>\n }\n <!-- messages -->\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n </div>\n</div>\n\n<ng-template #labelTemplate>\n @if (label) {\n {{ label }}\n } @else {\n <ng-content></ng-content>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-toggle', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrFormToggleComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, EduMessageSeverityDirective], template: "<div class=\"fr-toggle\" [ngClass]=\"groupClasses()\">\n <input\n type=\"checkbox\"\n class=\"fr-toggle__input\"\n [id]=\"inputId\"\n [attr.name]=\"name ? name : null\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [disabled]=\"disabled\"\n [attr.checked]=\"value ? true : null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n (blur)=\"onBlur()\"\n [(ngModel)]=\"value\" />\n <label\n class=\"fr-toggle__label\"\n [for]=\"inputId\"\n [attr.data-fr-checked-label]=\"getDataLabelChecked()\"\n [attr.data-fr-unchecked-label]=\"getDataLabelUnchecked()\">\n @if (labelSrOnly) {\n <span class=\"fr-sr-only\">\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n </span>\n } @else {\n <ng-container [ngTemplateOutlet]=\"labelTemplate\"></ng-container>\n }\n </label>\n @if (hint) {\n <p [id]=\"hintId\" class=\"fr-hint-text\">{{ hint }}</p>\n }\n <!-- messages -->\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n </div>\n</div>\n\n<ng-template #labelTemplate>\n @if (label) {\n {{ label }}\n } @else {\n <ng-content></ng-content>\n }\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { labelPosition: [{
type: Input
}], labelSrOnly: [{
type: Input
}], ariaInvalid: [{
type: Input
}], showStatusLabel: [{
type: Input
}], checkedStatusLabel: [{
type: Input
}], uncheckedStatusLabel: [{
type: Input
}], showSeparator: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], showCheckedHint: [{
type: Input
}], checkedHintLabel: [{
type: Input
}], uncheckedHintLabel: [{
type: Input
}] } });
class DsfrFormToggleModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleModule, imports: [CommonModule, FormsModule, DsfrFormToggleComponent], exports: [DsfrFormToggleComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleModule, imports: [CommonModule, FormsModule, DsfrFormToggleComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormToggleModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormToggleComponent],
imports: [CommonModule, FormsModule, DsfrFormToggleComponent],
}]
}] });
class DsfrFormTogglesGroupComponent extends BaseFieldsetComponent {
constructor() {
super();
this._elementRef = inject(ElementRef);
this._showSeparators = true;
this.togglesElements = contentChildren(DsfrFormToggleComponent, ...(ngDevMode ? [{ debugName: "togglesElements", read: ElementRef }] : [{ read: ElementRef }]));
this.toggles = contentChildren(DsfrFormToggleComponent, ...(ngDevMode ? [{ debugName: "toggles" }] : []));
afterRenderEffect(() => {
this.surroundButtonsWithLi();
});
}
get showSeparators() {
return this._showSeparators;
}
/** Permet de masquer la bordure horizontale séparant les toggles dans le groupe. */
set showSeparators(value) {
this._showSeparators = value;
this.updateTogglesBorderBottom();
}
/* Entoure chaque composant toggle par une balise <li> si nécessaire */
surroundButtonsWithLi() {
DomUtils.surroundElementsWithLi(this._elementRef, this.togglesElements());
}
updateTogglesBorderBottom() {
if (this.toggles()) {
this.toggles().forEach((t) => (t.showSeparator = this.showSeparators));
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormTogglesGroupComponent, isStandalone: true, selector: "dsfr-form-toggles-group", inputs: { showSeparators: "showSeparators" }, queries: [{ propertyName: "togglesElements", predicate: DsfrFormToggleComponent, read: ElementRef, isSignal: true }, { propertyName: "toggles", predicate: DsfrFormToggleComponent, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<dsfr-fieldset\n [fieldsetId]=\"fieldsetId\"\n [legend]=\"legend\"\n [legendRegular]=\"legendRegular\"\n [legendSrOnly]=\"legendSrOnly\"\n [hint]=\"hint\"\n [disabled]=\"disabled()\"\n [error]=\"error()\"\n [valid]=\"valid()\"\n [message]=\"message()\"\n [messageSeverity]=\"messageSeverity()\">\n @if (!legend) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n\n <ul *fieldsetElement class=\"fr-toggle__list\">\n <ng-content></ng-content>\n </ul>\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-toggles-group', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective], template: "<dsfr-fieldset\n [fieldsetId]=\"fieldsetId\"\n [legend]=\"legend\"\n [legendRegular]=\"legendRegular\"\n [legendSrOnly]=\"legendSrOnly\"\n [hint]=\"hint\"\n [disabled]=\"disabled()\"\n [error]=\"error()\"\n [valid]=\"valid()\"\n [message]=\"message()\"\n [messageSeverity]=\"messageSeverity()\">\n @if (!legend) {\n <ng-container legend>\n <ng-content select=\"[legend]\"></ng-content>\n </ng-container>\n }\n\n <ul *fieldsetElement class=\"fr-toggle__list\">\n <ng-content></ng-content>\n </ul>\n</dsfr-fieldset>\n" }]
}], ctorParameters: () => [], propDecorators: { togglesElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrFormToggleComponent), { ...{ read: ElementRef }, isSignal: true }] }], toggles: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsfrFormToggleComponent), { isSignal: true }] }], showSeparators: [{
type: Input
}] } });
class DsfrFormTogglesGroupModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupModule, imports: [CommonModule, DsfrFormToggleComponent, DsfrFormFieldsetComponent, DsfrFormTogglesGroupComponent], exports: [DsfrFormTogglesGroupComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupModule, imports: [CommonModule, DsfrFormToggleComponent, DsfrFormFieldsetComponent, DsfrFormTogglesGroupComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormTogglesGroupModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormTogglesGroupComponent],
imports: [CommonModule, DsfrFormToggleComponent, DsfrFormFieldsetComponent, DsfrFormTogglesGroupComponent],
}]
}] });
class DsfrUploadComponent extends DefaultControlComponent {
constructor() {
super(...arguments);
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
/** Permet l'ajout de plusieurs fichiers à la fois. */
this.multiple = false;
/** Evénement émis à la sélection d'un ou plusieurs fichiers. */
this.fileSelect = new EventEmitter();
/** Evénement émis lorsque l'utilisateur annule la sélection. */
this.fileCancel = new EventEmitter();
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-upload-group--error': message && severity === DsfrSeverityConst.ERROR,
'fr-upload-group--valid': message && severity === DsfrSeverityConst.VALID,
'fr-upload-group--disabled': this.disabled,
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
this._renderer = inject(Renderer2);
this.i18n = inject(DsfrI18nService);
}
/** @deprecated (since 1.4.0) utiliser hint à la place. */
get description() {
return this.hint;
}
/**
* Description pour l'upload (précise les contraintes au niveau du ou des fichiers attendus : format, poids attendus, nombre de fichiers possible…).
*
* @deprecated (since 1.4.0) utiliser hint à la place.
*
* @internal
*/
set description(value) {
this.hint = value;
}
/**
* Surcharge permettant de gérer la mise à jour du DOM.
*
* @internal
*/
writeValue(value) {
// afin de se prémunir des initialisations avec une chaîne de caractères (vide ou pas) il faut s'assurer du type
// d'argument transmis en entrée et forcer la chaîne vide le cas échéant
const isTypeFileList = Object.prototype.toString.call(value).includes(FileList.name);
if (!isTypeFileList) {
value = '';
}
super.writeValue(value);
if (this.inputFile) {
if (isTypeFileList && value?.length) {
this._renderer.setProperty(this.inputFile.nativeElement, 'files', value);
}
else {
this._renderer.setProperty(this.inputFile.nativeElement, 'value', '');
this._renderer.setProperty(this.inputFile.nativeElement, 'files', null);
}
}
}
/** @internal */
ngOnInit() {
super.ngOnInit();
this.name ??= 'file-upload'; // 'file-upload' est la valeur statique jusqu'en 1.3
}
/** @internal */
ngOnChanges({ label, hint }) {
if (label)
this.label ||= this.i18n.t('upload.label'); // default
if (hint)
this.hint ||= this.i18n.t('upload.hint'); // default
}
/**
* Permet de programmatiquement réinitialiser le champ après une sélection de fichier(s).
*/
reset() {
if (this.inputFile)
this.inputFile.nativeElement.value = '';
}
/** @internal */
onCancel() {
this.fileCancel.emit();
}
/** @internal */
onFileChange(event) {
const target = event.target;
this.value = target.files ?? undefined;
if (this.value)
this.fileSelect.emit(this.value);
this.fnOnTouched(); // touched est envoyé uniquement a la validation d'un fichier
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrUploadComponent, isStandalone: true, selector: "dsfr-form-upload, dsfr-upload", inputs: { labelSrOnly: "labelSrOnly", ariaInvalid: "ariaInvalid", multiple: "multiple", accept: "accept", description: "description" }, outputs: { fileSelect: "fileSelect", fileCancel: "fileCancel" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrUploadComponent),
multi: true,
},
], viewQueries: [{ propertyName: "inputFile", first: true, predicate: ["inputFile"], descendants: true, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"fr-upload-group\" [ngClass]=\"groupClasses()\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\"\n >{{ label }}\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n #inputFile\n class=\"fr-upload\"\n type=\"file\"\n [id]=\"inputId || null\"\n [name]=\"name || null\"\n [attr.name]=\"name || null\"\n [multiple]=\"multiple\"\n [attr.accept]=\"accept || null\"\n [disabled]=\"disabled\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n (change)=\"onFileChange($event)\"\n (cancel)=\"onCancel()\" />\n\n <!-- messages -->\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n </div>\n</div>\n", styles: [".fr-label.fr-sr-only+.fr-upload{margin-top:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-upload, dsfr-upload', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrUploadComponent),
multi: true,
},
], imports: [CommonModule, FormsModule, EduHintComponent, EduMessageSeverityDirective], template: "<div class=\"fr-upload-group\" [ngClass]=\"groupClasses()\">\n <label class=\"fr-label\" [class.fr-sr-only]=\"labelSrOnly\" [for]=\"inputId\"\n >{{ label }}\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n <!--\n On bind \u00E0 la fois la propri\u00E9t\u00E9 et l'attribut 'name' pour contourner le bug suivant: https://github.com/angular/angular/issues/11757\n cf. issue#360\n -->\n <input\n #inputFile\n class=\"fr-upload\"\n type=\"file\"\n [id]=\"inputId || null\"\n [name]=\"name || null\"\n [attr.name]=\"name || null\"\n [multiple]=\"multiple\"\n [attr.accept]=\"accept || null\"\n [disabled]=\"disabled\"\n [attr.aria-describedby]=\"combinedAriaDescribedBy()\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n (change)=\"onFileChange($event)\"\n (cancel)=\"onCancel()\" />\n\n <!-- messages -->\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n </div>\n</div>\n", styles: [".fr-label.fr-sr-only+.fr-upload{margin-top:0}\n"] }]
}], propDecorators: { inputFile: [{
type: ViewChild,
args: ['inputFile', { static: true }]
}], labelSrOnly: [{
type: Input
}], ariaInvalid: [{
type: Input
}], multiple: [{
type: Input
}], accept: [{
type: Input
}], fileSelect: [{
type: Output
}], fileCancel: [{
type: Output
}], description: [{
type: Input
}] } });
class DsfrUploadModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadModule, imports: [CommonModule, FormsModule, DsfrUploadComponent], exports: [DsfrUploadComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadModule, imports: [CommonModule, FormsModule, DsfrUploadComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrUploadModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrUploadComponent],
imports: [CommonModule, FormsModule, DsfrUploadComponent],
}]
}] });
class DsfrFormPasswordComponent extends DefaultControlComponent {
/** @internal */
constructor() {
super();
/** @since 1.6 'current-password' par défaut */
this.autocomplete = 'current-password';
/**
* Cache le label visuellement en le laissant disponible aux lecteurs d'écran.
*/
this.labelSrOnly = false;
/**
* Indique si le champ est obligatoire ou non, faux par défaut.
*/
this.required = false;
/** Propage l'évènement Event du DOM à la sélection d'un lien. */
this.recoveryRouteSelect = new EventEmitter();
/** @internal */ this.checkboxId = '';
this.groupClasses = computed(() => {
const severity = this.severityViewModel();
const message = this.messageViewModel();
return {
'fr-sr-only': this.labelSrOnly,
'fr-input-group--error': message && severity === DsfrSeverityConst.ERROR,
'fr-input-group--valid': message && (severity === DsfrSeverityConst.SUCCESS || severity === DsfrSeverityConst.VALID),
'fr-input-group--info': message && severity === DsfrSeverityConst.INFO,
'fr-input-group--warning': message && severity === DsfrSeverityConst.WARNING,
};
}, ...(ngDevMode ? [{ debugName: "groupClasses" }] : []));
}
/** @internal */
ngOnInit() {
super.ngOnInit();
this.checkboxId = newUniqueId();
}
/** @internal */
hasRecovery() {
return this.recoveryLink || this.recoveryRoute || this.recoveryRouterLink;
}
/** @internal */
onRecovery() {
if (this.recoveryRoute) {
this.recoveryRouteSelect.emit(this.recoveryRoute);
}
}
/** @internal */
hasMessages() {
return !!(this.validationRules && this.validationRules.length > 0);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrFormPasswordComponent, isStandalone: true, selector: "dsfr-form-password", inputs: { autocomplete: "autocomplete", labelSrOnly: "labelSrOnly", required: "required", validationRules: "validationRules", recoveryLink: "recoveryLink", recoveryTargetLink: "recoveryTargetLink", recoveryRouterLink: "recoveryRouterLink", recoveryRouterLinkActive: "recoveryRouterLinkActive", recoveryRouterLinkExtras: "recoveryRouterLinkExtras", recoveryRoute: "recoveryRoute", ariaInvalid: "ariaInvalid" }, outputs: { recoveryRouteSelect: "recoveryRouteSelect" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: DsfrFormPasswordComponent,
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<div class=\"fr-password\">\n <label class=\"fr-label\" [ngClass]=\"groupClasses()\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <div class=\"fr-input-wrap\">\n <input\n class=\"fr-password__input fr-input\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n [attr.aria-describedby]=\"hasMessages() ? messagesGroupId : null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [required]=\"required\"\n [(ngModel)]=\"value\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.name]=\"name || null\"\n [autocomplete]=\"autocomplete\"\n [id]=\"inputId\"\n type=\"password\" />\n </div>\n\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p\n class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n\n @if (hasMessages()) {\n <p class=\"fr-message\">{{ 'password.message' | dsfrI18n }}</p>\n }\n\n @if (validationRules && validationRules.length > 0) {\n <ul class=\"fr-raw-list\">\n @for (rule of validationRules; track i; let i = $index) {\n @if (rule.onError) {\n <li class=\"fr-message fr-message--error\" id=\"password-{{ this.inputId }}-input-message-error-{{ i }}\">\n {{ rule.message }}<span class=\"fr-sr-only\"> - {{ 'password.invalidCriteria' | dsfrI18n }}</span>\n </li>\n } @else if (rule.onError === false) {\n <li class=\"fr-message fr-message--valid\" id=\"password-{{ this.inputId }}-input-message-valid-{{ i }}\">\n {{ rule.message }}<span class=\"fr-sr-only\"> - {{ 'password.validCriteria' | dsfrI18n }}</span>\n </li>\n } @else if (rule.onError === undefined) {\n <li class=\"fr-message fr-message--info\" id=\"password-{{ this.inputId }}-input-message-info-{{ i }}\">\n {{ rule.message }}\n </li>\n }\n }\n </ul>\n }\n </div>\n\n <div class=\"fr-password__checkbox fr-checkbox-group fr-checkbox-group--sm\">\n <input [attr.aria-label]=\"'password.aria.label' | dsfrI18n\" [id]=\"checkboxId\" type=\"checkbox\" />\n <!-- fr--password utilis\u00E9 sur la d\u00E9mo DSFR et permet de fixer le padding avec la checkbox mais illogique -->\n <label class=\"fr--password__checkbox fr-label\" [for]=\"checkboxId\">\n {{ 'password.show' | dsfrI18n }}\n </label>\n </div>\n\n @if (hasRecovery()) {\n <p>\n <dsfr-link\n [link]=\"recoveryLink\"\n [linkTarget]=\"recoveryTargetLink\"\n [route]=\"recoveryRoute\"\n [routePath]=\"recoveryRouterLink\"\n [routerLinkActive]=\"recoveryRouterLinkActive ?? ''\"\n [routerLinkExtras]=\"recoveryRouterLinkExtras\"\n (linkSelect)=\"onRecovery()\"\n >{{ 'password.forgotPassword' | dsfrI18n }}</dsfr-link\n >\n </p>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaLabelledBy", "ariaDescribedBy", "ariaCurrentWhenActive", "link", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "directive", type: EduMessageSeverityDirective, selector: "[eduMessageSeverity]", inputs: ["eduMessageSeverity"] }, { kind: "component", type: EduHintComponent, selector: "edu-hint-form", inputs: ["hint"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-form-password', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: DsfrFormPasswordComponent,
multi: true,
},
], imports: [CommonModule, FormsModule, DsfrLinkComponent, DsfrI18nPipe, EduMessageSeverityDirective, EduHintComponent], template: "<div class=\"fr-password\">\n <label class=\"fr-label\" [ngClass]=\"groupClasses()\" [for]=\"inputId\">\n @if (label) {\n {{ label }}\n } @else {\n <ng-content select=\"[label]\"></ng-content>\n }\n @if (hint) {\n <edu-hint-form [hint]=\"hint\"></edu-hint-form>\n }\n </label>\n\n <div class=\"fr-input-wrap\">\n <input\n class=\"fr-password__input fr-input\"\n autocapitalize=\"off\"\n autocorrect=\"off\"\n [attr.aria-describedby]=\"hasMessages() ? messagesGroupId : null\"\n [attr.aria-invalid]=\"ariaInvalid || null\"\n [attr.aria-required]=\"required || null\"\n [required]=\"required\"\n [(ngModel)]=\"value\"\n [disabled]=\"disabled\"\n (blur)=\"onBlur()\"\n [attr.name]=\"name || null\"\n [autocomplete]=\"autocomplete\"\n [id]=\"inputId\"\n type=\"password\" />\n </div>\n\n <div class=\"fr-messages-group\" [id]=\"messagesGroupId\" aria-live=\"polite\">\n <!-- prettier-ignore -->\n @if (messageViewModel()) {\n <p\n class=\"fr-message\"\n [eduMessageSeverity]=\"severityViewModel()\"\n >{{ messageViewModel() }}</p>\n }\n\n @if (hasMessages()) {\n <p class=\"fr-message\">{{ 'password.message' | dsfrI18n }}</p>\n }\n\n @if (validationRules && validationRules.length > 0) {\n <ul class=\"fr-raw-list\">\n @for (rule of validationRules; track i; let i = $index) {\n @if (rule.onError) {\n <li class=\"fr-message fr-message--error\" id=\"password-{{ this.inputId }}-input-message-error-{{ i }}\">\n {{ rule.message }}<span class=\"fr-sr-only\"> - {{ 'password.invalidCriteria' | dsfrI18n }}</span>\n </li>\n } @else if (rule.onError === false) {\n <li class=\"fr-message fr-message--valid\" id=\"password-{{ this.inputId }}-input-message-valid-{{ i }}\">\n {{ rule.message }}<span class=\"fr-sr-only\"> - {{ 'password.validCriteria' | dsfrI18n }}</span>\n </li>\n } @else if (rule.onError === undefined) {\n <li class=\"fr-message fr-message--info\" id=\"password-{{ this.inputId }}-input-message-info-{{ i }}\">\n {{ rule.message }}\n </li>\n }\n }\n </ul>\n }\n </div>\n\n <div class=\"fr-password__checkbox fr-checkbox-group fr-checkbox-group--sm\">\n <input [attr.aria-label]=\"'password.aria.label' | dsfrI18n\" [id]=\"checkboxId\" type=\"checkbox\" />\n <!-- fr--password utilis\u00E9 sur la d\u00E9mo DSFR et permet de fixer le padding avec la checkbox mais illogique -->\n <label class=\"fr--password__checkbox fr-label\" [for]=\"checkboxId\">\n {{ 'password.show' | dsfrI18n }}\n </label>\n </div>\n\n @if (hasRecovery()) {\n <p>\n <dsfr-link\n [link]=\"recoveryLink\"\n [linkTarget]=\"recoveryTargetLink\"\n [route]=\"recoveryRoute\"\n [routePath]=\"recoveryRouterLink\"\n [routerLinkActive]=\"recoveryRouterLinkActive ?? ''\"\n [routerLinkExtras]=\"recoveryRouterLinkExtras\"\n (linkSelect)=\"onRecovery()\"\n >{{ 'password.forgotPassword' | dsfrI18n }}</dsfr-link\n >\n </p>\n }\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { autocomplete: [{
type: Input
}], labelSrOnly: [{
type: Input
}], required: [{
type: Input
}], validationRules: [{
type: Input
}], recoveryLink: [{
type: Input
}], recoveryTargetLink: [{
type: Input
}], recoveryRouterLink: [{
type: Input
}], recoveryRouterLinkActive: [{
type: Input
}], recoveryRouterLinkExtras: [{
type: Input
}], recoveryRoute: [{
type: Input
}], ariaInvalid: [{
type: Input
}], recoveryRouteSelect: [{
type: Output
}] } });
class DsfrFormPasswordModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordModule, imports: [CommonModule, FormsModule, DsfrLinkComponent, DsfrI18nPipe, DsfrFormPasswordComponent], exports: [DsfrFormPasswordComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordModule, imports: [CommonModule, FormsModule, DsfrLinkComponent, DsfrFormPasswordComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrFormPasswordModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrFormPasswordComponent],
imports: [CommonModule, FormsModule, DsfrLinkComponent, DsfrI18nPipe, DsfrFormPasswordComponent],
}]
}] });
class DsfrLoginComponent {
constructor() {
/** Se souvenir de l'utilisateur. */
this.rememberMe = false;
/** Utilisation de FranceConnect+ (qui est plus sécurisé). */
this.secure = false;
/** Demande de connexion via FranceConnect. */
this.franceConnectSelect = new EventEmitter();
/** Evénement lorsque le mot de passe change. */
this.passwordChange = new EventEmitter();
/** Emission de `recoveryRoute` s'il y a lieu. */
this.recoveryRouteSelect = new EventEmitter();
/** Demande de connexion avec en paramètre : '{login : string ; password : string}'. */
this.signinSelect = new EventEmitter();
/** Demande de création de compte */
this.signupSelect = new EventEmitter();
this.password = ''; // Password de l'utilisateur.
this.login = ''; // Email de l'utilisateur.
this.fb = inject(FormBuilder);
this.loginFormGroup = this.fb.group({
login: ['', Validators.required],
password: ['', Validators.required],
rememberMe: false,
});
}
ngOnInit() {
if (isOnBrowser()) {
this.rememberMe ??= Boolean(LocalStorage.get(StorageEnum.REMEMBER_ME));
if (this.rememberMe)
this.login ??= LocalStorage.get(StorageEnum.LOGIN) ?? '';
}
this.setControlValue('login', this.login);
this.setControlValue('password', this.password);
this.setControlValue('rememberMe', this.rememberMe);
}
onFranceConnect() {
this.franceConnectSelect.emit();
}
/**
* Inscription, on laisse le développeur faire ce qu'il veut dans ce cas.
*/
onSignUp() {
this.signupSelect.emit();
}
/** Connexion */
onSignIn() {
if (!this.isFormLoginValid())
return; // Ce qui ne peut pas arriver
const login = this.getControlValue('login');
const password = this.getControlValue('password');
const rememberMe = this.getControlValue('rememberMe');
// localStorage
this.saveToLocalStorage(rememberMe, login);
// Event
this.signinSelect.emit({ login: login, password: password, rememberMe: rememberMe });
}
onPasswordChange(value) {
this.passwordChange.emit(value);
}
onRecoveryRouteSelect(route) {
this.recoveryRouteSelect.emit(route);
}
isFormLoginValid() {
return this.loginFormGroup.valid && this.emailComponent?.isValid();
}
setControlValue(name, value) {
this.loginFormGroup.controls[name]?.setValue(value);
}
getControlValue(name) {
return this.loginFormGroup.controls[name]?.value;
}
saveToLocalStorage(rememberMe, login) {
if (this.rememberMe) {
LocalStorage.set(StorageEnum.REMEMBER_ME, String(rememberMe));
LocalStorage.set(StorageEnum.LOGIN, login);
}
else {
LocalStorage.remove(StorageEnum.REMEMBER_ME);
LocalStorage.remove(StorageEnum.LOGIN);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrLoginComponent, isStandalone: true, selector: "dsfr-login", inputs: { labelEmail: "labelEmail", hintEmail: "hintEmail", error: "error", recoveryLink: "recoveryLink", recoveryRoute: "recoveryRoute", recoveryRouterLink: "recoveryRouterLink", recoveryRouterLinkActive: "recoveryRouterLinkActive", recoveryRouterLinkExtras: "recoveryRouterLinkExtras", rememberMe: "rememberMe", secure: ["secure", "secure", booleanAttribute], service: "service" }, outputs: { franceConnectSelect: "franceConnectSelect", passwordChange: "passwordChange", recoveryRouteSelect: "recoveryRouteSelect", signinSelect: "signinSelect", signupSelect: "signupSelect" }, viewQueries: [{ propertyName: "emailComponent", first: true, predicate: DsfrFormEmailComponent, descendants: true }], ngImport: i0, template: "<div class=\"fr-container fr-container--fluid fr-mb-md-14v\">\n <div class=\"fr-grid-row fr-grid-row--gutters fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-8 fr-col-lg-6\">\n <!-- BUG: classe fr-background-alt--grey inexistante c\u00F4t\u00E9 DSFR 1.11 mais utilis\u00E9 dans leur template -->\n <div class=\"fr-container fr-background-alt--grey fr-px-md-0 fr-pt-10v fr-pt-md-14v fr-pb-6v fr-pb-md-10v\">\n <div class=\"fr-grid-row fr-grid-row--gutters fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-9 fr-col-lg-8\">\n <!-- Heading h4 ------------------------------------------------------------------------------------------->\n <h1 class=\"fr-h4\">{{ ('login.heading' | dsfrI18n) + service }}</h1>\n <!-- France Connect h5 ------------------------------------------------------------------------------------>\n <div class=\"fr-mb-6v\">\n <h2 class=\"fr-h5\">{{ 'login.franceConnect.heading' | dsfrI18n }}</h2>\n <dsfr-franceconnect [secure]=\"secure\" (franceConnectSelect)=\"onFranceConnect()\"></dsfr-franceconnect>\n </div>\n <p class=\"fr-hr-or\">{{ 'login.or' | dsfrI18n }}</p>\n <!-- Formulaire de login h5 ------------------------------------------------------------------------------->\n <div>\n <form [formGroup]=\"loginFormGroup\" (ngSubmit)=\"onSignIn()\">\n <dsfr-fieldset>\n <h2 class=\"fr-h5\" legend>{{ 'login.account.heading' | dsfrI18n }}</h2>\n <dsfr-fieldset\n *fieldsetElement\n [legend]=\"'login.account.srOnly.label' | dsfrI18n\"\n [legendSrOnly]=\"true\"\n [messageSeverity]=\"'error'\"\n [message]=\"error\">\n @if (error) {\n <dsfr-alert *fieldsetElement severity=\"error\" alertSize=\"SM\" [message]=\"error\"></dsfr-alert>\n }\n <!-- Email ---------------------------------------------------------------------------------------->\n <dsfr-form-email\n *fieldsetElement\n formControlName=\"login\"\n autofocus\n [required]=\"true\"\n [hint]=\"hintEmail ?? ('email.hint' | dsfrI18n)\"\n [label]=\"labelEmail ?? ('login.account.email.label' | dsfrI18n)\"></dsfr-form-email>\n <!-- Password ------------------------------------------------------------------------------------->\n <dsfr-form-password\n *fieldsetElement\n formControlName=\"password\"\n [recoveryLink]=\"recoveryLink\"\n [recoveryRoute]=\"recoveryRoute\"\n [recoveryRouterLink]=\"recoveryRouterLink\"\n [recoveryRouterLinkActive]=\"recoveryRouterLinkActive\"\n [label]=\"'login.account.password.label' | dsfrI18n\"\n [recoveryRouterLinkExtras]=\"recoveryRouterLinkExtras\"\n (ngModelChange)=\"onPasswordChange($event)\"\n (recoveryRouteSelect)=\"onRecoveryRouteSelect($event)\"></dsfr-form-password>\n </dsfr-fieldset>\n <!-- Remember Me ------------------------------------------------------------------------------------>\n <dsfr-form-checkbox\n *fieldsetElement\n formControlName=\"rememberMe\"\n [small]=\"true\"\n [label]=\"'login.account.rememberMe.label' | dsfrI18n\"></dsfr-form-checkbox>\n <!-- Bouton de login -------------------------------------------------------------------------------->\n <dsfr-buttons-group *fieldsetElement>\n <dsfr-button\n type=\"submit\"\n customClass=\"fr-mt-2v\"\n [label]=\"'login.account.button.label' | dsfrI18n\"\n [disabled]=\"!isFormLoginValid()\"></dsfr-button>\n </dsfr-buttons-group>\n </dsfr-fieldset>\n </form>\n </div>\n <hr />\n <!-- Cr\u00E9ation de compte h5 -------------------------------------------------------------------------------->\n <h2 class=\"fr-h5\">{{ 'login.noAccount.heading' | dsfrI18n }}</h2>\n <dsfr-buttons-group>\n <dsfr-button\n variant=\"secondary\"\n [label]=\"'login.noAccount.button.label' | dsfrI18n\"\n (click)=\"onSignUp()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: DsfrFranceConnectComponent, selector: "dsfr-franceconnect", inputs: ["secure"], outputs: ["franceConnectSelect"] }, { kind: "component", type: DsfrFormEmailComponent, selector: "dsfr-form-email" }, { kind: "component", type: DsfrFormPasswordComponent, selector: "dsfr-form-password", inputs: ["autocomplete", "labelSrOnly", "required", "validationRules", "recoveryLink", "recoveryTargetLink", "recoveryRouterLink", "recoveryRouterLinkActive", "recoveryRouterLinkExtras", "recoveryRoute", "ariaInvalid"], outputs: ["recoveryRouteSelect"] }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "component", type: DsfrFormCheckboxComponent, selector: "dsfr-form-checkbox", inputs: ["labelSrOnly", "ariaInvalid", "small", "indeterminate"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: DsfrButtonsGroupComponent, selector: "dsfr-buttons-group", inputs: ["inline", "alignment", "equisized", "buttonGroupSize", "groupMarkup", "size", "iconPosition"] }, { kind: "component", type: DsfrAlertComponent, selector: "dsfr-alert", inputs: ["heading", "headingLevel", "message", "severity", "ariaRoleValue", "alertSize", "closeControlLabel", "hasAriaRole", "closeable", "size", "closable"], outputs: ["conceal"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrLoginComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-login', imports: [
CommonModule,
ReactiveFormsModule,
DsfrFranceConnectComponent,
DsfrFormEmailComponent,
DsfrFormPasswordComponent,
DsfrFormFieldsetComponent,
DsfrFormCheckboxComponent,
DsfrButtonComponent,
DsfrButtonsGroupComponent,
DsfrAlertComponent,
DsfrI18nPipe,
DsfrFormFieldsetElementDirective,
], template: "<div class=\"fr-container fr-container--fluid fr-mb-md-14v\">\n <div class=\"fr-grid-row fr-grid-row--gutters fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-8 fr-col-lg-6\">\n <!-- BUG: classe fr-background-alt--grey inexistante c\u00F4t\u00E9 DSFR 1.11 mais utilis\u00E9 dans leur template -->\n <div class=\"fr-container fr-background-alt--grey fr-px-md-0 fr-pt-10v fr-pt-md-14v fr-pb-6v fr-pb-md-10v\">\n <div class=\"fr-grid-row fr-grid-row--gutters fr-grid-row--center\">\n <div class=\"fr-col-12 fr-col-md-9 fr-col-lg-8\">\n <!-- Heading h4 ------------------------------------------------------------------------------------------->\n <h1 class=\"fr-h4\">{{ ('login.heading' | dsfrI18n) + service }}</h1>\n <!-- France Connect h5 ------------------------------------------------------------------------------------>\n <div class=\"fr-mb-6v\">\n <h2 class=\"fr-h5\">{{ 'login.franceConnect.heading' | dsfrI18n }}</h2>\n <dsfr-franceconnect [secure]=\"secure\" (franceConnectSelect)=\"onFranceConnect()\"></dsfr-franceconnect>\n </div>\n <p class=\"fr-hr-or\">{{ 'login.or' | dsfrI18n }}</p>\n <!-- Formulaire de login h5 ------------------------------------------------------------------------------->\n <div>\n <form [formGroup]=\"loginFormGroup\" (ngSubmit)=\"onSignIn()\">\n <dsfr-fieldset>\n <h2 class=\"fr-h5\" legend>{{ 'login.account.heading' | dsfrI18n }}</h2>\n <dsfr-fieldset\n *fieldsetElement\n [legend]=\"'login.account.srOnly.label' | dsfrI18n\"\n [legendSrOnly]=\"true\"\n [messageSeverity]=\"'error'\"\n [message]=\"error\">\n @if (error) {\n <dsfr-alert *fieldsetElement severity=\"error\" alertSize=\"SM\" [message]=\"error\"></dsfr-alert>\n }\n <!-- Email ---------------------------------------------------------------------------------------->\n <dsfr-form-email\n *fieldsetElement\n formControlName=\"login\"\n autofocus\n [required]=\"true\"\n [hint]=\"hintEmail ?? ('email.hint' | dsfrI18n)\"\n [label]=\"labelEmail ?? ('login.account.email.label' | dsfrI18n)\"></dsfr-form-email>\n <!-- Password ------------------------------------------------------------------------------------->\n <dsfr-form-password\n *fieldsetElement\n formControlName=\"password\"\n [recoveryLink]=\"recoveryLink\"\n [recoveryRoute]=\"recoveryRoute\"\n [recoveryRouterLink]=\"recoveryRouterLink\"\n [recoveryRouterLinkActive]=\"recoveryRouterLinkActive\"\n [label]=\"'login.account.password.label' | dsfrI18n\"\n [recoveryRouterLinkExtras]=\"recoveryRouterLinkExtras\"\n (ngModelChange)=\"onPasswordChange($event)\"\n (recoveryRouteSelect)=\"onRecoveryRouteSelect($event)\"></dsfr-form-password>\n </dsfr-fieldset>\n <!-- Remember Me ------------------------------------------------------------------------------------>\n <dsfr-form-checkbox\n *fieldsetElement\n formControlName=\"rememberMe\"\n [small]=\"true\"\n [label]=\"'login.account.rememberMe.label' | dsfrI18n\"></dsfr-form-checkbox>\n <!-- Bouton de login -------------------------------------------------------------------------------->\n <dsfr-buttons-group *fieldsetElement>\n <dsfr-button\n type=\"submit\"\n customClass=\"fr-mt-2v\"\n [label]=\"'login.account.button.label' | dsfrI18n\"\n [disabled]=\"!isFormLoginValid()\"></dsfr-button>\n </dsfr-buttons-group>\n </dsfr-fieldset>\n </form>\n </div>\n <hr />\n <!-- Cr\u00E9ation de compte h5 -------------------------------------------------------------------------------->\n <h2 class=\"fr-h5\">{{ 'login.noAccount.heading' | dsfrI18n }}</h2>\n <dsfr-buttons-group>\n <dsfr-button\n variant=\"secondary\"\n [label]=\"'login.noAccount.button.label' | dsfrI18n\"\n (click)=\"onSignUp()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { emailComponent: [{
type: ViewChild,
args: [DsfrFormEmailComponent]
}], labelEmail: [{
type: Input
}], hintEmail: [{
type: Input
}], error: [{
type: Input
}], recoveryLink: [{
type: Input
}], recoveryRoute: [{
type: Input
}], recoveryRouterLink: [{
type: Input
}], recoveryRouterLinkActive: [{
type: Input
}], recoveryRouterLinkExtras: [{
type: Input
}], rememberMe: [{
type: Input
}], secure: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], service: [{
type: Input
}], franceConnectSelect: [{
type: Output
}], passwordChange: [{
type: Output
}], recoveryRouteSelect: [{
type: Output
}], signinSelect: [{
type: Output
}], signupSelect: [{
type: Output
}] } });
class DsfrResponseComponent {
constructor() {
/**
* Type d'erreur (404, 500,...).
*/
this.error = 500;
/**
* Conditionne l'affichage du bouton contactez-nous.
*/
this.showContact = true;
/**
* Conditionne l'affichage du bouton page d'accueil'.
*/
this.showBackToHome = false;
/**
* Indique que le bouton contact est cliqué.
*/
this.contactSelect = new EventEmitter();
/**
* Indique que le bouton page d'accueil est cliqué.
*/
this.backToHomeSelect = new EventEmitter();
this.noFirstNameModel = false;
this.config = inject(DSFR_CONFIG_TOKEN);
}
get pictoPath() {
return this.artworkDirPath;
}
/**
* Chemin des pictogrammes (du composant display) renseigné par le développeur.
*
* Note: ce chemin doit permettre de récupérer directement les fichiers SVG suivants : moon.svg, sun.svg, system.svg
*
* @deprecated Use `artworkDirPath` instead.
*/
set pictoPath(path) {
this.artworkDirPath = path;
}
ngOnInit() {
if (this.artworkDirPath === undefined && this.config.artworkDirPath) {
this.artworkDirPath = this.config.artworkDirPath;
}
}
onContact() {
this.contactSelect.emit();
}
onHome() {
this.backToHomeSelect.emit();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrResponseComponent, isStandalone: true, selector: "dsfr-response", inputs: { heading: "heading", headingLevel: "headingLevel", error: "error", description: "description", detail: "detail", showContact: "showContact", showBackToHome: "showBackToHome", artworkDirPath: "artworkDirPath", imagePath: "imagePath", imageAlt: "imageAlt", pictoPath: "pictoPath" }, outputs: { contactSelect: "contactSelect", backToHomeSelect: "backToHomeSelect" }, ngImport: i0, template: "<div class=\"fr-container\">\n <div\n class=\"fr-my-7w fr-mt-md-12w fr-mb-md-10w fr-grid-row fr-grid-row--gutters fr-grid-row--middle fr-grid-row--center\">\n <div class=\"fr-py-0 fr-col-12 fr-col-md-6\">\n <!-- Titre du contenu -->\n <ng-container>\n <ng-container *ngTemplateOutlet=\"dsfrPageResponseHeading\"></ng-container>\n </ng-container>\n <p class=\"fr-text--sm fr-mb-3w\">Erreur {{ error }}</p>\n <p class=\"fr-text--lead fr-mb-3w\">\n {{ description ?? 'pageResponse.description' | dsfrI18n }}\n </p>\n <p class=\"fr-text--sm fr-mb-5w\">\n {{ detail ?? ('pageResponse.detail' | dsfrI18n) }}\n </p>\n <dsfr-buttons-group *ngIf=\"showBackToHome || showContact\" inline=\"always\">\n <dsfr-button\n type=\"button\"\n *ngIf=\"showBackToHome\"\n label=\"Page d'accueil\"\n variant=\"primary\"\n (click)=\"onHome()\"></dsfr-button>\n <dsfr-button\n type=\"button\"\n *ngIf=\"showContact\"\n [label]=\"'response.contactUs' | dsfrI18n\"\n variant=\"secondary\"\n (click)=\"onContact()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n <div class=\"fr-col-12 fr-col-md-3 fr-col-offset-md-1 fr-px-6w fr-px-md-0 fr-py-0\">\n @if (imagePath) {\n <img class=\"fr-responsive-img\" [src]=\"imagePath\" [attr.alt]=\"imageAlt\" />\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"fr-responsive-img fr-artwork\"\n aria-hidden=\"true\"\n width=\"160\"\n height=\"200\"\n viewBox=\"0 0 160 200\"\n data-fr-js-ratio=\"true\">\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-motif'\"></use>\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-background'\"></use>\n <g transform=\"translate(40, 60)\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-major'\"></use>\n </g>\n </svg>\n }\n </div>\n </div>\n</div>\n\n<ng-template #dsfrPageResponseHeading>\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchDefault>{{ heading }}</h1>\n <h2 *ngSwitchCase=\"'H2'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h2>\n <h3 *ngSwitchCase=\"'H3'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h3>\n <h4 *ngSwitchCase=\"'H4'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h4>\n <h5 *ngSwitchCase=\"'H5'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h5>\n <h6 *ngSwitchCase=\"'H6'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h6>\n </ng-container>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: DsfrButtonsGroupComponent, selector: "dsfr-buttons-group", inputs: ["inline", "alignment", "equisized", "buttonGroupSize", "groupMarkup", "size", "iconPosition"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-response', encapsulation: ViewEncapsulation.None, imports: [CommonModule, DsfrButtonComponent, DsfrButtonsGroupComponent, DsfrI18nPipe], template: "<div class=\"fr-container\">\n <div\n class=\"fr-my-7w fr-mt-md-12w fr-mb-md-10w fr-grid-row fr-grid-row--gutters fr-grid-row--middle fr-grid-row--center\">\n <div class=\"fr-py-0 fr-col-12 fr-col-md-6\">\n <!-- Titre du contenu -->\n <ng-container>\n <ng-container *ngTemplateOutlet=\"dsfrPageResponseHeading\"></ng-container>\n </ng-container>\n <p class=\"fr-text--sm fr-mb-3w\">Erreur {{ error }}</p>\n <p class=\"fr-text--lead fr-mb-3w\">\n {{ description ?? 'pageResponse.description' | dsfrI18n }}\n </p>\n <p class=\"fr-text--sm fr-mb-5w\">\n {{ detail ?? ('pageResponse.detail' | dsfrI18n) }}\n </p>\n <dsfr-buttons-group *ngIf=\"showBackToHome || showContact\" inline=\"always\">\n <dsfr-button\n type=\"button\"\n *ngIf=\"showBackToHome\"\n label=\"Page d'accueil\"\n variant=\"primary\"\n (click)=\"onHome()\"></dsfr-button>\n <dsfr-button\n type=\"button\"\n *ngIf=\"showContact\"\n [label]=\"'response.contactUs' | dsfrI18n\"\n variant=\"secondary\"\n (click)=\"onContact()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n <div class=\"fr-col-12 fr-col-md-3 fr-col-offset-md-1 fr-px-6w fr-px-md-0 fr-py-0\">\n @if (imagePath) {\n <img class=\"fr-responsive-img\" [src]=\"imagePath\" [attr.alt]=\"imageAlt\" />\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"fr-responsive-img fr-artwork\"\n aria-hidden=\"true\"\n width=\"160\"\n height=\"200\"\n viewBox=\"0 0 160 200\"\n data-fr-js-ratio=\"true\">\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-motif'\"></use>\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-background'\"></use>\n <g transform=\"translate(40, 60)\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-major'\"></use>\n </g>\n </svg>\n }\n </div>\n </div>\n</div>\n\n<ng-template #dsfrPageResponseHeading>\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchDefault>{{ heading }}</h1>\n <h2 *ngSwitchCase=\"'H2'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h2>\n <h3 *ngSwitchCase=\"'H3'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h3>\n <h4 *ngSwitchCase=\"'H4'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h4>\n <h5 *ngSwitchCase=\"'H5'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h5>\n <h6 *ngSwitchCase=\"'H6'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h6>\n </ng-container>\n</ng-template>\n" }]
}], ctorParameters: () => [], propDecorators: { heading: [{
type: Input
}], headingLevel: [{
type: Input
}], error: [{
type: Input
}], description: [{
type: Input
}], detail: [{
type: Input
}], showContact: [{
type: Input
}], showBackToHome: [{
type: Input
}], artworkDirPath: [{
type: Input
}], imagePath: [{
type: Input
}], imageAlt: [{
type: Input
}], contactSelect: [{
type: Output
}], backToHomeSelect: [{
type: Output
}], pictoPath: [{
type: Input
}] } });
/**
* @deprecated use DsfrResponseComponent instead
*/
class DsfrPageResponseComponent extends DsfrResponseComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrPageResponseComponent, isStandalone: true, selector: "dsfr-page-response", usesInheritance: true, ngImport: i0, template: "<div class=\"fr-container\">\n <div\n class=\"fr-my-7w fr-mt-md-12w fr-mb-md-10w fr-grid-row fr-grid-row--gutters fr-grid-row--middle fr-grid-row--center\">\n <div class=\"fr-py-0 fr-col-12 fr-col-md-6\">\n <!-- Titre du contenu -->\n <ng-container>\n <ng-container *ngTemplateOutlet=\"dsfrPageResponseHeading\"></ng-container>\n </ng-container>\n <p class=\"fr-text--sm fr-mb-3w\">Erreur {{ error }}</p>\n <p class=\"fr-text--lead fr-mb-3w\">\n {{ description ?? 'pageResponse.description' | dsfrI18n }}\n </p>\n <p class=\"fr-text--sm fr-mb-5w\">\n {{ detail ?? ('pageResponse.detail' | dsfrI18n) }}\n </p>\n <dsfr-buttons-group *ngIf=\"showBackToHome || showContact\" inline=\"always\">\n <dsfr-button\n type=\"button\"\n *ngIf=\"showBackToHome\"\n label=\"Page d'accueil\"\n variant=\"primary\"\n (click)=\"onHome()\"></dsfr-button>\n <dsfr-button\n type=\"button\"\n *ngIf=\"showContact\"\n [label]=\"'response.contactUs' | dsfrI18n\"\n variant=\"secondary\"\n (click)=\"onContact()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n <div class=\"fr-col-12 fr-col-md-3 fr-col-offset-md-1 fr-px-6w fr-px-md-0 fr-py-0\">\n @if (imagePath) {\n <img class=\"fr-responsive-img\" [src]=\"imagePath\" [attr.alt]=\"imageAlt\" />\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"fr-responsive-img fr-artwork\"\n aria-hidden=\"true\"\n width=\"160\"\n height=\"200\"\n viewBox=\"0 0 160 200\"\n data-fr-js-ratio=\"true\">\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-motif'\"></use>\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-background'\"></use>\n <g transform=\"translate(40, 60)\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-major'\"></use>\n </g>\n </svg>\n }\n </div>\n </div>\n</div>\n\n<ng-template #dsfrPageResponseHeading>\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchDefault>{{ heading }}</h1>\n <h2 *ngSwitchCase=\"'H2'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h2>\n <h3 *ngSwitchCase=\"'H3'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h3>\n <h4 *ngSwitchCase=\"'H4'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h4>\n <h5 *ngSwitchCase=\"'H5'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h5>\n <h6 *ngSwitchCase=\"'H6'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h6>\n </ng-container>\n</ng-template>\n", dependencies: [{ kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: DsfrButtonsGroupComponent, selector: "dsfr-buttons-group", inputs: ["inline", "alignment", "equisized", "buttonGroupSize", "groupMarkup", "size", "iconPosition"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-page-response', encapsulation: ViewEncapsulation.None, imports: [DsfrButtonComponent, DsfrButtonsGroupComponent, DsfrI18nPipe, CommonModule], template: "<div class=\"fr-container\">\n <div\n class=\"fr-my-7w fr-mt-md-12w fr-mb-md-10w fr-grid-row fr-grid-row--gutters fr-grid-row--middle fr-grid-row--center\">\n <div class=\"fr-py-0 fr-col-12 fr-col-md-6\">\n <!-- Titre du contenu -->\n <ng-container>\n <ng-container *ngTemplateOutlet=\"dsfrPageResponseHeading\"></ng-container>\n </ng-container>\n <p class=\"fr-text--sm fr-mb-3w\">Erreur {{ error }}</p>\n <p class=\"fr-text--lead fr-mb-3w\">\n {{ description ?? 'pageResponse.description' | dsfrI18n }}\n </p>\n <p class=\"fr-text--sm fr-mb-5w\">\n {{ detail ?? ('pageResponse.detail' | dsfrI18n) }}\n </p>\n <dsfr-buttons-group *ngIf=\"showBackToHome || showContact\" inline=\"always\">\n <dsfr-button\n type=\"button\"\n *ngIf=\"showBackToHome\"\n label=\"Page d'accueil\"\n variant=\"primary\"\n (click)=\"onHome()\"></dsfr-button>\n <dsfr-button\n type=\"button\"\n *ngIf=\"showContact\"\n [label]=\"'response.contactUs' | dsfrI18n\"\n variant=\"secondary\"\n (click)=\"onContact()\"></dsfr-button>\n </dsfr-buttons-group>\n </div>\n <div class=\"fr-col-12 fr-col-md-3 fr-col-offset-md-1 fr-px-6w fr-px-md-0 fr-py-0\">\n @if (imagePath) {\n <img class=\"fr-responsive-img\" [src]=\"imagePath\" [attr.alt]=\"imageAlt\" />\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"fr-responsive-img fr-artwork\"\n aria-hidden=\"true\"\n width=\"160\"\n height=\"200\"\n viewBox=\"0 0 160 200\"\n data-fr-js-ratio=\"true\">\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-motif'\"></use>\n <use class=\"fr-artwork-motif\" [attr.href]=\"pictoPath + '/background/ovoid.svg#artwork-background'\"></use>\n <g transform=\"translate(40, 60)\">\n <use\n class=\"fr-artwork-decorative\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-decorative'\"></use>\n <use\n class=\"fr-artwork-minor\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-minor'\"></use>\n <use\n class=\"fr-artwork-major\"\n [attr.href]=\"pictoPath + '/pictograms/system/technical-error.svg#artwork-major'\"></use>\n </g>\n </svg>\n }\n </div>\n </div>\n</div>\n\n<ng-template #dsfrPageResponseHeading>\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchDefault>{{ heading }}</h1>\n <h2 *ngSwitchCase=\"'H2'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h2>\n <h3 *ngSwitchCase=\"'H3'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h3>\n <h4 *ngSwitchCase=\"'H4'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h4>\n <h5 *ngSwitchCase=\"'H5'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h5>\n <h6 *ngSwitchCase=\"'H6'\">{{ heading ?? ('pageResponse.heading' | dsfrI18n) }}</h6>\n </ng-container>\n</ng-template>\n" }]
}] });
class DsfrResponseModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseModule, imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrI18nPipe, DsfrResponseComponent], exports: [DsfrResponseComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseModule, imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrResponseComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrResponseModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrResponseComponent],
imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrI18nPipe, DsfrResponseComponent],
}]
}] });
/**
* @deprecated use DsfrResponseModule instead
*/
class DsfrPageResponseModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseModule, imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrI18nPipe, DsfrPageResponseComponent], exports: [DsfrPageResponseComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseModule, imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrPageResponseComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrPageResponseModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrPageResponseComponent],
imports: [CommonModule, DsfrButtonModule, DsfrButtonsGroupModule, DsfrI18nPipe, DsfrPageResponseComponent],
}]
}] });
class DsfrAddressComponent {
constructor() {
/**
* Légende du fieldset.
*/
this.legend = 'Adresse postale';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrAddressComponent, isStandalone: true, selector: "dsfr-address", inputs: { legend: "legend", address: "address", addressSupplement: "addressSupplement", addressDetails: "addressDetails", zipCode: "zipCode", city: "city", locality: "locality" }, ngImport: i0, template: "<dsfr-fieldset [legend]=\"legend\" [legendSrOnly]=\"true\">\n @if (!addressDetails) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"address.name || 'address-line1'\"\n [label]=\"address.label || 'Adresse'\"\n [hint]=\"address.hint || 'Indication : num\u00E9ro et voie'\"\n [autocomplete]=\"address.autoComplete ?? ''\"\n [message]=\"address.message\"\n [messageSeverity]=\"address.messageSeverity\"\n [required]=\"address.required ?? false\"\n [inputId]=\"address.id ?? ''\"\n [disabled]=\"address.disabled ?? false\"\n [(ngModel)]=\"address.value\"></dsfr-form-input>\n\n @if (addressSupplement) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressSupplement.name || 'address-line2'\"\n [label]=\"addressSupplement.label || 'Compl\u00E9ment d\\'adresse (optionnel)'\"\n [hint]=\"addressSupplement.hint || 'Indication : b\u00E2timent, immeuble, escalier et num\u00E9ro d\\\u2019appartement'\"\n [autocomplete]=\"addressSupplement.autoComplete ?? ''\"\n [message]=\"addressSupplement.message\"\n [messageSeverity]=\"addressSupplement.messageSeverity\"\n [required]=\"addressSupplement.required ?? false\"\n [inputId]=\"addressSupplement.id ?? ''\"\n [disabled]=\"addressSupplement.disabled ?? false\"\n [(ngModel)]=\"addressSupplement.value\"></dsfr-form-input>\n }\n }\n\n @if (addressDetails) {\n @if (addressDetails.number) {\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow fr-fieldset__element--number'\"\n [name]=\"addressDetails.number.name || 'number'\"\n [label]=\"addressDetails.number.label || 'Num\u00E9ro'\"\n [hint]=\"addressDetails.number.hint\"\n [autocomplete]=\"addressDetails.number.autoComplete ?? ''\"\n [message]=\"addressDetails.number.message\"\n [messageSeverity]=\"addressDetails.number.messageSeverity\"\n [required]=\"addressDetails.number.required ?? false\"\n [inputId]=\"addressDetails.number.id ?? ''\"\n [disabled]=\"addressDetails.number.disabled ?? false\"\n [(ngModel)]=\"addressDetails.number.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.street) {\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow'\"\n [name]=\"addressDetails.street.name || 'street'\"\n [label]=\"addressDetails.street.label || 'Voie'\"\n [hint]=\"addressDetails.street.hint\"\n [autocomplete]=\"addressDetails.street.autoComplete ?? ''\"\n [message]=\"addressDetails.street.message\"\n [messageSeverity]=\"addressDetails.street.messageSeverity\"\n [required]=\"addressDetails.street.required ?? false\"\n [inputId]=\"addressDetails.street.id ?? ''\"\n [disabled]=\"addressDetails.street.disabled ?? false\"\n [(ngModel)]=\"addressDetails.street.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.building) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.building.name || 'building'\"\n [label]=\"addressDetails.building.label || 'B\u00E2timent'\"\n [hint]=\"addressDetails.building.hint\"\n [autocomplete]=\"addressDetails.building.autoComplete ?? ''\"\n [message]=\"addressDetails.building.message\"\n [messageSeverity]=\"addressDetails.building.messageSeverity\"\n [required]=\"addressDetails.building.required ?? false\"\n [inputId]=\"addressDetails.building.id ?? ''\"\n [disabled]=\"addressDetails.building.disabled ?? false\"\n [(ngModel)]=\"addressDetails.building.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.block) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.block.name || 'block'\"\n [label]=\"addressDetails.block.label || 'Immeuble'\"\n [hint]=\"addressDetails.block.hint\"\n [autocomplete]=\"addressDetails.block.autoComplete ?? ''\"\n [message]=\"addressDetails.block.message\"\n [messageSeverity]=\"addressDetails.block.messageSeverity\"\n [required]=\"addressDetails.block.required ?? false\"\n [inputId]=\"addressDetails.block.id ?? ''\"\n [disabled]=\"addressDetails.block.disabled ?? false\"\n [(ngModel)]=\"addressDetails.block.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.stare) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.stare.name || 'stare'\"\n [label]=\"addressDetails.stare.label || 'Escalier'\"\n [hint]=\"addressDetails.stare.hint\"\n [autocomplete]=\"addressDetails.stare.autoComplete ?? ''\"\n [message]=\"addressDetails.stare.message\"\n [messageSeverity]=\"addressDetails.stare.messageSeverity\"\n [required]=\"addressDetails.stare.required ?? false\"\n [inputId]=\"addressDetails.stare.id ?? ''\"\n [disabled]=\"addressDetails.stare.disabled ?? false\"\n [(ngModel)]=\"addressDetails.stare.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.apartment) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.apartment.name || 'apartment'\"\n [label]=\"addressDetails.apartment.label || 'Num\u00E9ro d\\'appartement'\"\n [hint]=\"addressDetails.apartment.hint\"\n [autocomplete]=\"addressDetails.apartment.autoComplete ?? ''\"\n [message]=\"addressDetails.apartment.messageSeverity\"\n [messageSeverity]=\"addressDetails.apartment.messageSeverity\"\n [required]=\"addressDetails.apartment.required ?? false\"\n [inputId]=\"addressDetails.apartment.id ?? ''\"\n [disabled]=\"addressDetails.apartment.disabled ?? false\"\n [(ngModel)]=\"addressDetails.apartment.value\"></dsfr-form-input>\n }\n }\n\n @if (locality) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"locality.name || 'address-level3'\"\n [label]=\"locality.label || 'Lieu-dit, commune d\u00E9l\u00E9gu\u00E9e ou bo\u00EEte postale'\"\n [hint]=\"locality.hint\"\n [autocomplete]=\"locality.autoComplete ?? ''\"\n [message]=\"locality.message\"\n [messageSeverity]=\"locality.messageSeverity\"\n [required]=\"locality.required ?? false\"\n [inputId]=\"locality.id ?? ''\"\n [disabled]=\"locality.disabled ?? false\"\n [(ngModel)]=\"locality.value\"></dsfr-form-input>\n }\n\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--postal'\"\n [name]=\"zipCode.name || 'postal-code'\"\n [label]=\"zipCode.label || 'Code postal'\"\n [hint]=\"zipCode.hint || 'Format attendu : 5 chiffres'\"\n [autocomplete]=\"zipCode.autoComplete ?? ''\"\n [message]=\"zipCode.message ?? ''\"\n [messageSeverity]=\"zipCode.messageSeverity\"\n [required]=\"zipCode.required ?? false\"\n [inputId]=\"zipCode.id ?? ''\"\n [disabled]=\"zipCode.disabled ?? false\"\n [(ngModel)]=\"zipCode.value\"></dsfr-form-input>\n\n <!-- BUG Classe 'fr-fieldset__element--inline@md' inconnue en DSFR 1.10.0 -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline@md fr-fieldset__element--inline-grow'\"\n [name]=\"city.name || 'address-level2'\"\n [label]=\"city.label || 'Ville ou commune'\"\n [hint]=\"city.hint || 'Exemple : Montpellier'\"\n [autocomplete]=\"city.autoComplete ?? ''\"\n [message]=\"city.message\"\n [messageSeverity]=\"city.messageSeverity\"\n [required]=\"city.required ?? false\"\n [inputId]=\"city.id ?? ''\"\n [disabled]=\"city.disabled ?? false\"\n [(ngModel)]=\"city.value\"></dsfr-form-input>\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "component", type: DsfrFormInputComponent, selector: "dsfr-form-input", inputs: ["rows", "labelSrOnly", "width", "textarea"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-address', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
FormsModule,
DsfrFormFieldsetElementDirective,
], template: "<dsfr-fieldset [legend]=\"legend\" [legendSrOnly]=\"true\">\n @if (!addressDetails) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"address.name || 'address-line1'\"\n [label]=\"address.label || 'Adresse'\"\n [hint]=\"address.hint || 'Indication : num\u00E9ro et voie'\"\n [autocomplete]=\"address.autoComplete ?? ''\"\n [message]=\"address.message\"\n [messageSeverity]=\"address.messageSeverity\"\n [required]=\"address.required ?? false\"\n [inputId]=\"address.id ?? ''\"\n [disabled]=\"address.disabled ?? false\"\n [(ngModel)]=\"address.value\"></dsfr-form-input>\n\n @if (addressSupplement) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressSupplement.name || 'address-line2'\"\n [label]=\"addressSupplement.label || 'Compl\u00E9ment d\\'adresse (optionnel)'\"\n [hint]=\"addressSupplement.hint || 'Indication : b\u00E2timent, immeuble, escalier et num\u00E9ro d\\\u2019appartement'\"\n [autocomplete]=\"addressSupplement.autoComplete ?? ''\"\n [message]=\"addressSupplement.message\"\n [messageSeverity]=\"addressSupplement.messageSeverity\"\n [required]=\"addressSupplement.required ?? false\"\n [inputId]=\"addressSupplement.id ?? ''\"\n [disabled]=\"addressSupplement.disabled ?? false\"\n [(ngModel)]=\"addressSupplement.value\"></dsfr-form-input>\n }\n }\n\n @if (addressDetails) {\n @if (addressDetails.number) {\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow fr-fieldset__element--number'\"\n [name]=\"addressDetails.number.name || 'number'\"\n [label]=\"addressDetails.number.label || 'Num\u00E9ro'\"\n [hint]=\"addressDetails.number.hint\"\n [autocomplete]=\"addressDetails.number.autoComplete ?? ''\"\n [message]=\"addressDetails.number.message\"\n [messageSeverity]=\"addressDetails.number.messageSeverity\"\n [required]=\"addressDetails.number.required ?? false\"\n [inputId]=\"addressDetails.number.id ?? ''\"\n [disabled]=\"addressDetails.number.disabled ?? false\"\n [(ngModel)]=\"addressDetails.number.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.street) {\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow'\"\n [name]=\"addressDetails.street.name || 'street'\"\n [label]=\"addressDetails.street.label || 'Voie'\"\n [hint]=\"addressDetails.street.hint\"\n [autocomplete]=\"addressDetails.street.autoComplete ?? ''\"\n [message]=\"addressDetails.street.message\"\n [messageSeverity]=\"addressDetails.street.messageSeverity\"\n [required]=\"addressDetails.street.required ?? false\"\n [inputId]=\"addressDetails.street.id ?? ''\"\n [disabled]=\"addressDetails.street.disabled ?? false\"\n [(ngModel)]=\"addressDetails.street.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.building) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.building.name || 'building'\"\n [label]=\"addressDetails.building.label || 'B\u00E2timent'\"\n [hint]=\"addressDetails.building.hint\"\n [autocomplete]=\"addressDetails.building.autoComplete ?? ''\"\n [message]=\"addressDetails.building.message\"\n [messageSeverity]=\"addressDetails.building.messageSeverity\"\n [required]=\"addressDetails.building.required ?? false\"\n [inputId]=\"addressDetails.building.id ?? ''\"\n [disabled]=\"addressDetails.building.disabled ?? false\"\n [(ngModel)]=\"addressDetails.building.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.block) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.block.name || 'block'\"\n [label]=\"addressDetails.block.label || 'Immeuble'\"\n [hint]=\"addressDetails.block.hint\"\n [autocomplete]=\"addressDetails.block.autoComplete ?? ''\"\n [message]=\"addressDetails.block.message\"\n [messageSeverity]=\"addressDetails.block.messageSeverity\"\n [required]=\"addressDetails.block.required ?? false\"\n [inputId]=\"addressDetails.block.id ?? ''\"\n [disabled]=\"addressDetails.block.disabled ?? false\"\n [(ngModel)]=\"addressDetails.block.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.stare) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.stare.name || 'stare'\"\n [label]=\"addressDetails.stare.label || 'Escalier'\"\n [hint]=\"addressDetails.stare.hint\"\n [autocomplete]=\"addressDetails.stare.autoComplete ?? ''\"\n [message]=\"addressDetails.stare.message\"\n [messageSeverity]=\"addressDetails.stare.messageSeverity\"\n [required]=\"addressDetails.stare.required ?? false\"\n [inputId]=\"addressDetails.stare.id ?? ''\"\n [disabled]=\"addressDetails.stare.disabled ?? false\"\n [(ngModel)]=\"addressDetails.stare.value\"></dsfr-form-input>\n }\n\n @if (addressDetails.apartment) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"addressDetails.apartment.name || 'apartment'\"\n [label]=\"addressDetails.apartment.label || 'Num\u00E9ro d\\'appartement'\"\n [hint]=\"addressDetails.apartment.hint\"\n [autocomplete]=\"addressDetails.apartment.autoComplete ?? ''\"\n [message]=\"addressDetails.apartment.messageSeverity\"\n [messageSeverity]=\"addressDetails.apartment.messageSeverity\"\n [required]=\"addressDetails.apartment.required ?? false\"\n [inputId]=\"addressDetails.apartment.id ?? ''\"\n [disabled]=\"addressDetails.apartment.disabled ?? false\"\n [(ngModel)]=\"addressDetails.apartment.value\"></dsfr-form-input>\n }\n }\n\n @if (locality) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"locality.name || 'address-level3'\"\n [label]=\"locality.label || 'Lieu-dit, commune d\u00E9l\u00E9gu\u00E9e ou bo\u00EEte postale'\"\n [hint]=\"locality.hint\"\n [autocomplete]=\"locality.autoComplete ?? ''\"\n [message]=\"locality.message\"\n [messageSeverity]=\"locality.messageSeverity\"\n [required]=\"locality.required ?? false\"\n [inputId]=\"locality.id ?? ''\"\n [disabled]=\"locality.disabled ?? false\"\n [(ngModel)]=\"locality.value\"></dsfr-form-input>\n }\n\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--postal'\"\n [name]=\"zipCode.name || 'postal-code'\"\n [label]=\"zipCode.label || 'Code postal'\"\n [hint]=\"zipCode.hint || 'Format attendu : 5 chiffres'\"\n [autocomplete]=\"zipCode.autoComplete ?? ''\"\n [message]=\"zipCode.message ?? ''\"\n [messageSeverity]=\"zipCode.messageSeverity\"\n [required]=\"zipCode.required ?? false\"\n [inputId]=\"zipCode.id ?? ''\"\n [disabled]=\"zipCode.disabled ?? false\"\n [(ngModel)]=\"zipCode.value\"></dsfr-form-input>\n\n <!-- BUG Classe 'fr-fieldset__element--inline@md' inconnue en DSFR 1.10.0 -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline@md fr-fieldset__element--inline-grow'\"\n [name]=\"city.name || 'address-level2'\"\n [label]=\"city.label || 'Ville ou commune'\"\n [hint]=\"city.hint || 'Exemple : Montpellier'\"\n [autocomplete]=\"city.autoComplete ?? ''\"\n [message]=\"city.message\"\n [messageSeverity]=\"city.messageSeverity\"\n [required]=\"city.required ?? false\"\n [inputId]=\"city.id ?? ''\"\n [disabled]=\"city.disabled ?? false\"\n [(ngModel)]=\"city.value\"></dsfr-form-input>\n</dsfr-fieldset>\n" }]
}], propDecorators: { legend: [{
type: Input
}], address: [{
type: Input,
args: [{ required: true }]
}], addressSupplement: [{
type: Input
}], addressDetails: [{
type: Input
}], zipCode: [{
type: Input,
args: [{ required: true }]
}], city: [{
type: Input,
args: [{ required: true }]
}], locality: [{
type: Input
}] } });
class DsfrAddressModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressModule, imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormInputComponent, FormsModule, DsfrAddressComponent], exports: [DsfrAddressComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressModule, imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormInputComponent, FormsModule, DsfrAddressComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrAddressModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrAddressComponent],
imports: [CommonModule, DsfrFormFieldsetComponent, DsfrFormInputComponent, FormsModule, DsfrAddressComponent],
}]
}] });
const DATE_ERROR = {
INVALID_FORMAT_DAY: 'err_invalid_format_day',
INVALID_FORMAT_MONTH: 'err_invalid_format_month',
INVALID_FORMAT_YEAR: 'err_invalid_format_year',
INVALID_DAY: 'err_invalid_day',
INVALID_MONTH: 'err_invalid_month',
INVALID_DATE: 'err_invalid_date',
REQUIRED: 'err_required',
};
/**
* Cette classe représente les valeurs saisies par un utilisateur sur 3 champs distincts, jour, mois année.
* 🔥 Bien que ces propriétés soient en théorie des nombres, à l'exécution, on reçoit des strings.
* - Chaque valeur est initialisée à `undefined` mais peut aussi avoir la valeur "" (chaine vide).
* 👆 Par défaut la date est une date UTC.
* L'objectif de la classe est de :
* - Gérer des dates saisies par l'utilisateur, potentiellement avec des valeurs `undefined` ou ""
* - Manipuler les mois de 1 à 12 (et non de 0 à 11)
* - D'encapsuler l'api [Date](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Date) et plus tard l'API Temporal
*/
class DateModel {
constructor(fullYear, monthNum, dayNum, hourNum = 0) {
this.fullYear = fullYear;
this.monthNum = monthNum;
this.dayNum = dayNum;
this.hourNum = hourNum;
}
get month() {
return this.monthNum ? this.monthNum - 1 : -1;
}
/**
* @param value : 3 formes basiques pour utiliser la méthode
* - string : Une chaîne de caractères qui représente une date, selon le format reconnu par la méthode `Date.parse()` (ex : '04 Dec 1995 00:12:00 GMT')
* - number : Une valeur entière qui représente le nombre de millisecondes depuis le premier janvier 1970
* - date : Un objet de type date
*/
static of(value, loggerService) {
const date = DateUtils.dateUtcOfWithHours(value);
if (!date && typeof value === 'string') {
loggerService.warn(`La date '${value}' n'est pas valide`);
}
if (!date) {
return new DateModel();
}
else {
return new DateModel(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours());
}
}
isValid() {
const date = new Date(this.fullYear, this.month, this.dayNum);
// Nécessaire car les années à deux digits sont prises en compte comme des dates de format 19XX
if (DateUtils.isTwoDigitsYear(this.fullYear)) {
date.setFullYear(this.fullYear);
}
return (date.getFullYear() === Number(this.fullYear) &&
date.getMonth() === Number(this.month) &&
date.getDate() === Number(this.dayNum));
}
toDate(hourTimezoneOffset) {
let date = undefined;
let fullYear = Number(this.fullYear);
let monthNum = Number(this.monthNum);
let dayNum = Number(this.dayNum);
let hourNum = Number(this.hourNum);
// on vérifie quand même qu'on a bien affaire à des nombres
if (fullYear !== null &&
fullYear !== undefined &&
monthNum !== null &&
monthNum !== undefined &&
dayNum !== null &&
dayNum !== undefined &&
hourNum !== null &&
hourNum !== undefined &&
!isNaN(fullYear) &&
!isNaN(monthNum) &&
!isNaN(dayNum) &&
!isNaN(hourNum) &&
1 <= monthNum &&
monthNum <= 12 &&
1 <= dayNum &&
dayNum <= 31) {
const hourToOutput = hourTimezoneOffset ?? hourNum;
const dateWithoutTwoDigitsProblems = DateUtils.createDate(fullYear, this.month, dayNum, hourToOutput, typeof hourTimezoneOffset === 'number');
date = this.isValid() ? dateWithoutTwoDigitsProblems : undefined;
}
return date;
}
/**
* Valide le model.
* @param required indique si la date est requise ou non
* @return une liste de codes d'erreur ou tableau vide
*/
validate(required = false) {
const errors = [];
const dayNum = this.dayNum;
const monthNum = this.monthNum;
const fullYear = this.fullYear;
// Format
if (!this.isNumber(dayNum) || !this.isNumber(monthNum) || !this.isNumber(fullYear)) {
if (!this.isNumber(dayNum)) {
errors.push(DATE_ERROR.INVALID_FORMAT_DAY);
}
if (!this.isNumber(monthNum)) {
errors.push(DATE_ERROR.INVALID_FORMAT_MONTH);
}
if (!this.isNumber(fullYear)) {
errors.push(DATE_ERROR.INVALID_FORMAT_YEAR);
}
}
else {
// Erreur sur le jour, le mois
if (dayNum && (dayNum < 1 || dayNum > 31))
errors.push(DATE_ERROR.INVALID_DAY);
if (monthNum && (monthNum < 1 || monthNum > 12))
errors.push(DATE_ERROR.INVALID_MONTH);
}
// All touched : erreur sur la date elle-même
if (errors.length === 0 && this.allTouched()) {
// Tous les champs sont remplis, on vérifie la date
if (this.allFilled()) {
if (!this.isValid())
errors.push(DATE_ERROR.INVALID_DATE);
}
// Au moins 1 champ n'est pas rempli
else {
if (required)
errors.push(DATE_ERROR.REQUIRED);
}
}
return errors;
}
allTouched() {
return this.fullYear !== undefined && this.monthNum !== undefined && this.dayNum !== undefined;
}
allFilled() {
return !!this.dayNum && !!this.monthNum && !!this.fullYear;
}
/** @return true si undefined ou number */
isNumber(value) {
return value === undefined || !isNaN(value);
}
}
const PATH_ERR_I18N = {
err_invalid_format_day: 'date.error.invalid.format_day',
err_invalid_format_month: 'date.error.invalid.format_month',
err_invalid_format_year: 'date.error.invalid.format_year',
err_invalid_day: 'date.error.invalid.day',
err_invalid_month: 'date.error.invalid.month',
err_invalid_date: 'date.error.invalid.date',
err_required: 'date.error.required',
};
class DsfrDateComponent extends DefaultValueAccessorComponent {
constructor() {
super(...arguments);
/**
* Indique si les champs de saisie doivent être auto-complétés à partir de la date de naissance de l'utilisateur.
*/
this.autocomplete = false;
/**
* Indique si la date est obligatoire.
*/
this.required = false;
/** Permet de rétablir une graisse standard sur la légende. */
this.legendRegular = false;
/**
* Message d'information lié au composant
*/
this.message = undefined;
/**
* Positionne le champ en statut `VALID` avec le message indiqué.
* @deprecated (since 1.15) utiliser message/messageSeverity à la place
*/
this.valid = undefined;
/**
* Signale le changement de date.
*/
// Certainement inutile vs ngModelChange, à voir !
this.dateChange = new EventEmitter();
/** @internal */ this.dateModel = new DateModel(); // View-Model
/** @internal */ this.validationErrors = null;
this.i18n = inject(DsfrI18nService);
this.logger = inject(LoggerService);
/** Permet de stocker la fonction pouvant être appelée pour forcer une revalidation. */
this.fnOnValidatorChange = () => { };
}
get internalError() {
const internalError = this.validationErrors === null ? [] : this.validationErrors.map((err) => Object.values(err)[0]);
return internalError.length > 0 ? internalError : undefined;
}
// Sur changement de la date, on réécrit jour mois année
/** @internal */
writeValue(value) {
// Bien que la value soit de type date, le développeur peut, à notre insu, nous envoyer une chaine ou un nombre (comme Storybook), ex : Date.now()
this.dateModel = DateModel.of(value, this.logger);
super.writeValue(this.dateModel.toDate());
this.validationErrors = this.internalValidate(); // la validation doit être rejouée sur une modification programmatique
this.dateChange.emit(this.value);
}
// Sur changement du modèle, c.-à-d. jour, mois ou année, on récrit la date
/** @internal */
onFocusOut() {
const { fullYear, monthNum, dayNum } = this.dateModel;
const hourTimezoneOffset = new Date(fullYear, monthNum - 1, dayNum).getTimezoneOffset() / 60;
this.validationErrors = this.internalValidate();
this.value = this.dateModel.toDate(hourTimezoneOffset);
this.dateChange.emit(this.value);
}
/** @internal */
registerOnValidatorChange(fn) {
this.fnOnValidatorChange = fn;
}
/** @internal */
validate(control) {
return this.internalValidate();
}
/**
* Valide le composant et retourne ValidationErrors
*/
internalValidate() {
const errors = this.dateModel.validate(this.required);
// Pas d'erreur, on retourne null
if (errors.length === 0)
return null;
// On transforme la liste des codes d'erreur en ValidationErrors
return errors.map((codeErr) => {
// @ts-ignore
const pathErr = PATH_ERR_I18N[codeErr];
const labelErr = this.i18n.t(pathErr);
const validationErr = {};
// @ts-ignore
validationErr[codeErr] = labelErr;
return validationErr;
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "20.3.23", type: DsfrDateComponent, isStandalone: true, selector: "dsfr-date", inputs: { autocomplete: "autocomplete", error: "error", hint: "hint", legend: "legend", required: ["required", "required", booleanAttribute], readonly: ["readonly", "readonly", booleanAttribute], legendRegular: ["legendRegular", "legendRegular", booleanAttribute], message: "message", messageSeverity: "messageSeverity", valid: "valid" }, outputs: { dateChange: "dateChange" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrDateComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DsfrDateComponent),
multi: true,
},
], usesInheritance: true, ngImport: i0, template: "<dsfr-fieldset\n [disabled]=\"disabled\"\n [legend]=\"legend\"\n [legendRegular]=\"legendRegular\"\n [hint]=\"hint\"\n [error]=\"error\"\n [valid]=\"valid\"\n [message]=\"message\"\n [messageSeverity]=\"messageSeverity\"\n [internalError]=\"internalError\">\n <!-- Day -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--number'\"\n [label]=\"'date.day.label' | dsfrI18n\"\n [maxLength]=\"2\"\n [hint]=\"'date.day.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-day' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.dayNum\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n <!-- Month -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--number'\"\n [label]=\"'date.month.label' | dsfrI18n\"\n [maxLength]=\"2\"\n [hint]=\"'date.month.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-month' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.monthNum\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n <!-- Year -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow fr-fieldset__element--year'\"\n [label]=\"'date.year.label' | dsfrI18n\"\n [maxLength]=\"4\"\n [hint]=\"'date.year.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-year' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.fullYear\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "component", type: DsfrFormInputComponent, selector: "dsfr-form-input", inputs: ["rows", "labelSrOnly", "width", "textarea"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-date', encapsulation: ViewEncapsulation.None, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DsfrDateComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DsfrDateComponent),
multi: true,
},
], imports: [
CommonModule,
FormsModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrI18nPipe,
DsfrFormFieldsetElementDirective,
], template: "<dsfr-fieldset\n [disabled]=\"disabled\"\n [legend]=\"legend\"\n [legendRegular]=\"legendRegular\"\n [hint]=\"hint\"\n [error]=\"error\"\n [valid]=\"valid\"\n [message]=\"message\"\n [messageSeverity]=\"messageSeverity\"\n [internalError]=\"internalError\">\n <!-- Day -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--number'\"\n [label]=\"'date.day.label' | dsfrI18n\"\n [maxLength]=\"2\"\n [hint]=\"'date.day.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-day' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.dayNum\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n <!-- Month -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--number'\"\n [label]=\"'date.month.label' | dsfrI18n\"\n [maxLength]=\"2\"\n [hint]=\"'date.month.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-month' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.monthNum\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n <!-- Year -->\n <dsfr-form-input\n *fieldsetElement=\"'fr-fieldset__element--inline fr-fieldset__element--inline-grow fr-fieldset__element--year'\"\n [label]=\"'date.year.label' | dsfrI18n\"\n [maxLength]=\"4\"\n [hint]=\"'date.year.hint' | dsfrI18n\"\n [autocomplete]=\"autocomplete ? 'bday-year' : undefined\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [(ngModel)]=\"dateModel.fullYear\"\n (focusout)=\"onFocusOut()\"></dsfr-form-input>\n</dsfr-fieldset>\n" }]
}], propDecorators: { autocomplete: [{
type: Input
}], error: [{
type: Input
}], hint: [{
type: Input
}], legend: [{
type: Input
}], required: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], readonly: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], legendRegular: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], message: [{
type: Input
}], messageSeverity: [{
type: Input
}], valid: [{
type: Input
}], dateChange: [{
type: Output
}] } });
class DsfrDateModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateModule, imports: [CommonModule,
FormsModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrI18nPipe,
DsfrDateComponent], exports: [DsfrDateComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateModule, imports: [CommonModule,
FormsModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrDateComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrDateModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrDateComponent],
imports: [
CommonModule,
FormsModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrI18nPipe,
DsfrDateComponent,
],
}]
}] });
class DsfrNameComponent {
constructor() {
/**
* Permet d'afficher l'option "Je n'ai pas de prénom".
*/
this.noFirstName = false;
/**
* Permet d'afficher un bouton qui ajoute un nouveau champ de de saisi pour un prénom additionnel.
*/
this.addFirstName = false;
/**
* Notifie que l'utilisateur a cliqué sur le bouton d'ajout de prénom.
*/
this.addFirstNameSelect = new EventEmitter();
/**
* Notifie la suppression d'un prénom additionnel (le tableau 'firstnames' devrait être mis à jour en conséquence).
*/
this.deleteFirstNameSelect = new EventEmitter();
this.noFirstNameModel = false;
}
onAddFirstNameInput() {
this.addFirstNameSelect.emit();
}
onDeleteFirstNameInput(index) {
this.deleteFirstNameSelect.emit(index);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.23", type: DsfrNameComponent, isStandalone: true, selector: "dsfr-name", inputs: { legend: "legend", lastName: "lastName", usualName: "usualName", firstNames: "firstNames", noFirstName: "noFirstName", addFirstName: "addFirstName", country: "country" }, outputs: { addFirstNameSelect: "addFirstNameSelect", deleteFirstNameSelect: "deleteFirstNameSelect" }, ngImport: i0, template: "<dsfr-fieldset [legend]=\"'name.legend' | dsfrI18n\" [legendSrOnly]=\"true\">\n @if (country) {\n <dsfr-form-select\n *fieldsetElement\n [label]=\"country.label || 'name.country.label' | dsfrI18n\"\n [placeholder]=\"country.placeHolder || 'name.country.placeholder' | dsfrI18n\"\n [options]=\"country.options ?? []\"\n [(ngModel)]=\"country.value\"></dsfr-form-select>\n }\n\n <dsfr-form-input\n *fieldsetElement\n [name]=\"lastName.name || 'family-name'\"\n [spellcheck]=\"false\"\n [label]=\"lastName.label || 'name.lastname' | dsfrI18n\"\n [autocomplete]=\"'family-name'\"\n [(ngModel)]=\"lastName.value\"></dsfr-form-input>\n\n @if (usualName) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"usualName.name || 'married-name'\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'family-name'\"\n [label]=\"usualName.label || 'name.usualName.label' | dsfrI18n\"\n [hint]=\"usualName.hint || 'name.usualName.hint' | dsfrI18n\"\n [(ngModel)]=\"usualName.value\"></dsfr-form-input>\n }\n\n @if (!noFirstName) {\n @for (item of firstNames; track index; let index = $index) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n\n @if (addFirstName) {\n <dsfr-button\n *fieldsetElement\n [label]=\"'name.addFirstName' | dsfrI18n\"\n iconPosition=\"left\"\n variant=\"tertiary\"\n buttonSize=\"SM\"\n icon=\"fr-icon-add-line\"\n [disabled]=\"noFirstNameModel === true\"\n (click)=\"onAddFirstNameInput()\"></dsfr-button>\n }\n }\n\n @if (noFirstName) {\n <dsfr-fieldset *fieldsetElement [legend]=\"'name.legendFirstName' | dsfrI18n\" [legendSrOnly]=\"true\">\n @for (item of firstNames; track index; let index = $index) {\n @if (index === 0) {\n <dsfr-form-input\n *fieldsetElement\n [disabled]=\"item.disabled || noFirstNameModel === true\"\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n @if (index > 0) {\n <dsfr-form-input\n *fieldsetElement\n [disabled]=\"item.disabled || noFirstNameModel === true\"\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n inputWrapMode=\"action\"\n [buttonIcon]=\"index !== 0 ? 'fr-icon-delete-line' : undefined\"\n buttonVariant=\"tertiary\"\n (buttonSelect)=\"onDeleteFirstNameInput(index)\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n }\n\n @if (addFirstName) {\n <dsfr-button\n *fieldsetElement\n [label]=\"'name.addFirstName' | dsfrI18n\"\n iconPosition=\"left\"\n variant=\"tertiary\"\n buttonSize=\"SM\"\n icon=\"fr-icon-add-line\"\n [disabled]=\"noFirstNameModel === true\"\n (click)=\"onAddFirstNameInput()\"></dsfr-button>\n }\n\n <dsfr-form-checkbox\n *fieldsetElement\n [small]=\"true\"\n name=\"firstname-disabled\"\n [label]=\"'name.noFirstName' | dsfrI18n\"\n [(ngModel)]=\"noFirstNameModel\"></dsfr-form-checkbox>\n </dsfr-fieldset>\n }\n</dsfr-fieldset>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsfrFormFieldsetComponent, selector: "dsfr-fieldset, dsfr-form-fieldset", inputs: ["inline"] }, { kind: "component", type: DsfrFormInputComponent, selector: "dsfr-form-input", inputs: ["rows", "labelSrOnly", "width", "textarea"] }, { kind: "component", type: DsfrFormCheckboxComponent, selector: "dsfr-form-checkbox", inputs: ["labelSrOnly", "ariaInvalid", "small", "indeterminate"] }, { kind: "component", type: DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "ariaHidden", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "component", type: DsfrFormSelectComponent, selector: "dsfr-form-select", inputs: ["placeholder", "required", "ariaLabel", "ariaInvalid", "labelSrOnly", "options", "placeHolder", "value", "compareWith"], outputs: ["selectChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: DsfrFormFieldsetElementDirective, selector: "[fieldsetElement]", inputs: ["fieldsetElement"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-name', encapsulation: ViewEncapsulation.None, imports: [
CommonModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrFormCheckboxComponent,
DsfrButtonComponent,
DsfrFormSelectComponent,
FormsModule,
DsfrFormFieldsetElementDirective,
DsfrI18nPipe,
], template: "<dsfr-fieldset [legend]=\"'name.legend' | dsfrI18n\" [legendSrOnly]=\"true\">\n @if (country) {\n <dsfr-form-select\n *fieldsetElement\n [label]=\"country.label || 'name.country.label' | dsfrI18n\"\n [placeholder]=\"country.placeHolder || 'name.country.placeholder' | dsfrI18n\"\n [options]=\"country.options ?? []\"\n [(ngModel)]=\"country.value\"></dsfr-form-select>\n }\n\n <dsfr-form-input\n *fieldsetElement\n [name]=\"lastName.name || 'family-name'\"\n [spellcheck]=\"false\"\n [label]=\"lastName.label || 'name.lastname' | dsfrI18n\"\n [autocomplete]=\"'family-name'\"\n [(ngModel)]=\"lastName.value\"></dsfr-form-input>\n\n @if (usualName) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"usualName.name || 'married-name'\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'family-name'\"\n [label]=\"usualName.label || 'name.usualName.label' | dsfrI18n\"\n [hint]=\"usualName.hint || 'name.usualName.hint' | dsfrI18n\"\n [(ngModel)]=\"usualName.value\"></dsfr-form-input>\n }\n\n @if (!noFirstName) {\n @for (item of firstNames; track index; let index = $index) {\n <dsfr-form-input\n *fieldsetElement\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n\n @if (addFirstName) {\n <dsfr-button\n *fieldsetElement\n [label]=\"'name.addFirstName' | dsfrI18n\"\n iconPosition=\"left\"\n variant=\"tertiary\"\n buttonSize=\"SM\"\n icon=\"fr-icon-add-line\"\n [disabled]=\"noFirstNameModel === true\"\n (click)=\"onAddFirstNameInput()\"></dsfr-button>\n }\n }\n\n @if (noFirstName) {\n <dsfr-fieldset *fieldsetElement [legend]=\"'name.legendFirstName' | dsfrI18n\" [legendSrOnly]=\"true\">\n @for (item of firstNames; track index; let index = $index) {\n @if (index === 0) {\n <dsfr-form-input\n *fieldsetElement\n [disabled]=\"item.disabled || noFirstNameModel === true\"\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n @if (index > 0) {\n <dsfr-form-input\n *fieldsetElement\n [disabled]=\"item.disabled || noFirstNameModel === true\"\n [name]=\"item.name || 'given-name' + index\"\n [spellcheck]=\"false\"\n [autocomplete]=\"'given-name'\"\n [label]=\"item.label || 'name.firstname' | dsfrI18n\"\n inputWrapMode=\"action\"\n [buttonIcon]=\"index !== 0 ? 'fr-icon-delete-line' : undefined\"\n buttonVariant=\"tertiary\"\n (buttonSelect)=\"onDeleteFirstNameInput(index)\"\n [(ngModel)]=\"item.value\"></dsfr-form-input>\n }\n }\n\n @if (addFirstName) {\n <dsfr-button\n *fieldsetElement\n [label]=\"'name.addFirstName' | dsfrI18n\"\n iconPosition=\"left\"\n variant=\"tertiary\"\n buttonSize=\"SM\"\n icon=\"fr-icon-add-line\"\n [disabled]=\"noFirstNameModel === true\"\n (click)=\"onAddFirstNameInput()\"></dsfr-button>\n }\n\n <dsfr-form-checkbox\n *fieldsetElement\n [small]=\"true\"\n name=\"firstname-disabled\"\n [label]=\"'name.noFirstName' | dsfrI18n\"\n [(ngModel)]=\"noFirstNameModel\"></dsfr-form-checkbox>\n </dsfr-fieldset>\n }\n</dsfr-fieldset>\n" }]
}], propDecorators: { legend: [{
type: Input
}], lastName: [{
type: Input,
args: [{ required: true }]
}], usualName: [{
type: Input
}], firstNames: [{
type: Input,
args: [{ required: true }]
}], noFirstName: [{
type: Input
}], addFirstName: [{
type: Input
}], country: [{
type: Input
}], addFirstNameSelect: [{
type: Output
}], deleteFirstNameSelect: [{
type: Output
}] } });
class DsfrNameModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameModule, imports: [CommonModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrFormCheckboxComponent,
DsfrButtonComponent,
DsfrFormSelectComponent,
FormsModule,
DsfrNameComponent], exports: [DsfrNameComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameModule, imports: [CommonModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrFormCheckboxComponent,
DsfrButtonComponent,
DsfrFormSelectComponent,
FormsModule,
DsfrNameComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.23", ngImport: i0, type: DsfrNameModule, decorators: [{
type: NgModule,
args: [{
exports: [DsfrNameComponent],
imports: [
CommonModule,
DsfrFormFieldsetComponent,
DsfrFormInputComponent,
DsfrFormCheckboxComponent,
DsfrButtonComponent,
DsfrFormSelectComponent,
FormsModule,
DsfrNameComponent,
],
}]
}] });
/* Public API Surface of ngx-dsfr */
/**
* Generated bundle index. Do not edit.
*/
export { BaseInputComponent, BasePanelComponent, ConfigService, DEFAULT_HEADING_LEVEL, DISPLAY_MODAL_ID, DSFR_CONFIG_TOKEN, DSFR_TAB_SELECTORS, DateUtils, DefaultControlComponent, DefaultValueAccessorComponent, DomUtils, DsfrAccordionComponent, DsfrAccordionModule, DsfrAccordionsGroupComponent, DsfrAccordionsGroupModule, DsfrAddressComponent, DsfrAddressModule, DsfrAlertComponent, DsfrAlertModule, DsfrAlertSizeConst, DsfrAlignConst, DsfrBackToTopComponent, DsfrBackToTopModule, DsfrBadgeComponent, DsfrBadgeModule, DsfrBadgeSeverityConst, DsfrBadgeSizeConst, DsfrBadgesGroupComponent, DsfrBadgesGroupModule, DsfrBreadcrumbComponent, DsfrBreadcrumbModule, DsfrButtonComponent, DsfrButtonModule, DsfrButtonTypeConst, DsfrButtonVariantConst, DsfrButtonsGroupComponent, DsfrButtonsGroupMarkupConst, DsfrButtonsGroupModule, DsfrCalloutComponent, DsfrCalloutDescMarkupConst, DsfrCalloutModule, DsfrCardBackgroundConst, DsfrCardBorderConst, DsfrCardComponent, DsfrCardModule, DsfrCellDirective, DsfrCellSizeConst, DsfrColumnDirective, DsfrConfigModule, DsfrConsentBannerComponent, DsfrConsentBannerModule, DsfrContentComponent, DsfrContentModule, DsfrDataTableModule, DsfrDataTableService, DsfrDateComponent, DsfrDateModule, DsfrDisableRouterLinkDirective, DsfrDisplayComponent, DsfrDisplayModule, DsfrDownloadComponent, DsfrDownloadModule, DsfrDownloadVariantConst, DsfrDownloadsGroupComponent, DsfrDownloadsGroupModule, DsfrFileSizeUnitConst, DsfrFollowComponent, DsfrFollowLinkComponent, DsfrFollowModule, DsfrFollowNameConst, DsfrFollowTypeConst, DsfrFooterComponent, DsfrFooterModule, DsfrFooterPresMarkupConst, DsfrFormCheckboxComponent, DsfrFormCheckboxModule, DsfrFormEmailComponent, DsfrFormEmailModule, DsfrFormFieldsetComponent, DsfrFormFieldsetElementDirective, DsfrFormFieldsetModule, DsfrFormInputComponent, DsfrFormInputModule, DsfrFormPasswordComponent, DsfrFormPasswordModule, DsfrFormRadioComponent, DsfrFormRadioModule, DsfrFormRadioRichComponent, DsfrFormRadioRichModule, DsfrFormSelectComponent, DsfrFormSelectModule, DsfrFormTelComponent, DsfrFormToggleComponent, DsfrFormToggleModule, DsfrFormTogglesGroupComponent, DsfrFormTogglesGroupModule, DsfrFranceConnectComponent, DsfrHeaderComponent, DsfrHeaderModule, DsfrHeadingLevelConst, DsfrHighlightComponent, DsfrHighlightModule, DsfrI18nPipe, DsfrI18nService, DsfrImageFitConst, DsfrImageRatioConst, DsfrImageTypeConst, DsfrInlineConst, DsfrInputModeConst, DsfrInputTypeConst, DsfrLinkComponent, DsfrLinkModule, DsfrLinkTargetConst, DsfrLinksGroupComponent, DsfrLinksGroupModule, DsfrLogLevel, DsfrLoginComponent, DsfrLogoComponent, DsfrLogoModule, DsfrMediaConst, DsfrMegaMenuComponent, DsfrMimeTypeConst, DsfrModalComponent, DsfrModalModule, DsfrNameComponent, DsfrNameModule, DsfrNoticeComponent, DsfrNoticeModule, DsfrNoticeTypeConst, DsfrPageResponseComponent, DsfrPageResponseModule, DsfrPaginationComponent, DsfrPaginationModule, DsfrPanelBackgroundConst, DsfrPanelBorderConst, DsfrPanelDescMarkupConst, DsfrPositionConst, DsfrPreviousPageComponent, DsfrProportionConst, DsfrQuoteComponent, DsfrQuoteModule, DsfrRangeComponent, DsfrResponseComponent, DsfrResponseModule, DsfrRowGroupDirective, DsfrRowGroupHeaderDirective, DsfrSearchBarComponent, DsfrSearchBarModule, DsfrSegmentedControlComponent, DsfrSegmentedControlModule, DsfrSelectAllDirective, DsfrSelectRowDirective, DsfrSeverityConst, DsfrShareComponent, DsfrShareLinkComponent, DsfrShareModule, DsfrShareNameConst, DsfrSidemenuComponent, DsfrSidemenuModule, DsfrSizeConst, DsfrSkipLinksComponent, DsfrSkiplinksModule, DsfrSortColumnDirective, DsfrStepperComponent, DsfrStepperModule, DsfrSummaryComponent, DsfrSummaryModule, DsfrTabComponent, DsfrTableComponent, DsfrTableFooterComponent, DsfrTableLegacyComponent, DsfrTableModule, DsfrTabnavComponent, DsfrTabsComponent, DsfrTabsModule, DsfrTagComponent, DsfrTagModeConst, DsfrTagModule, DsfrTagsGroupComponent, DsfrTagsGroupModule, DsfrTargetLinkConst, DsfrTextSizeConst, DsfrTileComponent, DsfrTileModule, DsfrToolLinkComponent, DsfrToolLinkMenuComponent, DsfrTooltipButtonComponent, DsfrTooltipDirective, DsfrTranscriptionComponent, DsfrTranslateComponent, DsfrTranslateModule, DsfrTreeGridNavigationDirective, DsfrUploadComponent, DsfrUploadModule, DsfrUserMenuComponent, EduCheckboxLightComponent, HeadingComponent, HeadingModule, InputGroupComponent, LABELS_BUNDLE_EN, LABELS_BUNDLE_FR, LangService, Language, LinkDownloadComponent, LocalStorage, LoggerService, PictogramComponent, PictogramModule, StorageEnum, SvgIconComponent, SvgIconModule, ViewMenu, downloadDetail, fileSizeToString, i18nDeepMerge, isOnBrowser, isStringEmptyOrNull, jsonPath2Value, newUniqueId, toArray };
//# sourceMappingURL=edugouvfr-ngx-dsfr.mjs.map