UNPKG

@edugouvfr/ngx-dsfr-ext

Version:

NgxDsfrExt est une extension au package @edugouvfr/ngx-dsfr (portage Angular des éléments DSFR)

1 lines 82.7 kB
{"version":3,"file":"edugouvfr-ngx-dsfr-ext-grid.mjs","sources":["../../../projects/ngx-dsfr-ext/grid/src/lib/components/button-cell-grid.component.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/components/select-column-filter.component.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/interfaces/remote-ag-grid-api.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/directives/remote-ag-grid.directive.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/i18n/grid.fr.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/i18n/messages.en.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/i18n/messages.fr.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/grid.component.ts","../../../projects/ngx-dsfr-ext/grid/src/lib/grid.component.html","../../../projects/ngx-dsfr-ext/grid/src/lib/grid.module.ts","../../../projects/ngx-dsfr-ext/grid/src/public-api.ts","../../../projects/ngx-dsfr-ext/grid/src/edugouvfr-ngx-dsfr-ext-grid.ts"],"sourcesContent":["import { Component, ViewEncapsulation } from '@angular/core';\nimport { DsfrButtonModule, DsfrPosition, DsfrSize } from '@edugouvfr/ngx-dsfr';\nimport { ICellRendererAngularComp } from 'ag-grid-angular';\n\n@Component({\n selector: 'dsfr-button-cell-grid',\n styles: ['.ag-cell-value .fr-btn { margin: 0 0.25rem; vertical-align: sub }'],\n template: `<dsfr-button\n [id]=\"id\"\n [icon]=\"icon\"\n [label]=\"label\"\n [iconPosition]=\"iconPosition\"\n [ariaLabel]=\"ariaLabel\"\n [customClass]=\"customClass\"\n [tooltipMessage]=\"tooltipMessage\"\n [ariaControls]=\"ariaControls\"\n [buttonSize]=\"size ?? 'MD'\"\n (click)=\"onClick($event)\"\n variant=\"secondary\"></dsfr-button>`,\n encapsulation: ViewEncapsulation.None,\n imports: [DsfrButtonModule],\n})\nexport class DsfrButtonCellGridComponent implements ICellRendererAngularComp {\n id?: string;\n params: any;\n label!: string;\n size?: DsfrSize;\n icon?: string;\n iconPosition?: DsfrPosition;\n ariaLabel?: string;\n customClass?: string;\n tooltipMessage?: string;\n ariaControls?: string;\n\n agInit(params: any): void {\n this.id = params.id;\n this.params = params;\n this.label = params.label;\n this.size = params.size;\n this.icon = params.icon;\n this.tooltipMessage = params.tooltipMessage;\n this.ariaLabel = params.ariaLabel;\n this.customClass = params.customClass;\n this.iconPosition = params.iconPosition;\n this.ariaControls = params.ariaControls;\n }\n\n refresh(params?: any): boolean {\n return true;\n }\n\n /**\n * Permet d'appeler la méthode définie sur le bouton en passant les données de la ligne\n * @param $event Event lié au clic sur le bouton\n */\n onClick($event: any) {\n if (this.params.onClick instanceof Function) {\n const params = {\n event: $event,\n rowData: this.params.node.data,\n };\n this.params.onClick(params);\n }\n }\n}\n","import { Component } from '@angular/core';\nimport { FormsModule, ReactiveFormsModule } from '@angular/forms';\nimport { DsfrFormSelectModule, DsfrOption } from '@edugouvfr/ngx-dsfr';\nimport { IFloatingFilterAngularComp } from 'ag-grid-angular';\nimport { IFloatingFilterParams, ISimpleFilter } from 'ag-grid-community';\n\nexport interface DsfrFilterSelectParams {\n label: string;\n id: string;\n options: DsfrOption[];\n compareFn?: any;\n placeholder?: string;\n}\n\n@Component({\n imports: [FormsModule, ReactiveFormsModule, DsfrFormSelectModule],\n template: `<dsfr-form-select\n [inputId]=\"params.id\"\n [(ngModel)]=\"currentValue\"\n [placeholder]=\"params.placeholder ?? ''\"\n [options]=\"options\"\n (selectChange)=\"onSelectChanged()\">\n <ng-container label\n ><span class=\"fr-sr-only\"> {{ label }}</span>\n </ng-container>\n </dsfr-form-select>`,\n})\nexport class DsfrSelectFilterComponent<T> implements IFloatingFilterAngularComp {\n params!: IFloatingFilterParams<ISimpleFilter> & DsfrFilterSelectParams;\n currentValue?: T;\n style: any;\n options: DsfrOption[] = [];\n label?: string;\n compareFn: any;\n\n agInit(params: IFloatingFilterParams<ISimpleFilter> & DsfrFilterSelectParams): void {\n this.params = params;\n this.label = params.label;\n this.options = params.options;\n }\n\n onParentModelChanged(parentModel: any) {\n // When the filter is empty we will receive a null value here\n if (!parentModel) {\n this.currentValue = undefined;\n } else {\n this.currentValue = parentModel.filter;\n }\n }\n\n onSelectChanged() {\n if (!this.currentValue) {\n // Remove the filter\n this.params.parentFilterInstance((instance: any) => {\n instance.onFloatingFilterChanged(null, null);\n });\n return;\n }\n\n //this.currentValue = Number(this.currentValue);\n this.params.parentFilterInstance((instance: any) => {\n instance.onFloatingFilterChanged('equals', this.currentValue);\n });\n }\n}\n","import { GridApi, IGetRowsParams, SortModelItem } from 'ag-grid-community';\nimport { Observable } from 'rxjs';\n\nexport enum DsfrGridModeConst {\n CLIENT = 'client',\n SERVER = 'server',\n}\n\nexport type DsfrGridMode = `${DsfrGridModeConst}`;\n\n/** Données retournées par la grid */\nexport interface DsfrRemoteGridResult {\n data: any[];\n totalRecords: number;\n}\n\n/** Mapper le modèle de l'API par rapport aux query params utilisés par la grid */\nexport interface DsfrGridParams {\n /** nombre de résultat à retourner (taille de la page) */\n limit: number;\n /** Index du premier résultat à afficher */\n offset: number;\n /** Tri */\n sort?: SortModelItem[];\n /** Filtres */\n filters?: any[];\n}\n\n/** Mapper les résultats de la grid par rapport au résultat retournés par l'API */\nexport interface DsfrGridResultModel {\n /** données */\n data: string;\n /** total de résultats */\n totalRecords: string;\n /** fonction de mapping optionnelle des données */\n mappingFunction?: (e: any) => void;\n}\n\n/**\n * Datasource pour implémentation de la pagination coté backend de Ag-grid community\n * cf: https://medium.com/weekly-webtips/tech-building-reusable-server-side-pagination-for-ag-grid-in-angular-c8127e2da346\n * et https://www.ag-grid.com/javascript-data-grid/infinite-scrolling/\n * */\nexport abstract class EduRemoteAgGridApi {\n getDataError?: (err: any) => void;\n gridApi?: GridApi | undefined;\n prepareUpdateData!: (params: IGetRowsParams) => void;\n result$: Observable<DsfrRemoteGridResult> | undefined;\n}\n","import { Directive, EventEmitter, HostListener, NgZone, inject } from '@angular/core';\nimport { AgGridAngular } from 'ag-grid-angular';\nimport { IDatasource, IGetRowsParams } from 'ag-grid-community';\nimport { EMPTY } from 'rxjs';\nimport { DsfrRemoteGridResult, EduRemoteAgGridApi } from '../interfaces/remote-ag-grid-api';\n\n/**\n * Directive pour l'implémentation de la pagination coté backend de Ag-grid community\n * cf: https://medium.com/weekly-webtips/tech-building-reusable-server-side-pagination-for-ag-grid-in-angular-c8127e2da346\n */\n@Directive({\n selector: '[remoteAgGridBinding]',\n standalone: true,\n})\nexport class EduRemoteAgGridDirective {\n remoteGridReady = new EventEmitter();\n\n // DOC: https://www.ag-grid.com/javascript-data-grid/infinite-scrolling/#datasource-interface\n dataSource: IDatasource = {\n getRows: (params: IGetRowsParams) => {\n this.zone.run(() => {\n // force event to run inside angular zone for detection (#fix 31.0.2)\n this.host.prepareUpdateData(params);\n });\n this.host.result$?.subscribe({\n next: (e: DsfrRemoteGridResult) => {\n params.successCallback(e.data, e.totalRecords);\n },\n error: (error) => {\n this.handleError(error);\n },\n });\n },\n };\n\n private host = inject(EduRemoteAgGridApi);\n private zone = inject(NgZone);\n\n // https://www.ag-grid.com/angular-data-grid/grid-events/\n /**\n * Initialisation grid\n * @param grid\n */\n @HostListener('gridReady', ['$event' as any])\n gridReady(grid: AgGridAngular) {\n grid.api.setGridOption('datasource', this.dataSource);\n this.remoteGridReady.emit(grid);\n }\n\n handleError(err: any) {\n this.host.getDataError && this.host.getDataError(err);\n return EMPTY;\n }\n}\n","export const AG_GRID_LOCALE_FR = {\n // Set Filter\n selectAll: '(Tout sélectionner)',\n selectAllSearchResults: '(Sélectionner tous les résultats)',\n addCurrentSelectionToFilter: 'Ajouter la sélection courante au filtre',\n searchOoo: 'Rechercher...',\n blanks: '(Blancs)',\n noMatches: 'Aucun résultat',\n\n // Number Filter & Text Filter\n filterOoo: 'Filtre...',\n equals: 'Egal',\n notEqual: 'Différent de',\n blank: 'Vide',\n notBlank: 'Non vide',\n empty: 'Choisir',\n\n // Number Filter\n lessThan: 'Inférieur à',\n greaterThan: 'Supérieur à',\n lessThanOrEqual: 'Inférieur ou égal à',\n greaterThanOrEqual: 'Supérieur ou égal à',\n inRange: 'Fourchette',\n inRangeStart: 'entre',\n inRangeEnd: 'et',\n\n // Text Filter\n contains: 'Contient',\n notContains: 'Ne contient pas',\n startsWith: 'Commence par',\n endsWith: 'Finit par',\n\n // Date Filter\n dateFormatOoo: 'jj/mm/aaaa',\n before: 'Avant',\n after: 'Après',\n\n // Filter Conditions\n andCondition: 'ET',\n orCondition: 'OU',\n\n // Filter Buttons\n applyFilter: 'Appliquer',\n resetFilter: 'Réinitialiser',\n clearFilter: 'Vider',\n cancelFilter: 'Annuler',\n\n // Filter Titles\n textFilter: 'Filtre textuel',\n numberFilter: 'Filtre numérique',\n dateFilter: 'Filtre de date',\n setFilter: 'Appliquer le filtre',\n\n // Side Bar\n columns: 'Colonnes',\n filters: 'Filtres',\n\n // columns tool panel\n pivotMode: 'Mode Pivot',\n groups: 'Groupes de lignes',\n rowGroupColumnsEmptyMessage: 'Faites glisser ici pour définir les groupes de lignes',\n values: 'Valeurs',\n valueColumnsEmptyMessage: 'Faites glisser ici pour aggréger',\n pivots: 'Labels de colonnes',\n pivotColumnsEmptyMessage: 'Faites glisser ici pour définir les labels de colonnes',\n\n // Header of the Default Group Column\n group: 'Groupe',\n\n // Row Drag\n rowDragRow: 'ligne',\n rowDragRows: 'lignes',\n\n // Other\n loadingOoo: 'Chargement...',\n loadingError: 'ERREUR',\n noRowsToShow: 'Aucun résultat',\n enabled: 'Activé',\n\n // Group Column Filter\n groupFilterSelect: 'Sélectionner un champ:',\n\n // Menu\n pinColumn: 'Epingler la colonne',\n pinLeft: 'Epingler à gauche',\n pinRight: 'Epingler à droite',\n noPin: \"Pas d'épingle\",\n valueAggregation: \"Valeur d'aggrégation\",\n autosizeThiscolumn: 'Adapter la taille de cette colonne',\n autosizeAllColumns: 'Adapter la taille de toutes les colonnes',\n groupBy: 'Grouper par',\n ungroupBy: 'Dégrouper par',\n addToValues: 'Ajouter ${variable} aux valeurs',\n removeFromValues: 'Retirer ${variable} des valeurs',\n addToLabels: 'Ajouter ${variable} aux labels',\n removeFromLabels: 'Retirer ${variable} des labels',\n resetColumns: 'Réinitialiser les colonnes',\n expandAll: 'Développer tout',\n collapseAll: 'Fermer tout',\n copy: 'Copier',\n ctrlC: 'Ctrl + C',\n copyWithHeaders: 'Copier avec les en-têtes',\n copyWithGroupHeaders: 'Copier avec les en-têtes de groupes',\n paste: 'Coller',\n ctrlV: 'Ctrl + V',\n export: 'Exporter',\n csvExport: 'Export en CSV ',\n excelExport: 'Export au format Excel',\n noAggregation: 'Aucune',\n ungroupAll: 'Tout dégrouper',\n ctrlX: 'Ctrl+X',\n cut: 'Couper',\n columnFilter: 'Filtre de colonne',\n columnChooser: 'Choisir la colonne',\n sortAscending: 'Tri ascendant',\n sortDescending: 'Tri descendant',\n sortUnSort: 'Effacer le tri',\n\n // Enterprise Menu Aggregation and Status Bar\n sum: 'Somme',\n min: 'Min',\n max: 'Max',\n none: 'Aucun',\n count: 'Compte',\n avg: 'Moyenne',\n filteredRows: 'Filtré',\n selectedRows: 'Selectionné',\n totalRows: 'Nombre total de lignes',\n totalAndFilteredRows: 'Lignes',\n more: 'Plus',\n to: 'à',\n of: 'sur',\n page: 'Page',\n nextPage: 'Page suivante',\n lastPage: 'Dernière Page',\n firstPage: 'Premère Page',\n previousPage: 'Page précédente',\n pageSize: 'Nombre de résultats affichés par page',\n\n // Pivoting\n pivotColumnGroupTotals: 'Total',\n\n // ARIA\n ariaChecked: 'sélectionné',\n ariaColumn: 'colonne',\n ariaColumnGroup: 'Groupe de colonnes',\n ariaColumnFiltered: 'Colonne Filtrée',\n ariaColumnList: 'Liste de colonnes',\n ariaColumnSelectAll: 'Sélectionner toutes les colonnes',\n ariaDateFilterInput: 'Champ du filtre de date',\n ariaDefaultListName: 'Liste',\n ariaFilterColumnsInput: 'Champ du filtre des colonnes',\n ariaFilterFromValue: 'Filtrer à partir de la valeur',\n ariaFilterInput: '',\n ariaFilterList: 'Filtre de liste',\n ariaFilterToValue: 'Valeur du filtre \"de\"',\n ariaFilterValue: 'Valeur du filtre',\n ariaFilteringOperator: 'Opérateur de filtre',\n ariaHidden: 'masqué',\n ariaIndeterminate: 'indéterminé',\n ariaInputEditor: 'Editeur',\n ariaMenuColumn: 'Appuyez sur CTRL ENTREE pour ouvrir le menu de colonne.',\n ariaRowDeselect: 'Appuyez sur ESPACE pour déselectionner cette ligne',\n ariaRowSelectAll: 'Appuyez sur ESPACE pour activer la sélection de toutes les lignes',\n ariaRowToggleSelection: 'Appuyez sur ESPACE pour activer la sélection de ligne',\n ariaRowSelect: 'Appuyez sur ESPACE pour sélectionner cette ligne',\n ariaSearch: 'Rechercher',\n ariaSortableColumn: 'Appuyez sur ENTREE pour trier',\n ariaToggleVisibility: 'Appuyez sur ESPACE pour activer la visibilité',\n ariaUnchecked: 'désélectionné',\n ariaVisible: 'visible',\n ariaSearchFilterValues: 'Chercher les valeurs de filtres',\n ariaFilterMenuOpen: 'Ouvrir le menu de filtre',\n ariaFilterColumn: 'Appuyer sur CTRL ENTER pour ouvrir le filtre',\n ariaRowSelectionDisabled: 'La sélection est désactivée pour cette ligne',\n ariaToggleCellValue: 'Appuyez sur ESPACE pour changer la valeur de la cellule',\n ariaPageSizeSelectorLabel: 'Nombre de résultats affichés par page',\n ariaSkeletonCellLoadingFailed: 'Le chargement de la ligne a échoué',\n ariaSkeletonCellLoading: 'Chargement des données en cours',\n\n // ARIA Labels for Drop Zones\n\n ariaRowGroupDropZonePanelLabel: 'Groupes de lignes',\n ariaValuesDropZonePanelLabel: 'Valeurs',\n ariaPivotDropZonePanelLabel: 'Labels des colonnes',\n ariaDropZoneColumnComponentDescription: 'Appuyez sur SUPPR pour supprimer',\n ariaDropZoneColumnValueItemDescription: \"Appuyez sur ENTREE pour changer le type d'aggregation\",\n ariaDropZoneColumnGroupItemDescription: 'Appuyez sur ENTREE pour trier',\n // used for aggregate drop zone, format: {aggregation}{ariaDropZoneColumnComponentAggFuncSeperator}{column name}\n ariaDropZoneColumnComponentAggFuncSeperator: ' de ',\n ariaDropZoneColumnComponentSortAscending: 'croissant',\n ariaDropZoneColumnComponentSortDescending: 'décroissant',\n\n // ARIA Labels for Dialogs\n ariaLabelColumnMenu: 'Menu des colonnes',\n ariaLabelCellEditor: 'Editeur de cellule',\n ariaLabelDialog: 'Modale',\n ariaLabelSelectField: 'Sélectionner le champ',\n ariaLabelTooltip: 'Info-bulle',\n ariaLabelContextMenu: 'Menu contextuel',\n ariaLabelSubMenu: 'Sous-Menu',\n ariaLabelAggregationFunction: \"Fonction d'aggrégation\",\n ariaLabelColumnFilter: 'Filtre de colonne',\n // Number Format (Status Bar, Pagination Panel)\n thousandSeparator: ' ',\n decimalSeparator: ',',\n\n // Data types\n true: 'Vrai',\n false: 'Faux',\n invalidDate: 'Date invalide',\n invalidNumber: 'Nombre invalide',\n january: 'Janvier',\n february: 'Février',\n march: 'Mars',\n april: 'Avril',\n may: 'Mai',\n june: 'Juin',\n july: 'Juillet',\n august: 'Août',\n september: 'Septembre',\n october: 'Octobre',\n november: 'Novembre',\n december: 'Décembre',\n\n // Time formats\n timeFormatSlashesDDMMYYYY: 'JJ/MM/AAAA',\n timeFormatSlashesMMDDYYYY: 'MM/JJ/AAAA',\n timeFormatSlashesDDMMYY: 'JJ/MM/AA',\n timeFormatSlashesMMDDYY: 'MM/AA/AA',\n timeFormatDotsDDMYY: 'JJ.M.AA',\n timeFormatDotsMDDYY: 'M.JJ.AA',\n timeFormatDashesYYYYMMDD: 'AAAA-MM-JJ',\n timeFormatSpacesDDMMMMYYYY: 'JJ MMMM AAAA',\n timeFormatHHMMSS: 'HH:MM:SS',\n timeFormatHHMMSSAmPm: 'HH:MM:SS AM/PM',\n};\n","// Ordre alphabétique requis\nexport const MESSAGES_EN = {\n grid: {\n noData: 'No matching data',\n loading: 'Data loading...',\n },\n};\n","// Ordre alphabétique requis\nexport const MESSAGES_FR = {\n grid: {\n noData: 'Aucune donnée correspondante',\n loading: 'Chargement des données...',\n },\n};\n","import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ContentChild,\n ElementRef,\n EventEmitter,\n Input,\n OnInit,\n Output,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n booleanAttribute,\n forwardRef,\n inject,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n DsfrButtonModule,\n DsfrFormSelectModule,\n DsfrI18nService,\n DsfrOption,\n DsfrPaginationModule,\n DsfrTableFooterComponent,\n DsfrTableState,\n Language,\n newUniqueId,\n} from '@edugouvfr/ngx-dsfr';\nimport { AgGridAngular } from 'ag-grid-angular';\nimport {\n AllCommunityModule,\n CellClickedEvent,\n CellEditingStoppedEvent,\n ColDef,\n FilterChangedEvent,\n GetRowIdParams,\n GridApi,\n GridOptions,\n GridPreDestroyedEvent,\n GridReadyEvent,\n GridState,\n IGetRowsParams,\n IRowNode,\n ModuleRegistry,\n PaginationChangedEvent,\n RowSelectedEvent,\n SelectionChangedEvent,\n SortChangedEvent,\n SortModelItem,\n SuppressHeaderKeyboardEventParams,\n SuppressKeyboardEventParams,\n} from 'ag-grid-community';\nimport { Observable, ReplaySubject, Subject } from 'rxjs';\nimport { EduRemoteAgGridDirective } from './directives/remote-ag-grid.directive';\nimport { AG_GRID_LOCALE_FR } from './i18n/grid.fr';\nimport { MESSAGES_EN } from './i18n/messages.en';\nimport { MESSAGES_FR } from './i18n/messages.fr';\nimport {\n DsfrGridMode,\n DsfrGridModeConst,\n DsfrGridParams,\n DsfrRemoteGridResult,\n EduRemoteAgGridApi,\n} from './interfaces/remote-ag-grid-api';\n\n/** Représentation d'un évènement selectionChange ou rowSelect */\nexport interface DsfrGridSelectionEvent {\n /** type d'évènement ag-grid */\n event: RowSelectedEvent<any> | SelectionChangedEvent<any>;\n /** liste des noeuds ag-grid IRowNode */\n selectedNodes: IRowNode<any>[];\n}\n\nModuleRegistry.registerModules([AllCommunityModule]);\n\n@Component({\n selector: 'dsfr-ext-ag-grid, dsfrx-ag-grid',\n templateUrl: './grid.component.html',\n providers: [{ provide: EduRemoteAgGridApi, useExisting: forwardRef(() => DsfrGridComponent) }],\n styleUrls: ['./styles/grid.component.scss'],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n AgGridAngular,\n CommonModule,\n FormsModule,\n DsfrButtonModule,\n DsfrPaginationModule,\n DsfrFormSelectModule,\n DsfrTableFooterComponent,\n EduRemoteAgGridDirective,\n ],\n})\nexport class DsfrGridComponent implements EduRemoteAgGridApi, OnInit {\n @ViewChild('grid', { read: ElementRef }) grid?: ElementRef;\n\n /** Template personnalisé pour afficher le total de lignes */\n @ContentChild('footerResultsTemplate', { static: true }) footerResultsTemplate?: TemplateRef<void>;\n\n /** Template pour les groupes de boutons d'actions du footer */\n @ContentChild('footerActionsTemplate', { static: true }) footerActionsTemplate?: TemplateRef<void>;\n\n /** Template pour les groupes de boutons d'actions du header. (dsfr-button-group) */\n @ContentChild('headerActionsTemplate', { static: true }) headerActionsTemplate?: TemplateRef<void>;\n\n /** Id du composant, généré par défaut */\n @Input() gridId: string;\n\n /* Liste des colonnes au format agGrid ColDef[] */\n @Input({ required: true }) columnDefs!: ColDef[];\n\n /* Titre du tableau */\n @Input() caption?: string;\n\n /** Nombre d'éléments affichés par page */\n @Input() pageSize = 20;\n\n /** Traductions des éléments de agGrid. Français par défaut, anglais si valeur nulle. */\n @Input() localeText: any;\n\n /** Mode de la grid: données côté client ou serveur (défaut client) */\n @Input() mode: DsfrGridMode = DsfrGridModeConst.CLIENT;\n\n /** Données locale dans le cas de clientSide */\n @Input() rowData?: any[];\n\n /** Options des colonnes par défaut (cf. https://www.ag-grid.com/angular-data-grid/column-properties/ ) */\n @Input() defaultColDef: ColDef = {\n suppressHeaderMenuButton: true,\n sortable: true,\n filter: false,\n resizable: false,\n filterParams: {\n maxNumConditions: 1,\n },\n };\n\n /** Etat initial du tableau (ex. page courante, filtres..) */\n @Input() initialState?: GridState;\n\n /** Classes personnalisées pour la grid. Prédéfinies : \"fr-table-bordered\", \"fr-table-no-strip\". */\n @Input() customClass?: string;\n\n /** Afficher un texte par-dessus la grid pendant le chargement */\n @Input() showLoadingOverlay: boolean = true;\n\n /** Redéfinir le template de chargement */\n //FIXME: nommage non consistant, à déprécier au profit de `overlayLoadingTemplate`\n @Input() loadingOverlayTemplate?: string;\n\n /** Redéfinir le template aucun résultat. /!\\ L'id de ce container doit être passé à idNoRowsTemplate */\n @Input() overlayNoRowsTemplate?: string;\n\n /** Id du template overlayNoRowsTemplate contenant le texte utilisé pour le aria-describedby */\n @Input() idNoRowsTemplate = 'no-data-container';\n\n /** Hauteur fixe (scroll vertical si nécessaire) */\n @Input() fixedHeight?: string;\n\n /** Calcul dynamique de la hauteur des lignes pour afficher le contenu des cellules sur plusieurs lignes (uniquement utilisé en serverSide).\n * Déconseillé d'utiliser si le nombre de lignes par pages est élevé, peut causer des problèmes de performance d'affichage.\n */\n @Input({ transform: booleanAttribute }) autoRowsHeight: boolean = false;\n\n /** Surcharge d'options supplémentaires pour agGrid (optionnel) */\n @Input() gridOptions?: GridOptions<any>;\n\n /** Afficher le bouton d'action exporter au format CSV (uniqument si headerActionsTemplate non défini) */\n @Input({ transform: booleanAttribute }) showExportCsv = false;\n\n /** Champ qui correspond à l'identifiant de la donnée (optionnel) */\n @Input() rowId?: string;\n\n /** Afficher la pagination */\n @Input({ transform: booleanAttribute }) showPagination: boolean = true;\n\n /** Afficher le total de lignes */\n @Input({ transform: booleanAttribute }) showFooterResult: boolean = true;\n\n /** Mise à jour et application du tri des colonnes */\n @Output() sortChanged: EventEmitter<SortChangedEvent<any>> = new EventEmitter();\n\n /** Callback apres l'initialisation de la grid, retourne l'accès à l'API de agGrid */\n @Output() gridReady: EventEmitter<GridApi<any>> = new EventEmitter();\n\n /** Callback avant la destruction de la grid (destruction du composant), permet de récuperer le state */\n @Output() gridPreDestroyed: EventEmitter<GridPreDestroyedEvent<any>> = new EventEmitter();\n\n /** Mise à jour et application des filtres */\n @Output() filterChanged: EventEmitter<FilterChangedEvent<any>> = new EventEmitter();\n\n /** Sélection d'une cellule du tableau */\n @Output() cellSelect: EventEmitter<CellClickedEvent<any>> = new EventEmitter();\n\n /** Sélection ou déselection d'une ligne du tableau. Emission de l'évènement (RowSelectedEvent) et des noeuds sélectionnés (IRowNode[]) */\n @Output() rowSelect: EventEmitter<DsfrGridSelectionEvent> = new EventEmitter();\n\n /** La sélection des lignes a changé. Emission de l'évènement (SelectionChangedEvent) et des noeuds sélectionnés (IRowNode[]) */\n @Output() selectionChange: EventEmitter<DsfrGridSelectionEvent> = new EventEmitter();\n\n /** Indique une mise à jour nécessaire des données suite à un changement d'affichage (pagination, tri ou filtres). Si données côté serveur. */\n @Output() dataChange: EventEmitter<DsfrGridParams> = new EventEmitter();\n\n /** Callback après changement de page */\n @Output() paginationChange: EventEmitter<PaginationChangedEvent> = new EventEmitter();\n\n /** Callback après chaque changement des lignes affichées (modelUpdated) */\n @Output() dataRender: EventEmitter<void> = new EventEmitter();\n\n /** Callback après appel à l'api en cas d'erreur de celle-ci */\n @Output() dataFail: EventEmitter<any> = new EventEmitter();\n\n /** Evenement de fin d'édition d'une cellule */\n @Output() cellEditingStopped: EventEmitter<CellEditingStoppedEvent<any>> = new EventEmitter();\n\n /** @internal Pour acceder a Grid API */\n @ViewChild(AgGridAngular) agGrid!: AgGridAngular;\n\n /** Pagination */\n\n /** Choix de taille de page (par défaut 10, 20, 50, 100). Pas d'affichage du select si non défini */\n @Input()\n pageSizes: DsfrOption[] = [\n {\n value: 10,\n label: '10 lignes par page',\n },\n {\n value: 20,\n label: '20 lignes par page',\n },\n {\n value: 50,\n label: '50 lignes par page',\n },\n {\n value: 100,\n label: '100 lignes par page',\n },\n ];\n\n /** @internal Total résultats*/\n totalElements = 0;\n /** @internal Page courante*/\n currentPage: number | undefined = undefined;\n /** @internal Total pages */\n totalPage = 0;\n /**@internal Id généré du titre du tableau */\n captionId?: string;\n /** @internal Index de la première ligne affichée */\n startRow: number = 1;\n /** @internal Index de la première ligne affichée */\n endRow: number = 1;\n /** @internal */\n isLoadingServerSide = false;\n\n protected initialStatePagination: DsfrTableState | undefined; // Pour compatibilité avec le composant footer de la lib ngx-dsfr\n protected loading = false;\n protected selectable = false;\n\n /** @internal */\n private _resultSubject$: Subject<DsfrRemoteGridResult> = new ReplaySubject<DsfrRemoteGridResult>(1);\n\n /** Nombre d'éléments visuellement affichés en mode serveur */\n private _nbElementsDisplayed: number = 0;\n private i18n = inject(DsfrI18nService);\n\n constructor() {\n this.gridId ??= newUniqueId();\n this.i18n.extendsLabelsBundle(Language.EN, MESSAGES_EN);\n this.i18n.extendsLabelsBundle(Language.FR, MESSAGES_FR);\n this.i18n.completeLangChange$.subscribe((code: string) => this.onLangChange(code));\n }\n\n /** @internal */\n public get result$(): Observable<DsfrRemoteGridResult> {\n return this._resultSubject$.asObservable();\n }\n\n /** Données renvoyées par l'API */\n @Input() set result(value: DsfrRemoteGridResult) {\n if (value && this.agGrid?.api) {\n this.totalElements = value.totalRecords;\n this.totalPage = this.calculateTotalPage();\n this._nbElementsDisplayed = value.data.length;\n\n this._resultSubject$.next(value);\n if (this.autoRowsHeight) {\n this.setHeightOnRows();\n }\n\n this.isLoadingServerSide = false;\n\n this.forceReloadOnNoData();\n this.loading = false;\n\n if (!this.gridOptions?.autoSizeStrategy) {\n this.agGrid.api.sizeColumnsToFit();\n }\n\n if (this.totalElements === 0) {\n this.agGrid.api.showNoRowsOverlay();\n }\n }\n }\n\n public ngOnInit(): void {\n this.captionId = 'caption-' + newUniqueId();\n this.defaultColDef = {\n wrapText: this.autoRowsHeight ? true : false,\n filterParams: { maxNumConditions: 1, debounceMs: 1000 },\n suppressFloatingFilterButton: true,\n suppressHeaderMenuButton: true,\n suppressHeaderFilterButton: true,\n resizable: false,\n unSortIcon: true,\n ...this.defaultColDef,\n };\n\n // gestion de la navigation au clavier par défaut\n if (this.defaultColDef && !this.defaultColDef?.suppressKeyboardEvent) this.suppressCellKeyboardEvent();\n if (this.defaultColDef && !this.defaultColDef?.suppressHeaderKeyboardEvent) this.suppressHeaderKeyboardEvent();\n this.gridOptions = {\n rowModelType: 'infinite',\n pagination: this.showPagination,\n suppressContextMenu: true,\n paginationPageSize: this.pageSize,\n maxBlocksInCache: 1,\n domLayout: this.fixedHeight ? 'normal' : 'autoHeight',\n enableCellTextSelection: true,\n suppressPaginationPanel: true,\n ensureDomOrder: true,\n rowBuffer: 20,\n icons: {\n filter: '<i class=\"fr-icon-filter-line\"></i>',\n sortAscending: '<i class=\"fr-icon-arrow-up-line\"></i>',\n sortDescending: '<i class=\"fr-icon-arrow-down-line\"></i>',\n sortUnSort: '<i class=\"fr-icon-arrow-up-down-line\"></i> ',\n },\n onGridPreDestroyed: ($event) => this.onGridPreDestroyed($event),\n ...this.gridOptions,\n getRowId: this.rowId ? (rowParams) => this.getRowNodeId(rowParams) : undefined,\n };\n\n if (!this.initialState && this.gridOptions.initialState) {\n this.initialState = this.gridOptions.initialState;\n }\n\n this.selectable = !!this.gridOptions.rowSelection;\n\n this.initPagination();\n\n if (this.mode === DsfrGridModeConst.CLIENT) {\n this.gridOptions.rowModelType = 'clientSide';\n } else {\n this.gridOptions.cacheBlockSize = this.pageSize;\n this.isLoadingServerSide = true;\n }\n }\n\n /**\n * Au changement de paramètres des lignes affichées (pagination, filtre etc.), préparation des query params et envoi de l'evenement updateData\n * @param params IGetRowsParams paramètres modifiés\n * @internal\n */\n public prepareUpdateData(params: IGetRowsParams): void {\n if (this.showLoadingOverlay) this.loading = true;\n\n // Paramètres de la pagination\n if (!this.currentPage && this.initialState) {\n // si c'est le premier chargement du tableau et initialState renseigné, on remet les paramètres\n this.restoreInitialStateServer(params);\n }\n\n this.currentPage = Math.floor(params.startRow / this.pageSize) + 1;\n const httpParams: DsfrGridParams = { limit: this.pageSize, offset: params.startRow };\n\n // Ajout du tri\n if (params.sortModel && params.sortModel[0]) {\n this.addSort(params.sortModel, httpParams);\n }\n\n //Ajout des filtres\n if (params.filterModel) {\n this.addFilters(params.filterModel, httpParams);\n }\n\n this.dataChange.emit(httpParams);\n }\n\n /**\n * Erreur au retour de l'appel à l'api\n * @internal\n */\n public getDataError(err: any) {\n this.loading = false;\n if (this.totalElements === 0) {\n this.agGrid.api.showNoRowsOverlay();\n }\n this.dataFail.emit(err);\n }\n\n /**\n * Modification et application du tri\n * @internal\n */\n public onSortChange(e: any): void {\n this.sortChanged.emit(e);\n }\n\n /**\n * Modification et application des filtres\n * @internal\n */\n public onFilterChange(e: any): void {\n this.filterChanged.emit(e);\n }\n\n /**@internal */\n public onCellEditingStop(e: any) {\n this.cellEditingStopped.emit(e);\n }\n\n /**\n * Après mise à jour des données\n * @internal\n */\n public onModelUpdated(e: any) {\n if (this.mode === DsfrGridModeConst.CLIENT) {\n this.loading = false;\n } else if (this.totalElements === 0) {\n this.loading = false;\n this.agGrid.api.showNoRowsOverlay();\n }\n\n this.dataRender.emit();\n }\n\n /**\n * Au redimensionnement de la fenêtre\n * @internal\n */\n public onGridSizeChanged(e: any) {\n if (this.autoRowsHeight && this.mode !== DsfrGridModeConst.CLIENT) {\n // recalculer la hauteur des lignes\n this.setHeightOnRows();\n }\n // this.agGrid.api.sizeColumnsToFit(); disable automatic resize for perfs\n }\n\n /**\n * Au redimensionnement d'une colonne\n * @internal\n */\n public onColumnResized() {\n if (this.autoRowsHeight && this.mode !== DsfrGridModeConst.CLIENT) {\n // recalculer la hauteur des lignes\n this.setHeightOnRows();\n }\n }\n\n /**\n * Premier affichage des données\n * bug ag-grid: en mode infite + pagination + initialPagination, si le nombre de lignes a afficher est < a pageSize\n * des lignes vides s'affichent. Execution d'un double appel avec paginationGoToPage pour forcer un rendu correct\n * @internal\n */\n public onFirstDataRendered() {\n if (!this.gridOptions?.autoSizeStrategy) {\n this.agGrid.api.sizeColumnsToFit();\n }\n\n if (this.currentPage && this.currentPage > 1 && this._nbElementsDisplayed < this.pageSize) {\n this.agGrid.api.paginationGoToPage(this.currentPage);\n }\n }\n\n /**\n * Initialisation de la grid\n * @param params\n * @internal\n */\n public onGridReady(params: GridReadyEvent): void {\n this.agGrid.api = params.api;\n if (this.caption && this.captionId) {\n this.agGrid.api.setGridAriaProperty('labelledby', this.captionId);\n }\n\n this.gridReady.emit(this.agGrid.api);\n }\n\n /**\n * Evenement clic sur une cellule\n * @internal\n */\n public onCellClicked(e: CellClickedEvent<any>): void {\n this.cellSelect.emit(e);\n }\n\n /**\n * Evenement à la sélection ou déselection d'une ligne\n * @internal\n */\n public onRowSelected(e: RowSelectedEvent<any>): void {\n this.rowSelect.emit({ event: e, selectedNodes: this.agGrid.api?.getSelectedNodes() });\n }\n\n /**\n * Evenement au changement de sélection\n * @internal\n */\n public onSelectionChanged(e: SelectionChangedEvent<any>): void {\n this.selectionChange.emit({ event: e, selectedNodes: this.agGrid.api?.getSelectedNodes() });\n }\n\n /**\n * Changement de page\n * @param e numéro de page\n * @internal\n */\n public onPage(e: number): void {\n this.agGrid.api.paginationGoToPage(e - 1);\n\n // Fix bug mode server avec initial page et retour a page 1\n if (\n this.agGrid.api.paginationGetCurrentPage() === 0 &&\n this.initialStatePagination &&\n this.initialStatePagination.page > 1\n ) {\n this.initialStatePagination.page = 1;\n this.agGrid.api.refreshInfiniteCache();\n }\n }\n\n /**\n * @internal\n */\n public onClickExportCsv(): void {\n this.agGrid.api.exportDataAsCsv();\n }\n\n /**\n * @internal\n */\n public onChangePageSize(pageSize: number): void {\n this.pageSize = pageSize;\n this.agGrid.api.updateGridOptions({\n paginationPageSize: this.pageSize,\n cacheBlockSize: this.pageSize,\n });\n }\n\n /**\n * @internal\n */\n public onGridPreDestroyed(e: GridPreDestroyedEvent) {\n if (e?.state?.pagination?.page) {\n // fix current page number base 1\n e.state.pagination.page = this.currentPage;\n }\n\n this.gridPreDestroyed.emit(e);\n }\n\n protected onPaginationChanged(e: PaginationChangedEvent): void {\n if (this.totalElements === 0) {\n this.agGrid.api.setGridAriaProperty('describedby', this.idNoRowsTemplate);\n } else {\n this.agGrid.api.setGridAriaProperty('describedby', null);\n }\n\n if (!this.showPagination) {\n this.totalElements = this.agGrid.api.getDisplayedRowCount();\n } else {\n this.totalElements = this.agGrid.api.paginationGetRowCount();\n this.totalPage = this.agGrid.api.paginationGetTotalPages();\n if (this.mode === DsfrGridModeConst.CLIENT) this.currentPage = this.agGrid.api.paginationGetCurrentPage() + 1;\n }\n\n this.paginationChange.emit(e);\n }\n\n private calculateTotalPage(): number {\n return Math.ceil(this.totalElements / this.pageSize);\n }\n\n /* Retourne un identifiant unique selon la propriété définie par rowId */\n private getRowNodeId(row: GetRowIdParams<any>) {\n if (!row.data || !this.rowId || !row.data[this.rowId]) {\n throw Error(`L'identifiant rowId '${this.rowId}' n'est pas défini pour une donnée`);\n }\n\n return row.data[this.rowId];\n }\n\n /** Ajout du tri aux query params */\n private addSort(sortModel: SortModelItem[], httpParams: any): any {\n httpParams.sort = sortModel;\n\n return httpParams;\n }\n\n /**\n * Ajout des filtres aux query params\n * @internal */\n private addFilters(filterModel: any, httpParams: any): any {\n httpParams.filters = filterModel;\n }\n\n /**\n * Remettre manuellement les paramètres de query en cas d'initialState\n * @param params query params\n */\n private restoreInitialStateServer(params: IGetRowsParams) {\n if (!this.initialState) return;\n\n if (this.initialState.pagination?.page) {\n params.startRow = (this.initialState.pagination.page - 1) * this.pageSize;\n }\n if (this.initialState.filter?.filterModel) {\n params.filterModel = this.initialState.filter.filterModel;\n }\n if (this.initialState.sort?.sortModel) {\n params.sortModel = this.initialState.sort.sortModel;\n }\n }\n\n /**\n * Détection du changement de langue depuis le service i18n (display)\n * @param code\n */\n private onLangChange(code: string) {\n // si la langue n'est pas gérée (ni anglais ni francais), pas de traitement\n if (this.localeText && this.localeText !== 'fr' && this.localeText !== 'en') return;\n // sinon mis a jour en français ou anglais\n this.localeText = code === 'fr' ? AG_GRID_LOCALE_FR : undefined;\n }\n\n /**\n * Permettre d'afficher sur plusieurs lignes en mode serveur (equivalent autoHeight en mode client)\n * Calculer la dimension de chaque row par rapport à son contenu et recalculer la taille de la grille\n * globale (limitation ag-grid sur le mode infinite)\n */\n private setHeightOnRows() {\n if (!this.grid || !this.grid.nativeElement) return;\n let gridHeight = 0;\n\n // Dans le cas des colonnes pinned les rows peuvent être divisées en 3 containers\n const containers = [\n this.grid.nativeElement.querySelector('.ag-center-cols-viewport'),\n this.grid.nativeElement.querySelector('.ag-pinned-left-cols-container'),\n this.grid.nativeElement.querySelector('.ag-pinned-right-cols-container'),\n ].filter((el): el is HTMLElement => !!el && el.querySelector('.ag-row'));\n\n const hasPinnedColumns = containers.length > 1;\n\n this.agGrid.api.forEachNode((node) => {\n let maxRowHeight = 0;\n\n // Calculer la hauteur maximale pour cette row sur tous les containers\n containers.forEach((container) => {\n const rowElements = this.getRowElementFromNode(node.id, container);\n\n rowElements.forEach((rowEl) => {\n const cells = rowEl.getElementsByClassName('ag-cell-value');\n const rowHeight = this.getMaxHeightOnRow(cells) + 10;\n if (rowHeight > maxRowHeight) maxRowHeight = rowHeight;\n\n if (!hasPinnedColumns) {\n this.transformRowHeight(rowEl, maxRowHeight, gridHeight);\n }\n });\n });\n\n // Appliquer cette hauteur et position à tous les containers en cas de colonnes pinned\n if (hasPinnedColumns) {\n containers.forEach((container) => {\n const rowElements = this.getRowElementFromNode(node.id, container);\n rowElements.forEach((rowEl) => {\n this.transformRowHeight(rowEl, maxRowHeight, gridHeight);\n });\n });\n }\n\n gridHeight += maxRowHeight; // increment grid height\n });\n\n // Maj de la hauteur totale de la grille\n if (containers[0]) {\n (containers[0] as HTMLElement).style.height = `${gridHeight}px`;\n }\n }\n\n private transformRowHeight(rowEl: HTMLElement, maxRowHeight: number, gridHeight: number) {\n rowEl.style.height = `${maxRowHeight}px`;\n rowEl.style.setProperty('transform', `translateY(${gridHeight}px)`);\n }\n\n private getRowElementFromNode(nodeId: string | undefined, container: HTMLElement): NodeListOf<HTMLElement> {\n return container.querySelectorAll(`.ag-row[row-id=\"${nodeId}\"]`);\n }\n\n /**\n * Get max cell height needed on row\n * @param nodesCell each cells on row\n * @returns max height\n */\n private getMaxHeightOnRow(nodesCell: HTMLCollectionOf<Element>): number {\n const maxHeight = Array.from(nodesCell)?.reduce((a, b) => (a.clientHeight > b.clientHeight ? a : b)).clientHeight;\n // set min height of 30px if row height is not defined\n return maxHeight && maxHeight > 31 ? maxHeight : 31;\n }\n\n /**\n * Redéfinir le focus pour les cellules possédant des éléments interactifs (focusables, ex. buttons)\n * Suppression de la gestion du focus par défaut de ag grid lorsque c'est le cas\n */\n private suppressCellKeyboardEvent() {\n this.defaultColDef.suppressKeyboardEvent = (params: SuppressKeyboardEventParams) => {\n const e: KeyboardEvent = params.event;\n\n return this.suppressDefaultKeyboardEvent(e, '.ag-cell');\n };\n }\n\n /**\n * Redéfinir le focus pour les cellules d'en-tête possédant des floatings filters avec éléments interactifs (focusables, ex. buttons)\n * Suppression de la gestion du focus par défaut de ag grid lorsque c'est le cas\n */\n private suppressHeaderKeyboardEvent() {\n this.defaultColDef.suppressHeaderKeyboardEvent = (params: SuppressHeaderKeyboardEventParams) => {\n const e: KeyboardEvent = params.event;\n return this.suppressDefaultKeyboardEvent(e, '.ag-floating-cell');\n };\n }\n\n private suppressDefaultKeyboardEvent(e: KeyboardEvent, classToSearch: string): boolean {\n if (e.code == 'Tab' || e.key == 'Tab') {\n // get interactive children of cell\n const target = e.target as HTMLElement;\n const focusableChildrenOfParent = target\n ?.closest(classToSearch)\n ?.querySelectorAll(\n 'button, [href], :not(.ag-hidden) > input[tabindex]:not([tabindex=\"-1\"], select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (\n !focusableChildrenOfParent ||\n focusableChildrenOfParent.length == 0 ||\n (!e.shiftKey && target == focusableChildrenOfParent[focusableChildrenOfParent.length - 1]) ||\n (e.shiftKey && target == focusableChildrenOfParent[0]) ||\n (e.shiftKey && target.classList.contains('ag-cell'))\n ) {\n return false; // if no interactive elements are present do not suppress\n }\n return true; // else suppress\n }\n return false; // do not suppress by default\n }\n\n /* \n Fix to force reload of ag-Grid data when no data is previously displayed (e.g., after applying a filter).\n The modelUpdated callback is not called the first time, and cacheBlockState remains in \"no data\" mode.\n ag-Grid bug on infite mode ? Inconvenience: Data is loaded twice in this case.\n */\n private forceReloadOnNoData(): void {\n const cacheBlock: any = this.agGrid.api.getCacheBlockState();\n\n if ((!cacheBlock || Object.keys(cacheBlock).length === 0) && this.totalElements > 0) {\n this.agGrid.api.refreshInfiniteCache();\n }\n }\n\n private initPagination(): void {\n this.pageSize =\n !this.pageSize && this.initialState?.pagination?.pageSize\n ? this.initialState?.pagination?.pageSize\n : this.pageSize;\n\n if (this.gridOptions) this.gridOptions.paginationPageSize = this.pageSize;\n\n this.initialStatePagination = {\n rowsPerPage: this.pageSize,\n page: this.initialState?.pagination?.page ?? 1, // l'indexation de la pagination doit commencer à 1 ici\n sort: null,\n };\n }\n}\n","<!-- AG Grid Angular Component -->\n<div\n class=\"edu-grid__wrapper fr-table\"\n [class]=\"customClass ?? ''\"\n [ngClass]=\"{ 'dsfrx-auto-height__server': autoRowsHeight, 'fr-table--selectable': selectable }\">\n <!-- Header ------------------------------->\n <div class=\"fr-table__header\">\n <!-- Gestion du titre du tableau -->\n @if (caption) {\n <p [id]=\"captionId\" class=\"edu-grid__title\">\n {{ caption }}\n </p>\n } @else {\n <div [id]=\"captionId\" class=\"edu-grid__title\">\n <ng-content select=\"[caption]\"></ng-content>\n </div>\n }\n <!-- Template actions groupées -->\n @if (headerActionsTemplate) {\n <div class=\"edu-grid__actions\">\n <ng-container *ngTemplateOutlet=\"headerActionsTemplate\"></ng-container>\n </div>\n } @else if (showExportCsv) {\n <div class=\"edu-grid__actions\">\n <dsfr-button\n label=\"Exporter au format CSV\"\n customClass=\"edu-grid__actions--export\"\n (click)=\"onClickExportCsv()\"\n variant=\"secondary\"></dsfr-button>\n </div>\n }\n </div>\n\n <!-- Client side-->\n @if (mode === 'client') {\n <ag-grid-angular\n [rowData]=\"rowData\"\n #grid\n style=\"width: 100%\"\n [id]=\"gridId\"\n [ngStyle]=\"{ height: fixedHeight ? fixedHeight : '100%' }\"\n [ngClass]=\"customClass\"\n [columnDefs]=\"columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [initialState]=\"initialState\"\n [gridOptions]=\"gridOptions\"\n [loading]=\"loading\"\n [overlayLoadingTemplate]=\"loadingOverlayTemplate\"\n [overlayNoRowsTemplate]=\"overlayNoRowsTemplate\"\n [suppressPaginationPanel]=\"true\"\n [localeText]=\"localeText\"\n [animateRows]=\"false\"\n (columnResized)=\"onColumnResized()\"\n (gridSizeChanged)=\"onGridSizeChanged($event)\"\n (modelUpdated)=\"onModelUpdated($event)\"\n (cellClicked)=\"onCellClicked($event)\"\n (cellEditingStopped)=\"onCellEditingStop($event)\"\n (filterChanged)=\"onFilterChange($event)\"\n (paginationChanged)=\"onPaginationChanged($event)\"\n (sortChanged)=\"onSortChange($event)\"\n (rowSelected)=\"onRowSelected($event)\"\n (selectionChanged)=\"onSelectionChanged($event)\"\n (gridReady)=\"onGridReady($event)\"></ag-grid-angular>\n } @else if (mode === 'server') {\n <!-- Remote data-->\n <ag-grid-angular\n remoteAgGridBinding\n #grid\n style=\"width: 100%\"\n [id]=\"gridId\"\n [ngStyle]=\"{ height: fixedHeight ? fixedHeight : '100%' }\"\n [ngClass]=\"customClass\"\n [columnDefs]=\"columnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [initialState]=\"initialState\"\n [gridOptions]=\"gridOptions\"\n [loading]=\"loading\"\n [overlayLoadingTemplate]=\"loadingOverlayTemplate\"\n [overlayNoRowsTemplate]=\"overlayNoRowsTemplate\"\n [suppressPaginationPanel]=\"true\"\n [localeText]=\"localeText\"\n [animateRows]=\"false\"\n (columnResized)=\"onColumnResized()\"\n (firstDataRendered)=\"onFirstDataRendered()\"\n (gridSizeChanged)=\"onGridSizeChanged($event)\"\n (modelUpdated)=\"onModelUpdated($event)\"\n (cellClicked)=\"onCellClicked($event)\"\n (cellEditingStopped)=\"onCellEditingStop($event)\"\n (filterChanged)=\"onFilterChange($event)\"\n (sortChanged)=\"onSortChange($event)\"\n (paginationChanged)=\"onPaginationChanged($event)\"\n (rowSelected)=\"onRowSelected($event)\"\n (selectionChanged)=\"onSelectionChanged($event)\"\n (gridReady)=\"onGridReady($event)\"></ag-grid-angular>\n }\n\n <!-- Footer -->\n @if (totalElements > 0 && !isLoadingServerSide) {\n <dsfr-table-footer\n [initialState]=\"initialStatePagination\"\n [rowsPerPageOptions]=\"pageSizes\"\n [showFooterResult]=\"showFooterResult\"\n [showPagination]=\"showPagination\"\n [currentPage]=\"currentPage\"\n [footerActionsTemplate]=\"footerActionsTemplate\"\n [footerResultsTemplate]=\"footerResultsTemplate\"\n [totalPage]=\"totalPage\"\n [totalElements]=\"totalElements\"\n (rowsPerPageChange)=\"onChangePageSize($event)\"\n (pageSelect)=\"onPage($event)\"\n data-testid=\"ag-grid-footer\"></dsfr-table-footer>\n }\n</div>\n","import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';\nimport { NgModule } from '@angular/core';\nimport { DsfrGridComponent } from './grid.component';\n\n@NgModule({\n imports: [DsfrGridComponent],\n exports: [DsfrGridComponent],\n providers: [provideHttpClient(withInterceptorsFromDi())],\n})\nexport class DsfrGridModule {}\n","/**\n * API publique du module Grid.\n *\n * @module ngx-dsfr-ext/grid\n */\n\nexport * from './lib';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1"],"mappings":";;;;;;;;;;;;;MAsBa,2BAA2B,CAAA;AAYtC,IAAA,MAAM,CAAC,MAAW,EAAA;AAChB,QAAA,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE;AACnB,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK;AACzB,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;AACvB,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;AACvB,QAAA,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc;AAC3C,QAAA,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS;AACjC,QAAA,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW;AACrC,QAAA,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY;AACvC,QAAA,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY;IACzC;AAEA,IAAA,OAAO,CAAC,MAAY,EAAA;AAClB,QAAA,OAAO,IAAI;IACb;AAEA;;;AAGG;AACH,IAAA,OAAO,CAAC,MAAW,EAAA;QACjB,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,YAAY,QAAQ,EAAE;AAC3C,YAAA,MAAM,MAAM,GAAG;AACb,gBAAA,KAAK,EAAE,MAAM;AACb,gBAAA,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI;aAC/B;AACD,YAAA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC7B;IACF;+GAzCW,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,2BAA2B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAf5B,CAAA;;;;;;;;;;;AAW2B,sCAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,8DAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAE3B,gBAAgB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAA,EAAA,CAAA,mBAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,gBAAA,EAAA,SAAA,EAAA,YAAA,EAAA,MAAA,EAAA,cAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,WAAA,EAAA,yBAAA,EAAA,IAAA,EAAA,UAAA,EAAA,cAAA,EAAA,aAAA,EAAA,cAAA,EAAA,cAAA,EAAA,YAAA,EAAA,UAAA,EAAA,