UNPKG

@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).

491 lines 103 kB
import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, EventEmitter, forwardRef, inject, Input, Output, ViewChildren, ViewEncapsulation, } from '@angular/core'; import { toObservable } from '@angular/core/rxjs-interop'; import { BehaviorSubject, combineLatest, map, skip, Subject, takeUntil } from 'rxjs'; import { newUniqueId } from '../../shared'; import { DsfrI18nPipe } from '../../shared/i18n/i18n.pipe'; import { LoggerService } from '../../shared/services/logger.service'; import { BasePaginatedTableComponent } from './component/paginated-table.component'; import { DsfrTableFooterComponent } from './component/table-footer.component'; import { EduTableHeaderComponent } from './component/table-header.component'; import { DsfrCellDirective } from './directive/cell.directive'; import { DsfrColumnDirective } from './directive/column.directive'; import { DsfrSelectAllDirective } from './directive/header-select.directive'; import { DsfrSelectRowDirective } from './directive/row-select.directive'; import { DsfrSortColumnDirective } from './directive/sort-column.directive'; import { DsfrDataTableService } from './service/datatable.service'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; export class DsfrTableComponent extends BasePaginatedTableComponent { constructor() { super(...arguments); /** Rend les th sticky en haut du tableau (à condition que fixedHeight soit renseigné) */ this.thSticky = 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.isAllChecked$ = new BehaviorSubject(false); /** @internal lignes sélectionnées */ this.selectedRows = []; /**@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); this.isSortable = false; // données en entrée modifiées this.dataChanged$ = new BehaviorSubject([]); this.notifyOnDestroy = new Subject(); this.logger = inject(LoggerService); this.cd = inject(ChangeDetectorRef); } ngOnInit() { this.tableId ??= newUniqueId(); this.tableClasses = this.setTableClasses(); if (!this.serverSide) this.totalElements.set(this.data.length); // Initialisation de l'état if (this.initialState) { this.dataTableService.setState(this.initialState); } // Abonnement à la mise à jour des options this.dataTableService.tableOptions$ .pipe(skip(1), takeUntil(this.notifyOnDestroy)) .subscribe((options) => this.applyOptionsToTable(options)); 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é.'); } } /** * Initialisation et mise a jour des checkboxes sélectionnées à chaque rafraichissement des données * Cas ou le SLOT est utilisé pour les row (tableBodyTemplate) */ ngAfterContentInit() { if (this.selectedRows.length > 0) this.selectCellsAfterRender(this.cellCheckboxes, this.selectedRows, true); this.cellCheckboxes.changes.pipe(takeUntil(this.notifyOnDestroy)).subscribe(() => { this.selectCellsAfterRender(this.cellCheckboxes, this.selectedRows, true); }); } /** * Initialisation et mise a jour des checkboxes sélectionnées à chaque rafraichissement des données * Cas ou on utilise le template par DEFAUT pour les rows (tableBodyTemplate non défini) */ ngAfterViewInit() { if (this.selectedRows.length > 0) this.selectCellsAfterRender(this.cellCheckboxesDefault, this.selectedRows, true); this.cellCheckboxesDefault.changes.pipe(takeUntil(this.notifyOnDestroy)).subscribe(() => { this.selectCellsAfterRender(this.cellCheckboxesDefault, this.selectedRows, true); }); } /** * 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, à l'initialisation ou dynamiquement if (changes.tableOptions && !changes.tableOptions.isFirstChange()) { this.dataTableService.setTableOptions(this.tableOptions); } } ngOnDestroy() { this.notifyOnDestroy.next(); this.notifyOnDestroy.complete(); } /** * @internal * Initialisation de la table si sélection * - calcul du nombre d'elements selectionnables * - application de la sélection initiale * - coche de isAllChecked si toutes les lignes sont sélectionnées */ initSelectableTable(rows) { this.selectableRows = this.rowsOptions ? rows.filter((r) => !r.rowOptions || !r.rowOptions.disableSelect) : rows; if (this.initialSelection && this.selectableRows && (this.selectedRows.length === 0 || this.serverSide)) { if (this.dataKey) { this.selectedRows = this.selectableRows?.filter((row) => this.initialSelection.includes(row[this.dataKey])); } } else if (this.selectedRows.length > 0 && !this.serverSide) { // Lignes déja sélectionnées (rafraichissement de la vue) this.selectedRows = this.selectedRows.filter((selectedRow) => this.selectableRows?.find((r) => r[this.dataKey] === selectedRow[this.dataKey])); } this.isAllChecked$.next(this.selectedRows.length >= this.selectableRows?.length); } /** @internal */ setTableClasses() { const classes = ['fr-table']; if (this.tableOptions.bordered) classes.push('fr-table--bordered'); if (this.tableOptions.noScroll) classes.push('fr-table--no-scroll'); if (this.tableOptions.bottomCaption) classes.push('fr-table--caption-bottom'); if (this.tableOptions.captionSrOnly) classes.push('fr-table--no-caption'); if (this.fixedHeight > 0) classes.push('edu-fixed--height'); if (this.fixedHeight > 0 && this.thSticky) classes.push('edu-th--sticky'); // Size class switch (this.tableOptions.cellSize) { case 'SM': classes.push('fr-table--sm'); break; case 'LG': classes.push('fr-table--lg'); break; default: break; } return classes; } /** @internal */ trackById(dataKey, index, item) { return item[dataKey] || index; } /** @internal */ onviewSelect(view) { this.showTableView = view === this.headerViewsOptions[0].value; this.viewSelect.emit(view); } /** @internal */ onsearchChange(search) { this.searchChange.emit(search); } /** @internal */ onsearchSelect(search) { this.searchSelect.emit(search); } /** * Sélection ou déselection d'une ligne * @internal */ selectRow(row) { // recherche de la ligne selon si dataKey est défini ou par l'index sinon const i = this.dataKey ? this.selectedRows.findIndex((r) => r[this.dataKey] === row[this.dataKey]) : this.selectedRows.findIndex((r) => r.index === row.index); if (i !== -1) { this.selectedRows.splice(i, 1); // suppression de la ligne } else { this.selectedRows.push(row); // ajout de la ligne } this.isAllChecked$.next(this.selectedRows.length >= this.selectableRows?.length); // vérification de isAllChecked this.selectionChange.emit({ row: row, selectedRows: this.selectedRows }); } /** * Sélection ou déselection de toutes les lignes * @internal * */ toggleshowSelectAll(allSelected) { this.selectedRows = this.selectedRows.length === this.selectableRows?.length ? [] : [...this.selectableRows]; this.showSelectAllCells(allSelected); this.selectionChange.emit({ row: null, selectedRows: this.selectedRows }); this.showSelectAllChange.emit(allSelected); } /** * 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()); } /** * 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))); 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) { if (state) { // application du tri if (state.sort) { data = this.sortRows(data, state); } if (this.pagination) { return this.paginateData(data, state); } else { this.totalElements.set(this.data.length); } } return data; } /** * Application des nouvelles options d'affichage du tableau * @param options du tableau modifiées */ applyOptionsToTable(options) { this.tableOptions = options; this.tableClasses = this.setTableClasses(); // Indispensable en strategy.onPush dans ce cas précis pour forcer le rechargement de tout le tableau (body compris) this.cd.markForCheck(); } /** * 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'afffichage et index */ mapDataToRows(data) { const rows = data.map((row, i) => ({ ...row, index: i, rowOptions: this.getRowOption(row), })); if (this.tableOptions?.selectable) { this.initSelectableTable(rows); } return rows; } /** Mettre à jour la sélection lors de l'appel a la methode sur le service */ refreshSelection(selectedRows) { const oldSelectedRows = [...this.selectedRows]; const checkboxesComponent = this.tableBodyTemplate ? this.cellCheckboxes : this.cellCheckboxesDefault; this.selectedRows = this.selectableRows?.filter((row) => selectedRows.includes(row[this.dataKey])); // nécessite de rafraichir manuellement les composants de checkboxes if (checkboxesComponent) { this.selectCellsAfterRender(checkboxesComponent, oldSelectedRows, false); this.selectCellsAfterRender(checkboxesComponent, this.selectedRows, true); } } /** * Au clic sur tout sélectionner/tout déselectionner * Selection des composants de checkbox (cellCheckboxesDefault ou cellCheckboxes) pour application du script DSFR * */ showSelectAllCells(allSelected) { const checkBoxes = this.cellCheckboxesDefault?.length > this.cellCheckboxes?.length ? this.cellCheckboxesDefault : this.cellCheckboxes; checkBoxes.forEach((cellCheckbox) => { cellCheckbox.simulateClick(allSelected); }); } /** * Au nouveau rendu du composant tableau (initialisation, refreshData ou pagination) ou setSelected * Simulation du clic sur les checkboxes pour application du script DSFR (style) * @param cellsCheckboxes liste complete des directives de checkboxes * @param selectedRows liste des lignes a mettre a jour * @param select sélectionner ou déselectionner * */ selectCellsAfterRender(cellsCheckboxes, selectedRows, select) { const checkboxesToggleSelect = cellsCheckboxes.filter((cell) => selectedRows.find((s) => { return s[this.dataKey] === cell.row[this.dataKey]; })); checkboxesToggleSelect.forEach((cellCheckbox) => { cellCheckbox.simulateClick(select); }); } /** Fonction de tri par défaut insensible à la casse et aux diacritiques */ sortByDefault(a, b, field, order) { const valueA = a[field] ?? ''; // Remplacer undefined par chaîne vide const valueB = b[field] ?? ''; return valueA === valueB ? 0 : valueA.localeCompare(valueB) * (order === 'ascending' ? 1 : -1); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DsfrTableComponent, isStandalone: true, selector: "dsfr-datatable", inputs: { tableId: "tableId", caption: "caption", fixedHeight: "fixedHeight", thSticky: "thSticky", data: "data", dataKey: "dataKey", tableOptions: "tableOptions", rowsOptions: "rowsOptions", columns: "columns", showFooterResult: "showFooterResult", emptyResultsMessage: "emptyResultsMessage", loading: "loading", showSearch: "showSearch", searchInputPlaceholder: "searchInputPlaceholder", searchButtonTitle: "searchButtonTitle", searchInputValue: "searchInputValue", showHeaderViews: "showHeaderViews", headerViewsOptions: "headerViewsOptions", initialSelection: "initialSelection" }, outputs: { sortChange: "sortChange", viewSelect: "viewSelect", searchChange: "searchChange", searchSelect: "searchSelect", selectionChange: "selectionChange", showSelectAllChange: "showSelectAllChange" }, providers: [DsfrDataTableService], queries: [{ propertyName: "headerActionsTemplate", first: true, predicate: ["headerActionsTemplate"], descendants: true, static: true }, { propertyName: "headerSummaryTemplate", first: true, predicate: ["headerSummaryTemplate"], 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: "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 }, { propertyName: "cellCheckboxes", predicate: DsfrSelectRowDirective, descendants: true }], viewQueries: [{ propertyName: "cellCheckboxesDefault", predicate: DsfrSelectRowDirective, descendants: 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 (searchChange)=\"onsearchChange($event)\"\n (searchSelect)=\"onsearchSelect($event)\"\n [headerViewsOptions]=\"headerViewsOptions\"\n [headerActionsTemplate]=\"headerActionsTemplate\"\n [countSelectedRows]=\"tableOptions.selectable ? selectedRows.length : 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.px]=\"fixedHeight > 0 ? fixedHeight : 'auto'\">\n <!-- prettier-ignore -->\n @if (!caption && isSortable) {\n <span [id]=\"tableId + '-sort-explanation'\" class=\"fr-sr-only\">{{ 'table.sortExplanation' | dsfrI18n }}</span>\n }\n <table\n [class.fr-cell--multiline]=\"tableOptions.allowWrap\"\n [id]=\"tableId\"\n [attr.role]=\"tableOptions.role ? tableOptions.role : null\"\n [attr.aria-labelledby]=\"!caption && tableOptions.ariaLabelledBy ? tableOptions.ariaLabelledBy : null\"\n [attr.aria-describedby]=\"\n !caption\n ? (isSortable ? tableId + '-sort-explanation ' : '') + (tableOptions.ariaDescribedBy ?? '')\n : null\n \">\n @if (caption || headerSummaryTemplate) {\n <caption>\n {{\n caption\n }}\n @if (isSortable) {\n <span class=\"fr-sr-only\">({{ 'table.sortExplanation' | dsfrI18n }})</span>\n }\n @if (headerSummaryTemplate) {\n <div class=\"fr-table__caption__desc\">\n <ng-container *ngTemplateOutlet=\"headerSummaryTemplate\"></ng-container>\n </div>\n }\n </caption>\n }\n <!-- Header ------------------------------------------------------------------------------------------------->\n <thead>\n <!-- Slot -->\n @if (tableHeaderTemplate) {\n <ng-container *ngTemplateOutlet=\"tableHeaderTemplate\"></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 <!-- Default -->\n </thead>\n <!-- Body --------------------------------------------------------------------------------------------------->\n <tbody>\n @for (row of displayedRows$ | async; track trackById.bind(this, this.dataKey)(i, row); let i = $index) {\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 <!-- 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 </tr>\n }\n <!-- Default -->\n }\n @if (totalElements() === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length\">{{ emptyResultsMessage ?? ('table.noResult' | dsfrI18n) }}</td>\n </tr>\n }\n </tbody>\n @if (tableFooterTemplate) {\n <tfoot>\n <ng-container *ngTemplateOutlet=\"tableFooterTemplate\"></ng-container>\n </tfoot>\n }\n </table>\n <!-- Afficher un template de chargement si d\u00E9fini -->\n @if (loading && loadingTemplate) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate\"></ng-container>\n }\n </div>\n </div>\n </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"alternativeViewTemplate\"></ng-template>\n }\n\n <!-- Footer -->\n @if (showFooterResult || pagination || footerActionsTemplate) {\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]=\"footerActionsTemplate\"\n [footerResultsTemplate]=\"footerResultsTemplate\"\n [totalElements]=\"totalElements()\">\n </dsfr-table-footer>\n }\n</div>\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.edu-fixed--height .fr-table__wrapper:after{z-index:2}.fr-table.edu-fixed--height .fr-table__content{overflow:auto}.fr-table.edu-fixed--height .fr-table__content thead:before{z-index:2}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tbody{position:relative}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tr:after{z-index:0}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tr:first-child:after{top:1px}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table thead{position:sticky;top:0;z-index:2}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table thead th.fr-cell--fixed{z-index:2}.fr-table__wrapper:has(.fr-cell--fixed+.fr-cell--fixed) .fr-cell--fixed:first-child{left:1px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgTemplateOutlet), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i0.forwardRef(() => i1.AsyncPipe), name: "async" }, { 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: "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: "component", type: i0.forwardRef(() => EduTableHeaderComponent), selector: "edu-table-header", inputs: ["headerActionsTemplate", "countSelectedRows", "showHeaderViews", "headerViewsOptions", "showSearch", "searchInputPlaceholder", "searchButtonTitle", "searchInputValue", "searchInputInitialValue"], outputs: ["viewSelect", "searchChange", "searchSelect"] }, { kind: "pipe", type: i0.forwardRef(() => DsfrI18nPipe), name: "dsfrI18n" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrTableComponent, decorators: [{ type: Component, args: [{ selector: 'dsfr-datatable', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, providers: [DsfrDataTableService], imports: [ CommonModule, forwardRef(() => DsfrColumnDirective), forwardRef(() => DsfrSortColumnDirective), forwardRef(() => DsfrCellDirective), forwardRef(() => DsfrSelectRowDirective), forwardRef(() => DsfrSelectAllDirective), DsfrTableFooterComponent, 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 (searchChange)=\"onsearchChange($event)\"\n (searchSelect)=\"onsearchSelect($event)\"\n [headerViewsOptions]=\"headerViewsOptions\"\n [headerActionsTemplate]=\"headerActionsTemplate\"\n [countSelectedRows]=\"tableOptions.selectable ? selectedRows.length : 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.px]=\"fixedHeight > 0 ? fixedHeight : 'auto'\">\n <!-- prettier-ignore -->\n @if (!caption && isSortable) {\n <span [id]=\"tableId + '-sort-explanation'\" class=\"fr-sr-only\">{{ 'table.sortExplanation' | dsfrI18n }}</span>\n }\n <table\n [class.fr-cell--multiline]=\"tableOptions.allowWrap\"\n [id]=\"tableId\"\n [attr.role]=\"tableOptions.role ? tableOptions.role : null\"\n [attr.aria-labelledby]=\"!caption && tableOptions.ariaLabelledBy ? tableOptions.ariaLabelledBy : null\"\n [attr.aria-describedby]=\"\n !caption\n ? (isSortable ? tableId + '-sort-explanation ' : '') + (tableOptions.ariaDescribedBy ?? '')\n : null\n \">\n @if (caption || headerSummaryTemplate) {\n <caption>\n {{\n caption\n }}\n @if (isSortable) {\n <span class=\"fr-sr-only\">({{ 'table.sortExplanation' | dsfrI18n }})</span>\n }\n @if (headerSummaryTemplate) {\n <div class=\"fr-table__caption__desc\">\n <ng-container *ngTemplateOutlet=\"headerSummaryTemplate\"></ng-container>\n </div>\n }\n </caption>\n }\n <!-- Header ------------------------------------------------------------------------------------------------->\n <thead>\n <!-- Slot -->\n @if (tableHeaderTemplate) {\n <ng-container *ngTemplateOutlet=\"tableHeaderTemplate\"></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 <!-- Default -->\n </thead>\n <!-- Body --------------------------------------------------------------------------------------------------->\n <tbody>\n @for (row of displayedRows$ | async; track trackById.bind(this, this.dataKey)(i, row); let i = $index) {\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 <!-- 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 </tr>\n }\n <!-- Default -->\n }\n @if (totalElements() === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length\">{{ emptyResultsMessage ?? ('table.noResult' | dsfrI18n) }}</td>\n </tr>\n }\n </tbody>\n @if (tableFooterTemplate) {\n <tfoot>\n <ng-container *ngTemplateOutlet=\"tableFooterTemplate\"></ng-container>\n </tfoot>\n }\n </table>\n <!-- Afficher un template de chargement si d\u00E9fini -->\n @if (loading && loadingTemplate) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate\"></ng-container>\n }\n </div>\n </div>\n </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"alternativeViewTemplate\"></ng-template>\n }\n\n <!-- Footer -->\n @if (showFooterResult || pagination || footerActionsTemplate) {\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]=\"footerActionsTemplate\"\n [footerResultsTemplate]=\"footerResultsTemplate\"\n [totalElements]=\"totalElements()\">\n </dsfr-table-footer>\n }\n</div>\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.edu-fixed--height .fr-table__wrapper:after{z-index:2}.fr-table.edu-fixed--height .fr-table__content{overflow:auto}.fr-table.edu-fixed--height .fr-table__content thead:before{z-index:2}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tbody{position:relative}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tr:after{z-index:0}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table tr:first-child:after{top:1px}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table thead{position:sticky;top:0;z-index:2}.fr-table.edu-fixed--height.edu-th--sticky .fr-table__content table thead th.fr-cell--fixed{z-index:2}.fr-table__wrapper:has(.fr-cell--fixed+.fr-cell--fixed) .fr-cell--fixed:first-child{left:1px}\n"] }] }], propDecorators: { headerActionsTemplate: [{ type: ContentChild, args: ['headerActionsTemplate', { static: true }] }], headerSummaryTemplate: [{ type: ContentChild, args: ['headerSummaryTemplate', { static: true }] }], tableHeaderTemplate: [{ type: ContentChild, args: ['tableHeaderTemplate', { static: true }] }], tableBodyTemplate: [{ type: ContentChild, args: ['tableBodyTemplate', { static: true }] }], tableFooterTemplate: [{ type: ContentChild, args: ['tableFooterTemplate', { 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 }] }], cellCheckboxes: [{ type: ContentChildren, args: [DsfrSelectRowDirective, { descendants: true }] }], cellCheckboxesDefault: [{ type: ViewChildren, args: [DsfrSelectRowDirective] }], tableId: [{ type: Input }], caption: [{ type: Input }], fixedHeight: [{ type: Input }], thSticky: [{ 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 }], searchButtonTitle: [{ type: Input }], searchInputValue: [{ type: Input }], showHeaderViews: [{ type: Input }], headerViewsOptions: [{ type: Input }], initialSelection: [{ type: Input }], sortChange: [{ type: Output }], viewSelect: [{ type: Output }], searchChange: [{ type: Output }], searchSelect: [{ type: Output }], selectionChange: [{ type: Output }], showSelectAllChange: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFibGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWRzZnItY29tcG9uZW50cy9zcmMvbGliL2NvbXBvbmVudHMvdGFibGUvdGFibGUuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWRzZnItY29tcG9uZW50cy9zcmMvbGliL2NvbXBvbmVudHMvdGFibGUvdGFibGUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFHTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxZQUFZLEVBQ1osZUFBZSxFQUNmLFlBQVksRUFDWixVQUFVLEVBQ1YsTUFBTSxFQUNOLEtBQUssRUFJTCxNQUFNLEVBSU4sWUFBWSxFQUNaLGlCQUFpQixHQUNsQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDMUQsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsR0FBRyxFQUFjLElBQUksRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2pHLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDM0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzNELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUVyRSxPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNwRixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUM5RSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUM3RSxPQUFPLEVBQUUsaUJBQWlC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