chrv-components 1.12.230 → 1.12.231

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7596,1851 +7596,1883 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7596
7596
  }]
7597
7597
  }], propDecorators: { columnName: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrCellDef", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrCellDefType", required: false }] }] } });
7598
7598
 
7599
- class DataGrid {
7600
- constructor() {
7601
- this.formatterService = inject(DataFormatterService);
7602
- this.id = signal(crypto.randomUUID(), /* @ts-ignore */
7603
- ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
7604
- this.title = input(null, /* @ts-ignore */
7605
- ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
7606
- this.table = model(/* @ts-ignore */
7607
- ...(ngDevMode ? [undefined, { debugName: "table" }] : /* istanbul ignore next */ []));
7608
- this.viewport = viewChild('viewport', /* @ts-ignore */
7609
- ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
7610
- this.headerScroll = viewChild('headerScroll', /* @ts-ignore */
7611
- ...(ngDevMode ? [{ debugName: "headerScroll" }] : /* istanbul ignore next */ []));
7612
- this.currentPage = model(1, /* @ts-ignore */
7613
- ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
7614
- this.pageSize = model(10, /* @ts-ignore */
7615
- ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
7616
- // public pageChange = output<number>();
7617
- // public pageSizeChange = output<number>();
7618
- this.onInit = output();
7619
- this.selectedColumn = signal(null, /* @ts-ignore */
7620
- ...(ngDevMode ? [{ debugName: "selectedColumn" }] : /* istanbul ignore next */ []));
7621
- this.sort = model(null, /* @ts-ignore */
7622
- ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
7623
- this.filter = model({}, /* @ts-ignore */
7624
- ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
7625
- this.groupBy = model(null, /* @ts-ignore */
7626
- ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
7627
- this.cellChange = output();
7628
- this.rowClick = output();
7629
- this.enableVirtualization = input(false, /* @ts-ignore */
7630
- ...(ngDevMode ? [{ debugName: "enableVirtualization" }] : /* istanbul ignore next */ []));
7631
- this.allowFiltering = input(true, /* @ts-ignore */
7632
- ...(ngDevMode ? [{ debugName: "allowFiltering" }] : /* istanbul ignore next */ []));
7633
- this.allowGrouping = input(true, /* @ts-ignore */
7634
- ...(ngDevMode ? [{ debugName: "allowGrouping" }] : /* istanbul ignore next */ []));
7635
- this.allowInsert = input(false, /* @ts-ignore */
7636
- ...(ngDevMode ? [{ debugName: "allowInsert" }] : /* istanbul ignore next */ []));
7637
- this.allowUpdate = input(false, /* @ts-ignore */
7638
- ...(ngDevMode ? [{ debugName: "allowUpdate" }] : /* istanbul ignore next */ []));
7639
- this.allowDelete = input(false, /* @ts-ignore */
7640
- ...(ngDevMode ? [{ debugName: "allowDelete" }] : /* istanbul ignore next */ []));
7641
- this.allowDuplicate = input(false, /* @ts-ignore */
7642
- ...(ngDevMode ? [{ debugName: "allowDuplicate" }] : /* istanbul ignore next */ []));
7643
- this.allowSync = input(true, /* @ts-ignore */
7644
- ...(ngDevMode ? [{ debugName: "allowSync" }] : /* istanbul ignore next */ []));
7645
- this.allowRefresh = input(false, /* @ts-ignore */
7646
- ...(ngDevMode ? [{ debugName: "allowRefresh" }] : /* istanbul ignore next */ []));
7647
- this.allowNavigation = input(false, /* @ts-ignore */
7648
- ...(ngDevMode ? [{ debugName: "allowNavigation" }] : /* istanbul ignore next */ []));
7649
- this.allowExport = input(false, /* @ts-ignore */
7650
- ...(ngDevMode ? [{ debugName: "allowExport" }] : /* istanbul ignore next */ []));
7651
- this.allowManageTrackingPersistence = input(false, /* @ts-ignore */
7652
- ...(ngDevMode ? [{ debugName: "allowManageTrackingPersistence" }] : /* istanbul ignore next */ []));
7653
- this.allowManageDistincValueFetching = input(false, /* @ts-ignore */
7654
- ...(ngDevMode ? [{ debugName: "allowManageDistincValueFetching" }] : /* istanbul ignore next */ []));
7655
- this.maxHeight = input('100%', /* @ts-ignore */
7656
- ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
7657
- this.onExport = output();
7658
- this.onSync = output();
7659
- this.onRefresh = output();
7660
- this.onAddRow = output();
7661
- this.onDeleteRow = output();
7662
- /**
7663
- * Only supported for DB2 tables. For other types, use the onNavigate event.
7664
- */
7665
- this.onForeignTableNavigate = output();
7666
- this.onNavigate = output();
7667
- this.onFilterValueChange = output();
7668
- this.allowFetchDistinctValues = model(false, /* @ts-ignore */
7669
- ...(ngDevMode ? [{ debugName: "allowFetchDistinctValues" }] : /* istanbul ignore next */ []));
7670
- this.crossRequestTrackingPersistence = model(false, /* @ts-ignore */
7671
- ...(ngDevMode ? [{ debugName: "crossRequestTrackingPersistence" }] : /* istanbul ignore next */ []));
7672
- this.modifiedCells = model([], /* @ts-ignore */
7673
- ...(ngDevMode ? [{ debugName: "modifiedCells" }] : /* istanbul ignore next */ []));
7674
- this.modifiedRows = model([], /* @ts-ignore */
7675
- ...(ngDevMode ? [{ debugName: "modifiedRows" }] : /* istanbul ignore next */ []));
7676
- this.addedRows = model([], /* @ts-ignore */
7677
- ...(ngDevMode ? [{ debugName: "addedRows" }] : /* istanbul ignore next */ []));
7678
- this.deletedRows = model([], /* @ts-ignore */
7679
- ...(ngDevMode ? [{ debugName: "deletedRows" }] : /* istanbul ignore next */ []));
7680
- this.distinctValues = input({}, /* @ts-ignore */
7681
- ...(ngDevMode ? [{ debugName: "distinctValues" }] : /* istanbul ignore next */ []));
7682
- this.actions = input([], /* @ts-ignore */
7683
- ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
7684
- this.cellTemplates = contentChildren(ChrCellDefDirective, /* @ts-ignore */
7685
- ...(ngDevMode ? [{ debugName: "cellTemplates" }] : /* istanbul ignore next */ []));
7686
- this.templatesByColumn = computed(() => {
7687
- const map = new Map();
7688
- for (const t of this.cellTemplates()) {
7689
- const col = t.columnName();
7690
- if (col)
7691
- map.set(col.toLowerCase(), t);
7692
- }
7693
- return map;
7694
- }, /* @ts-ignore */
7695
- ...(ngDevMode ? [{ debugName: "templatesByColumn" }] : /* istanbul ignore next */ []));
7696
- this.templatesByType = computed(() => {
7697
- const map = new Map();
7698
- for (const t of this.cellTemplates()) {
7699
- const type = t.dataType();
7700
- if (type)
7701
- map.set(type.toLowerCase(), t);
7702
- }
7703
- return map;
7704
- }, /* @ts-ignore */
7705
- ...(ngDevMode ? [{ debugName: "templatesByType" }] : /* istanbul ignore next */ []));
7706
- this.columnSuggestions = computed(() => {
7707
- const distinctVals = this.distinctValues();
7708
- // Si des valeurs distinctes sont fournies par le parent, on les utilise
7709
- if (Object.keys(distinctVals).length > 0) {
7710
- return distinctVals;
7711
- }
7712
- // Sinon, on utilise l'ancien comportement (valeurs locales)
7713
- const suggestions = {};
7714
- this.columns().forEach((column) => {
7715
- suggestions[column.columnName] = new Set();
7716
- });
7717
- this.table()?.rows.forEach((row) => {
7718
- this.columns().forEach((column) => {
7719
- const value = row[column.columnName];
7720
- if (value !== null && value !== undefined) {
7721
- suggestions[column.columnName].add(value);
7722
- }
7723
- });
7724
- });
7725
- return Object.entries(suggestions).reduce((acc, [key, value]) => {
7726
- acc[key] = Array.from(value);
7727
- return acc;
7728
- }, {});
7729
- }, /* @ts-ignore */
7730
- ...(ngDevMode ? [{ debugName: "columnSuggestions" }] : /* istanbul ignore next */ []));
7731
- // protected _addedRows = computed(() => this.addedRows().map((r) => r.row));
7732
- this.trackByMode = model('index', /* @ts-ignore */
7733
- ...(ngDevMode ? [{ debugName: "trackByMode" }] : /* istanbul ignore next */ []));
7734
- this.hasChanges = computed(() => {
7735
- return (this.addedRows().length > 0 ||
7736
- this.modifiedRows().length > 0 ||
7737
- this.deletedRows().length > 0);
7738
- }, /* @ts-ignore */
7739
- ...(ngDevMode ? [{ debugName: "hasChanges" }] : /* istanbul ignore next */ []));
7740
- this.groupByMap = computed(() => {
7741
- const includedMap = {};
7742
- this.groupBy()?.included.forEach((col) => {
7743
- includedMap[col] = true;
7744
- });
7745
- return includedMap;
7746
- }, /* @ts-ignore */
7747
- ...(ngDevMode ? [{ debugName: "groupByMap" }] : /* istanbul ignore next */ []));
7748
- this.columns = computed(() => {
7749
- const table = this.table();
7750
- const metadatas = table?.columnMetadatas || [];
7751
- const groupedColumns = this.groupBy()?.included || [];
7752
- //Sort grouped columns first then computed columns then the rest
7753
- if (this.groupBy() !== null)
7754
- return [
7755
- ...metadatas.filter((col) => groupedColumns.includes(col.columnName)),
7756
- ...metadatas.filter((col) => !groupedColumns.includes(col.columnName) && !col.tableName),
7757
- ...metadatas.filter((col) => !groupedColumns.includes(col.columnName) && col.tableName),
7758
- ];
7759
- else
7760
- return metadatas;
7761
- }, /* @ts-ignore */
7762
- ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
7763
- this.rows = computed(() => {
7764
- const table = this.table();
7765
- if (!table)
7766
- return [];
7767
- const computedRows = [];
7768
- for (const row of table.rows) {
7769
- const rowId = table.getRowIdentifier(row);
7770
- const updated = this.modifiedRows().find((mr) => table.getRowIdentifier(mr.current) === rowId);
7771
- const deleted = this.deletedRows().find((dr) => table.getRowIdentifier(dr.row) === rowId);
7772
- computedRows.push({
7773
- id: rowId,
7774
- data: updated ? updated.current : row,
7775
- flag: deleted ? 'deleted' : updated ? 'modified' : 'unchanged',
7776
- valid: this.validateRow(deleted ? deleted.row : updated ? updated.current : row),
7777
- });
7778
- }
7779
- for (const addedEntry of this.addedRows()) {
7780
- const rowId = table.getRowIdentifier(addedEntry.row);
7781
- computedRows.push({
7782
- id: rowId,
7783
- data: addedEntry.row,
7784
- flag: 'added',
7785
- valid: this.validateRow(addedEntry.row),
7786
- });
7787
- }
7788
- return computedRows;
7789
- }, /* @ts-ignore */
7790
- ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
7791
- this.totalItems = computed(() => {
7792
- return 'total' in (this.table() || {})
7793
- ? this.table()?.total || -1
7794
- : this.table()?.count || -1;
7795
- }, /* @ts-ignore */
7796
- ...(ngDevMode ? [{ debugName: "totalItems" }] : /* istanbul ignore next */ []));
7797
- this.currentItems = computed(() => {
7798
- return 'total' in (this.table() || {})
7799
- ? this.table()?.total || 0
7800
- : this.table()?.count || 0;
7801
- }, /* @ts-ignore */
7802
- ...(ngDevMode ? [{ debugName: "currentItems" }] : /* istanbul ignore next */ []));
7803
- this.onFilterValue = (columnName, value) => {
7804
- if (this.allowFetchDistinctValues()) {
7805
- this.onFilterValueChange.emit({
7806
- column: columnName,
7807
- value: value,
7808
- });
7809
- }
7810
- };
7811
- this.onGroupByIncludedChange = (columnName, included) => {
7812
- this.groupBy.update((currentGroup) => {
7813
- if (currentGroup === null)
7814
- return currentGroup;
7815
- if (included) {
7816
- currentGroup?.included.push(columnName);
7817
- }
7818
- else {
7819
- currentGroup.included = currentGroup.included.filter((c) => c !== columnName);
7820
- }
7821
- return { ...currentGroup };
7822
- });
7823
- };
7824
- this.onCellChange = (index, row, column, value) => {
7825
- const initial = { ...row.data };
7826
- row.data[column.columnName] = value;
7827
- // If the row is created, we don't track it as modified
7828
- switch (row.flag) {
7829
- case 'added':
7830
- this.addedRows.update((rows) => rows.map((r) => {
7831
- if (this.table()?.getRowIdentifier(r.row) ===
7832
- this.table()?.getRowIdentifier(row.data)) {
7833
- r.row[column.columnName] = value;
7834
- }
7835
- return r;
7836
- }));
7837
- // this.addedRows.update((rows) => {
7838
- // rows.find(
7839
- // (r) =>
7840
- // this.table()?.getRowIdentifier(r.row) ===
7841
- // this.table()?.getRowIdentifier(row.data)
7842
- // )!.row[column.columnName] = value;
7843
- // return [...rows];
7844
- // });
7845
- return;
7846
- case 'modified':
7847
- this.modifiedRows.update((rows) => rows.map((r) => {
7848
- if (r.index === index) {
7849
- r.current = row.data;
7850
- }
7851
- return r;
7852
- }));
7853
- // this.modifiedRows.update((rows) => {
7854
- // const changed = this.modifiedRows().find(
7855
- // (modifiedRow) => modifiedRow.index === index
7856
- // );
7857
- // changed!.current = row.data;
7858
- // return [...rows];
7859
- // });
7860
- break;
7861
- case 'unchanged':
7862
- this.modifiedRows.set([
7863
- ...this.modifiedRows(),
7864
- { index: index, original: initial, current: row.data },
7865
- ]);
7866
- break;
7599
+ class TableConstraint {
7600
+ constructor(data = {}) {
7601
+ this.columns = [];
7602
+ this.constraintCatalog = data.constraintCatalog || '';
7603
+ this.constraintSchema = data.constraintSchema || '';
7604
+ this.constraintName = data.constraintName || '';
7605
+ this.constraintType = data.constraintType || '';
7606
+ this.tableCatalog = data.tableCatalog || '';
7607
+ this.tableName = data.tableName || '';
7608
+ this.tableSchema = data.tableSchema || '';
7609
+ this.columns = data.columns
7610
+ ? data.columns.map((d) => new TableConstraintColumn(d))
7611
+ : [];
7612
+ }
7613
+ fullTableName() {
7614
+ return `${this.tableSchema}.${this.tableName}`;
7615
+ }
7616
+ fullConstraintName() {
7617
+ return `${this.constraintSchema}.${this.constraintName}`;
7618
+ }
7619
+ isPrimaryKey() {
7620
+ return this.constraintType === 'PRIMARY KEY';
7621
+ }
7622
+ isForeignKey() {
7623
+ return this.constraintType === 'FOREIGN KEY';
7624
+ }
7625
+ isUnique() {
7626
+ return this.constraintType === 'UNIQUE';
7627
+ }
7628
+ isCheck() {
7629
+ return this.constraintType === 'CHECK';
7630
+ }
7631
+ }
7632
+
7633
+ class BaseTable {
7634
+ addRow(row) {
7635
+ this.rows.push(row);
7636
+ this.count += 1;
7637
+ return row;
7638
+ }
7639
+ updateRow(row) {
7640
+ const index = this.rows.findIndex((r) => this.compareRows(r, row));
7641
+ if (index !== -1) {
7642
+ this.rows[index] = row;
7643
+ return true;
7644
+ }
7645
+ return false;
7646
+ }
7647
+ removeRow(row) {
7648
+ const index = this.rows.findIndex((r) => this.compareRows(r, row));
7649
+ if (index !== -1) {
7650
+ this.rows.splice(index, 1);
7651
+ this.count -= 1;
7652
+ return true;
7653
+ }
7654
+ return false;
7655
+ }
7656
+ compareRows(row1, row2) {
7657
+ return JSON.stringify(row1) === JSON.stringify(row2);
7658
+ }
7659
+ getColumnByName(columnName) {
7660
+ return this.columnMetadatas.find((cmd) => cmd.columnName === columnName);
7661
+ }
7662
+ getConstraintsByType(type) {
7663
+ return this.tableConstraints.filter((tc) => tc.constraintType === type);
7664
+ }
7665
+ }
7666
+ class AnonymousTable extends BaseTable {
7667
+ constructor(data) {
7668
+ super();
7669
+ this.catalog = data.catalog;
7670
+ this.schema = data.schema;
7671
+ this.name = data.name;
7672
+ this.description = data.description;
7673
+ this.isReadOnly = data.isReadOnly;
7674
+ this.rowNumberIdentity = data.rowNumberIdentity;
7675
+ this.columnMetadatas = data.columnMetadatas
7676
+ ? data.columnMetadatas
7677
+ .map((d) => new ColumnMetadata(d))
7678
+ .sort((a, b) => a.ordinalPosition - b.ordinalPosition)
7679
+ : [];
7680
+ this.tableConstraints = data.tableConstraints
7681
+ ? data.tableConstraints.map((d) => new TableConstraint(d))
7682
+ : [];
7683
+ this.hasPrimaryKey =
7684
+ data.hasPrimaryKey === undefined ? false : Boolean(data.hasPrimaryKey);
7685
+ this.rows = data.rows || [];
7686
+ this.count = data.count || 0;
7687
+ this.total = data.total || 0;
7688
+ }
7689
+ fullName() {
7690
+ return `${this.schema}.${this.name}`;
7691
+ }
7692
+ primaryKeyColumns() {
7693
+ const pkColumns = this.tableConstraints.filter((tc) => tc.isPrimaryKey())?.[0]?.columns;
7694
+ if (!pkColumns || pkColumns.length === 0) {
7695
+ const rn = this.columnMetadatas.find((cmd) => cmd.columnName === 'ROW_NUMBER');
7696
+ return rn ? [rn] : [];
7697
+ }
7698
+ return this.columnMetadatas.filter((cmd) => pkColumns?.some((pc) => pc.columnName === cmd.columnName));
7699
+ }
7700
+ getRowIdentifier(row) {
7701
+ if (!this.hasPrimaryKey) {
7702
+ if (row[this.rowNumberIdentity] !== undefined) {
7703
+ return `${row[this.rowNumberIdentity]}`;
7867
7704
  }
7868
- };
7869
- this.addRow = () => {
7870
- this.addedRows.set([
7871
- ...this.addedRows(),
7872
- { index: this.addedRows().length, row: {} },
7873
- ]);
7874
- this.onAddRow.emit();
7875
- };
7876
- this.getRowTrackBy = (row, index) => {
7877
- const table = this.table();
7878
- // return table ? table.getRowIdentifier(row) : `row-${index}`;
7879
- return table?.getRowNumber(row) ? table.getRowNumber(row) : index;
7880
- };
7881
- this.hasInvalidRows = () => {
7882
- const allRows = [
7883
- ...this.addedRows().map((r) => r.row),
7884
- ...this.modifiedRows().map((r) => r.current),
7885
- ];
7886
- return allRows.some((row) => !this.validateRow(row));
7887
- };
7888
- this.validateRow = (row) => {
7889
- if (this.groupBy() !== null)
7890
- return true;
7891
- return this.formatterService.validateRow(row, this.columns());
7892
- };
7893
- this.canEdit = (column) => {
7894
- const formatter = this.formatterService.getFormatter(null, column);
7895
- if (!formatter)
7896
- return false && this.allowUpdate();
7897
- return this.allowUpdate() && formatter.canEdit(null, column);
7898
- };
7899
- this.isPrimaryKeyColumn = (column) => {
7900
- const table = this.table();
7901
- if (!table)
7902
- return false;
7903
- return table
7904
- .primaryKeyColumns()
7905
- .some((pk) => pk.columnName === column.columnName);
7906
- };
7907
- this.isUniqueKeyColumn = (column) => {
7908
- const table = this.table();
7909
- if (!table)
7910
- return false;
7911
- const uniqueConstraints = table.tableConstraints.filter((tc) => tc.constraintType === 'UNIQUE');
7912
- for (const uc of uniqueConstraints) {
7913
- if (uc.columns.some((col) => col.columnName === column.columnName)) {
7914
- return true;
7705
+ }
7706
+ const pkColumns = this.primaryKeyColumns();
7707
+ if (this.hasPrimaryKey && pkColumns.length > 0) {
7708
+ let key = '';
7709
+ let index = 0;
7710
+ for (const pc of pkColumns) {
7711
+ if (row[pc.columnName] === undefined || row[pc.columnName] === null) {
7712
+ if (row[this.rowNumberIdentity] !== undefined)
7713
+ return `${row[this.rowNumberIdentity]}`;
7714
+ else
7715
+ break;
7915
7716
  }
7916
- }
7917
- return false;
7918
- };
7919
- this.isEditSafe = (column) => {
7920
- if (this.groupBy() !== null)
7921
- return false;
7922
- if (this.trackByMode() === 'index')
7923
- return true;
7924
- if (this.crossRequestTrackingPersistence() === false)
7925
- return true;
7926
- return (this.trackByMode() === 'primaryKey' &&
7927
- this.table()
7928
- ?.primaryKeyColumns()
7929
- .some((pk) => pk.columnName === column.columnName) == false);
7930
- };
7931
- this.isForeignKeyColumn = (column) => {
7932
- const table = this.table();
7933
- if (!table)
7934
- return false;
7935
- const fkConstraints = table.tableConstraints.filter((tc) => tc.constraintType === 'FOREIGN KEY');
7936
- for (const fk of fkConstraints) {
7937
- if (fk.columns.some((col) => col.columnName === column.columnName)) {
7938
- return true;
7717
+ if (index == pkColumns.length - 1) {
7718
+ key += `${pc.columnName}:${row[pc.columnName]}`;
7939
7719
  }
7720
+ else
7721
+ key += `${pc.columnName}:${row[pc.columnName]}|`;
7722
+ index++;
7940
7723
  }
7941
- return false;
7942
- };
7943
- this.datagridContextMenuActions = (column, filterElement, groupElement) => {
7944
- const actions = [
7945
- {
7946
- label: 'Trier par ordre croissant',
7947
- icon: 'arrow_upward',
7948
- action: () => this.setSort(column, true),
7949
- },
7950
- {
7951
- label: 'Trier par ordre décroissant',
7952
- icon: 'arrow_downward',
7953
- action: () => this.setSort(column, false),
7954
- },
7955
- {
7956
- label: 'Effacer le tri',
7957
- icon: 'clear',
7958
- action: () => this.sort.set(null),
7959
- },
7960
- ];
7961
- if (filterElement && this.allowFiltering()) {
7962
- actions.push({
7963
- label: 'Filtrer sur cette colonne...',
7964
- icon: 'filter_list',
7965
- action: () => filterElement.toggle(),
7966
- });
7967
- }
7968
- if (groupElement && this.allowGrouping()) {
7969
- actions.push({
7970
- label: 'Grouper par cette colonne',
7971
- icon: 'group',
7972
- action: () => groupElement.isOpen.set(true),
7973
- });
7974
- }
7975
- return actions;
7976
- };
7977
- this.datarowContextMenuActions = (row, rowIndex) => {
7978
- const items = [];
7979
- for (const action of this.actions()) {
7980
- items.push({
7981
- label: action.display,
7982
- action: action.callback.bind(null, row.data),
7983
- color: action.color,
7984
- icon: action.icon,
7985
- });
7986
- }
7987
- switch (row.flag) {
7988
- case 'deleted':
7989
- items.push({
7990
- label: 'Annuler la suppression',
7991
- action: () => this.deletedRows.set(this.deletedRows().filter((r) => r.index !== rowIndex)),
7992
- icon: 'undo',
7993
- color: 'primary',
7994
- });
7995
- break;
7996
- case 'added':
7997
- items.push({
7998
- label: 'Supprimer la ligne ajoutée',
7999
- action: () => {
8000
- this.addedRows.update((rows) => rows.filter((r) => this.table().getRowIdentifier(r.row) !== row.id));
8001
- this.table()?.removeRow(row);
8002
- },
8003
- icon: 'delete',
8004
- color: 'error',
8005
- });
8006
- break;
8007
- default:
8008
- if (this.allowInsert() && this.allowDuplicate()) {
8009
- items.push({
8010
- label: 'Dupliquer la ligne',
8011
- action: () => {
8012
- const newRow = {
8013
- [`${this.table()?.getRowIdentifier(row.data)}`]: this.addedRows().length + 1,
8014
- ...row.data,
8015
- };
8016
- this.addedRows.set([
8017
- ...this.addedRows(),
8018
- { index: this.addedRows().length, row: newRow },
8019
- ]);
8020
- },
8021
- icon: 'file_copy',
8022
- color: 'primary',
8023
- });
8024
- }
8025
- if (this.allowDelete()) {
8026
- items.push({
8027
- label: 'Supprimer la ligne',
8028
- action: () => this.deleteRow(rowIndex, row.data),
8029
- icon: 'delete',
8030
- color: 'error',
8031
- });
8032
- }
8033
- break;
8034
- }
8035
- return items;
8036
- };
8037
- this.onAdd = () => {
8038
- this.onAddRow.emit();
8039
- setTimeout(() => {
8040
- this.viewport()?.scrollTo({ bottom: 0 });
8041
- }, 250);
8042
- };
8043
- this.log = (event) => {
8044
- console.log(`[DataGrid-${this.id()}]`, event);
8045
- };
8046
- effect(() => {
8047
- if (this.table() && !this.crossRequestTrackingPersistence()) {
8048
- this.resetChanges();
8049
- }
8050
- });
8051
- effect(() => {
8052
- const mode = this.table()?.hasPrimaryKey ? 'primaryKey' : 'index';
8053
- this.trackByMode.set(mode);
8054
- });
7724
+ return key;
7725
+ }
7726
+ return JSON.stringify(row);
8055
7727
  }
8056
- ngOnInit() {
8057
- this.onInit.emit();
7728
+ getRowNumber(row) {
7729
+ return row[this.rowNumberIdentity];
7730
+ }
7731
+ compareRows(row1, row2) {
7732
+ if (!this.hasPrimaryKey) {
7733
+ if (row1[this.rowNumberIdentity] !== undefined &&
7734
+ row2[this.rowNumberIdentity] !== undefined) {
7735
+ return row1[this.rowNumberIdentity] === row2[this.rowNumberIdentity];
7736
+ }
7737
+ }
7738
+ const pkColumns = this.primaryKeyColumns();
7739
+ if (this.hasPrimaryKey && pkColumns.length > 0) {
7740
+ return pkColumns.every((pc) => row1[pc.columnName] === row2[pc.columnName]);
7741
+ }
7742
+ return super.compareRows(row1, row2);
8058
7743
  }
8059
- ngOnDestroy() { }
8060
- getColumnTooltip(column) {
8061
- return column.tooltip;
7744
+ addRow(row) {
7745
+ row[this.rowNumberIdentity] = this.generateTempRowIdentifier();
7746
+ return super.addRow(row);
8062
7747
  }
8063
- toggleSort(columnName) {
8064
- const currentSort = this.sort();
8065
- let isAsc = true;
8066
- if (currentSort?.column === columnName)
8067
- isAsc = !currentSort.isAsc;
8068
- this.sort.set({ column: columnName, isAsc: isAsc });
7748
+ generateTempRowIdentifier() {
7749
+ return crypto.randomUUID();
8069
7750
  }
8070
- setSort(columnName, isAsc) {
8071
- this.sort.set({ column: columnName, isAsc: isAsc });
7751
+ }
7752
+
7753
+ class AutoDataTable extends BaseTable {
7754
+ get columnMetadatas() {
7755
+ return this._columnMetadatas.sort((a, b) => a.ordinalPosition - b.ordinalPosition);
8072
7756
  }
8073
- onFilterBy(columnName, filter) {
8074
- const currentFilters = { ...this.filter() };
8075
- if (filter.value) {
8076
- currentFilters[columnName] = {
8077
- column: filter.column,
8078
- mode: filter.mode,
8079
- value: filter.value,
8080
- };
8081
- }
8082
- else {
8083
- delete currentFilters[columnName];
8084
- }
8085
- this.filter.set(currentFilters);
7757
+ set columnMetadatas(value) {
7758
+ this._columnMetadatas = value;
8086
7759
  }
8087
- onGroupBy(group) {
8088
- if (group.mode === null)
8089
- this.groupBy.set(null);
8090
- else {
8091
- // group.included =
8092
- // this.table()?.columnMetadatas.map((c) => c.columnName) || [];
8093
- group.included = [group.column];
8094
- this.groupBy.set(group);
8095
- }
7760
+ get rows() {
7761
+ return this._rows;
8096
7762
  }
8097
- // Event handlers for pagination
8098
- onPageChange(page) {
8099
- this.currentPage.set(page);
7763
+ set rows(value) {
7764
+ this.count = value.length;
7765
+ this._rows = value.map((row, index) => {
7766
+ if (!this.hasPrimaryKey && !row.hasOwnProperty(this.rowNumberIdentity)) {
7767
+ return { [this.rowNumberIdentity]: index + 1, ...row };
7768
+ }
7769
+ return row;
7770
+ });
7771
+ this.deduceColumnTypes();
8100
7772
  }
8101
- onPageSizeChange(pageSize) {
8102
- this.pageSize.set(pageSize);
7773
+ constructor(name, primaryKey = null, metadatas = [], rowIdentifier = 'ROW_NUMBER') {
7774
+ super();
7775
+ this.name = '';
7776
+ this.schema = '';
7777
+ this.total = 0;
7778
+ this._columnMetadatas = [];
7779
+ this.tableConstraints = [];
7780
+ this._primaryKey = null;
7781
+ this.hasPrimaryKey = this._primaryKey !== null;
7782
+ this._rows = [];
7783
+ this.count = 0;
7784
+ this.getRowIdentifier = (row) => {
7785
+ if (this._primaryKey && row.hasOwnProperty(this._primaryKey)) {
7786
+ if (Array.isArray(this._primaryKey)) {
7787
+ return this._primaryKey.map((key) => row[key]).join('_');
7788
+ }
7789
+ return row[this._primaryKey].toString();
7790
+ }
7791
+ return row[this.rowNumberIdentity].toString();
7792
+ };
7793
+ this.getRowNumber = (row) => {
7794
+ return row[this.rowNumberIdentity];
7795
+ };
7796
+ this.primaryKeyColumns = () => {
7797
+ if (!this._primaryKey) {
7798
+ return [];
7799
+ }
7800
+ return this.columnMetadatas.filter((col) => col.columnName === this._primaryKey);
7801
+ };
7802
+ this.deduceColumnTypes = () => {
7803
+ if (this.rows.length === 0) {
7804
+ return;
7805
+ }
7806
+ if (this.columnMetadatas.length !== 0)
7807
+ return;
7808
+ for (const key of Object.keys(this.rows[0])) {
7809
+ const inferredType = typeof this.rows[0][key];
7810
+ const existingIndex = this.columnMetadatas.findIndex((md) => md.columnName === key);
7811
+ const existingMetadata = this.columnMetadatas[existingIndex];
7812
+ if (!existingMetadata) {
7813
+ const md = {
7814
+ ordinalPosition: this.columnMetadatas.length + 1,
7815
+ length: 0,
7816
+ tableName: this.name,
7817
+ tableSchema: this.schema,
7818
+ isIdentity: false,
7819
+ isGenerated: false,
7820
+ isUpdatable: false,
7821
+ tooltip: `${inferredType} ${length ? `(${inferredType})` : ''}`,
7822
+ columnName: key,
7823
+ dataType: inferredType,
7824
+ isNullable: false,
7825
+ };
7826
+ this.columnMetadatas.push(md);
7827
+ }
7828
+ }
7829
+ };
7830
+ /**
7831
+ * Retourne une copie de l'AutoDataTable
7832
+ */
7833
+ this.clone = () => {
7834
+ const copy = new AutoDataTable(this.name, this._primaryKey, this.columnMetadatas, this.rowNumberIdentity);
7835
+ for (const key of Object.keys(this)) {
7836
+ if (Array.isArray(this[key])) {
7837
+ copy[key] = [...this[key]];
7838
+ }
7839
+ else {
7840
+ copy[key] = this[key];
7841
+ }
7842
+ }
7843
+ return copy;
7844
+ };
7845
+ this.name = name;
7846
+ this._primaryKey = primaryKey;
7847
+ this.hasPrimaryKey = this._primaryKey !== null;
7848
+ this.rowNumberIdentity = rowIdentifier;
7849
+ this.columnMetadatas = metadatas.map((md, index) => ({
7850
+ displayName: md.displayName || md.columnName,
7851
+ ordinalPosition: index + 1,
7852
+ length: 0,
7853
+ tableName: this.name,
7854
+ tableSchema: this.schema,
7855
+ isIdentity: false,
7856
+ isGenerated: false,
7857
+ isUpdatable: false,
7858
+ tooltip: `${md.dataType?.toUpperCase()} ${md.length ? `(${md.length})` : ''}`,
7859
+ ...md,
7860
+ }));
8103
7861
  }
8104
- refresh() {
8105
- this.onRefresh.emit();
8106
- // this.sort.set(null);
8107
- // this.filter.set({});
7862
+ }
7863
+
7864
+ class AutoDataSource {
7865
+ constructor(config) {
7866
+ this._subscriptions = [];
7867
+ // Signaux internes
7868
+ this._datagrid = signal(null, /* @ts-ignore */
7869
+ ...(ngDevMode ? [{ debugName: "_datagrid" }] : /* istanbul ignore next */ []));
7870
+ this._datagridElement = signal(null, /* @ts-ignore */
7871
+ ...(ngDevMode ? [{ debugName: "_datagridElement" }] : /* istanbul ignore next */ []));
7872
+ this._data = signal(undefined, /* @ts-ignore */
7873
+ ...(ngDevMode ? [{ debugName: "_data" }] : /* istanbul ignore next */ []));
7874
+ // protected _currentPage = signal(1);
7875
+ // protected _pageSize = signal(10);
7876
+ this._sort = signal(null, /* @ts-ignore */
7877
+ ...(ngDevMode ? [{ debugName: "_sort" }] : /* istanbul ignore next */ []));
7878
+ this._filter = signal({}, /* @ts-ignore */
7879
+ ...(ngDevMode ? [{ debugName: "_filter" }] : /* istanbul ignore next */ []));
7880
+ this._groupBy = signal(null, /* @ts-ignore */
7881
+ ...(ngDevMode ? [{ debugName: "_groupBy" }] : /* istanbul ignore next */ []));
7882
+ this._loading = signal(false, /* @ts-ignore */
7883
+ ...(ngDevMode ? [{ debugName: "_loading" }] : /* istanbul ignore next */ []));
7884
+ this._error = signal(null, /* @ts-ignore */
7885
+ ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
7886
+ // Données exposées (read-only)
7887
+ this.data = this._data.asReadonly();
7888
+ // public readonly currentPage: Signal<number> = this._currentPage.asReadonly();
7889
+ // public readonly pageSize: Signal<number> = this._pageSize.asReadonly();
7890
+ this.sort = this._sort.asReadonly();
7891
+ this.filter = this._filter.asReadonly();
7892
+ this.groupBy = this._groupBy.asReadonly();
7893
+ this.loading = this._loading.asReadonly();
7894
+ this.error = this._error.asReadonly();
7895
+ // Données traitées
7896
+ this.processedData = computed(() => {
7897
+ let result = this._data();
7898
+ if (!result)
7899
+ return undefined;
7900
+ result.total = result.rows.length ?? -1;
7901
+ // Filtrage côté client
7902
+ if (this.config.enableClientSideFiltering) {
7903
+ result = this.applyFilters(result, this.filter());
7904
+ }
7905
+ // Tri côté client
7906
+ if (this.config.enableClientSideSorting) {
7907
+ result = this.applySort(result, this._sort());
7908
+ }
7909
+ // Pagination côté client
7910
+ if (this.config.enableClientSidePagination) {
7911
+ // Do nothing, it is handled by the data grid directly
7912
+ }
7913
+ result.count = result.rows.length;
7914
+ return result;
7915
+ }, /* @ts-ignore */
7916
+ ...(ngDevMode ? [{ debugName: "processedData" }] : /* istanbul ignore next */ []));
7917
+ this.config = {
7918
+ initialData: config?.initialData,
7919
+ pageSize: config?.pageSize ?? 10,
7920
+ enableClientSidePagination: config?.enableClientSidePagination ?? true,
7921
+ enableClientSideFiltering: config?.enableClientSideFiltering ?? true,
7922
+ enableClientSideSorting: config?.enableClientSideSorting ?? true,
7923
+ };
7924
+ if (this.config.initialData) {
7925
+ this._data.set(this.config.initialData);
7926
+ }
7927
+ // this._pageSize.set(this.config.pageSize);
7928
+ effect(() => {
7929
+ if (this.processedData() && this._datagrid()) {
7930
+ this._datagrid().table.set(this.processedData());
7931
+ this._datagrid().ngOnDestroy = () => {
7932
+ this._datagrid()?.ngOnDestroy();
7933
+ this._datagrid.set(null);
7934
+ for (const sub of this._subscriptions) {
7935
+ sub.unsubscribe();
7936
+ }
7937
+ };
7938
+ }
7939
+ });
8108
7940
  }
8109
- getCellValue(row, columnName) {
8110
- return row && row[columnName] !== undefined ? row[columnName] : '';
7941
+ // Méthode pour enregistrer la datagrid et écouter les événements de base
7942
+ registerDataGrid(element) {
7943
+ if (!element)
7944
+ return;
7945
+ this._datagridElement.set(element instanceof ElementRef ? element : null);
7946
+ const datagrid = (element instanceof ElementRef ? element.nativeElement : element);
7947
+ this._datagrid.set(datagrid);
7948
+ this._subscriptions.push(
7949
+ // datagrid.pageChange.subscribe((page: number) => {
7950
+ // console.log(
7951
+ // 'Page change event received:',
7952
+ // page,
7953
+ // this._datagridElement(),
7954
+ // element,
7955
+ // );
7956
+ // // this._subscriptions.push(this.handlePageChange(page).subscribe());
7957
+ // }),
7958
+ // datagrid.pageSizeChange.subscribe((pageSize: number) => {
7959
+ // // this._subscriptions
7960
+ // // .push(
7961
+ // // this.handlePageSizeChange(pageSize).subscribe(),
7962
+ // // );
7963
+ // }),
7964
+ datagrid.sort.subscribe((sort) => {
7965
+ this._subscriptions.push(this.handleSortChange(sort).subscribe());
7966
+ }), datagrid.filter.subscribe((filter) => {
7967
+ this._subscriptions.push(this.handleFilterChange(filter).subscribe());
7968
+ }), datagrid.groupBy.subscribe((group) => {
7969
+ this._subscriptions.push(this.handleGroupChange(group).subscribe());
7970
+ }));
8111
7971
  }
8112
- isRowAdded(row, index) {
8113
- return this.addedRows().some((addedRow) => addedRow.index === index);
7972
+ // Méthodes publiques pour gérer les événements de la datagrid
7973
+ // public handlePageChange(page: number): Observable<void> {
7974
+ // this._currentPage.set(page);
7975
+ // return this.onPageChange(page);
7976
+ // }
7977
+ // public handlePageSizeChange(pageSize: number): Observable<void> {
7978
+ // this._pageSize.set(pageSize);
7979
+ // this._currentPage.set(1); // Reset à la première page
7980
+ // return this.onPageSizeChange(pageSize);
7981
+ // }
7982
+ handleSortChange(sort) {
7983
+ this._sort.set(sort);
7984
+ return this.onSortChange(sort);
8114
7985
  }
8115
- isRowChanged(row, index) {
8116
- return this.modifiedRows().some((modifiedRow) => modifiedRow.index === index);
7986
+ handleFilterChange(filter) {
7987
+ this._filter.set(filter);
7988
+ this._datagrid()?.currentPage.set(1); // Reset à la première page
7989
+ return this.onFilterChange(filter);
8117
7990
  }
8118
- isCellChanged(rowIndex, columnName) {
8119
- return this.modifiedCells().some((cell) => cell.rowIndex === rowIndex && cell.columnName === columnName);
7991
+ handleGroupChange(group) {
7992
+ this._groupBy.set(group);
7993
+ this._datagrid()?.currentPage.set(1); // Reset à la première page
7994
+ return this.onGroupChange(group);
8120
7995
  }
8121
- isRowDeleted(row, index) {
8122
- return this.deletedRows().some((deletedRow) => deletedRow.index === index);
7996
+ refresh() {
7997
+ return this.loadData();
8123
7998
  }
8124
- deleteRow(index, row) {
8125
- // If the row was modified, we need to get the original value and remove it from modifiedRows
8126
- if (this.isRowChanged(row, index)) {
8127
- const original = this.modifiedRows().find((modifiedRow) => modifiedRow.index === index)
8128
- ?.original || row;
8129
- this.deletedRows.set([...this.deletedRows(), { index, row: original }]);
8130
- this.modifiedRows.set(this.modifiedRows().filter((modifiedRow) => modifiedRow.index !== index));
7999
+ setRows(rows) {
8000
+ if (!this._data()) {
8131
8001
  return;
8132
8002
  }
8133
- // If the row was added, we just remove it from addedRows
8134
- if (this.isRowAdded(row, index)) {
8135
- this.addedRows.set(this.addedRows().filter((addedRow) => addedRow.index !== index));
8136
- }
8137
- if (!this.isRowDeleted(row, index)) {
8138
- this.deletedRows.set([...this.deletedRows(), { index, row }]);
8139
- }
8140
- this.onDeleteRow.emit({ index, row });
8003
+ const updated = this._data().clone();
8004
+ updated.rows = rows;
8005
+ updated.total = rows.length;
8006
+ this._data.set(updated);
8141
8007
  }
8142
- navigateToForeignTable(column) {
8143
- if (!this.allowNavigation())
8144
- return;
8145
- if (column instanceof ColumnMetadata) {
8146
- this.table()
8147
- ?.getConstraintsByType('FOREIGN KEY')
8148
- .forEach((constraint) => {
8149
- const cstCol = constraint.columns.find((c) => c.columnName === column.columnName);
8150
- if (cstCol && cstCol instanceof TableConstraintColumn) {
8151
- const reference = cstCol
8152
- .reference;
8153
- if (reference) {
8154
- return this.onForeignTableNavigate.emit({
8155
- targetTable: cstCol.reference.otherKey.tableName,
8156
- targetSchema: cstCol.reference.otherKey.tableSchema,
8157
- });
8158
- }
8159
- }
8160
- });
8161
- }
8162
- else {
8163
- this.onNavigate.emit({ targetColumnMetadata: column });
8164
- }
8008
+ setData(data) {
8009
+ this._data.set(data);
8165
8010
  }
8166
- resetChanges() {
8167
- this.modifiedRows.set([]);
8168
- this.addedRows.set([]);
8169
- this.deletedRows.set([]);
8011
+ // Méthodes virtuelles à surcharger pour créer une data source serveur-side
8012
+ onPageChange(page) {
8013
+ // Comportement par défaut : rien (pagination côté client)
8014
+ return of(void 0);
8170
8015
  }
8171
- getChangeSummary() {
8172
- const modified = this.modifiedRows();
8173
- const added = this.addedRows();
8174
- const deleted = this.deletedRows();
8175
- return {
8176
- modified,
8177
- added,
8178
- deleted,
8179
- hasChanges: modified.length > 0 || added.length > 0 || deleted.length > 0,
8180
- };
8016
+ onPageSizeChange(pageSize) {
8017
+ // Comportement par défaut : rien (pagination côté client)
8018
+ return of(void 0);
8181
8019
  }
8182
- getCellTemplate(columnName, dataType) {
8183
- const colMatch = this.templatesByColumn().get(columnName.toLowerCase());
8184
- if (colMatch)
8185
- return colMatch.template;
8186
- const typeMatch = this.templatesByType().get(dataType.toLowerCase());
8187
- if (typeMatch)
8188
- return typeMatch.template;
8189
- return null;
8020
+ onSortChange(sort) {
8021
+ // Comportement par défaut : rien (tri côté client)
8022
+ return of(void 0);
8190
8023
  }
8191
- toggleCrossRequestTrackingPersistence() {
8192
- this.crossRequestTrackingPersistence.set(!this.crossRequestTrackingPersistence());
8024
+ onFilterChange(filter) {
8025
+ // Comportement par défaut : rien (filtrage côté client)
8026
+ return of(void 0);
8193
8027
  }
8194
- toggleAllowFetchDistinctValues() {
8195
- this.allowFetchDistinctValues.set(!this.allowFetchDistinctValues());
8028
+ onGroupChange(group) {
8029
+ // Comportement par défaut : rien
8030
+ return of(void 0);
8196
8031
  }
8197
- onViewportScroll(event) {
8198
- const viewport = event.target;
8199
- const headerScrollElement = this.headerScroll()?.nativeElement;
8200
- if (headerScrollElement) {
8201
- headerScrollElement.scrollLeft = viewport.scrollLeft;
8202
- }
8032
+ loadData() {
8033
+ // À surcharger pour charger les données
8034
+ return of(void 0);
8203
8035
  }
8204
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataGrid, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8205
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataGrid, isStandalone: true, selector: "chr-data-grid", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, enableVirtualization: { classPropertyName: "enableVirtualization", publicName: "enableVirtualization", isSignal: true, isRequired: false, transformFunction: null }, allowFiltering: { classPropertyName: "allowFiltering", publicName: "allowFiltering", isSignal: true, isRequired: false, transformFunction: null }, allowGrouping: { classPropertyName: "allowGrouping", publicName: "allowGrouping", isSignal: true, isRequired: false, transformFunction: null }, allowInsert: { classPropertyName: "allowInsert", publicName: "allowInsert", isSignal: true, isRequired: false, transformFunction: null }, allowUpdate: { classPropertyName: "allowUpdate", publicName: "allowUpdate", isSignal: true, isRequired: false, transformFunction: null }, allowDelete: { classPropertyName: "allowDelete", publicName: "allowDelete", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicate: { classPropertyName: "allowDuplicate", publicName: "allowDuplicate", isSignal: true, isRequired: false, transformFunction: null }, allowSync: { classPropertyName: "allowSync", publicName: "allowSync", isSignal: true, isRequired: false, transformFunction: null }, allowRefresh: { classPropertyName: "allowRefresh", publicName: "allowRefresh", isSignal: true, isRequired: false, transformFunction: null }, allowNavigation: { classPropertyName: "allowNavigation", publicName: "allowNavigation", isSignal: true, isRequired: false, transformFunction: null }, allowExport: { classPropertyName: "allowExport", publicName: "allowExport", isSignal: true, isRequired: false, transformFunction: null }, allowManageTrackingPersistence: { classPropertyName: "allowManageTrackingPersistence", publicName: "allowManageTrackingPersistence", isSignal: true, isRequired: false, transformFunction: null }, allowManageDistincValueFetching: { classPropertyName: "allowManageDistincValueFetching", publicName: "allowManageDistincValueFetching", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, allowFetchDistinctValues: { classPropertyName: "allowFetchDistinctValues", publicName: "allowFetchDistinctValues", isSignal: true, isRequired: false, transformFunction: null }, crossRequestTrackingPersistence: { classPropertyName: "crossRequestTrackingPersistence", publicName: "crossRequestTrackingPersistence", isSignal: true, isRequired: false, transformFunction: null }, modifiedCells: { classPropertyName: "modifiedCells", publicName: "modifiedCells", isSignal: true, isRequired: false, transformFunction: null }, modifiedRows: { classPropertyName: "modifiedRows", publicName: "modifiedRows", isSignal: true, isRequired: false, transformFunction: null }, addedRows: { classPropertyName: "addedRows", publicName: "addedRows", isSignal: true, isRequired: false, transformFunction: null }, deletedRows: { classPropertyName: "deletedRows", publicName: "deletedRows", isSignal: true, isRequired: false, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, trackByMode: { classPropertyName: "trackByMode", publicName: "trackByMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { table: "tableChange", currentPage: "currentPageChange", pageSize: "pageSizeChange", onInit: "onInit", sort: "sortChange", filter: "filterChange", groupBy: "groupByChange", cellChange: "cellChange", rowClick: "rowClick", onExport: "onExport", onSync: "onSync", onRefresh: "onRefresh", onAddRow: "onAddRow", onDeleteRow: "onDeleteRow", onForeignTableNavigate: "onForeignTableNavigate", onNavigate: "onNavigate", onFilterValueChange: "onFilterValueChange", allowFetchDistinctValues: "allowFetchDistinctValuesChange", crossRequestTrackingPersistence: "crossRequestTrackingPersistenceChange", modifiedCells: "modifiedCellsChange", modifiedRows: "modifiedRowsChange", addedRows: "addedRowsChange", deletedRows: "deletedRowsChange", trackByMode: "trackByModeChange" }, providers: [
8206
- PaginationService,
8207
- {
8208
- provide: CHR_DEBOUNCE_DEFAULTS,
8209
- useValue: new DebounceDefaults({ hoverTitle: 0 }),
8210
- },
8211
- // {
8212
- // provide: VIRTUAL_SCROLL_STRATEGY,
8213
- // useClass: DynamicSizeScrollStrategy,
8214
- // },
8215
- ], queries: [{ propertyName: "cellTemplates", predicate: ChrCellDefDirective, isSignal: true }], viewQueries: [{ propertyName: "viewport", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "headerScroll", first: true, predicate: ["headerScroll"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (table()) {\r\n<div class=\"data-grid-container\">\r\n <div class=\"data-grid-header\">\r\n <h3>{{ title() ? title() : `${table()?.schema ? `${table()?.schema}.` : ''}${table()?.name}` }}</h3>\r\n @if(table()?.description){\r\n <span class=\"table-description\">{{ table()?.description }}</span>\r\n }\r\n <div class=\"data-grid-info\">\r\n <!-- {{allowSync()}} {{hasChanges()}} {{!hasInvalidRows()}} -->\r\n @if(allowManageDistincValueFetching()){\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"allowFetchDistinctValues()\" (click)=\"toggleAllowFetchDistinctValues()\"\r\n title=\"R\u00E9cup\u00E9ration des valeurs distinctes d'une colonne lors du filtrage\">filter_list</span>\r\n </div>\r\n }\r\n @if (allowSync() && allowManageTrackingPersistence()) {\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"crossRequestTrackingPersistence()\"\r\n (click)=\"toggleCrossRequestTrackingPersistence()\"\r\n title=\"Persistance du suivi des modifications entre les requ\u00EAtes\">track_changes</span>\r\n </div>\r\n }\r\n @if(allowSync() && hasChanges() && !hasInvalidRows()){\r\n <span class=\"mat-icon icon-button sync-button\" (click)=\"onSync.emit()\"\r\n title=\"Synchroniser les changements\">file_upload</span>\r\n }\r\n @if(allowRefresh()){\r\n <span class=\"mat-icon icon-button\" (click)=\"refresh()\" title=\"Recharger les donn\u00E9es\">refresh</span>\r\n }\r\n @if(allowInsert()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onAdd()\" title=\"Ajouter une ligne\">add</span>\r\n }\r\n @if(allowExport()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onExport.emit()\"\r\n title=\"T\u00E9l\u00E9charger les donn\u00E9es\">file_download</span>\r\n }\r\n @if(totalItems() !== -1){\r\n <span>Total: {{ totalItems() }} enregistrements</span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"grid-container data-grid-wrapper\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [style.max-height]=\"maxHeight()\" [style.overflow-y]=\"enableVirtualization() ? 'clip': 'auto'\">\r\n <div class=\"grid-header\" [style.column-span]=\"'all'\">\r\n @for (column of columns(); track column.columnName) {\r\n @let isForeignKey = isForeignKeyColumn(column);\r\n @let isPrimaryKey = isPrimaryKeyColumn(column);\r\n @let isComputedColumn = column.isGenerated;\r\n @let isUniqueColumn = isUniqueKeyColumn(column);\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"\r\n [ngTemplateOutletContext]=\"{column: column, isForeignKey: isForeignKey, isPrimaryKey: isPrimaryKey, isComputedColumn: isComputedColumn, isUniqueColumn: isUniqueColumn}\"></ng-container>\r\n }\r\n </div>\r\n <div class=\"grid-body\">\r\n @if(enableVirtualization()){\r\n <cdk-virtual-scroll-viewport #viewport [itemSize]=\"41\" [style.height]=\"maxHeight()\" class=\"viewport\">\r\n <ng-container *cdkVirtualFor=\"let row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: totalItems()}; let $index = index\" [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n </cdk-virtual-scroll-viewport>\r\n } @else {\r\n @for (row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: currentItems()};\r\n track getRowTrackBy(row.data, $index)) {\r\n <ng-container [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #headerTemplate let-column=\"column\" let-isForeignKey=\"isForeignKey\" let-isPrimaryKey=\"isPrimaryKey\"\r\n let-isComputedColumn=\"isComputedColumn\" let-isUniqueColumn=\"isUniqueColumn\">\r\n <div class=\"grid-header-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\"\r\n [contextMenu]=\"datagridContextMenuActions(column.columnName, filterElement, groupElement)\"\r\n [title]=\"getColumnTooltip(column)\">\r\n @if(allowNavigation() && isForeignKey){\r\n <span ControlClick class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\"\r\n (ctrlClick)=\"navigateToForeignTable(column)\">{{ column.displayName || column.columnName\r\n }}</span>\r\n } @else {\r\n <span class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\">{{ column.displayName ||\r\n column.columnName\r\n }}</span>\r\n }\r\n <small class=\"sort-receiver column-type\" (click)=\"toggleSort(column.columnName)\">{{\r\n column.dataType }}\r\n {{column.length ?\r\n `(${column.length}${column.scale ? `, ${column.scale}` : ''})`: ''}}</small>\r\n <div class=\"column-icons\">\r\n @if(isUniqueColumn){\r\n <span class=\"mat-icon unique-key-icon\" title=\"Colonne avec contrainte d'unicit\u00E9\">looks_one</span>\r\n }\r\n @if(isPrimaryKey || isForeignKey){\r\n <span class=\"mat-icon\" [class.primary-key-icon]=\"isPrimaryKey\" [title]=\"'Colonne de la clef ' + (isPrimaryKey ?\r\n 'primaire' : '\u00E9trang\u00E8re')\" [class.key-icon]=\"isForeignKey\">key</span>\r\n }\r\n @if(allowSync() && (!canEdit(column) || !isEditSafe(column))){\r\n <span class=\"lock-icon mat-icon\" title=\"Colonne verrouill\u00E9e\">lock</span>\r\n }\r\n </div>\r\n @if (sort()?.column === column.columnName) {\r\n @if (sort()?.isAsc) {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_upward</span>\r\n } @else {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_downward</span>\r\n }\r\n }\r\n <div class=\"filter-group-controls\" #controls>\r\n <chr-column-filter #filterElement [class.hidden]=\"!allowFiltering()\" [by]=\"column.columnName\"\r\n [mode]=\"filter()[column.columnName]?.mode ?? null\" [attr.data-applied]=\"\"\r\n [value]=\"filter()[column.columnName]?.value ?? ''\" [columnMetadata]=\"column\"\r\n [suggestions]=\"columnSuggestions()[column.columnName]\"\r\n (valueChange)=\"onFilterValue(column.columnName, $event)\"\r\n (filterChange)=\"onFilterBy(column.columnName, $event)\" title=\"Double-clic pour d\u00E9finir un filtre\"\r\n class=\"filter\"></chr-column-filter>\r\n @let isGroupBySource = groupBy() == null || groupBy()?.column === column.columnName;\r\n <chr-column-group #groupElement [class.hidden]=\"!allowGrouping()\"\r\n [style.display]=\"!isGroupBySource ? 'none' : ''\" [by]=\"column.columnName\"\r\n [mode]=\"groupBy()?.mode ?? null\" (groupChange)=\"onGroupBy($event)\" class=\"group\"\r\n title=\"Double-clic pour d\u00E9finir un groupement\"></chr-column-group>\r\n @if(isComputedColumn){\r\n <span class=\"filter-icon mat-icon\" title=\"Colonne calcul\u00E9e\">functions</span>\r\n }@else if(!isGroupBySource && groupBy() !== null){\r\n <div class=\"group-by-include\" title=\"Inclure cette colonne dans le r\u00E9sultat group\u00E9\">\r\n <span class=\"mat-icon filter-icon disabled\">stack</span>\r\n <span class=\"toggle faded\">Inclure</span>\r\n <input #checkbox [checked]=\"groupByMap()[column.columnName]\"\r\n (change)=\"onGroupByIncludedChange(column.columnName, checkbox.checked)\"\r\n [name]=\"column.columnName+'-include'\" type=\"checkbox\" />\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #rowTemplate let-row let-index=\"index\">\r\n @let contextMenuItems = datarowContextMenuActions(row, index);\r\n <div #rowElement class=\"grid-row\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [attr.data-is-added]=\"row.flag === 'added'\" [scrollIntoView]=\"!row.valid\" [attr.data-is-valid]=\"row.valid\"\r\n (click)=\"rowClick.emit({ rowIndex: index, row: row.data, event: $event })\" [contextMenu]=\"contextMenuItems\"\r\n [attr.data-has-changed]=\"row.flag === 'modified'\" [attr.data-is-deleted]=\"row.flag === 'deleted'\">\r\n @for (column of columns(); track column.columnName) {\r\n <div class=\"grid-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\">\r\n <chr-editable-cell class=\"cell\" [value]=\"row.data[column.columnName]\" [row]=\"row.data\"\r\n [customTemplate]=\"getCellTemplate(column.columnName, column.dataType ?? 'varchar')\"\r\n (isEditingChange)=\"$event ? selectedColumn.set(column.columnName) : selectedColumn.set(null)\"\r\n (valueChange)=\"onCellChange(index, row, column, $event)\"\r\n [allowUpdate]=\"allowUpdate() && (isEditSafe(column)||row.flag === 'added') && row.flag !== 'deleted' && canEdit(column)\"\r\n [columnMetadata]=\"column\"></chr-editable-cell>\r\n @if($last){\r\n @if(actions().length > 0){\r\n <div class=\"actions-menu\">\r\n <span class=\"actions-menu-trigger mat-icon icon-button\" [style.cursor]=\"'pointer'\"\r\n [openOnClick]=\"true\" [contextMenu]=\"contextMenuItems\">more_vert</span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <chr-paginator [id]=\"id()\" [page]=\"currentPage()\" [pageSize]=\"pageSize()\" (pageChange)=\"onPageChange($event)\"\r\n (pageSizeChange)=\"onPageSizeChange($event)\"></chr-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"empty-state\">\r\n <p>Aucune donn\u00E9e</p>\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.hidden{display:none}.data-grid-wrapper{position:relative;display:grid!important;overflow:auto}.data-grid-container{background:inherit;border-radius:8px;box-shadow:0 2px 4px #0000001a;margin:1rem 0;--faded-text-color: color-mix( in srgb, var(--text-color) 40%, var(--background-color) 50% )}.data-grid-container .data-grid-header{border-top-left-radius:inherit;border-top-right-radius:inherit;background:var(--primary-color);padding:1rem 1.5rem;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem}.data-grid-container .data-grid-header h3{margin:0;color:var(--primary-contrast-color);font-size:1.25rem;font-weight:600}.data-grid-container .data-grid-header .table-description{color:var(--primary-contrast-color);font-size:.875rem;font-style:italic}.data-grid-container .data-grid-header .data-grid-info{display:flex;align-items:center;gap:1rem;color:var(--text-color);font-size:.875rem}.data-grid-container .data-grid-header .data-grid-info span{background:var(--background-color);padding:.25rem .75rem;border-radius:1.25rem}.data-grid-container .data-grid-header .data-grid-info .sync-button{border:2px solid var(--warn-color);box-shadow:0 0 .5rem .15rem var(--warn-color)}.data-grid-container .data-grid-header .data-grid-info .sync-button:hover{background-color:var(--warn-color);color:var(--warn-contrast-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button{color:var(--success-contrast-color);background-color:var(--error-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button[data-enabled=true]{background-color:var(--success-color)}.data-grid-container .data-grid-header .data-grid-info .icon-button{cursor:pointer;padding:.1rem .25rem;font-size:1rem;transition:background-color .2s ease-in-out,color .2s ease-in-out}.data-grid-container .data-grid-header .data-grid-info .icon-button:hover{background-color:color-mix(in srgb,var(--background-color) 80%,var(--secondary-color) 80%);color:var(--secondary-contrast-color)}.data-grid-container .viewport{font-size:.875rem;scrollbar-width:none;grid-column:1/-1}.data-grid-container .viewport ::-webkit-scrollbar{display:none}.data-grid-container .viewport{-ms-overflow-style:none}.data-grid-container .grid-header,.data-grid-container .grid-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;font-size:.875rem}.data-grid-container .grid-row{display:grid!important;grid-column:1/-1}.data-grid-container .grid-header{position:sticky!important;top:0;z-index:10;background:color-mix(in srgb,var(--background-color) 80%,transparent 40%);-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-bottom:2px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%)}.data-grid-container .grid-header-cell{position:relative;padding:.75rem 1rem;text-align:left;font-weight:600;color:color-mix(in srgb,var(--text-color) 90%,var(--background-color) 30%);-webkit-user-select:none;user-select:none}.data-grid-container .grid-header-cell .sort-receiver{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.data-grid-container .grid-header-cell .sort-icon{position:absolute;right:.75rem;top:calc(50% - .75rem);font-size:1rem;color:var(--tertiary-color)}.data-grid-container .grid-header-cell .column-icons{position:absolute;right:.75rem;top:.5rem;display:flex;gap:.25rem;align-items:center}.data-grid-container .grid-header-cell .column-icons span,.data-grid-container .grid-header-cell .column-icons .mat-icon{font-size:1rem}.data-grid-container .grid-header-cell .primary-key-icon{color:var(--primary-color)}.data-grid-container .grid-header-cell .key-icon{color:var(--tertiary-color)}.data-grid-container .grid-header-cell .lock-icon{color:var(--warn-color)}.data-grid-container .grid-header-cell .column-type{font-weight:400;color:var(--faded-text-color);font-size:.75rem;text-transform:uppercase}.data-grid-container .grid-header-cell .filter-group-controls{display:flex;flex-direction:column;justify-content:center;gap:.25rem;width:calc(100% - .875rem)}.data-grid-container .grid-header-cell .group-by-include{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;accent-color:var(--primary-color)}.data-grid-container .grid-header-cell .filter-icon{font-size:1rem}.data-grid-container .grid-row{border-bottom:1px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%);transition:background-color .2s ease;position:relative}.data-grid-container .grid-row:nth-child(2n){background-color:color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-row:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:.25rem;background-color:transparent}.data-grid-container .grid-row[data-is-deleted=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--error-color) 20%)}.data-grid-container .grid-row[data-is-deleted=true]:after{background-color:var(--error-color)}.data-grid-container .grid-row[data-has-changed=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--warn-color) 20%)}.data-grid-container .grid-row[data-has-changed=true]:after{background-color:var(--warn-color)}.data-grid-container .grid-row[data-is-added=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--primary-color) 20%)}.data-grid-container .grid-row[data-is-added=true]:after{background-color:var(--primary-color)}.data-grid-container .grid-row[data-is-valid=false]{box-shadow:inset 0 0 .25rem var(--error-color)}.data-grid-container .grid-row:hover{background-color:var(--neutral-color)}.data-grid-container .grid-cell{position:relative;padding:.75rem 1rem;border-right:1px solid color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-cell .cell{display:inline-block;min-width:100%;max-width:12rem}.data-grid-container .grid-cell .cell[data-changed=true]{font-weight:600;color:var(--warn-color)}.data-grid-container .grid-cell:last-child{border-right:none}.data-grid-container .grid-cell .actions-menu{position:absolute;right:0rem;top:50%;transform:translateY(-50%)}.data-grid-container .no-data-row{padding:2rem;text-align:center}.data-grid-container .no-data-row .no-data{color:var(--faded-text-color);font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChrPaginatorComponent, selector: "app-chr-paginator,chr-paginator, chr-paginator", inputs: ["page", "pageSize", "id", "allowSizeChange", "allowPageEdit", "isPageEditing"], outputs: ["pageChange", "pageSizeChange", "isPageEditingChange"] }, { kind: "ngmodule", type: NgxPaginationModule }, { kind: "component", type: EditableCell, selector: "chr-editable-cell", inputs: ["allowUpdate", "customTemplate", "row", "value", "columnMetadata"], outputs: ["valueChange", "isEditingChange"] }, { kind: "directive", type: ContextMenuDirective, selector: "[contextMenu], [chrContextMenu], [chr-right-click]", inputs: ["contextMenu", "openOnClick"] }, { kind: "directive", type: ScrollIntoViewDirective, selector: "[scrollIntoView]", inputs: ["scrollIntoView"] }, { kind: "directive", type: ControlClickDirective, selector: "[CtrlClick], [ControlClick]", inputs: ["callback"], outputs: ["ctrlClick"] }, { kind: "component", type: ColumnFilter, selector: "chr-column-filter", inputs: ["isOpen", "by", "suggestions", "mode", "columnMetadata", "value"], outputs: ["isOpenChange", "modeChange", "valueChange", "filterChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1$1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1$1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: ColumnGroup, selector: "chr-column-group", inputs: ["isOpen", "by", "mode"], outputs: ["isOpenChange", "modeChange", "groupChange"] }, { kind: "directive", type: ChrHoverTitleDirective, selector: "[chrTitle], [title]", inputs: ["chrTitle", "chrTitlePosition", "chrTitleSnap", "chrTitleDebounce"], outputs: ["chrTitleChange"] }, { kind: "pipe", type: i2.PaginatePipe, name: "paginate" }] }); }
8216
- }
8217
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataGrid, decorators: [{
8218
- type: Component,
8219
- args: [{ selector: 'chr-data-grid', imports: [
8220
- CommonModule,
8221
- ChrPaginatorComponent,
8222
- NgxPaginationModule,
8223
- EditableCell,
8224
- ContextMenuDirective,
8225
- ScrollIntoViewDirective,
8226
- ControlClickDirective,
8227
- ColumnFilter,
8228
- ScrollingModule,
8229
- ColumnGroup,
8230
- ChrHoverTitleDirective,
8231
- ], providers: [
8232
- PaginationService,
8233
- {
8234
- provide: CHR_DEBOUNCE_DEFAULTS,
8235
- useValue: new DebounceDefaults({ hoverTitle: 0 }),
8236
- },
8237
- // {
8238
- // provide: VIRTUAL_SCROLL_STRATEGY,
8239
- // useClass: DynamicSizeScrollStrategy,
8240
- // },
8241
- ], template: "@if (table()) {\r\n<div class=\"data-grid-container\">\r\n <div class=\"data-grid-header\">\r\n <h3>{{ title() ? title() : `${table()?.schema ? `${table()?.schema}.` : ''}${table()?.name}` }}</h3>\r\n @if(table()?.description){\r\n <span class=\"table-description\">{{ table()?.description }}</span>\r\n }\r\n <div class=\"data-grid-info\">\r\n <!-- {{allowSync()}} {{hasChanges()}} {{!hasInvalidRows()}} -->\r\n @if(allowManageDistincValueFetching()){\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"allowFetchDistinctValues()\" (click)=\"toggleAllowFetchDistinctValues()\"\r\n title=\"R\u00E9cup\u00E9ration des valeurs distinctes d'une colonne lors du filtrage\">filter_list</span>\r\n </div>\r\n }\r\n @if (allowSync() && allowManageTrackingPersistence()) {\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"crossRequestTrackingPersistence()\"\r\n (click)=\"toggleCrossRequestTrackingPersistence()\"\r\n title=\"Persistance du suivi des modifications entre les requ\u00EAtes\">track_changes</span>\r\n </div>\r\n }\r\n @if(allowSync() && hasChanges() && !hasInvalidRows()){\r\n <span class=\"mat-icon icon-button sync-button\" (click)=\"onSync.emit()\"\r\n title=\"Synchroniser les changements\">file_upload</span>\r\n }\r\n @if(allowRefresh()){\r\n <span class=\"mat-icon icon-button\" (click)=\"refresh()\" title=\"Recharger les donn\u00E9es\">refresh</span>\r\n }\r\n @if(allowInsert()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onAdd()\" title=\"Ajouter une ligne\">add</span>\r\n }\r\n @if(allowExport()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onExport.emit()\"\r\n title=\"T\u00E9l\u00E9charger les donn\u00E9es\">file_download</span>\r\n }\r\n @if(totalItems() !== -1){\r\n <span>Total: {{ totalItems() }} enregistrements</span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"grid-container data-grid-wrapper\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [style.max-height]=\"maxHeight()\" [style.overflow-y]=\"enableVirtualization() ? 'clip': 'auto'\">\r\n <div class=\"grid-header\" [style.column-span]=\"'all'\">\r\n @for (column of columns(); track column.columnName) {\r\n @let isForeignKey = isForeignKeyColumn(column);\r\n @let isPrimaryKey = isPrimaryKeyColumn(column);\r\n @let isComputedColumn = column.isGenerated;\r\n @let isUniqueColumn = isUniqueKeyColumn(column);\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"\r\n [ngTemplateOutletContext]=\"{column: column, isForeignKey: isForeignKey, isPrimaryKey: isPrimaryKey, isComputedColumn: isComputedColumn, isUniqueColumn: isUniqueColumn}\"></ng-container>\r\n }\r\n </div>\r\n <div class=\"grid-body\">\r\n @if(enableVirtualization()){\r\n <cdk-virtual-scroll-viewport #viewport [itemSize]=\"41\" [style.height]=\"maxHeight()\" class=\"viewport\">\r\n <ng-container *cdkVirtualFor=\"let row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: totalItems()}; let $index = index\" [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n </cdk-virtual-scroll-viewport>\r\n } @else {\r\n @for (row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: currentItems()};\r\n track getRowTrackBy(row.data, $index)) {\r\n <ng-container [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #headerTemplate let-column=\"column\" let-isForeignKey=\"isForeignKey\" let-isPrimaryKey=\"isPrimaryKey\"\r\n let-isComputedColumn=\"isComputedColumn\" let-isUniqueColumn=\"isUniqueColumn\">\r\n <div class=\"grid-header-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\"\r\n [contextMenu]=\"datagridContextMenuActions(column.columnName, filterElement, groupElement)\"\r\n [title]=\"getColumnTooltip(column)\">\r\n @if(allowNavigation() && isForeignKey){\r\n <span ControlClick class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\"\r\n (ctrlClick)=\"navigateToForeignTable(column)\">{{ column.displayName || column.columnName\r\n }}</span>\r\n } @else {\r\n <span class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\">{{ column.displayName ||\r\n column.columnName\r\n }}</span>\r\n }\r\n <small class=\"sort-receiver column-type\" (click)=\"toggleSort(column.columnName)\">{{\r\n column.dataType }}\r\n {{column.length ?\r\n `(${column.length}${column.scale ? `, ${column.scale}` : ''})`: ''}}</small>\r\n <div class=\"column-icons\">\r\n @if(isUniqueColumn){\r\n <span class=\"mat-icon unique-key-icon\" title=\"Colonne avec contrainte d'unicit\u00E9\">looks_one</span>\r\n }\r\n @if(isPrimaryKey || isForeignKey){\r\n <span class=\"mat-icon\" [class.primary-key-icon]=\"isPrimaryKey\" [title]=\"'Colonne de la clef ' + (isPrimaryKey ?\r\n 'primaire' : '\u00E9trang\u00E8re')\" [class.key-icon]=\"isForeignKey\">key</span>\r\n }\r\n @if(allowSync() && (!canEdit(column) || !isEditSafe(column))){\r\n <span class=\"lock-icon mat-icon\" title=\"Colonne verrouill\u00E9e\">lock</span>\r\n }\r\n </div>\r\n @if (sort()?.column === column.columnName) {\r\n @if (sort()?.isAsc) {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_upward</span>\r\n } @else {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_downward</span>\r\n }\r\n }\r\n <div class=\"filter-group-controls\" #controls>\r\n <chr-column-filter #filterElement [class.hidden]=\"!allowFiltering()\" [by]=\"column.columnName\"\r\n [mode]=\"filter()[column.columnName]?.mode ?? null\" [attr.data-applied]=\"\"\r\n [value]=\"filter()[column.columnName]?.value ?? ''\" [columnMetadata]=\"column\"\r\n [suggestions]=\"columnSuggestions()[column.columnName]\"\r\n (valueChange)=\"onFilterValue(column.columnName, $event)\"\r\n (filterChange)=\"onFilterBy(column.columnName, $event)\" title=\"Double-clic pour d\u00E9finir un filtre\"\r\n class=\"filter\"></chr-column-filter>\r\n @let isGroupBySource = groupBy() == null || groupBy()?.column === column.columnName;\r\n <chr-column-group #groupElement [class.hidden]=\"!allowGrouping()\"\r\n [style.display]=\"!isGroupBySource ? 'none' : ''\" [by]=\"column.columnName\"\r\n [mode]=\"groupBy()?.mode ?? null\" (groupChange)=\"onGroupBy($event)\" class=\"group\"\r\n title=\"Double-clic pour d\u00E9finir un groupement\"></chr-column-group>\r\n @if(isComputedColumn){\r\n <span class=\"filter-icon mat-icon\" title=\"Colonne calcul\u00E9e\">functions</span>\r\n }@else if(!isGroupBySource && groupBy() !== null){\r\n <div class=\"group-by-include\" title=\"Inclure cette colonne dans le r\u00E9sultat group\u00E9\">\r\n <span class=\"mat-icon filter-icon disabled\">stack</span>\r\n <span class=\"toggle faded\">Inclure</span>\r\n <input #checkbox [checked]=\"groupByMap()[column.columnName]\"\r\n (change)=\"onGroupByIncludedChange(column.columnName, checkbox.checked)\"\r\n [name]=\"column.columnName+'-include'\" type=\"checkbox\" />\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #rowTemplate let-row let-index=\"index\">\r\n @let contextMenuItems = datarowContextMenuActions(row, index);\r\n <div #rowElement class=\"grid-row\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [attr.data-is-added]=\"row.flag === 'added'\" [scrollIntoView]=\"!row.valid\" [attr.data-is-valid]=\"row.valid\"\r\n (click)=\"rowClick.emit({ rowIndex: index, row: row.data, event: $event })\" [contextMenu]=\"contextMenuItems\"\r\n [attr.data-has-changed]=\"row.flag === 'modified'\" [attr.data-is-deleted]=\"row.flag === 'deleted'\">\r\n @for (column of columns(); track column.columnName) {\r\n <div class=\"grid-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\">\r\n <chr-editable-cell class=\"cell\" [value]=\"row.data[column.columnName]\" [row]=\"row.data\"\r\n [customTemplate]=\"getCellTemplate(column.columnName, column.dataType ?? 'varchar')\"\r\n (isEditingChange)=\"$event ? selectedColumn.set(column.columnName) : selectedColumn.set(null)\"\r\n (valueChange)=\"onCellChange(index, row, column, $event)\"\r\n [allowUpdate]=\"allowUpdate() && (isEditSafe(column)||row.flag === 'added') && row.flag !== 'deleted' && canEdit(column)\"\r\n [columnMetadata]=\"column\"></chr-editable-cell>\r\n @if($last){\r\n @if(actions().length > 0){\r\n <div class=\"actions-menu\">\r\n <span class=\"actions-menu-trigger mat-icon icon-button\" [style.cursor]=\"'pointer'\"\r\n [openOnClick]=\"true\" [contextMenu]=\"contextMenuItems\">more_vert</span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <chr-paginator [id]=\"id()\" [page]=\"currentPage()\" [pageSize]=\"pageSize()\" (pageChange)=\"onPageChange($event)\"\r\n (pageSizeChange)=\"onPageSizeChange($event)\"></chr-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"empty-state\">\r\n <p>Aucune donn\u00E9e</p>\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.hidden{display:none}.data-grid-wrapper{position:relative;display:grid!important;overflow:auto}.data-grid-container{background:inherit;border-radius:8px;box-shadow:0 2px 4px #0000001a;margin:1rem 0;--faded-text-color: color-mix( in srgb, var(--text-color) 40%, var(--background-color) 50% )}.data-grid-container .data-grid-header{border-top-left-radius:inherit;border-top-right-radius:inherit;background:var(--primary-color);padding:1rem 1.5rem;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem}.data-grid-container .data-grid-header h3{margin:0;color:var(--primary-contrast-color);font-size:1.25rem;font-weight:600}.data-grid-container .data-grid-header .table-description{color:var(--primary-contrast-color);font-size:.875rem;font-style:italic}.data-grid-container .data-grid-header .data-grid-info{display:flex;align-items:center;gap:1rem;color:var(--text-color);font-size:.875rem}.data-grid-container .data-grid-header .data-grid-info span{background:var(--background-color);padding:.25rem .75rem;border-radius:1.25rem}.data-grid-container .data-grid-header .data-grid-info .sync-button{border:2px solid var(--warn-color);box-shadow:0 0 .5rem .15rem var(--warn-color)}.data-grid-container .data-grid-header .data-grid-info .sync-button:hover{background-color:var(--warn-color);color:var(--warn-contrast-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button{color:var(--success-contrast-color);background-color:var(--error-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button[data-enabled=true]{background-color:var(--success-color)}.data-grid-container .data-grid-header .data-grid-info .icon-button{cursor:pointer;padding:.1rem .25rem;font-size:1rem;transition:background-color .2s ease-in-out,color .2s ease-in-out}.data-grid-container .data-grid-header .data-grid-info .icon-button:hover{background-color:color-mix(in srgb,var(--background-color) 80%,var(--secondary-color) 80%);color:var(--secondary-contrast-color)}.data-grid-container .viewport{font-size:.875rem;scrollbar-width:none;grid-column:1/-1}.data-grid-container .viewport ::-webkit-scrollbar{display:none}.data-grid-container .viewport{-ms-overflow-style:none}.data-grid-container .grid-header,.data-grid-container .grid-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;font-size:.875rem}.data-grid-container .grid-row{display:grid!important;grid-column:1/-1}.data-grid-container .grid-header{position:sticky!important;top:0;z-index:10;background:color-mix(in srgb,var(--background-color) 80%,transparent 40%);-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-bottom:2px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%)}.data-grid-container .grid-header-cell{position:relative;padding:.75rem 1rem;text-align:left;font-weight:600;color:color-mix(in srgb,var(--text-color) 90%,var(--background-color) 30%);-webkit-user-select:none;user-select:none}.data-grid-container .grid-header-cell .sort-receiver{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.data-grid-container .grid-header-cell .sort-icon{position:absolute;right:.75rem;top:calc(50% - .75rem);font-size:1rem;color:var(--tertiary-color)}.data-grid-container .grid-header-cell .column-icons{position:absolute;right:.75rem;top:.5rem;display:flex;gap:.25rem;align-items:center}.data-grid-container .grid-header-cell .column-icons span,.data-grid-container .grid-header-cell .column-icons .mat-icon{font-size:1rem}.data-grid-container .grid-header-cell .primary-key-icon{color:var(--primary-color)}.data-grid-container .grid-header-cell .key-icon{color:var(--tertiary-color)}.data-grid-container .grid-header-cell .lock-icon{color:var(--warn-color)}.data-grid-container .grid-header-cell .column-type{font-weight:400;color:var(--faded-text-color);font-size:.75rem;text-transform:uppercase}.data-grid-container .grid-header-cell .filter-group-controls{display:flex;flex-direction:column;justify-content:center;gap:.25rem;width:calc(100% - .875rem)}.data-grid-container .grid-header-cell .group-by-include{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;accent-color:var(--primary-color)}.data-grid-container .grid-header-cell .filter-icon{font-size:1rem}.data-grid-container .grid-row{border-bottom:1px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%);transition:background-color .2s ease;position:relative}.data-grid-container .grid-row:nth-child(2n){background-color:color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-row:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:.25rem;background-color:transparent}.data-grid-container .grid-row[data-is-deleted=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--error-color) 20%)}.data-grid-container .grid-row[data-is-deleted=true]:after{background-color:var(--error-color)}.data-grid-container .grid-row[data-has-changed=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--warn-color) 20%)}.data-grid-container .grid-row[data-has-changed=true]:after{background-color:var(--warn-color)}.data-grid-container .grid-row[data-is-added=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--primary-color) 20%)}.data-grid-container .grid-row[data-is-added=true]:after{background-color:var(--primary-color)}.data-grid-container .grid-row[data-is-valid=false]{box-shadow:inset 0 0 .25rem var(--error-color)}.data-grid-container .grid-row:hover{background-color:var(--neutral-color)}.data-grid-container .grid-cell{position:relative;padding:.75rem 1rem;border-right:1px solid color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-cell .cell{display:inline-block;min-width:100%;max-width:12rem}.data-grid-container .grid-cell .cell[data-changed=true]{font-weight:600;color:var(--warn-color)}.data-grid-container .grid-cell:last-child{border-right:none}.data-grid-container .grid-cell .actions-menu{position:absolute;right:0rem;top:50%;transform:translateY(-50%)}.data-grid-container .no-data-row{padding:2rem;text-align:center}.data-grid-container .no-data-row .no-data{color:var(--faded-text-color);font-style:italic}\n"] }]
8242
- }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: false }] }, { type: i0.Output, args: ["tableChange"] }], viewport: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], headerScroll: [{ type: i0.ViewChild, args: ['headerScroll', { isSignal: true }] }], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], onInit: [{ type: i0.Output, args: ["onInit"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }, { type: i0.Output, args: ["filterChange"] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }, { type: i0.Output, args: ["groupByChange"] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], enableVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableVirtualization", required: false }] }], allowFiltering: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFiltering", required: false }] }], allowGrouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowGrouping", required: false }] }], allowInsert: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowInsert", required: false }] }], allowUpdate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowUpdate", required: false }] }], allowDelete: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDelete", required: false }] }], allowDuplicate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicate", required: false }] }], allowSync: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowSync", required: false }] }], allowRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowRefresh", required: false }] }], allowNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowNavigation", required: false }] }], allowExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowExport", required: false }] }], allowManageTrackingPersistence: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManageTrackingPersistence", required: false }] }], allowManageDistincValueFetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManageDistincValueFetching", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], onExport: [{ type: i0.Output, args: ["onExport"] }], onSync: [{ type: i0.Output, args: ["onSync"] }], onRefresh: [{ type: i0.Output, args: ["onRefresh"] }], onAddRow: [{ type: i0.Output, args: ["onAddRow"] }], onDeleteRow: [{ type: i0.Output, args: ["onDeleteRow"] }], onForeignTableNavigate: [{ type: i0.Output, args: ["onForeignTableNavigate"] }], onNavigate: [{ type: i0.Output, args: ["onNavigate"] }], onFilterValueChange: [{ type: i0.Output, args: ["onFilterValueChange"] }], allowFetchDistinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFetchDistinctValues", required: false }] }, { type: i0.Output, args: ["allowFetchDistinctValuesChange"] }], crossRequestTrackingPersistence: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossRequestTrackingPersistence", required: false }] }, { type: i0.Output, args: ["crossRequestTrackingPersistenceChange"] }], modifiedCells: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiedCells", required: false }] }, { type: i0.Output, args: ["modifiedCellsChange"] }], modifiedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiedRows", required: false }] }, { type: i0.Output, args: ["modifiedRowsChange"] }], addedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "addedRows", required: false }] }, { type: i0.Output, args: ["addedRowsChange"] }], deletedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "deletedRows", required: false }] }, { type: i0.Output, args: ["deletedRowsChange"] }], distinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "distinctValues", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ChrCellDefDirective), { isSignal: true }] }], trackByMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByMode", required: false }] }, { type: i0.Output, args: ["trackByModeChange"] }] } });
8243
-
8244
- class MessageBanner {
8245
- constructor() {
8246
- this.isVisible = signal(true, /* @ts-ignore */
8247
- ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
8248
- this.closable = input(false, /* @ts-ignore */
8249
- ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
8250
- this.title = input(null, /* @ts-ignore */
8251
- ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
8252
- this.icon = input(null, /* @ts-ignore */
8253
- ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
8254
- this.message = input(/* @ts-ignore */
8255
- ...(ngDevMode ? [undefined, { debugName: "message" }] : /* istanbul ignore next */ []));
8256
- this.type = input('none', /* @ts-ignore */
8257
- ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
8258
- this.close = () => {
8259
- this.isVisible.set(false);
8260
- };
8036
+ // Méthodes de traitement côté client (peuvent être surchargées)
8037
+ applyFilters(data, filters) {
8038
+ if (Object.keys(filters).length === 0)
8039
+ return data;
8040
+ const filteredRows = data.rows.filter((row) => {
8041
+ return Object.values(filters).every((filter) => {
8042
+ const value = row[filter.column];
8043
+ const filterValue = String(filter.value).toLowerCase();
8044
+ const stringValue = String(value ?? '').toLowerCase();
8045
+ switch (filter.mode) {
8046
+ case DGFilterMode.CONTAINS:
8047
+ return stringValue.includes(filterValue);
8048
+ case DGFilterMode.STARTS_WITH:
8049
+ return stringValue.startsWith(filterValue);
8050
+ case DGFilterMode.ENDS_WITH:
8051
+ return stringValue.endsWith(filterValue);
8052
+ case DGFilterMode.EQUALS:
8053
+ return stringValue === filterValue;
8054
+ case DGFilterMode.NOT_EQUALS:
8055
+ return stringValue !== filterValue;
8056
+ case DGFilterMode.GREATER_OR_EQUAL:
8057
+ return Number(value) >= Number(filter.value);
8058
+ case DGFilterMode.LOWER_OR_EQUAL:
8059
+ return Number(value) <= Number(filter.value);
8060
+ default:
8061
+ return true;
8062
+ }
8063
+ });
8064
+ });
8065
+ const cloned = data.clone();
8066
+ cloned.rows = filteredRows;
8067
+ return cloned;
8261
8068
  }
8262
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MessageBanner, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8263
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MessageBanner, isStandalone: true, selector: "chr-message-banner", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if(isVisible()){\r\n<div class=\"message-banner\" [attr.data-type]=\"type()\">\r\n <div class=\"banner-header\">\r\n @if(icon()){\r\n <span class=\"mat-icon material-symbols banner-icon\" [attr.data-icon]=\"icon()\">{{ icon() }}</span>\r\n\r\n }\r\n @if(title()){\r\n <strong class=\"banner-title\">{{title()}}</strong>\r\n }\r\n </div>\r\n <div class=\"message-content\">\r\n {{message()}}\r\n <ng-content></ng-content>\r\n </div>\r\n @if(closable()){\r\n <span class=\"mat-icon material-symbols close-icon\" (click)=\"close()\">close</span>\r\n }\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.message-banner[data-type=none]{--banner-color: var(--neutral-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--neutral-color) 10% );--banner-icon-color: var(--text-color)}.message-banner[data-type=info]{--banner-color: var(--primary-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--primary-color) 10% );--banner-icon-color: var(--primary-color)}.message-banner[data-type=warning]{--banner-color: var(--warn-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--warn-color) 10% );--banner-icon-color: var(--warn-color)}.message-banner[data-type=error]{--banner-color: var(--error-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--error-color) 10% );--banner-icon-color: var(--error-color)}.message-banner{flex:1 1 auto;border:1px solid var(--banner-color);background-color:var(--banner-bg-color);color:var(--text-color);padding:.65rem 1rem;margin:.5rem 0;border-radius:.25rem;display:flex;align-items:center;gap:.5rem;position:relative;font-size:.875rem}.message-banner .banner-header{position:absolute;display:flex;align-items:center;gap:.5rem;top:-.75rem;left:.5rem;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-radius:.25rem;padding:0 .5rem;font-size:.75rem}.message-banner .banner-header .banner-icon{color:var(--banner-icon-color);fill:var(--banner-icon-color)}.message-banner ::ng-deep .message-content{display:flex;flex-direction:column;justify-items:center;width:100%}.message-banner ::ng-deep .message-content *{display:flex;align-items:center}\n"] }); }
8264
- }
8265
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MessageBanner, decorators: [{
8266
- type: Component,
8267
- args: [{ selector: 'chr-message-banner', imports: [], template: "@if(isVisible()){\r\n<div class=\"message-banner\" [attr.data-type]=\"type()\">\r\n <div class=\"banner-header\">\r\n @if(icon()){\r\n <span class=\"mat-icon material-symbols banner-icon\" [attr.data-icon]=\"icon()\">{{ icon() }}</span>\r\n\r\n }\r\n @if(title()){\r\n <strong class=\"banner-title\">{{title()}}</strong>\r\n }\r\n </div>\r\n <div class=\"message-content\">\r\n {{message()}}\r\n <ng-content></ng-content>\r\n </div>\r\n @if(closable()){\r\n <span class=\"mat-icon material-symbols close-icon\" (click)=\"close()\">close</span>\r\n }\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.message-banner[data-type=none]{--banner-color: var(--neutral-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--neutral-color) 10% );--banner-icon-color: var(--text-color)}.message-banner[data-type=info]{--banner-color: var(--primary-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--primary-color) 10% );--banner-icon-color: var(--primary-color)}.message-banner[data-type=warning]{--banner-color: var(--warn-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--warn-color) 10% );--banner-icon-color: var(--warn-color)}.message-banner[data-type=error]{--banner-color: var(--error-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--error-color) 10% );--banner-icon-color: var(--error-color)}.message-banner{flex:1 1 auto;border:1px solid var(--banner-color);background-color:var(--banner-bg-color);color:var(--text-color);padding:.65rem 1rem;margin:.5rem 0;border-radius:.25rem;display:flex;align-items:center;gap:.5rem;position:relative;font-size:.875rem}.message-banner .banner-header{position:absolute;display:flex;align-items:center;gap:.5rem;top:-.75rem;left:.5rem;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-radius:.25rem;padding:0 .5rem;font-size:.75rem}.message-banner .banner-header .banner-icon{color:var(--banner-icon-color);fill:var(--banner-icon-color)}.message-banner ::ng-deep .message-content{display:flex;flex-direction:column;justify-items:center;width:100%}.message-banner ::ng-deep .message-content *{display:flex;align-items:center}\n"] }]
8268
- }], propDecorators: { closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
8269
-
8270
- class Spinner {
8271
- constructor() {
8272
- this.spinnerService = inject(SpinnerService);
8273
- this.shown = input(false, /* @ts-ignore */
8274
- ...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
8275
- this.isLoading = this.spinnerService.isLoading;
8276
- this.shouldShow = computed(() => this.isLoading() || this.shown(), /* @ts-ignore */
8277
- ...(ngDevMode ? [{ debugName: "shouldShow" }] : /* istanbul ignore next */ []));
8069
+ applySort(data, sort) {
8070
+ if (!sort)
8071
+ return data;
8072
+ const sortedRows = [...data.rows].sort((a, b) => {
8073
+ const aValue = a[sort.column];
8074
+ const bValue = b[sort.column];
8075
+ if (aValue === bValue)
8076
+ return 0;
8077
+ if (aValue === null || aValue === undefined)
8078
+ return 1;
8079
+ if (bValue === null || bValue === undefined)
8080
+ return -1;
8081
+ const comparison = aValue > bValue ? 1 : -1;
8082
+ return sort.isAsc ? comparison : -comparison;
8083
+ });
8084
+ const cloned = data.clone();
8085
+ cloned.rows = sortedRows;
8086
+ return cloned;
8278
8087
  }
8279
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8280
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: Spinner, isStandalone: true, selector: "chr-spinner", inputs: { shown: { classPropertyName: "shown", publicName: "shown", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (shouldShow()) {\r\n<div class=\"spinner-backdrop\">\r\n <div class=\"spinner-container\">\r\n <div class=\"pulse-rings\">\r\n <div class=\"pulse-ring pulse-ring-1\"></div>\r\n <div class=\"pulse-ring pulse-ring-2\"></div>\r\n <div class=\"pulse-ring pulse-ring-3\"></div>\r\n <div class=\"pulse-ring pulse-ring-4\"></div>\r\n </div>\r\n\r\n <div class=\"loading-text\">\r\n <p>Chargement...</p>\r\n </div>\r\n </div>\r\n</div>\r\n}", styles: ["@charset \"UTF-8\";.spinner-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,black 40%,transparent);display:flex;justify-content:center;align-items:center;z-index:9997;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:fadeIn .3s ease-in-out}.spinner-container{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:3rem;border-radius:1.5rem;min-width:250px;animation:slideUp .4s ease-out}.pulse-rings{position:relative;width:120px;height:120px;display:flex;align-items:center;justify-content:center}.pulse-ring{position:absolute;border:3px solid transparent;border-radius:50%;animation:pulse-ring 2.5s cubic-bezier(.25,.46,.45,.94) infinite}.pulse-ring-1{width:40px;height:40px;border-color:var(--primary-color);animation-delay:0s;box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 30%,transparent)}.pulse-ring-2{width:60px;height:60px;border-color:var(--tertiary-color);animation-delay:.6s;box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 25%,transparent)}.pulse-ring-3{width:80px;height:80px;border-color:var(--secondary-color);animation-delay:1.2s;box-shadow:0 0 30px color-mix(in srgb,var(--secondary-color) 20%,transparent)}.pulse-ring-4{width:100px;height:100px;border-color:var(--primary-color);animation-delay:1.8s;opacity:.7;box-shadow:0 0 35px color-mix(in srgb,var(--primary-color) 15%,transparent)}.loading-text{text-align:center;color:var(--text-color);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.loading-text p{margin:0;font-size:1.2rem;font-weight:500;color:var(--primary-color);letter-spacing:.5px;animation:text-pulse 2s ease-in-out infinite}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(30px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes pulse-ring{0%{transform:scale(.6);opacity:1;border-width:4px}50%{opacity:.8;border-width:2px}to{transform:scale(1.3);opacity:0;border-width:1px}}@keyframes text-pulse{0%,to{opacity:.8}50%{opacity:1;text-shadow:0 0 10px color-mix(in srgb,var(--primary-color) 30%,transparent)}}@media(max-width:768px){.spinner-container{min-width:200px;padding:2rem;gap:1.5rem}.pulse-rings{width:100px;height:100px}.pulse-ring-1{width:35px;height:35px}.pulse-ring-2{width:50px;height:50px}.pulse-ring-3{width:65px;height:65px}.pulse-ring-4{width:80px;height:80px}.loading-text p{font-size:1rem}}.dark-theme .spinner-container{box-shadow:0 20px 40px color-mix(in srgb,black 40%,transparent);border-color:var(--neutral-color)}.dark-theme .pulse-ring-1{box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 50%,transparent)}.dark-theme .pulse-ring-2{box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 40%,transparent)}.as400-theme .pulse-ring{filter:drop-shadow(0 0 8px var(--primary-color))}.as400-theme .loading-text p{text-shadow:0 0 10px var(--primary-color)}\n"] }); }
8281
8088
  }
8282
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: Spinner, decorators: [{
8283
- type: Component,
8284
- args: [{ selector: 'chr-spinner', imports: [], template: "@if (shouldShow()) {\r\n<div class=\"spinner-backdrop\">\r\n <div class=\"spinner-container\">\r\n <div class=\"pulse-rings\">\r\n <div class=\"pulse-ring pulse-ring-1\"></div>\r\n <div class=\"pulse-ring pulse-ring-2\"></div>\r\n <div class=\"pulse-ring pulse-ring-3\"></div>\r\n <div class=\"pulse-ring pulse-ring-4\"></div>\r\n </div>\r\n\r\n <div class=\"loading-text\">\r\n <p>Chargement...</p>\r\n </div>\r\n </div>\r\n</div>\r\n}", styles: ["@charset \"UTF-8\";.spinner-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,black 40%,transparent);display:flex;justify-content:center;align-items:center;z-index:9997;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:fadeIn .3s ease-in-out}.spinner-container{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:3rem;border-radius:1.5rem;min-width:250px;animation:slideUp .4s ease-out}.pulse-rings{position:relative;width:120px;height:120px;display:flex;align-items:center;justify-content:center}.pulse-ring{position:absolute;border:3px solid transparent;border-radius:50%;animation:pulse-ring 2.5s cubic-bezier(.25,.46,.45,.94) infinite}.pulse-ring-1{width:40px;height:40px;border-color:var(--primary-color);animation-delay:0s;box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 30%,transparent)}.pulse-ring-2{width:60px;height:60px;border-color:var(--tertiary-color);animation-delay:.6s;box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 25%,transparent)}.pulse-ring-3{width:80px;height:80px;border-color:var(--secondary-color);animation-delay:1.2s;box-shadow:0 0 30px color-mix(in srgb,var(--secondary-color) 20%,transparent)}.pulse-ring-4{width:100px;height:100px;border-color:var(--primary-color);animation-delay:1.8s;opacity:.7;box-shadow:0 0 35px color-mix(in srgb,var(--primary-color) 15%,transparent)}.loading-text{text-align:center;color:var(--text-color);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.loading-text p{margin:0;font-size:1.2rem;font-weight:500;color:var(--primary-color);letter-spacing:.5px;animation:text-pulse 2s ease-in-out infinite}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(30px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes pulse-ring{0%{transform:scale(.6);opacity:1;border-width:4px}50%{opacity:.8;border-width:2px}to{transform:scale(1.3);opacity:0;border-width:1px}}@keyframes text-pulse{0%,to{opacity:.8}50%{opacity:1;text-shadow:0 0 10px color-mix(in srgb,var(--primary-color) 30%,transparent)}}@media(max-width:768px){.spinner-container{min-width:200px;padding:2rem;gap:1.5rem}.pulse-rings{width:100px;height:100px}.pulse-ring-1{width:35px;height:35px}.pulse-ring-2{width:50px;height:50px}.pulse-ring-3{width:65px;height:65px}.pulse-ring-4{width:80px;height:80px}.loading-text p{font-size:1rem}}.dark-theme .spinner-container{box-shadow:0 20px 40px color-mix(in srgb,black 40%,transparent);border-color:var(--neutral-color)}.dark-theme .pulse-ring-1{box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 50%,transparent)}.dark-theme .pulse-ring-2{box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 40%,transparent)}.as400-theme .pulse-ring{filter:drop-shadow(0 0 8px var(--primary-color))}.as400-theme .loading-text p{text-shadow:0 0 10px var(--primary-color)}\n"] }]
8285
- }], propDecorators: { shown: [{ type: i0.Input, args: [{ isSignal: true, alias: "shown", required: false }] }] } });
8286
8089
 
8287
- class ChrSimpleTableComponent {
8090
+ class DataGrid {
8288
8091
  constructor() {
8289
- this.id = input(crypto.randomUUID(), /* @ts-ignore */
8092
+ this.formatterService = inject(DataFormatterService);
8093
+ this.id = signal(crypto.randomUUID(), /* @ts-ignore */
8290
8094
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
8291
- this.data = input.required(/* @ts-ignore */
8292
- ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
8293
- this.layout = input('auto', /* @ts-ignore */
8294
- ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
8295
- this.page = model(1, /* @ts-ignore */
8296
- ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
8095
+ this.title = input(null, /* @ts-ignore */
8096
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
8097
+ this.table = model(/* @ts-ignore */
8098
+ ...(ngDevMode ? [undefined, { debugName: "table" }] : /* istanbul ignore next */ []));
8099
+ this.viewport = viewChild('viewport', /* @ts-ignore */
8100
+ ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
8101
+ this.headerScroll = viewChild('headerScroll', /* @ts-ignore */
8102
+ ...(ngDevMode ? [{ debugName: "headerScroll" }] : /* istanbul ignore next */ []));
8103
+ this.currentPage = model(1, /* @ts-ignore */
8104
+ ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
8297
8105
  this.pageSize = model(10, /* @ts-ignore */
8298
8106
  ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
8299
- }
8300
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSimpleTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrSimpleTableComponent, isStandalone: true, selector: "chr-table", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", pageSize: "pageSizeChange" }, queries: [{ propertyName: "captionTemplate", first: true, predicate: ["caption"], descendants: true, read: TemplateRef }, { propertyName: "headerTemplate", first: true, predicate: ["header"], descendants: true, read: TemplateRef }, { propertyName: "rowTemplate", first: true, predicate: ["row"], descendants: true, read: TemplateRef }], ngImport: i0, template: "<table class=\"chr-table\" [attr.data-layout]=\"layout()\">\n @if (captionTemplate) {\n <caption>\n <ng-container [ngTemplateOutlet]=\"captionTemplate\"></ng-container>\n </caption>\n }\n @if (headerTemplate) {\n <thead>\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"></ng-container>\n </thead>\n }\n @if (rowTemplate) {\n <tbody>\n @for (entry of data() | paginate: { id: id(), itemsPerPage: pageSize(), currentPage: page() }; track $index) {\n <ng-container [ngTemplateOutlet]=\"rowTemplate\" [ngTemplateOutletContext]=\"{ $implicit: entry }\">\n </ng-container>\n } @empty {\n <tr>\n <td>Aucune donn\u00E9e disponible</td>\n </tr>\n }\n </tbody>\n }\n</table>\n<chr-paginator [id]=\"id()\" [(page)]=\"page\" [(pageSize)]=\"pageSize\"></chr-paginator>", styles: [":host{display:block}.chr-table{table-layout:auto;width:100%;border-collapse:collapse;border-radius:.5rem;overflow:hidden;box-shadow:0 .125rem .5rem #00000012}.chr-table[data-layout=fixed]{table-layout:fixed}.chr-table thead th{text-align:left}.chr-table thead th:last-child:not(:first-child){text-align:right}.chr-table tbody th{text-align:center}.chr-table tbody td{text-align:left}.chr-table tbody td:last-child:not(:first-child){text-align:right}.chr-table th,.chr-table td{padding:.75rem;text-align:left}.chr-table td{padding:.25rem .75rem}.chr-table caption{padding:.75rem;text-align:left;width:100%}.chr-table th,.chr-table caption{background-color:var(--primary-color);color:var(--primary-contrast-color);font-weight:600}.chr-table tr:nth-child(2n){background-color:var(--neutral-color)}.chr-table tr:hover{box-shadow:inset 0 0 0 .125rem color-mix(in srgb,var(--primary-color) 30%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgxPaginationModule }, { kind: "component", type: ChrPaginatorComponent, selector: "app-chr-paginator,chr-paginator, chr-paginator", inputs: ["page", "pageSize", "id", "allowSizeChange", "allowPageEdit", "isPageEditing"], outputs: ["pageChange", "pageSizeChange", "isPageEditingChange"] }, { kind: "pipe", type: i2.PaginatePipe, name: "paginate" }], encapsulation: i0.ViewEncapsulation.None }); }
8302
- }
8303
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSimpleTableComponent, decorators: [{
8304
- type: Component,
8305
- args: [{ selector: 'chr-table', imports: [CommonModule, NgxPaginationModule, ChrPaginatorComponent], encapsulation: ViewEncapsulation.None, template: "<table class=\"chr-table\" [attr.data-layout]=\"layout()\">\n @if (captionTemplate) {\n <caption>\n <ng-container [ngTemplateOutlet]=\"captionTemplate\"></ng-container>\n </caption>\n }\n @if (headerTemplate) {\n <thead>\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"></ng-container>\n </thead>\n }\n @if (rowTemplate) {\n <tbody>\n @for (entry of data() | paginate: { id: id(), itemsPerPage: pageSize(), currentPage: page() }; track $index) {\n <ng-container [ngTemplateOutlet]=\"rowTemplate\" [ngTemplateOutletContext]=\"{ $implicit: entry }\">\n </ng-container>\n } @empty {\n <tr>\n <td>Aucune donn\u00E9e disponible</td>\n </tr>\n }\n </tbody>\n }\n</table>\n<chr-paginator [id]=\"id()\" [(page)]=\"page\" [(pageSize)]=\"pageSize\"></chr-paginator>", styles: [":host{display:block}.chr-table{table-layout:auto;width:100%;border-collapse:collapse;border-radius:.5rem;overflow:hidden;box-shadow:0 .125rem .5rem #00000012}.chr-table[data-layout=fixed]{table-layout:fixed}.chr-table thead th{text-align:left}.chr-table thead th:last-child:not(:first-child){text-align:right}.chr-table tbody th{text-align:center}.chr-table tbody td{text-align:left}.chr-table tbody td:last-child:not(:first-child){text-align:right}.chr-table th,.chr-table td{padding:.75rem;text-align:left}.chr-table td{padding:.25rem .75rem}.chr-table caption{padding:.75rem;text-align:left;width:100%}.chr-table th,.chr-table caption{background-color:var(--primary-color);color:var(--primary-contrast-color);font-weight:600}.chr-table tr:nth-child(2n){background-color:var(--neutral-color)}.chr-table tr:hover{box-shadow:inset 0 0 0 .125rem color-mix(in srgb,var(--primary-color) 30%,transparent)}\n"] }]
8306
- }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], captionTemplate: [{
8307
- type: ContentChild,
8308
- args: ['caption', { read: TemplateRef }]
8309
- }], headerTemplate: [{
8310
- type: ContentChild,
8311
- args: ['header', { read: TemplateRef }]
8312
- }], rowTemplate: [{
8313
- type: ContentChild,
8314
- args: ['row', { read: TemplateRef }]
8315
- }] } });
8316
-
8317
- class TabToEnterHandlerDirective {
8318
- constructor() {
8319
- this.el = inject(ElementRef);
8320
- }
8321
- handleTab(event) {
8322
- if (event.key === 'Tab') {
8323
- const enter = new KeyboardEvent('keyup', {
8324
- ...event,
8325
- key: 'Enter',
8326
- code: 'Enter',
8327
- bubbles: true,
8328
- keyCode: 13,
8107
+ // public pageChange = output<number>();
8108
+ // public pageSizeChange = output<number>();
8109
+ this.onInit = output();
8110
+ this.selectedColumn = signal(null, /* @ts-ignore */
8111
+ ...(ngDevMode ? [{ debugName: "selectedColumn" }] : /* istanbul ignore next */ []));
8112
+ this.sort = model(null, /* @ts-ignore */
8113
+ ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
8114
+ this.filter = model({}, /* @ts-ignore */
8115
+ ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
8116
+ this.groupBy = model(null, /* @ts-ignore */
8117
+ ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
8118
+ this.cellChange = output();
8119
+ this.rowClick = output();
8120
+ this.enableVirtualization = input(false, /* @ts-ignore */
8121
+ ...(ngDevMode ? [{ debugName: "enableVirtualization" }] : /* istanbul ignore next */ []));
8122
+ this.allowFiltering = input(true, /* @ts-ignore */
8123
+ ...(ngDevMode ? [{ debugName: "allowFiltering" }] : /* istanbul ignore next */ []));
8124
+ this.allowGrouping = input(true, /* @ts-ignore */
8125
+ ...(ngDevMode ? [{ debugName: "allowGrouping" }] : /* istanbul ignore next */ []));
8126
+ this.allowInsert = input(false, /* @ts-ignore */
8127
+ ...(ngDevMode ? [{ debugName: "allowInsert" }] : /* istanbul ignore next */ []));
8128
+ this.allowUpdate = input(false, /* @ts-ignore */
8129
+ ...(ngDevMode ? [{ debugName: "allowUpdate" }] : /* istanbul ignore next */ []));
8130
+ this.allowDelete = input(false, /* @ts-ignore */
8131
+ ...(ngDevMode ? [{ debugName: "allowDelete" }] : /* istanbul ignore next */ []));
8132
+ this.allowDuplicate = input(false, /* @ts-ignore */
8133
+ ...(ngDevMode ? [{ debugName: "allowDuplicate" }] : /* istanbul ignore next */ []));
8134
+ this.allowSync = input(true, /* @ts-ignore */
8135
+ ...(ngDevMode ? [{ debugName: "allowSync" }] : /* istanbul ignore next */ []));
8136
+ this.allowRefresh = input(false, /* @ts-ignore */
8137
+ ...(ngDevMode ? [{ debugName: "allowRefresh" }] : /* istanbul ignore next */ []));
8138
+ this.allowNavigation = input(false, /* @ts-ignore */
8139
+ ...(ngDevMode ? [{ debugName: "allowNavigation" }] : /* istanbul ignore next */ []));
8140
+ this.allowExport = input(false, /* @ts-ignore */
8141
+ ...(ngDevMode ? [{ debugName: "allowExport" }] : /* istanbul ignore next */ []));
8142
+ this.allowManageTrackingPersistence = input(false, /* @ts-ignore */
8143
+ ...(ngDevMode ? [{ debugName: "allowManageTrackingPersistence" }] : /* istanbul ignore next */ []));
8144
+ this.allowManageDistincValueFetching = input(false, /* @ts-ignore */
8145
+ ...(ngDevMode ? [{ debugName: "allowManageDistincValueFetching" }] : /* istanbul ignore next */ []));
8146
+ this.maxHeight = input('100%', /* @ts-ignore */
8147
+ ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
8148
+ this.onExport = output();
8149
+ this.onSync = output();
8150
+ this.onRefresh = output();
8151
+ this.onAddRow = output();
8152
+ this.onDeleteRow = output();
8153
+ /**
8154
+ * Only supported for DB2 tables. For other types, use the onNavigate event.
8155
+ */
8156
+ this.onForeignTableNavigate = output();
8157
+ this.onNavigate = output();
8158
+ this.onFilterValueChange = output();
8159
+ this.allowFetchDistinctValues = model(false, /* @ts-ignore */
8160
+ ...(ngDevMode ? [{ debugName: "allowFetchDistinctValues" }] : /* istanbul ignore next */ []));
8161
+ this.crossRequestTrackingPersistence = model(false, /* @ts-ignore */
8162
+ ...(ngDevMode ? [{ debugName: "crossRequestTrackingPersistence" }] : /* istanbul ignore next */ []));
8163
+ this.modifiedCells = model([], /* @ts-ignore */
8164
+ ...(ngDevMode ? [{ debugName: "modifiedCells" }] : /* istanbul ignore next */ []));
8165
+ this.modifiedRows = model([], /* @ts-ignore */
8166
+ ...(ngDevMode ? [{ debugName: "modifiedRows" }] : /* istanbul ignore next */ []));
8167
+ this.addedRows = model([], /* @ts-ignore */
8168
+ ...(ngDevMode ? [{ debugName: "addedRows" }] : /* istanbul ignore next */ []));
8169
+ this.deletedRows = model([], /* @ts-ignore */
8170
+ ...(ngDevMode ? [{ debugName: "deletedRows" }] : /* istanbul ignore next */ []));
8171
+ this.distinctValues = input({}, /* @ts-ignore */
8172
+ ...(ngDevMode ? [{ debugName: "distinctValues" }] : /* istanbul ignore next */ []));
8173
+ this.actions = input([], /* @ts-ignore */
8174
+ ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
8175
+ this.cellTemplates = contentChildren(ChrCellDefDirective, /* @ts-ignore */
8176
+ ...(ngDevMode ? [{ debugName: "cellTemplates" }] : /* istanbul ignore next */ []));
8177
+ this.templatesByColumn = computed(() => {
8178
+ const map = new Map();
8179
+ for (const t of this.cellTemplates()) {
8180
+ const col = t.columnName();
8181
+ if (col)
8182
+ map.set(col.toLowerCase(), t);
8183
+ }
8184
+ return map;
8185
+ }, /* @ts-ignore */
8186
+ ...(ngDevMode ? [{ debugName: "templatesByColumn" }] : /* istanbul ignore next */ []));
8187
+ this.templatesByType = computed(() => {
8188
+ const map = new Map();
8189
+ for (const t of this.cellTemplates()) {
8190
+ const type = t.dataType();
8191
+ if (type)
8192
+ map.set(type.toLowerCase(), t);
8193
+ }
8194
+ return map;
8195
+ }, /* @ts-ignore */
8196
+ ...(ngDevMode ? [{ debugName: "templatesByType" }] : /* istanbul ignore next */ []));
8197
+ this.columnSuggestions = computed(() => {
8198
+ const distinctVals = this.distinctValues();
8199
+ // Si des valeurs distinctes sont fournies par le parent, on les utilise
8200
+ if (Object.keys(distinctVals).length > 0) {
8201
+ return distinctVals;
8202
+ }
8203
+ // Sinon, on utilise l'ancien comportement (valeurs locales)
8204
+ const suggestions = {};
8205
+ this.columns().forEach((column) => {
8206
+ suggestions[column.columnName] = new Set();
8329
8207
  });
8330
- event.target?.dispatchEvent(enter);
8331
- }
8332
- }
8333
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TabToEnterHandlerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8334
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: TabToEnterHandlerDirective, isStandalone: true, selector: "[tab-enter]", host: { listeners: { "keydown": "handleTab($event)" } }, ngImport: i0 }); }
8335
- }
8336
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TabToEnterHandlerDirective, decorators: [{
8337
- type: Directive,
8338
- args: [{
8339
- selector: '[tab-enter]',
8340
- standalone: true,
8341
- }]
8342
- }], propDecorators: { handleTab: [{
8343
- type: HostListener,
8344
- args: ['keydown', ['$event']]
8345
- }] } });
8346
-
8347
- class ChrPreventReloadDirective {
8348
- constructor() {
8349
- this.host = inject(ElementRef);
8350
- this.chrPreventReload = input(false, /* @ts-ignore */
8351
- ...(ngDevMode ? [{ debugName: "chrPreventReload" }] : /* istanbul ignore next */ []));
8352
- }
8353
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrPreventReloadDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8354
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrPreventReloadDirective, isStandalone: true, selector: "[chrPreventReload]", inputs: { chrPreventReload: { classPropertyName: "chrPreventReload", publicName: "chrPreventReload", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:beforeunload": "chrPreventReload() ? $event.preventDefault() : null" } }, ngImport: i0 }); }
8355
- }
8356
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrPreventReloadDirective, decorators: [{
8357
- type: Directive,
8358
- args: [{
8359
- selector: '[chrPreventReload]',
8360
- host: {
8361
- '(window:beforeunload)': 'chrPreventReload() ? $event.preventDefault() : null',
8362
- },
8363
- }]
8364
- }], propDecorators: { chrPreventReload: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrPreventReload", required: false }] }] } });
8365
-
8366
- class ChrDebounceDirective {
8367
- constructor() {
8368
- this.host = inject(ElementRef);
8369
- this.defaults = inject(CHR_DEBOUNCE_DEFAULTS);
8370
- this.renderer = inject(Renderer2);
8371
- this.subject = new Subject();
8372
- this.chrDebounceTime = input(null, /* @ts-ignore */
8373
- ...(ngDevMode ? [{ debugName: "chrDebounceTime" }] : /* istanbul ignore next */ []));
8374
- this.debounceTimeValue = computed(() => {
8375
- const time = this.chrDebounceTime();
8376
- if (time !== null && !isNaN(Number(time))) {
8377
- return Number(time);
8208
+ this.table()?.rows.forEach((row) => {
8209
+ this.columns().forEach((column) => {
8210
+ const value = row[column.columnName];
8211
+ if (value !== null && value !== undefined) {
8212
+ suggestions[column.columnName].add(value);
8213
+ }
8214
+ });
8215
+ });
8216
+ return Object.entries(suggestions).reduce((acc, [key, value]) => {
8217
+ acc[key] = Array.from(value);
8218
+ return acc;
8219
+ }, {});
8220
+ }, /* @ts-ignore */
8221
+ ...(ngDevMode ? [{ debugName: "columnSuggestions" }] : /* istanbul ignore next */ []));
8222
+ // protected _addedRows = computed(() => this.addedRows().map((r) => r.row));
8223
+ this.trackByMode = model('index', /* @ts-ignore */
8224
+ ...(ngDevMode ? [{ debugName: "trackByMode" }] : /* istanbul ignore next */ []));
8225
+ this.hasChanges = computed(() => {
8226
+ return (this.addedRows().length > 0 ||
8227
+ this.modifiedRows().length > 0 ||
8228
+ this.deletedRows().length > 0);
8229
+ }, /* @ts-ignore */
8230
+ ...(ngDevMode ? [{ debugName: "hasChanges" }] : /* istanbul ignore next */ []));
8231
+ this.groupByMap = computed(() => {
8232
+ const includedMap = {};
8233
+ this.groupBy()?.included.forEach((col) => {
8234
+ includedMap[col] = true;
8235
+ });
8236
+ return includedMap;
8237
+ }, /* @ts-ignore */
8238
+ ...(ngDevMode ? [{ debugName: "groupByMap" }] : /* istanbul ignore next */ []));
8239
+ this.columns = computed(() => {
8240
+ const table = this.table();
8241
+ const metadatas = table?.columnMetadatas || [];
8242
+ const groupedColumns = this.groupBy()?.included || [];
8243
+ //Sort grouped columns first then computed columns then the rest
8244
+ if (this.groupBy() !== null)
8245
+ return [
8246
+ ...metadatas.filter((col) => groupedColumns.includes(col.columnName)),
8247
+ ...metadatas.filter((col) => !groupedColumns.includes(col.columnName) && !col.tableName),
8248
+ ...metadatas.filter((col) => !groupedColumns.includes(col.columnName) && col.tableName),
8249
+ ];
8250
+ else
8251
+ return metadatas;
8252
+ }, /* @ts-ignore */
8253
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
8254
+ this.rows = computed(() => {
8255
+ const table = this.table();
8256
+ if (!table)
8257
+ return [];
8258
+ const computedRows = [];
8259
+ for (const row of table.rows) {
8260
+ const rowId = table.getRowIdentifier(row);
8261
+ const updated = this.modifiedRows().find((mr) => table.getRowIdentifier(mr.current) === rowId);
8262
+ const deleted = this.deletedRows().find((dr) => table.getRowIdentifier(dr.row) === rowId);
8263
+ computedRows.push({
8264
+ id: rowId,
8265
+ data: updated ? updated.current : row,
8266
+ flag: deleted ? 'deleted' : updated ? 'modified' : 'unchanged',
8267
+ valid: this.validateRow(deleted ? deleted.row : updated ? updated.current : row),
8268
+ });
8378
8269
  }
8379
- return this.defaults.baseInputs;
8270
+ for (const addedEntry of this.addedRows()) {
8271
+ const rowId = table.getRowIdentifier(addedEntry.row);
8272
+ computedRows.push({
8273
+ id: rowId,
8274
+ data: addedEntry.row,
8275
+ flag: 'added',
8276
+ valid: this.validateRow(addedEntry.row),
8277
+ });
8278
+ }
8279
+ return computedRows;
8380
8280
  }, /* @ts-ignore */
8381
- ...(ngDevMode ? [{ debugName: "debounceTimeValue" }] : /* istanbul ignore next */ []));
8382
- this.subscription = of();
8383
- this.isDispatching = false;
8384
- }
8385
- ngOnInit() {
8386
- const element = this.host.nativeElement;
8387
- this.subscription = this.subject.pipe(debounceTime$1(this.debounceTimeValue()));
8388
- // Listener en phase CAPTURE pour intercepter AVANT tous les listeners Angular
8389
- this.captureListener = this.renderer.listen(element, 'input', (event) => {
8390
- if (this.isDispatching) {
8391
- return;
8281
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
8282
+ this.totalItems = computed(() => {
8283
+ return 'total' in (this.table() || {})
8284
+ ? this.table()?.total || -1
8285
+ : this.table()?.count || -1;
8286
+ }, /* @ts-ignore */
8287
+ ...(ngDevMode ? [{ debugName: "totalItems" }] : /* istanbul ignore next */ []));
8288
+ this.currentItems = computed(() => {
8289
+ return 'total' in (this.table() || {})
8290
+ ? this.table()?.total || 0
8291
+ : this.table()?.count || 0;
8292
+ }, /* @ts-ignore */
8293
+ ...(ngDevMode ? [{ debugName: "currentItems" }] : /* istanbul ignore next */ []));
8294
+ // Properties to allow the datagrid to work in standalone without weird hacks.
8295
+ this.data = input(null, /* @ts-ignore */
8296
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
8297
+ this.name = input(null, /* @ts-ignore */
8298
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
8299
+ this.primaryKey = input(null, /* @ts-ignore */
8300
+ ...(ngDevMode ? [{ debugName: "primaryKey" }] : /* istanbul ignore next */ []));
8301
+ this.metadata = input(/* @ts-ignore */
8302
+ ...(ngDevMode ? [undefined, { debugName: "metadata" }] : /* istanbul ignore next */ []));
8303
+ this.autoDataSource = null;
8304
+ this.onFilterValue = (columnName, value) => {
8305
+ if (this.allowFetchDistinctValues()) {
8306
+ this.onFilterValueChange.emit({
8307
+ column: columnName,
8308
+ value: value,
8309
+ });
8392
8310
  }
8393
- event.preventDefault();
8394
- event.stopImmediatePropagation();
8395
- event.stopPropagation();
8396
- this.subject.next(event);
8397
- }, { capture: true });
8398
- this.subscription.subscribe((event) => {
8399
- this.isDispatching = true;
8400
- const newEvent = new InputEvent('input', {
8401
- bubbles: true,
8402
- cancelable: true,
8403
- data: event.data,
8404
- inputType: event.inputType,
8311
+ };
8312
+ this.onGroupByIncludedChange = (columnName, included) => {
8313
+ this.groupBy.update((currentGroup) => {
8314
+ if (currentGroup === null)
8315
+ return currentGroup;
8316
+ if (included) {
8317
+ currentGroup?.included.push(columnName);
8318
+ }
8319
+ else {
8320
+ currentGroup.included = currentGroup.included.filter((c) => c !== columnName);
8321
+ }
8322
+ return { ...currentGroup };
8405
8323
  });
8406
- element.dispatchEvent(newEvent);
8407
- setTimeout(() => (this.isDispatching = false), 0);
8408
- });
8409
- }
8410
- ngOnDestroy() {
8411
- this.captureListener?.();
8412
- this.subject.complete();
8413
- }
8414
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrDebounceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8415
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrDebounceDirective, isStandalone: true, selector: "[chrDebounce]", inputs: { chrDebounceTime: { classPropertyName: "chrDebounceTime", publicName: "chrDebounceTime", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
8416
- }
8417
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrDebounceDirective, decorators: [{
8418
- type: Directive,
8419
- args: [{
8420
- selector: '[chrDebounce]',
8421
- }]
8422
- }], propDecorators: { chrDebounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrDebounceTime", required: false }] }] } });
8423
-
8424
- class ChrHoverDirective {
8425
- constructor() {
8426
- this.el = inject((ElementRef));
8427
- this.overlay = inject(Overlay);
8428
- this.vcr = inject(ViewContainerRef);
8429
- this.renderer = inject(Renderer2);
8430
- this.eventListeners = [];
8431
- this.chrHoverContext = input({}, /* @ts-ignore */
8432
- ...(ngDevMode ? [{ debugName: "chrHoverContext" }] : /* istanbul ignore next */ []));
8433
- this.chrHoverSnap = input(false, /* @ts-ignore */
8434
- ...(ngDevMode ? [{ debugName: "chrHoverSnap" }] : /* istanbul ignore next */ []));
8435
- this.chrHoverPosition = input('right', /* @ts-ignore */
8436
- ...(ngDevMode ? [{ debugName: "chrHoverPosition" }] : /* istanbul ignore next */ []));
8437
- this.chrHoverCloseDelay = input(10, /* @ts-ignore */
8438
- ...(ngDevMode ? [{ debugName: "chrHoverCloseDelay" }] : /* istanbul ignore next */ []));
8439
- this.closeTimeout = null;
8440
- this.overlayEventListeners = [];
8441
- this.mousePositionStrategy = (mousemove) => {
8442
- const positionOffset = { left: mousemove.clientX, top: mousemove.clientY };
8443
- const componentSize = this.overlayRef?.overlayElement.getBoundingClientRect();
8444
- switch (this.chrHoverPosition()) {
8445
- case 'above':
8446
- positionOffset.top -= (componentSize?.height || 0) + 20;
8447
- positionOffset.left -= (componentSize?.width || 0) / 2;
8448
- break;
8449
- case 'below':
8450
- positionOffset.top += 20;
8451
- positionOffset.left -= (componentSize?.width || 0) / 2;
8452
- break;
8453
- case 'left':
8454
- positionOffset.left -= (componentSize?.width || 0) + 20;
8455
- positionOffset.top -= (componentSize?.height || 0) / 2;
8324
+ };
8325
+ this.onCellChange = (index, row, column, value) => {
8326
+ const initial = { ...row.data };
8327
+ row.data[column.columnName] = value;
8328
+ // If the row is created, we don't track it as modified
8329
+ switch (row.flag) {
8330
+ case 'added':
8331
+ this.addedRows.update((rows) => rows.map((r) => {
8332
+ if (this.table()?.getRowIdentifier(r.row) ===
8333
+ this.table()?.getRowIdentifier(row.data)) {
8334
+ r.row[column.columnName] = value;
8335
+ }
8336
+ return r;
8337
+ }));
8338
+ // this.addedRows.update((rows) => {
8339
+ // rows.find(
8340
+ // (r) =>
8341
+ // this.table()?.getRowIdentifier(r.row) ===
8342
+ // this.table()?.getRowIdentifier(row.data)
8343
+ // )!.row[column.columnName] = value;
8344
+ // return [...rows];
8345
+ // });
8346
+ return;
8347
+ case 'modified':
8348
+ this.modifiedRows.update((rows) => rows.map((r) => {
8349
+ if (r.index === index) {
8350
+ r.current = row.data;
8351
+ }
8352
+ return r;
8353
+ }));
8354
+ // this.modifiedRows.update((rows) => {
8355
+ // const changed = this.modifiedRows().find(
8356
+ // (modifiedRow) => modifiedRow.index === index
8357
+ // );
8358
+ // changed!.current = row.data;
8359
+ // return [...rows];
8360
+ // });
8456
8361
  break;
8457
- case 'right':
8458
- positionOffset.left += 20;
8459
- positionOffset.top -= (componentSize?.height || 0) / 2;
8362
+ case 'unchanged':
8363
+ this.modifiedRows.set([
8364
+ ...this.modifiedRows(),
8365
+ { index: index, original: initial, current: row.data },
8366
+ ]);
8460
8367
  break;
8461
8368
  }
8462
- const positionStrategy = this.overlay
8463
- .position()
8464
- .global()
8465
- .left(`${positionOffset.left}px`)
8466
- .top(`${positionOffset.top}px`);
8467
- return positionStrategy;
8468
8369
  };
8469
- this.snappedStrategy = () => {
8470
- const positionStrategy = this.overlay
8471
- .position()
8472
- .flexibleConnectedTo(this.el);
8473
- switch (this.chrHoverPosition()) {
8474
- case 'above':
8475
- positionStrategy.withPositions([
8476
- {
8477
- originX: 'center',
8478
- originY: 'top',
8479
- overlayX: 'center',
8480
- overlayY: 'bottom',
8481
- },
8482
- {
8483
- originX: 'center',
8484
- originY: 'bottom',
8485
- overlayX: 'center',
8486
- overlayY: 'top',
8487
- },
8488
- ]);
8489
- break;
8490
- case 'below':
8491
- positionStrategy.withPositions([
8492
- {
8493
- originX: 'center',
8494
- originY: 'bottom',
8495
- overlayX: 'center',
8496
- overlayY: 'top',
8497
- },
8498
- {
8499
- originX: 'center',
8500
- originY: 'top',
8501
- overlayX: 'center',
8502
- overlayY: 'bottom',
8503
- },
8504
- ]);
8370
+ this.addRow = () => {
8371
+ this.addedRows.set([
8372
+ ...this.addedRows(),
8373
+ { index: this.addedRows().length, row: {} },
8374
+ ]);
8375
+ this.onAddRow.emit();
8376
+ };
8377
+ this.getRowTrackBy = (row, index) => {
8378
+ const table = this.table();
8379
+ // return table ? table.getRowIdentifier(row) : `row-${index}`;
8380
+ return table?.getRowNumber(row) ? table.getRowNumber(row) : index;
8381
+ };
8382
+ this.hasInvalidRows = () => {
8383
+ const allRows = [
8384
+ ...this.addedRows().map((r) => r.row),
8385
+ ...this.modifiedRows().map((r) => r.current),
8386
+ ];
8387
+ return allRows.some((row) => !this.validateRow(row));
8388
+ };
8389
+ this.validateRow = (row) => {
8390
+ if (this.groupBy() !== null)
8391
+ return true;
8392
+ return this.formatterService.validateRow(row, this.columns());
8393
+ };
8394
+ this.canEdit = (column) => {
8395
+ const formatter = this.formatterService.getFormatter(null, column);
8396
+ if (!formatter)
8397
+ return false && this.allowUpdate();
8398
+ return this.allowUpdate() && formatter.canEdit(null, column);
8399
+ };
8400
+ this.isPrimaryKeyColumn = (column) => {
8401
+ const table = this.table();
8402
+ if (!table)
8403
+ return false;
8404
+ return table
8405
+ .primaryKeyColumns()
8406
+ .some((pk) => pk.columnName === column.columnName);
8407
+ };
8408
+ this.isUniqueKeyColumn = (column) => {
8409
+ const table = this.table();
8410
+ if (!table)
8411
+ return false;
8412
+ const uniqueConstraints = table.tableConstraints.filter((tc) => tc.constraintType === 'UNIQUE');
8413
+ for (const uc of uniqueConstraints) {
8414
+ if (uc.columns.some((col) => col.columnName === column.columnName)) {
8415
+ return true;
8416
+ }
8417
+ }
8418
+ return false;
8419
+ };
8420
+ this.isEditSafe = (column) => {
8421
+ if (this.groupBy() !== null)
8422
+ return false;
8423
+ if (this.trackByMode() === 'index')
8424
+ return true;
8425
+ if (this.crossRequestTrackingPersistence() === false)
8426
+ return true;
8427
+ return (this.trackByMode() === 'primaryKey' &&
8428
+ this.table()
8429
+ ?.primaryKeyColumns()
8430
+ .some((pk) => pk.columnName === column.columnName) == false);
8431
+ };
8432
+ this.isForeignKeyColumn = (column) => {
8433
+ const table = this.table();
8434
+ if (!table)
8435
+ return false;
8436
+ const fkConstraints = table.tableConstraints.filter((tc) => tc.constraintType === 'FOREIGN KEY');
8437
+ for (const fk of fkConstraints) {
8438
+ if (fk.columns.some((col) => col.columnName === column.columnName)) {
8439
+ return true;
8440
+ }
8441
+ }
8442
+ return false;
8443
+ };
8444
+ this.datagridContextMenuActions = (column, filterElement, groupElement) => {
8445
+ const actions = [
8446
+ {
8447
+ label: 'Trier par ordre croissant',
8448
+ icon: 'arrow_upward',
8449
+ action: () => this.setSort(column, true),
8450
+ },
8451
+ {
8452
+ label: 'Trier par ordre décroissant',
8453
+ icon: 'arrow_downward',
8454
+ action: () => this.setSort(column, false),
8455
+ },
8456
+ {
8457
+ label: 'Effacer le tri',
8458
+ icon: 'clear',
8459
+ action: () => this.sort.set(null),
8460
+ },
8461
+ ];
8462
+ if (filterElement && this.allowFiltering()) {
8463
+ actions.push({
8464
+ label: 'Filtrer sur cette colonne...',
8465
+ icon: 'filter_list',
8466
+ action: () => filterElement.toggle(),
8467
+ });
8468
+ }
8469
+ if (groupElement && this.allowGrouping()) {
8470
+ actions.push({
8471
+ label: 'Grouper par cette colonne',
8472
+ icon: 'group',
8473
+ action: () => groupElement.isOpen.set(true),
8474
+ });
8475
+ }
8476
+ return actions;
8477
+ };
8478
+ this.datarowContextMenuActions = (row, rowIndex) => {
8479
+ const items = [];
8480
+ for (const action of this.actions()) {
8481
+ items.push({
8482
+ label: action.display,
8483
+ action: action.callback.bind(null, row.data),
8484
+ color: action.color,
8485
+ icon: action.icon,
8486
+ });
8487
+ }
8488
+ switch (row.flag) {
8489
+ case 'deleted':
8490
+ items.push({
8491
+ label: 'Annuler la suppression',
8492
+ action: () => this.deletedRows.set(this.deletedRows().filter((r) => r.index !== rowIndex)),
8493
+ icon: 'undo',
8494
+ color: 'primary',
8495
+ });
8505
8496
  break;
8506
- case 'left':
8507
- positionStrategy.withPositions([
8508
- {
8509
- originX: 'start',
8510
- originY: 'center',
8511
- overlayX: 'end',
8512
- overlayY: 'center',
8513
- },
8514
- {
8515
- originX: 'end',
8516
- originY: 'center',
8517
- overlayX: 'start',
8518
- overlayY: 'center',
8497
+ case 'added':
8498
+ items.push({
8499
+ label: 'Supprimer la ligne ajoutée',
8500
+ action: () => {
8501
+ this.addedRows.update((rows) => rows.filter((r) => this.table().getRowIdentifier(r.row) !== row.id));
8502
+ this.table()?.removeRow(row);
8519
8503
  },
8520
- ]);
8504
+ icon: 'delete',
8505
+ color: 'error',
8506
+ });
8521
8507
  break;
8522
- case 'right':
8523
- positionStrategy.withPositions([
8524
- {
8525
- originX: 'end',
8526
- originY: 'center',
8527
- overlayX: 'start',
8528
- overlayY: 'center',
8529
- },
8530
- {
8531
- originX: 'start',
8532
- originY: 'center',
8533
- overlayX: 'end',
8534
- overlayY: 'center',
8535
- },
8536
- ]);
8508
+ default:
8509
+ if (this.allowInsert() && this.allowDuplicate()) {
8510
+ items.push({
8511
+ label: 'Dupliquer la ligne',
8512
+ action: () => {
8513
+ const newRow = {
8514
+ [`${this.table()?.getRowIdentifier(row.data)}`]: this.addedRows().length + 1,
8515
+ ...row.data,
8516
+ };
8517
+ this.addedRows.set([
8518
+ ...this.addedRows(),
8519
+ { index: this.addedRows().length, row: newRow },
8520
+ ]);
8521
+ },
8522
+ icon: 'file_copy',
8523
+ color: 'primary',
8524
+ });
8525
+ }
8526
+ if (this.allowDelete()) {
8527
+ items.push({
8528
+ label: 'Supprimer la ligne',
8529
+ action: () => this.deleteRow(rowIndex, row.data),
8530
+ icon: 'delete',
8531
+ color: 'error',
8532
+ });
8533
+ }
8537
8534
  break;
8538
8535
  }
8539
- return positionStrategy;
8536
+ return items;
8540
8537
  };
8541
- const positionStrategy = this.snappedStrategy();
8542
- this.overlayRef = this.overlay.create({
8543
- positionStrategy,
8544
- hasBackdrop: false,
8545
- disposeOnNavigation: true,
8546
- disableClose: true,
8538
+ this.onAdd = () => {
8539
+ this.onAddRow.emit();
8540
+ setTimeout(() => {
8541
+ this.viewport()?.scrollTo({ bottom: 0 });
8542
+ }, 250);
8543
+ };
8544
+ this.log = (event) => {
8545
+ console.log(`[DataGrid-${this.id()}]`, event);
8546
+ };
8547
+ effect(() => {
8548
+ if (this.table() && !this.crossRequestTrackingPersistence()) {
8549
+ this.resetChanges();
8550
+ }
8551
+ });
8552
+ effect(() => {
8553
+ const mode = this.table()?.hasPrimaryKey ? 'primaryKey' : 'index';
8554
+ this.trackByMode.set(mode);
8555
+ });
8556
+ effect(() => {
8557
+ const data = this.data();
8558
+ if (!data)
8559
+ return;
8560
+ if (this.table() === undefined || this.table() === null) {
8561
+ if (this.autoDataSource != null)
8562
+ this.autoDataSource.setRows(data);
8563
+ }
8547
8564
  });
8548
8565
  }
8549
8566
  ngOnInit() {
8550
- this.overlayRef.updatePositionStrategy(this.snappedStrategy());
8551
- this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mouseenter', () => {
8552
- this.clearCloseTimeout();
8553
- this.open();
8554
- }));
8555
- this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mouseleave', () => {
8556
- this.startCloseTimeout();
8557
- }));
8558
- this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mousemove', (event) => {
8559
- if (!this.chrHoverSnap()) {
8560
- const positionStrategy = this.mousePositionStrategy(event);
8561
- this.overlayRef.updatePositionStrategy(positionStrategy);
8567
+ this.onInit.emit();
8568
+ if (this.table() === undefined || this.table() === null) {
8569
+ if (!this.autoDataSource) {
8570
+ this.autoDataSource = new AutoDataSource({
8571
+ enableClientSideFiltering: true,
8572
+ enableClientSidePagination: true,
8573
+ enableClientSideSorting: true,
8574
+ initialData: new AutoDataTable(this.name() ?? '', this.primaryKey(), this.metadata()),
8575
+ pageSize: this.pageSize(),
8576
+ });
8577
+ this.autoDataSource.registerDataGrid(this);
8562
8578
  }
8563
- }));
8579
+ this.autoDataSource.setRows(this.data() ?? []);
8580
+ }
8564
8581
  }
8565
- ngOnDestroy() {
8566
- this.clearCloseTimeout();
8567
- this.clearOverlayListeners();
8568
- this.overlayRef.dispose();
8569
- this.removeEventListeners();
8582
+ ngOnDestroy() { }
8583
+ getColumnTooltip(column) {
8584
+ return column.tooltip;
8570
8585
  }
8571
- removeEventListeners() {
8572
- this.eventListeners.forEach((unlisten) => unlisten());
8573
- this.eventListeners = [];
8586
+ toggleSort(columnName) {
8587
+ const currentSort = this.sort();
8588
+ let isAsc = true;
8589
+ if (currentSort?.column === columnName)
8590
+ isAsc = !currentSort.isAsc;
8591
+ this.sort.set({ column: columnName, isAsc: isAsc });
8574
8592
  }
8575
- startCloseTimeout() {
8576
- this.clearCloseTimeout();
8577
- this.closeTimeout = window.setTimeout(() => this.close(), this.chrHoverCloseDelay());
8593
+ setSort(columnName, isAsc) {
8594
+ this.sort.set({ column: columnName, isAsc: isAsc });
8578
8595
  }
8579
- clearCloseTimeout() {
8580
- if (this.closeTimeout != null) {
8581
- clearTimeout(this.closeTimeout);
8582
- this.closeTimeout = null;
8596
+ onFilterBy(columnName, filter) {
8597
+ const currentFilters = { ...this.filter() };
8598
+ if (filter.value) {
8599
+ currentFilters[columnName] = {
8600
+ column: filter.column,
8601
+ mode: filter.mode,
8602
+ value: filter.value,
8603
+ };
8604
+ }
8605
+ else {
8606
+ delete currentFilters[columnName];
8607
+ }
8608
+ this.filter.set(currentFilters);
8609
+ }
8610
+ onGroupBy(group) {
8611
+ if (group.mode === null)
8612
+ this.groupBy.set(null);
8613
+ else {
8614
+ // group.included =
8615
+ // this.table()?.columnMetadatas.map((c) => c.columnName) || [];
8616
+ group.included = [group.column];
8617
+ this.groupBy.set(group);
8583
8618
  }
8584
8619
  }
8585
- clearOverlayListeners() {
8586
- this.overlayEventListeners.forEach((u) => u());
8587
- this.overlayEventListeners = [];
8620
+ // Event handlers for pagination
8621
+ onPageChange(page) {
8622
+ this.currentPage.set(page);
8623
+ }
8624
+ onPageSizeChange(pageSize) {
8625
+ this.pageSize.set(pageSize);
8626
+ }
8627
+ refresh() {
8628
+ this.onRefresh.emit();
8629
+ // this.sort.set(null);
8630
+ // this.filter.set({});
8631
+ }
8632
+ getCellValue(row, columnName) {
8633
+ return row && row[columnName] !== undefined ? row[columnName] : '';
8634
+ }
8635
+ isRowAdded(row, index) {
8636
+ return this.addedRows().some((addedRow) => addedRow.index === index);
8588
8637
  }
8589
- open() {
8590
- if (!this.overlayRef.hasAttached()) {
8591
- if (!this.portal) {
8592
- this.portal = new TemplatePortal(this.template, this.vcr, this.chrHoverContext());
8593
- }
8594
- const ref = this.overlayRef.attach(this.portal);
8595
- // keep overlay open when overlay itself is hovered
8596
- const overlayEl = this.overlayRef.overlayElement;
8597
- this.clearOverlayListeners();
8598
- this.overlayEventListeners.push(this.renderer.listen(overlayEl, 'mouseenter', () => {
8599
- this.clearCloseTimeout();
8600
- }));
8601
- this.overlayEventListeners.push(this.renderer.listen(overlayEl, 'mouseleave', () => {
8602
- this.startCloseTimeout();
8603
- }));
8604
- }
8638
+ isRowChanged(row, index) {
8639
+ return this.modifiedRows().some((modifiedRow) => modifiedRow.index === index);
8605
8640
  }
8606
- close() {
8607
- this.clearCloseTimeout();
8608
- this.clearOverlayListeners();
8609
- this.overlayRef.detach();
8641
+ isCellChanged(rowIndex, columnName) {
8642
+ return this.modifiedCells().some((cell) => cell.rowIndex === rowIndex && cell.columnName === columnName);
8610
8643
  }
8611
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrHoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8612
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrHoverDirective, isStandalone: true, selector: "[chrHover]", inputs: { template: { classPropertyName: "template", publicName: "chrHover", isSignal: false, isRequired: false, transformFunction: null }, chrHoverContext: { classPropertyName: "chrHoverContext", publicName: "chrHoverContext", isSignal: true, isRequired: false, transformFunction: null }, chrHoverSnap: { classPropertyName: "chrHoverSnap", publicName: "chrHoverSnap", isSignal: true, isRequired: false, transformFunction: null }, chrHoverPosition: { classPropertyName: "chrHoverPosition", publicName: "chrHoverPosition", isSignal: true, isRequired: false, transformFunction: null }, chrHoverCloseDelay: { classPropertyName: "chrHoverCloseDelay", publicName: "chrHoverCloseDelay", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
8613
- {
8614
- provide: CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG,
8615
- useFactory: () => {
8616
- disableClose: true;
8617
- },
8618
- },
8619
- ], ngImport: i0 }); }
8620
- }
8621
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrHoverDirective, decorators: [{
8622
- type: Directive,
8623
- args: [{
8624
- selector: '[chrHover]',
8625
- providers: [
8626
- {
8627
- provide: CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG,
8628
- useFactory: () => {
8629
- disableClose: true;
8630
- },
8631
- },
8632
- ],
8633
- }]
8634
- }], ctorParameters: () => [], propDecorators: { template: [{
8635
- type: Input,
8636
- args: ['chrHover']
8637
- }], chrHoverContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverContext", required: false }] }], chrHoverSnap: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverSnap", required: false }] }], chrHoverPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverPosition", required: false }] }], chrHoverCloseDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverCloseDelay", required: false }] }] } });
8638
-
8639
- class CsvExporter {
8640
- export(data, columns) {
8641
- const csvRows = [];
8642
- csvRows.push(columns.join(','));
8643
- data.forEach((row) => {
8644
- const values = columns.map((col) => {
8645
- const cell = row[col];
8646
- return typeof cell === 'string' ? cell.replace(/;/g, ',') : cell;
8647
- });
8648
- csvRows.push(values.join(','));
8649
- });
8650
- const csvContent = csvRows.join('\n');
8651
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
8652
- return URL.createObjectURL(blob);
8644
+ isRowDeleted(row, index) {
8645
+ return this.deletedRows().some((deletedRow) => deletedRow.index === index);
8653
8646
  }
8654
- canExport(filetype) {
8655
- const supportedTypes = ['.csv', 'text/csv', 'csv'];
8656
- return supportedTypes.includes(filetype);
8647
+ deleteRow(index, row) {
8648
+ // If the row was modified, we need to get the original value and remove it from modifiedRows
8649
+ if (this.isRowChanged(row, index)) {
8650
+ const original = this.modifiedRows().find((modifiedRow) => modifiedRow.index === index)
8651
+ ?.original || row;
8652
+ this.deletedRows.set([...this.deletedRows(), { index, row: original }]);
8653
+ this.modifiedRows.set(this.modifiedRows().filter((modifiedRow) => modifiedRow.index !== index));
8654
+ return;
8655
+ }
8656
+ // If the row was added, we just remove it from addedRows
8657
+ if (this.isRowAdded(row, index)) {
8658
+ this.addedRows.set(this.addedRows().filter((addedRow) => addedRow.index !== index));
8659
+ }
8660
+ if (!this.isRowDeleted(row, index)) {
8661
+ this.deletedRows.set([...this.deletedRows(), { index, row }]);
8662
+ }
8663
+ this.onDeleteRow.emit({ index, row });
8657
8664
  }
8658
- }
8659
-
8660
- class JsonExporter {
8661
- export(data, columns) {
8662
- const jsonData = data.map((row) => {
8663
- const obj = {};
8664
- columns.forEach((col) => {
8665
- obj[col] = row[col];
8665
+ navigateToForeignTable(column) {
8666
+ if (!this.allowNavigation())
8667
+ return;
8668
+ if (column instanceof ColumnMetadata) {
8669
+ this.table()
8670
+ ?.getConstraintsByType('FOREIGN KEY')
8671
+ .forEach((constraint) => {
8672
+ const cstCol = constraint.columns.find((c) => c.columnName === column.columnName);
8673
+ if (cstCol && cstCol instanceof TableConstraintColumn) {
8674
+ const reference = cstCol
8675
+ .reference;
8676
+ if (reference) {
8677
+ return this.onForeignTableNavigate.emit({
8678
+ targetTable: cstCol.reference.otherKey.tableName,
8679
+ targetSchema: cstCol.reference.otherKey.tableSchema,
8680
+ });
8681
+ }
8682
+ }
8666
8683
  });
8667
- return obj;
8668
- });
8669
- const blob = new Blob([JSON.stringify(jsonData, null, 2)], {
8670
- type: 'application/json;charset=utf-8;',
8671
- });
8672
- return URL.createObjectURL(blob);
8684
+ }
8685
+ else {
8686
+ this.onNavigate.emit({ targetColumnMetadata: column });
8687
+ }
8673
8688
  }
8674
- canExport(filetype) {
8675
- const supportedTypes = ['.json', 'application/json', 'json'];
8676
- return supportedTypes.includes(filetype);
8689
+ resetChanges() {
8690
+ this.modifiedRows.set([]);
8691
+ this.addedRows.set([]);
8692
+ this.deletedRows.set([]);
8677
8693
  }
8678
- }
8679
-
8680
- class DataExporterService {
8681
- constructor() {
8682
- this.exporter = [new CsvExporter(), new JsonExporter()];
8694
+ getChangeSummary() {
8695
+ const modified = this.modifiedRows();
8696
+ const added = this.addedRows();
8697
+ const deleted = this.deletedRows();
8698
+ return {
8699
+ modified,
8700
+ added,
8701
+ deleted,
8702
+ hasChanges: modified.length > 0 || added.length > 0 || deleted.length > 0,
8703
+ };
8683
8704
  }
8684
- setExporters(exporters) {
8685
- this.exporter = exporters;
8705
+ getCellTemplate(columnName, dataType) {
8706
+ const colMatch = this.templatesByColumn().get(columnName.toLowerCase());
8707
+ if (colMatch)
8708
+ return colMatch.template;
8709
+ const typeMatch = this.templatesByType().get(dataType.toLowerCase());
8710
+ if (typeMatch)
8711
+ return typeMatch.template;
8712
+ return null;
8686
8713
  }
8687
- registerExporter(exporter) {
8688
- this.exporter.push(exporter);
8714
+ toggleCrossRequestTrackingPersistence() {
8715
+ this.crossRequestTrackingPersistence.set(!this.crossRequestTrackingPersistence());
8689
8716
  }
8690
- export(data, columns, filetype, filename = 'export', autodownload = false) {
8691
- const exporter = this.exporter.find((exp) => exp.canExport(filetype));
8692
- if (exporter) {
8693
- const url = exporter.export(data, columns);
8694
- if (autodownload) {
8695
- const link = document.createElement('a');
8696
- link.href = url;
8697
- link.download = `${filename}.${filetype.split('.').pop()}`;
8698
- document.body.appendChild(link);
8699
- link.click();
8700
- document.body.removeChild(link);
8701
- }
8702
- return url;
8717
+ toggleAllowFetchDistinctValues() {
8718
+ this.allowFetchDistinctValues.set(!this.allowFetchDistinctValues());
8719
+ }
8720
+ onViewportScroll(event) {
8721
+ const viewport = event.target;
8722
+ const headerScrollElement = this.headerScroll()?.nativeElement;
8723
+ if (headerScrollElement) {
8724
+ headerScrollElement.scrollLeft = viewport.scrollLeft;
8703
8725
  }
8704
- return null;
8705
8726
  }
8706
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8707
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, providedIn: 'root' }); }
8727
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataGrid, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8728
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: DataGrid, isStandalone: true, selector: "chr-data-grid", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, enableVirtualization: { classPropertyName: "enableVirtualization", publicName: "enableVirtualization", isSignal: true, isRequired: false, transformFunction: null }, allowFiltering: { classPropertyName: "allowFiltering", publicName: "allowFiltering", isSignal: true, isRequired: false, transformFunction: null }, allowGrouping: { classPropertyName: "allowGrouping", publicName: "allowGrouping", isSignal: true, isRequired: false, transformFunction: null }, allowInsert: { classPropertyName: "allowInsert", publicName: "allowInsert", isSignal: true, isRequired: false, transformFunction: null }, allowUpdate: { classPropertyName: "allowUpdate", publicName: "allowUpdate", isSignal: true, isRequired: false, transformFunction: null }, allowDelete: { classPropertyName: "allowDelete", publicName: "allowDelete", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicate: { classPropertyName: "allowDuplicate", publicName: "allowDuplicate", isSignal: true, isRequired: false, transformFunction: null }, allowSync: { classPropertyName: "allowSync", publicName: "allowSync", isSignal: true, isRequired: false, transformFunction: null }, allowRefresh: { classPropertyName: "allowRefresh", publicName: "allowRefresh", isSignal: true, isRequired: false, transformFunction: null }, allowNavigation: { classPropertyName: "allowNavigation", publicName: "allowNavigation", isSignal: true, isRequired: false, transformFunction: null }, allowExport: { classPropertyName: "allowExport", publicName: "allowExport", isSignal: true, isRequired: false, transformFunction: null }, allowManageTrackingPersistence: { classPropertyName: "allowManageTrackingPersistence", publicName: "allowManageTrackingPersistence", isSignal: true, isRequired: false, transformFunction: null }, allowManageDistincValueFetching: { classPropertyName: "allowManageDistincValueFetching", publicName: "allowManageDistincValueFetching", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, allowFetchDistinctValues: { classPropertyName: "allowFetchDistinctValues", publicName: "allowFetchDistinctValues", isSignal: true, isRequired: false, transformFunction: null }, crossRequestTrackingPersistence: { classPropertyName: "crossRequestTrackingPersistence", publicName: "crossRequestTrackingPersistence", isSignal: true, isRequired: false, transformFunction: null }, modifiedCells: { classPropertyName: "modifiedCells", publicName: "modifiedCells", isSignal: true, isRequired: false, transformFunction: null }, modifiedRows: { classPropertyName: "modifiedRows", publicName: "modifiedRows", isSignal: true, isRequired: false, transformFunction: null }, addedRows: { classPropertyName: "addedRows", publicName: "addedRows", isSignal: true, isRequired: false, transformFunction: null }, deletedRows: { classPropertyName: "deletedRows", publicName: "deletedRows", isSignal: true, isRequired: false, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, trackByMode: { classPropertyName: "trackByMode", publicName: "trackByMode", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, primaryKey: { classPropertyName: "primaryKey", publicName: "primaryKey", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { table: "tableChange", currentPage: "currentPageChange", pageSize: "pageSizeChange", onInit: "onInit", sort: "sortChange", filter: "filterChange", groupBy: "groupByChange", cellChange: "cellChange", rowClick: "rowClick", onExport: "onExport", onSync: "onSync", onRefresh: "onRefresh", onAddRow: "onAddRow", onDeleteRow: "onDeleteRow", onForeignTableNavigate: "onForeignTableNavigate", onNavigate: "onNavigate", onFilterValueChange: "onFilterValueChange", allowFetchDistinctValues: "allowFetchDistinctValuesChange", crossRequestTrackingPersistence: "crossRequestTrackingPersistenceChange", modifiedCells: "modifiedCellsChange", modifiedRows: "modifiedRowsChange", addedRows: "addedRowsChange", deletedRows: "deletedRowsChange", trackByMode: "trackByModeChange" }, providers: [
8729
+ PaginationService,
8730
+ {
8731
+ provide: CHR_DEBOUNCE_DEFAULTS,
8732
+ useValue: new DebounceDefaults({ hoverTitle: 0 }),
8733
+ },
8734
+ // {
8735
+ // provide: VIRTUAL_SCROLL_STRATEGY,
8736
+ // useClass: DynamicSizeScrollStrategy,
8737
+ // },
8738
+ ], queries: [{ propertyName: "cellTemplates", predicate: ChrCellDefDirective, isSignal: true }], viewQueries: [{ propertyName: "viewport", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "headerScroll", first: true, predicate: ["headerScroll"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (table()) {\r\n<div class=\"data-grid-container\">\r\n <div class=\"data-grid-header\">\r\n <h3>{{ title() ? title() : `${table()?.schema ? `${table()?.schema}.` : ''}${table()?.name}` }}</h3>\r\n @if(table()?.description){\r\n <span class=\"table-description\">{{ table()?.description }}</span>\r\n }\r\n <div class=\"data-grid-info\">\r\n <!-- {{allowSync()}} {{hasChanges()}} {{!hasInvalidRows()}} -->\r\n @if(allowManageDistincValueFetching()){\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"allowFetchDistinctValues()\" (click)=\"toggleAllowFetchDistinctValues()\"\r\n title=\"R\u00E9cup\u00E9ration des valeurs distinctes d'une colonne lors du filtrage\">filter_list</span>\r\n </div>\r\n }\r\n @if (allowSync() && allowManageTrackingPersistence()) {\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"crossRequestTrackingPersistence()\"\r\n (click)=\"toggleCrossRequestTrackingPersistence()\"\r\n title=\"Persistance du suivi des modifications entre les requ\u00EAtes\">track_changes</span>\r\n </div>\r\n }\r\n @if(allowSync() && hasChanges() && !hasInvalidRows()){\r\n <span class=\"mat-icon icon-button sync-button\" (click)=\"onSync.emit()\"\r\n title=\"Synchroniser les changements\">file_upload</span>\r\n }\r\n @if(allowRefresh()){\r\n <span class=\"mat-icon icon-button\" (click)=\"refresh()\" title=\"Recharger les donn\u00E9es\">refresh</span>\r\n }\r\n @if(allowInsert()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onAdd()\" title=\"Ajouter une ligne\">add</span>\r\n }\r\n @if(allowExport()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onExport.emit()\"\r\n title=\"T\u00E9l\u00E9charger les donn\u00E9es\">file_download</span>\r\n }\r\n @if(totalItems() !== -1){\r\n <span>Total: {{ totalItems() }} enregistrements</span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"grid-container data-grid-wrapper\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [style.max-height]=\"maxHeight()\" [style.overflow-y]=\"enableVirtualization() ? 'clip': 'auto'\">\r\n <div class=\"grid-header\" [style.column-span]=\"'all'\">\r\n @for (column of columns(); track column.columnName) {\r\n @let isForeignKey = isForeignKeyColumn(column);\r\n @let isPrimaryKey = isPrimaryKeyColumn(column);\r\n @let isComputedColumn = column.isGenerated;\r\n @let isUniqueColumn = isUniqueKeyColumn(column);\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"\r\n [ngTemplateOutletContext]=\"{column: column, isForeignKey: isForeignKey, isPrimaryKey: isPrimaryKey, isComputedColumn: isComputedColumn, isUniqueColumn: isUniqueColumn}\"></ng-container>\r\n }\r\n </div>\r\n <div class=\"grid-body\">\r\n @if(enableVirtualization()){\r\n <cdk-virtual-scroll-viewport #viewport [itemSize]=\"41\" [style.height]=\"maxHeight()\" class=\"viewport\">\r\n <ng-container *cdkVirtualFor=\"let row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: totalItems()}; let $index = index\" [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n </cdk-virtual-scroll-viewport>\r\n } @else {\r\n @for (row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: currentItems()};\r\n track getRowTrackBy(row.data, $index)) {\r\n <ng-container [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #headerTemplate let-column=\"column\" let-isForeignKey=\"isForeignKey\" let-isPrimaryKey=\"isPrimaryKey\"\r\n let-isComputedColumn=\"isComputedColumn\" let-isUniqueColumn=\"isUniqueColumn\">\r\n <div class=\"grid-header-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\"\r\n [contextMenu]=\"datagridContextMenuActions(column.columnName, filterElement, groupElement)\"\r\n [title]=\"getColumnTooltip(column)\">\r\n @if(allowNavigation() && isForeignKey){\r\n <span ControlClick class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\"\r\n (ctrlClick)=\"navigateToForeignTable(column)\">{{ column.displayName || column.columnName\r\n }}</span>\r\n } @else {\r\n <span class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\">{{ column.displayName ||\r\n column.columnName\r\n }}</span>\r\n }\r\n <small class=\"sort-receiver column-type\" (click)=\"toggleSort(column.columnName)\">{{\r\n column.dataType }}\r\n {{column.length ?\r\n `(${column.length}${column.scale ? `, ${column.scale}` : ''})`: ''}}</small>\r\n <div class=\"column-icons\">\r\n @if(isUniqueColumn){\r\n <span class=\"mat-icon unique-key-icon\" title=\"Colonne avec contrainte d'unicit\u00E9\">looks_one</span>\r\n }\r\n @if(isPrimaryKey || isForeignKey){\r\n <span class=\"mat-icon\" [class.primary-key-icon]=\"isPrimaryKey\" [title]=\"'Colonne de la clef ' + (isPrimaryKey ?\r\n 'primaire' : '\u00E9trang\u00E8re')\" [class.key-icon]=\"isForeignKey\">key</span>\r\n }\r\n @if(allowSync() && (!canEdit(column) || !isEditSafe(column))){\r\n <span class=\"lock-icon mat-icon\" title=\"Colonne verrouill\u00E9e\">lock</span>\r\n }\r\n </div>\r\n @if (sort()?.column === column.columnName) {\r\n @if (sort()?.isAsc) {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_upward</span>\r\n } @else {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_downward</span>\r\n }\r\n }\r\n <div class=\"filter-group-controls\" #controls>\r\n <chr-column-filter #filterElement [class.hidden]=\"!allowFiltering()\" [by]=\"column.columnName\"\r\n [mode]=\"filter()[column.columnName]?.mode ?? null\" [attr.data-applied]=\"\"\r\n [value]=\"filter()[column.columnName]?.value ?? ''\" [columnMetadata]=\"column\"\r\n [suggestions]=\"columnSuggestions()[column.columnName]\"\r\n (valueChange)=\"onFilterValue(column.columnName, $event)\"\r\n (filterChange)=\"onFilterBy(column.columnName, $event)\" title=\"Double-clic pour d\u00E9finir un filtre\"\r\n class=\"filter\"></chr-column-filter>\r\n @let isGroupBySource = groupBy() == null || groupBy()?.column === column.columnName;\r\n <chr-column-group #groupElement [class.hidden]=\"!allowGrouping()\"\r\n [style.display]=\"!isGroupBySource ? 'none' : ''\" [by]=\"column.columnName\"\r\n [mode]=\"groupBy()?.mode ?? null\" (groupChange)=\"onGroupBy($event)\" class=\"group\"\r\n title=\"Double-clic pour d\u00E9finir un groupement\"></chr-column-group>\r\n @if(isComputedColumn){\r\n <span class=\"filter-icon mat-icon\" title=\"Colonne calcul\u00E9e\">functions</span>\r\n }@else if(!isGroupBySource && groupBy() !== null){\r\n <div class=\"group-by-include\" title=\"Inclure cette colonne dans le r\u00E9sultat group\u00E9\">\r\n <span class=\"mat-icon filter-icon disabled\">stack</span>\r\n <span class=\"toggle faded\">Inclure</span>\r\n <input #checkbox [checked]=\"groupByMap()[column.columnName]\"\r\n (change)=\"onGroupByIncludedChange(column.columnName, checkbox.checked)\"\r\n [name]=\"column.columnName+'-include'\" type=\"checkbox\" />\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #rowTemplate let-row let-index=\"index\">\r\n @let contextMenuItems = datarowContextMenuActions(row, index);\r\n <div #rowElement class=\"grid-row\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [attr.data-is-added]=\"row.flag === 'added'\" [scrollIntoView]=\"!row.valid\" [attr.data-is-valid]=\"row.valid\"\r\n (click)=\"rowClick.emit({ rowIndex: index, row: row.data, event: $event })\" [contextMenu]=\"contextMenuItems\"\r\n [attr.data-has-changed]=\"row.flag === 'modified'\" [attr.data-is-deleted]=\"row.flag === 'deleted'\">\r\n @for (column of columns(); track column.columnName) {\r\n <div class=\"grid-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\">\r\n <chr-editable-cell class=\"cell\" [value]=\"row.data[column.columnName]\" [row]=\"row.data\"\r\n [customTemplate]=\"getCellTemplate(column.columnName, column.dataType ?? 'varchar')\"\r\n (isEditingChange)=\"$event ? selectedColumn.set(column.columnName) : selectedColumn.set(null)\"\r\n (valueChange)=\"onCellChange(index, row, column, $event)\"\r\n [allowUpdate]=\"allowUpdate() && (isEditSafe(column)||row.flag === 'added') && row.flag !== 'deleted' && canEdit(column)\"\r\n [columnMetadata]=\"column\"></chr-editable-cell>\r\n @if($last){\r\n @if(actions().length > 0){\r\n <div class=\"actions-menu\">\r\n <span class=\"actions-menu-trigger mat-icon icon-button\" [style.cursor]=\"'pointer'\"\r\n [openOnClick]=\"true\" [contextMenu]=\"contextMenuItems\">more_vert</span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <chr-paginator [id]=\"id()\" [page]=\"currentPage()\" [pageSize]=\"pageSize()\" (pageChange)=\"onPageChange($event)\"\r\n (pageSizeChange)=\"onPageSizeChange($event)\"></chr-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"empty-state\">\r\n <p>Aucune donn\u00E9e</p>\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.hidden{display:none}.data-grid-wrapper{position:relative;display:grid!important;overflow:auto}.data-grid-container{background:inherit;border-radius:8px;box-shadow:0 2px 4px #0000001a;margin:1rem 0;--faded-text-color: color-mix( in srgb, var(--text-color) 40%, var(--background-color) 50% )}.data-grid-container .data-grid-header{border-top-left-radius:inherit;border-top-right-radius:inherit;background:var(--primary-color);padding:1rem 1.5rem;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem}.data-grid-container .data-grid-header h3{margin:0;color:var(--primary-contrast-color);font-size:1.25rem;font-weight:600}.data-grid-container .data-grid-header .table-description{color:var(--primary-contrast-color);font-size:.875rem;font-style:italic}.data-grid-container .data-grid-header .data-grid-info{display:flex;align-items:center;gap:1rem;color:var(--text-color);font-size:.875rem}.data-grid-container .data-grid-header .data-grid-info span{background:var(--background-color);padding:.25rem .75rem;border-radius:1.25rem}.data-grid-container .data-grid-header .data-grid-info .sync-button{border:2px solid var(--warn-color);box-shadow:0 0 .5rem .15rem var(--warn-color)}.data-grid-container .data-grid-header .data-grid-info .sync-button:hover{background-color:var(--warn-color);color:var(--warn-contrast-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button{color:var(--success-contrast-color);background-color:var(--error-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button[data-enabled=true]{background-color:var(--success-color)}.data-grid-container .data-grid-header .data-grid-info .icon-button{cursor:pointer;padding:.1rem .25rem;font-size:1rem;transition:background-color .2s ease-in-out,color .2s ease-in-out}.data-grid-container .data-grid-header .data-grid-info .icon-button:hover{background-color:color-mix(in srgb,var(--background-color) 80%,var(--secondary-color) 80%);color:var(--secondary-contrast-color)}.data-grid-container .viewport{font-size:.875rem;scrollbar-width:none;grid-column:1/-1}.data-grid-container .viewport ::-webkit-scrollbar{display:none}.data-grid-container .viewport{-ms-overflow-style:none}.data-grid-container .grid-header,.data-grid-container .grid-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;font-size:.875rem}.data-grid-container .grid-row{display:grid!important;grid-column:1/-1}.data-grid-container .grid-header{position:sticky!important;top:0;z-index:10;background:color-mix(in srgb,var(--background-color) 80%,transparent 40%);-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-bottom:2px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%)}.data-grid-container .grid-header-cell{position:relative;padding:.75rem 1rem;text-align:left;font-weight:600;color:color-mix(in srgb,var(--text-color) 90%,var(--background-color) 30%);-webkit-user-select:none;user-select:none}.data-grid-container .grid-header-cell .sort-receiver{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.data-grid-container .grid-header-cell .sort-icon{position:absolute;right:.75rem;top:calc(50% - .75rem);font-size:1rem;color:var(--tertiary-color)}.data-grid-container .grid-header-cell .column-icons{position:absolute;right:.75rem;top:.5rem;display:flex;gap:.25rem;align-items:center}.data-grid-container .grid-header-cell .column-icons span,.data-grid-container .grid-header-cell .column-icons .mat-icon{font-size:1rem}.data-grid-container .grid-header-cell .primary-key-icon{color:var(--primary-color)}.data-grid-container .grid-header-cell .key-icon{color:var(--tertiary-color)}.data-grid-container .grid-header-cell .lock-icon{color:var(--warn-color)}.data-grid-container .grid-header-cell .column-type{font-weight:400;color:var(--faded-text-color);font-size:.75rem;text-transform:uppercase}.data-grid-container .grid-header-cell .filter-group-controls{display:flex;flex-direction:column;justify-content:center;gap:.25rem;width:calc(100% - .875rem)}.data-grid-container .grid-header-cell .group-by-include{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;accent-color:var(--primary-color)}.data-grid-container .grid-header-cell .filter-icon{font-size:1rem}.data-grid-container .grid-row{border-bottom:1px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%);transition:background-color .2s ease;position:relative}.data-grid-container .grid-row:nth-child(2n){background-color:color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-row:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:.25rem;background-color:transparent}.data-grid-container .grid-row[data-is-deleted=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--error-color) 20%)}.data-grid-container .grid-row[data-is-deleted=true]:after{background-color:var(--error-color)}.data-grid-container .grid-row[data-has-changed=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--warn-color) 20%)}.data-grid-container .grid-row[data-has-changed=true]:after{background-color:var(--warn-color)}.data-grid-container .grid-row[data-is-added=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--primary-color) 20%)}.data-grid-container .grid-row[data-is-added=true]:after{background-color:var(--primary-color)}.data-grid-container .grid-row[data-is-valid=false]{box-shadow:inset 0 0 .25rem var(--error-color)}.data-grid-container .grid-row:hover{background-color:var(--neutral-color)}.data-grid-container .grid-cell{position:relative;padding:.75rem 1rem;border-right:1px solid color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-cell .cell{display:inline-block;min-width:100%;max-width:12rem}.data-grid-container .grid-cell .cell[data-changed=true]{font-weight:600;color:var(--warn-color)}.data-grid-container .grid-cell:last-child{border-right:none}.data-grid-container .grid-cell .actions-menu{position:absolute;right:0rem;top:50%;transform:translateY(-50%)}.data-grid-container .no-data-row{padding:2rem;text-align:center}.data-grid-container .no-data-row .no-data{color:var(--faded-text-color);font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChrPaginatorComponent, selector: "app-chr-paginator,chr-paginator, chr-paginator", inputs: ["page", "pageSize", "id", "allowSizeChange", "allowPageEdit", "isPageEditing"], outputs: ["pageChange", "pageSizeChange", "isPageEditingChange"] }, { kind: "ngmodule", type: NgxPaginationModule }, { kind: "component", type: EditableCell, selector: "chr-editable-cell", inputs: ["allowUpdate", "customTemplate", "row", "value", "columnMetadata"], outputs: ["valueChange", "isEditingChange"] }, { kind: "directive", type: ContextMenuDirective, selector: "[contextMenu], [chrContextMenu], [chr-right-click]", inputs: ["contextMenu", "openOnClick"] }, { kind: "directive", type: ScrollIntoViewDirective, selector: "[scrollIntoView]", inputs: ["scrollIntoView"] }, { kind: "directive", type: ControlClickDirective, selector: "[CtrlClick], [ControlClick]", inputs: ["callback"], outputs: ["ctrlClick"] }, { kind: "component", type: ColumnFilter, selector: "chr-column-filter", inputs: ["isOpen", "by", "suggestions", "mode", "columnMetadata", "value"], outputs: ["isOpenChange", "modeChange", "valueChange", "filterChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1$1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1$1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: ColumnGroup, selector: "chr-column-group", inputs: ["isOpen", "by", "mode"], outputs: ["isOpenChange", "modeChange", "groupChange"] }, { kind: "directive", type: ChrHoverTitleDirective, selector: "[chrTitle], [title]", inputs: ["chrTitle", "chrTitlePosition", "chrTitleSnap", "chrTitleDebounce"], outputs: ["chrTitleChange"] }, { kind: "pipe", type: i2.PaginatePipe, name: "paginate" }] }); }
8708
8739
  }
8709
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, decorators: [{
8710
- type: Injectable,
8711
- args: [{
8712
- providedIn: 'root',
8713
- }]
8714
- }], ctorParameters: () => [] });
8740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataGrid, decorators: [{
8741
+ type: Component,
8742
+ args: [{ selector: 'chr-data-grid', imports: [
8743
+ CommonModule,
8744
+ ChrPaginatorComponent,
8745
+ NgxPaginationModule,
8746
+ EditableCell,
8747
+ ContextMenuDirective,
8748
+ ScrollIntoViewDirective,
8749
+ ControlClickDirective,
8750
+ ColumnFilter,
8751
+ ScrollingModule,
8752
+ ColumnGroup,
8753
+ ChrHoverTitleDirective,
8754
+ ], providers: [
8755
+ PaginationService,
8756
+ {
8757
+ provide: CHR_DEBOUNCE_DEFAULTS,
8758
+ useValue: new DebounceDefaults({ hoverTitle: 0 }),
8759
+ },
8760
+ // {
8761
+ // provide: VIRTUAL_SCROLL_STRATEGY,
8762
+ // useClass: DynamicSizeScrollStrategy,
8763
+ // },
8764
+ ], template: "@if (table()) {\r\n<div class=\"data-grid-container\">\r\n <div class=\"data-grid-header\">\r\n <h3>{{ title() ? title() : `${table()?.schema ? `${table()?.schema}.` : ''}${table()?.name}` }}</h3>\r\n @if(table()?.description){\r\n <span class=\"table-description\">{{ table()?.description }}</span>\r\n }\r\n <div class=\"data-grid-info\">\r\n <!-- {{allowSync()}} {{hasChanges()}} {{!hasInvalidRows()}} -->\r\n @if(allowManageDistincValueFetching()){\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"allowFetchDistinctValues()\" (click)=\"toggleAllowFetchDistinctValues()\"\r\n title=\"R\u00E9cup\u00E9ration des valeurs distinctes d'une colonne lors du filtrage\">filter_list</span>\r\n </div>\r\n }\r\n @if (allowSync() && allowManageTrackingPersistence()) {\r\n <div>\r\n <span class=\"mat-icon icon-button tracking-persistence-button\"\r\n [attr.data-enabled]=\"crossRequestTrackingPersistence()\"\r\n (click)=\"toggleCrossRequestTrackingPersistence()\"\r\n title=\"Persistance du suivi des modifications entre les requ\u00EAtes\">track_changes</span>\r\n </div>\r\n }\r\n @if(allowSync() && hasChanges() && !hasInvalidRows()){\r\n <span class=\"mat-icon icon-button sync-button\" (click)=\"onSync.emit()\"\r\n title=\"Synchroniser les changements\">file_upload</span>\r\n }\r\n @if(allowRefresh()){\r\n <span class=\"mat-icon icon-button\" (click)=\"refresh()\" title=\"Recharger les donn\u00E9es\">refresh</span>\r\n }\r\n @if(allowInsert()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onAdd()\" title=\"Ajouter une ligne\">add</span>\r\n }\r\n @if(allowExport()){\r\n <span class=\"mat-icon icon-button\" (click)=\"onExport.emit()\"\r\n title=\"T\u00E9l\u00E9charger les donn\u00E9es\">file_download</span>\r\n }\r\n @if(totalItems() !== -1){\r\n <span>Total: {{ totalItems() }} enregistrements</span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"grid-container data-grid-wrapper\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [style.max-height]=\"maxHeight()\" [style.overflow-y]=\"enableVirtualization() ? 'clip': 'auto'\">\r\n <div class=\"grid-header\" [style.column-span]=\"'all'\">\r\n @for (column of columns(); track column.columnName) {\r\n @let isForeignKey = isForeignKeyColumn(column);\r\n @let isPrimaryKey = isPrimaryKeyColumn(column);\r\n @let isComputedColumn = column.isGenerated;\r\n @let isUniqueColumn = isUniqueKeyColumn(column);\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"\r\n [ngTemplateOutletContext]=\"{column: column, isForeignKey: isForeignKey, isPrimaryKey: isPrimaryKey, isComputedColumn: isComputedColumn, isUniqueColumn: isUniqueColumn}\"></ng-container>\r\n }\r\n </div>\r\n <div class=\"grid-body\">\r\n @if(enableVirtualization()){\r\n <cdk-virtual-scroll-viewport #viewport [itemSize]=\"41\" [style.height]=\"maxHeight()\" class=\"viewport\">\r\n <ng-container *cdkVirtualFor=\"let row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: totalItems()}; let $index = index\" [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n </cdk-virtual-scroll-viewport>\r\n } @else {\r\n @for (row of rows() | paginate: {itemsPerPage: pageSize(), currentPage: currentPage(), id:\r\n this.id(),\r\n totalItems: currentItems()};\r\n track getRowTrackBy(row.data, $index)) {\r\n <ng-container [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{$implicit: row, index: getRowTrackBy(row.data, $index)}\">\r\n </ng-container>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #headerTemplate let-column=\"column\" let-isForeignKey=\"isForeignKey\" let-isPrimaryKey=\"isPrimaryKey\"\r\n let-isComputedColumn=\"isComputedColumn\" let-isUniqueColumn=\"isUniqueColumn\">\r\n <div class=\"grid-header-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\"\r\n [contextMenu]=\"datagridContextMenuActions(column.columnName, filterElement, groupElement)\"\r\n [title]=\"getColumnTooltip(column)\">\r\n @if(allowNavigation() && isForeignKey){\r\n <span ControlClick class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\"\r\n (ctrlClick)=\"navigateToForeignTable(column)\">{{ column.displayName || column.columnName\r\n }}</span>\r\n } @else {\r\n <span class=\"sort-receiver\" (click)=\"toggleSort(column.columnName)\">{{ column.displayName ||\r\n column.columnName\r\n }}</span>\r\n }\r\n <small class=\"sort-receiver column-type\" (click)=\"toggleSort(column.columnName)\">{{\r\n column.dataType }}\r\n {{column.length ?\r\n `(${column.length}${column.scale ? `, ${column.scale}` : ''})`: ''}}</small>\r\n <div class=\"column-icons\">\r\n @if(isUniqueColumn){\r\n <span class=\"mat-icon unique-key-icon\" title=\"Colonne avec contrainte d'unicit\u00E9\">looks_one</span>\r\n }\r\n @if(isPrimaryKey || isForeignKey){\r\n <span class=\"mat-icon\" [class.primary-key-icon]=\"isPrimaryKey\" [title]=\"'Colonne de la clef ' + (isPrimaryKey ?\r\n 'primaire' : '\u00E9trang\u00E8re')\" [class.key-icon]=\"isForeignKey\">key</span>\r\n }\r\n @if(allowSync() && (!canEdit(column) || !isEditSafe(column))){\r\n <span class=\"lock-icon mat-icon\" title=\"Colonne verrouill\u00E9e\">lock</span>\r\n }\r\n </div>\r\n @if (sort()?.column === column.columnName) {\r\n @if (sort()?.isAsc) {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_upward</span>\r\n } @else {\r\n <span class=\"sort-icon mat-icon\" (click)=\"toggleSort(column.columnName)\">arrow_downward</span>\r\n }\r\n }\r\n <div class=\"filter-group-controls\" #controls>\r\n <chr-column-filter #filterElement [class.hidden]=\"!allowFiltering()\" [by]=\"column.columnName\"\r\n [mode]=\"filter()[column.columnName]?.mode ?? null\" [attr.data-applied]=\"\"\r\n [value]=\"filter()[column.columnName]?.value ?? ''\" [columnMetadata]=\"column\"\r\n [suggestions]=\"columnSuggestions()[column.columnName]\"\r\n (valueChange)=\"onFilterValue(column.columnName, $event)\"\r\n (filterChange)=\"onFilterBy(column.columnName, $event)\" title=\"Double-clic pour d\u00E9finir un filtre\"\r\n class=\"filter\"></chr-column-filter>\r\n @let isGroupBySource = groupBy() == null || groupBy()?.column === column.columnName;\r\n <chr-column-group #groupElement [class.hidden]=\"!allowGrouping()\"\r\n [style.display]=\"!isGroupBySource ? 'none' : ''\" [by]=\"column.columnName\"\r\n [mode]=\"groupBy()?.mode ?? null\" (groupChange)=\"onGroupBy($event)\" class=\"group\"\r\n title=\"Double-clic pour d\u00E9finir un groupement\"></chr-column-group>\r\n @if(isComputedColumn){\r\n <span class=\"filter-icon mat-icon\" title=\"Colonne calcul\u00E9e\">functions</span>\r\n }@else if(!isGroupBySource && groupBy() !== null){\r\n <div class=\"group-by-include\" title=\"Inclure cette colonne dans le r\u00E9sultat group\u00E9\">\r\n <span class=\"mat-icon filter-icon disabled\">stack</span>\r\n <span class=\"toggle faded\">Inclure</span>\r\n <input #checkbox [checked]=\"groupByMap()[column.columnName]\"\r\n (change)=\"onGroupByIncludedChange(column.columnName, checkbox.checked)\"\r\n [name]=\"column.columnName+'-include'\" type=\"checkbox\" />\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #rowTemplate let-row let-index=\"index\">\r\n @let contextMenuItems = datarowContextMenuActions(row, index);\r\n <div #rowElement class=\"grid-row\"\r\n [style.grid-template-columns]=\"'repeat(' + columns().length + ', minmax(15rem, auto))'\"\r\n [attr.data-is-added]=\"row.flag === 'added'\" [scrollIntoView]=\"!row.valid\" [attr.data-is-valid]=\"row.valid\"\r\n (click)=\"rowClick.emit({ rowIndex: index, row: row.data, event: $event })\" [contextMenu]=\"contextMenuItems\"\r\n [attr.data-has-changed]=\"row.flag === 'modified'\" [attr.data-is-deleted]=\"row.flag === 'deleted'\">\r\n @for (column of columns(); track column.columnName) {\r\n <div class=\"grid-cell\" [attr.data-selected]=\"selectedColumn() === column.columnName\">\r\n <chr-editable-cell class=\"cell\" [value]=\"row.data[column.columnName]\" [row]=\"row.data\"\r\n [customTemplate]=\"getCellTemplate(column.columnName, column.dataType ?? 'varchar')\"\r\n (isEditingChange)=\"$event ? selectedColumn.set(column.columnName) : selectedColumn.set(null)\"\r\n (valueChange)=\"onCellChange(index, row, column, $event)\"\r\n [allowUpdate]=\"allowUpdate() && (isEditSafe(column)||row.flag === 'added') && row.flag !== 'deleted' && canEdit(column)\"\r\n [columnMetadata]=\"column\"></chr-editable-cell>\r\n @if($last){\r\n @if(actions().length > 0){\r\n <div class=\"actions-menu\">\r\n <span class=\"actions-menu-trigger mat-icon icon-button\" [style.cursor]=\"'pointer'\"\r\n [openOnClick]=\"true\" [contextMenu]=\"contextMenuItems\">more_vert</span>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <chr-paginator [id]=\"id()\" [page]=\"currentPage()\" [pageSize]=\"pageSize()\" (pageChange)=\"onPageChange($event)\"\r\n (pageSizeChange)=\"onPageSizeChange($event)\"></chr-paginator>\r\n</div>\r\n} @else {\r\n<div class=\"empty-state\">\r\n <p>Aucune donn\u00E9e</p>\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.hidden{display:none}.data-grid-wrapper{position:relative;display:grid!important;overflow:auto}.data-grid-container{background:inherit;border-radius:8px;box-shadow:0 2px 4px #0000001a;margin:1rem 0;--faded-text-color: color-mix( in srgb, var(--text-color) 40%, var(--background-color) 50% )}.data-grid-container .data-grid-header{border-top-left-radius:inherit;border-top-right-radius:inherit;background:var(--primary-color);padding:1rem 1.5rem;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem}.data-grid-container .data-grid-header h3{margin:0;color:var(--primary-contrast-color);font-size:1.25rem;font-weight:600}.data-grid-container .data-grid-header .table-description{color:var(--primary-contrast-color);font-size:.875rem;font-style:italic}.data-grid-container .data-grid-header .data-grid-info{display:flex;align-items:center;gap:1rem;color:var(--text-color);font-size:.875rem}.data-grid-container .data-grid-header .data-grid-info span{background:var(--background-color);padding:.25rem .75rem;border-radius:1.25rem}.data-grid-container .data-grid-header .data-grid-info .sync-button{border:2px solid var(--warn-color);box-shadow:0 0 .5rem .15rem var(--warn-color)}.data-grid-container .data-grid-header .data-grid-info .sync-button:hover{background-color:var(--warn-color);color:var(--warn-contrast-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button{color:var(--success-contrast-color);background-color:var(--error-color)}.data-grid-container .data-grid-header .data-grid-info .tracking-persistence-button[data-enabled=true]{background-color:var(--success-color)}.data-grid-container .data-grid-header .data-grid-info .icon-button{cursor:pointer;padding:.1rem .25rem;font-size:1rem;transition:background-color .2s ease-in-out,color .2s ease-in-out}.data-grid-container .data-grid-header .data-grid-info .icon-button:hover{background-color:color-mix(in srgb,var(--background-color) 80%,var(--secondary-color) 80%);color:var(--secondary-contrast-color)}.data-grid-container .viewport{font-size:.875rem;scrollbar-width:none;grid-column:1/-1}.data-grid-container .viewport ::-webkit-scrollbar{display:none}.data-grid-container .viewport{-ms-overflow-style:none}.data-grid-container .grid-header,.data-grid-container .grid-body{display:grid;grid-template-columns:subgrid;grid-column:1/-1;font-size:.875rem}.data-grid-container .grid-row{display:grid!important;grid-column:1/-1}.data-grid-container .grid-header{position:sticky!important;top:0;z-index:10;background:color-mix(in srgb,var(--background-color) 80%,transparent 40%);-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-bottom:2px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%)}.data-grid-container .grid-header-cell{position:relative;padding:.75rem 1rem;text-align:left;font-weight:600;color:color-mix(in srgb,var(--text-color) 90%,var(--background-color) 30%);-webkit-user-select:none;user-select:none}.data-grid-container .grid-header-cell .sort-receiver{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.data-grid-container .grid-header-cell .sort-icon{position:absolute;right:.75rem;top:calc(50% - .75rem);font-size:1rem;color:var(--tertiary-color)}.data-grid-container .grid-header-cell .column-icons{position:absolute;right:.75rem;top:.5rem;display:flex;gap:.25rem;align-items:center}.data-grid-container .grid-header-cell .column-icons span,.data-grid-container .grid-header-cell .column-icons .mat-icon{font-size:1rem}.data-grid-container .grid-header-cell .primary-key-icon{color:var(--primary-color)}.data-grid-container .grid-header-cell .key-icon{color:var(--tertiary-color)}.data-grid-container .grid-header-cell .lock-icon{color:var(--warn-color)}.data-grid-container .grid-header-cell .column-type{font-weight:400;color:var(--faded-text-color);font-size:.75rem;text-transform:uppercase}.data-grid-container .grid-header-cell .filter-group-controls{display:flex;flex-direction:column;justify-content:center;gap:.25rem;width:calc(100% - .875rem)}.data-grid-container .grid-header-cell .group-by-include{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;accent-color:var(--primary-color)}.data-grid-container .grid-header-cell .filter-icon{font-size:1rem}.data-grid-container .grid-row{border-bottom:1px solid color-mix(in srgb,var(--text-color) 20%,var(--background-color) 90%);transition:background-color .2s ease;position:relative}.data-grid-container .grid-row:nth-child(2n){background-color:color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-row:after{content:\"\";position:absolute;left:0;top:0;bottom:0;width:.25rem;background-color:transparent}.data-grid-container .grid-row[data-is-deleted=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--error-color) 20%)}.data-grid-container .grid-row[data-is-deleted=true]:after{background-color:var(--error-color)}.data-grid-container .grid-row[data-has-changed=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--warn-color) 20%)}.data-grid-container .grid-row[data-has-changed=true]:after{background-color:var(--warn-color)}.data-grid-container .grid-row[data-is-added=true]{background-color:color-mix(in srgb,var(--background-color) 90%,var(--primary-color) 20%)}.data-grid-container .grid-row[data-is-added=true]:after{background-color:var(--primary-color)}.data-grid-container .grid-row[data-is-valid=false]{box-shadow:inset 0 0 .25rem var(--error-color)}.data-grid-container .grid-row:hover{background-color:var(--neutral-color)}.data-grid-container .grid-cell{position:relative;padding:.75rem 1rem;border-right:1px solid color-mix(in srgb,var(--background-color) 90%,var(--neutral-color) 20%)}.data-grid-container .grid-cell .cell{display:inline-block;min-width:100%;max-width:12rem}.data-grid-container .grid-cell .cell[data-changed=true]{font-weight:600;color:var(--warn-color)}.data-grid-container .grid-cell:last-child{border-right:none}.data-grid-container .grid-cell .actions-menu{position:absolute;right:0rem;top:50%;transform:translateY(-50%)}.data-grid-container .no-data-row{padding:2rem;text-align:center}.data-grid-container .no-data-row .no-data{color:var(--faded-text-color);font-style:italic}\n"] }]
8765
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: false }] }, { type: i0.Output, args: ["tableChange"] }], viewport: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], headerScroll: [{ type: i0.ViewChild, args: ['headerScroll', { isSignal: true }] }], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], onInit: [{ type: i0.Output, args: ["onInit"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }, { type: i0.Output, args: ["filterChange"] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }, { type: i0.Output, args: ["groupByChange"] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], enableVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableVirtualization", required: false }] }], allowFiltering: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFiltering", required: false }] }], allowGrouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowGrouping", required: false }] }], allowInsert: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowInsert", required: false }] }], allowUpdate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowUpdate", required: false }] }], allowDelete: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDelete", required: false }] }], allowDuplicate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicate", required: false }] }], allowSync: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowSync", required: false }] }], allowRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowRefresh", required: false }] }], allowNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowNavigation", required: false }] }], allowExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowExport", required: false }] }], allowManageTrackingPersistence: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManageTrackingPersistence", required: false }] }], allowManageDistincValueFetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowManageDistincValueFetching", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], onExport: [{ type: i0.Output, args: ["onExport"] }], onSync: [{ type: i0.Output, args: ["onSync"] }], onRefresh: [{ type: i0.Output, args: ["onRefresh"] }], onAddRow: [{ type: i0.Output, args: ["onAddRow"] }], onDeleteRow: [{ type: i0.Output, args: ["onDeleteRow"] }], onForeignTableNavigate: [{ type: i0.Output, args: ["onForeignTableNavigate"] }], onNavigate: [{ type: i0.Output, args: ["onNavigate"] }], onFilterValueChange: [{ type: i0.Output, args: ["onFilterValueChange"] }], allowFetchDistinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFetchDistinctValues", required: false }] }, { type: i0.Output, args: ["allowFetchDistinctValuesChange"] }], crossRequestTrackingPersistence: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossRequestTrackingPersistence", required: false }] }, { type: i0.Output, args: ["crossRequestTrackingPersistenceChange"] }], modifiedCells: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiedCells", required: false }] }, { type: i0.Output, args: ["modifiedCellsChange"] }], modifiedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "modifiedRows", required: false }] }, { type: i0.Output, args: ["modifiedRowsChange"] }], addedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "addedRows", required: false }] }, { type: i0.Output, args: ["addedRowsChange"] }], deletedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "deletedRows", required: false }] }, { type: i0.Output, args: ["deletedRowsChange"] }], distinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "distinctValues", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ChrCellDefDirective), { isSignal: true }] }], trackByMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByMode", required: false }] }, { type: i0.Output, args: ["trackByModeChange"] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], primaryKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryKey", required: false }] }], metadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }] } });
8715
8766
 
8716
- class CachingDefaults {
8717
- constructor(init) {
8718
- this.duration = 1000 * 60 * 5; // 5 minutes par défaut;
8719
- Object.assign(this, init);
8767
+ class MessageBanner {
8768
+ constructor() {
8769
+ this.isVisible = signal(true, /* @ts-ignore */
8770
+ ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
8771
+ this.closable = input(false, /* @ts-ignore */
8772
+ ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
8773
+ this.title = input(null, /* @ts-ignore */
8774
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
8775
+ this.icon = input(null, /* @ts-ignore */
8776
+ ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
8777
+ this.message = input(/* @ts-ignore */
8778
+ ...(ngDevMode ? [undefined, { debugName: "message" }] : /* istanbul ignore next */ []));
8779
+ this.type = input('none', /* @ts-ignore */
8780
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
8781
+ this.close = () => {
8782
+ this.isVisible.set(false);
8783
+ };
8720
8784
  }
8785
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MessageBanner, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8786
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: MessageBanner, isStandalone: true, selector: "chr-message-banner", inputs: { closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if(isVisible()){\r\n<div class=\"message-banner\" [attr.data-type]=\"type()\">\r\n <div class=\"banner-header\">\r\n @if(icon()){\r\n <span class=\"mat-icon material-symbols banner-icon\" [attr.data-icon]=\"icon()\">{{ icon() }}</span>\r\n\r\n }\r\n @if(title()){\r\n <strong class=\"banner-title\">{{title()}}</strong>\r\n }\r\n </div>\r\n <div class=\"message-content\">\r\n {{message()}}\r\n <ng-content></ng-content>\r\n </div>\r\n @if(closable()){\r\n <span class=\"mat-icon material-symbols close-icon\" (click)=\"close()\">close</span>\r\n }\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.message-banner[data-type=none]{--banner-color: var(--neutral-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--neutral-color) 10% );--banner-icon-color: var(--text-color)}.message-banner[data-type=info]{--banner-color: var(--primary-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--primary-color) 10% );--banner-icon-color: var(--primary-color)}.message-banner[data-type=warning]{--banner-color: var(--warn-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--warn-color) 10% );--banner-icon-color: var(--warn-color)}.message-banner[data-type=error]{--banner-color: var(--error-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--error-color) 10% );--banner-icon-color: var(--error-color)}.message-banner{flex:1 1 auto;border:1px solid var(--banner-color);background-color:var(--banner-bg-color);color:var(--text-color);padding:.65rem 1rem;margin:.5rem 0;border-radius:.25rem;display:flex;align-items:center;gap:.5rem;position:relative;font-size:.875rem}.message-banner .banner-header{position:absolute;display:flex;align-items:center;gap:.5rem;top:-.75rem;left:.5rem;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-radius:.25rem;padding:0 .5rem;font-size:.75rem}.message-banner .banner-header .banner-icon{color:var(--banner-icon-color);fill:var(--banner-icon-color)}.message-banner ::ng-deep .message-content{display:flex;flex-direction:column;justify-items:center;width:100%}.message-banner ::ng-deep .message-content *{display:flex;align-items:center}\n"] }); }
8721
8787
  }
8722
- const CHR_CACHING_DEFAULTS = new InjectionToken('CHR_CACHING_DEFAULTS', {
8723
- factory: () => new CachingDefaults(),
8724
- });
8788
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MessageBanner, decorators: [{
8789
+ type: Component,
8790
+ args: [{ selector: 'chr-message-banner', imports: [], template: "@if(isVisible()){\r\n<div class=\"message-banner\" [attr.data-type]=\"type()\">\r\n <div class=\"banner-header\">\r\n @if(icon()){\r\n <span class=\"mat-icon material-symbols banner-icon\" [attr.data-icon]=\"icon()\">{{ icon() }}</span>\r\n\r\n }\r\n @if(title()){\r\n <strong class=\"banner-title\">{{title()}}</strong>\r\n }\r\n </div>\r\n <div class=\"message-content\">\r\n {{message()}}\r\n <ng-content></ng-content>\r\n </div>\r\n @if(closable()){\r\n <span class=\"mat-icon material-symbols close-icon\" (click)=\"close()\">close</span>\r\n }\r\n</div>\r\n}", styles: ["@import\"https://fonts.googleapis.com/css2?family=Material+Symbols:opsz,wght,FILL,GRAD@24,400,0,0\";@import\"https://fonts.googleapis.com/css2?family=Material+Icons\";.mat-icon:not(mat-icon),.material-symbols{font-family:Material Symbols;font-size:1.5rem;display:flex;align-items:center;justify-content:center}.mat-icon:not(mat-icon).small,.material-symbols.small{font-size:1rem}.mat-icon:not(mat-icon).large,.material-symbols.large{font-size:2rem}.message-banner[data-type=none]{--banner-color: var(--neutral-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--neutral-color) 10% );--banner-icon-color: var(--text-color)}.message-banner[data-type=info]{--banner-color: var(--primary-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--primary-color) 10% );--banner-icon-color: var(--primary-color)}.message-banner[data-type=warning]{--banner-color: var(--warn-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--warn-color) 10% );--banner-icon-color: var(--warn-color)}.message-banner[data-type=error]{--banner-color: var(--error-color);--banner-bg-color: color-mix( in srgb, var(--background-color) 90%, var(--error-color) 10% );--banner-icon-color: var(--error-color)}.message-banner{flex:1 1 auto;border:1px solid var(--banner-color);background-color:var(--banner-bg-color);color:var(--text-color);padding:.65rem 1rem;margin:.5rem 0;border-radius:.25rem;display:flex;align-items:center;gap:.5rem;position:relative;font-size:.875rem}.message-banner .banner-header{position:absolute;display:flex;align-items:center;gap:.5rem;top:-.75rem;left:.5rem;-webkit-backdrop-filter:blur(.1rem);backdrop-filter:blur(.1rem);border-radius:.25rem;padding:0 .5rem;font-size:.75rem}.message-banner .banner-header .banner-icon{color:var(--banner-icon-color);fill:var(--banner-icon-color)}.message-banner ::ng-deep .message-content{display:flex;flex-direction:column;justify-items:center;width:100%}.message-banner ::ng-deep .message-content *{display:flex;align-items:center}\n"] }]
8791
+ }], propDecorators: { closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
8725
8792
 
8726
- /**
8727
- * Token de contexte HTTP mettre cache la réponse d'une requête via l'intercepteur CachingInterceptor.
8728
- * La ressource doit être spécifiée pour identifier l'entrée de cache.
8729
- * Vous êtes responsable de l'invalidation du cache via le token INVALIDATE_CACHE.
8730
- */
8731
- const ENABLE_CACHE = new HttpContextToken(() => null);
8732
- /**
8733
- * Token de contexte HTTP pour invalider une entrée de cache spécifique.
8734
- * La ressource doit être spécifiée pour identifier l'entrée de cache à invalider.
8735
- */
8736
- const INVALIDATE_CACHE = new HttpContextToken(() => null);
8737
- class CacheService {
8793
+ class Spinner {
8738
8794
  constructor() {
8739
- this._default = inject(CHR_CACHING_DEFAULTS);
8740
- this._cache = {};
8741
- /**
8742
- * Récupère une valeur mise en cache par sa clé
8743
- * @param key La clé de la valeur à récupérer
8744
- * @returns La valeur mise en cache ou undefined si elle n'existe pas
8745
- */
8746
- this.get = (key) => {
8747
- const entry = this._cache[key];
8748
- if (entry) {
8749
- // Vérifie si l'entrée est expirée
8750
- if (entry.status != 'expired' && Date.now() < entry.expiry) {
8751
- return entry.value;
8752
- }
8753
- else {
8754
- // Marque l'entrée comme expirée
8755
- entry.status = 'expired';
8756
- return null;
8757
- }
8758
- }
8759
- return null;
8760
- };
8761
- /**
8762
- * Met en cache une valeur avec une clé spécifique
8763
- * @param key La clé sous laquelle stocker la valeur
8764
- * @param value La valeur à mettre en cache
8765
- */
8766
- this.set = (key, value) => {
8767
- this._cache[key] = {
8768
- value: value,
8769
- expiry: Date.now() + this._cacheValidityDuration,
8770
- status: 'valid',
8771
- };
8772
- };
8773
- /**
8774
- * Invalide une entrée de cache spécifique
8775
- * @param key La clé de l'entrée à invalider
8776
- */
8777
- this.invalidate = (key) => {
8778
- if (this._cache[key]) {
8779
- this._cache[key].status = 'expired';
8780
- }
8781
- };
8782
- /**
8783
- * Mets à jour la durée de validité du cache
8784
- * @param duration Durée en millisecondes
8785
- */
8786
- this.setCacheValidityDuration = (duration) => {
8787
- this._cacheValidityDuration = duration;
8788
- };
8789
- this._cacheValidityDuration = this._default.duration;
8795
+ this.spinnerService = inject(SpinnerService);
8796
+ this.shown = input(false, /* @ts-ignore */
8797
+ ...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
8798
+ this.isLoading = this.spinnerService.isLoading;
8799
+ this.shouldShow = computed(() => this.isLoading() || this.shown(), /* @ts-ignore */
8800
+ ...(ngDevMode ? [{ debugName: "shouldShow" }] : /* istanbul ignore next */ []));
8790
8801
  }
8791
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8792
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, providedIn: 'root' }); }
8802
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8803
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: Spinner, isStandalone: true, selector: "chr-spinner", inputs: { shown: { classPropertyName: "shown", publicName: "shown", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (shouldShow()) {\r\n<div class=\"spinner-backdrop\">\r\n <div class=\"spinner-container\">\r\n <div class=\"pulse-rings\">\r\n <div class=\"pulse-ring pulse-ring-1\"></div>\r\n <div class=\"pulse-ring pulse-ring-2\"></div>\r\n <div class=\"pulse-ring pulse-ring-3\"></div>\r\n <div class=\"pulse-ring pulse-ring-4\"></div>\r\n </div>\r\n\r\n <div class=\"loading-text\">\r\n <p>Chargement...</p>\r\n </div>\r\n </div>\r\n</div>\r\n}", styles: ["@charset \"UTF-8\";.spinner-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,black 40%,transparent);display:flex;justify-content:center;align-items:center;z-index:9997;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:fadeIn .3s ease-in-out}.spinner-container{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:3rem;border-radius:1.5rem;min-width:250px;animation:slideUp .4s ease-out}.pulse-rings{position:relative;width:120px;height:120px;display:flex;align-items:center;justify-content:center}.pulse-ring{position:absolute;border:3px solid transparent;border-radius:50%;animation:pulse-ring 2.5s cubic-bezier(.25,.46,.45,.94) infinite}.pulse-ring-1{width:40px;height:40px;border-color:var(--primary-color);animation-delay:0s;box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 30%,transparent)}.pulse-ring-2{width:60px;height:60px;border-color:var(--tertiary-color);animation-delay:.6s;box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 25%,transparent)}.pulse-ring-3{width:80px;height:80px;border-color:var(--secondary-color);animation-delay:1.2s;box-shadow:0 0 30px color-mix(in srgb,var(--secondary-color) 20%,transparent)}.pulse-ring-4{width:100px;height:100px;border-color:var(--primary-color);animation-delay:1.8s;opacity:.7;box-shadow:0 0 35px color-mix(in srgb,var(--primary-color) 15%,transparent)}.loading-text{text-align:center;color:var(--text-color);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.loading-text p{margin:0;font-size:1.2rem;font-weight:500;color:var(--primary-color);letter-spacing:.5px;animation:text-pulse 2s ease-in-out infinite}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(30px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes pulse-ring{0%{transform:scale(.6);opacity:1;border-width:4px}50%{opacity:.8;border-width:2px}to{transform:scale(1.3);opacity:0;border-width:1px}}@keyframes text-pulse{0%,to{opacity:.8}50%{opacity:1;text-shadow:0 0 10px color-mix(in srgb,var(--primary-color) 30%,transparent)}}@media(max-width:768px){.spinner-container{min-width:200px;padding:2rem;gap:1.5rem}.pulse-rings{width:100px;height:100px}.pulse-ring-1{width:35px;height:35px}.pulse-ring-2{width:50px;height:50px}.pulse-ring-3{width:65px;height:65px}.pulse-ring-4{width:80px;height:80px}.loading-text p{font-size:1rem}}.dark-theme .spinner-container{box-shadow:0 20px 40px color-mix(in srgb,black 40%,transparent);border-color:var(--neutral-color)}.dark-theme .pulse-ring-1{box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 50%,transparent)}.dark-theme .pulse-ring-2{box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 40%,transparent)}.as400-theme .pulse-ring{filter:drop-shadow(0 0 8px var(--primary-color))}.as400-theme .loading-text p{text-shadow:0 0 10px var(--primary-color)}\n"] }); }
8793
8804
  }
8794
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, decorators: [{
8795
- type: Injectable,
8796
- args: [{
8797
- providedIn: 'root',
8798
- }]
8799
- }], ctorParameters: () => [] });
8805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: Spinner, decorators: [{
8806
+ type: Component,
8807
+ args: [{ selector: 'chr-spinner', imports: [], template: "@if (shouldShow()) {\r\n<div class=\"spinner-backdrop\">\r\n <div class=\"spinner-container\">\r\n <div class=\"pulse-rings\">\r\n <div class=\"pulse-ring pulse-ring-1\"></div>\r\n <div class=\"pulse-ring pulse-ring-2\"></div>\r\n <div class=\"pulse-ring pulse-ring-3\"></div>\r\n <div class=\"pulse-ring pulse-ring-4\"></div>\r\n </div>\r\n\r\n <div class=\"loading-text\">\r\n <p>Chargement...</p>\r\n </div>\r\n </div>\r\n</div>\r\n}", styles: ["@charset \"UTF-8\";.spinner-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,black 40%,transparent);display:flex;justify-content:center;align-items:center;z-index:9997;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:fadeIn .3s ease-in-out}.spinner-container{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2rem;padding:3rem;border-radius:1.5rem;min-width:250px;animation:slideUp .4s ease-out}.pulse-rings{position:relative;width:120px;height:120px;display:flex;align-items:center;justify-content:center}.pulse-ring{position:absolute;border:3px solid transparent;border-radius:50%;animation:pulse-ring 2.5s cubic-bezier(.25,.46,.45,.94) infinite}.pulse-ring-1{width:40px;height:40px;border-color:var(--primary-color);animation-delay:0s;box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 30%,transparent)}.pulse-ring-2{width:60px;height:60px;border-color:var(--tertiary-color);animation-delay:.6s;box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 25%,transparent)}.pulse-ring-3{width:80px;height:80px;border-color:var(--secondary-color);animation-delay:1.2s;box-shadow:0 0 30px color-mix(in srgb,var(--secondary-color) 20%,transparent)}.pulse-ring-4{width:100px;height:100px;border-color:var(--primary-color);animation-delay:1.8s;opacity:.7;box-shadow:0 0 35px color-mix(in srgb,var(--primary-color) 15%,transparent)}.loading-text{text-align:center;color:var(--text-color);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.loading-text p{margin:0;font-size:1.2rem;font-weight:500;color:var(--primary-color);letter-spacing:.5px;animation:text-pulse 2s ease-in-out infinite}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(30px) scale(.9)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes pulse-ring{0%{transform:scale(.6);opacity:1;border-width:4px}50%{opacity:.8;border-width:2px}to{transform:scale(1.3);opacity:0;border-width:1px}}@keyframes text-pulse{0%,to{opacity:.8}50%{opacity:1;text-shadow:0 0 10px color-mix(in srgb,var(--primary-color) 30%,transparent)}}@media(max-width:768px){.spinner-container{min-width:200px;padding:2rem;gap:1.5rem}.pulse-rings{width:100px;height:100px}.pulse-ring-1{width:35px;height:35px}.pulse-ring-2{width:50px;height:50px}.pulse-ring-3{width:65px;height:65px}.pulse-ring-4{width:80px;height:80px}.loading-text p{font-size:1rem}}.dark-theme .spinner-container{box-shadow:0 20px 40px color-mix(in srgb,black 40%,transparent);border-color:var(--neutral-color)}.dark-theme .pulse-ring-1{box-shadow:0 0 20px color-mix(in srgb,var(--primary-color) 50%,transparent)}.dark-theme .pulse-ring-2{box-shadow:0 0 25px color-mix(in srgb,var(--tertiary-color) 40%,transparent)}.as400-theme .pulse-ring{filter:drop-shadow(0 0 8px var(--primary-color))}.as400-theme .loading-text p{text-shadow:0 0 10px var(--primary-color)}\n"] }]
8808
+ }], propDecorators: { shown: [{ type: i0.Input, args: [{ isSignal: true, alias: "shown", required: false }] }] } });
8800
8809
 
8801
- const ENABLE_PROGRESS = new HttpContextToken(() => null);
8802
- class ProgressService {
8810
+ class ChrSimpleTableComponent {
8803
8811
  constructor() {
8804
- this._progressStates = {};
8805
- /**
8806
- * Récupère l'état de progression pour une clé spécifique
8807
- * @param key La clé de l'état de progression à récupérer
8808
- * @returns L'état de progression ou null si inexistant
8809
- */
8810
- this.get = (key) => {
8811
- const obs = this._progressStates[key];
8812
- if (!obs)
8813
- return null;
8814
- return this._progressStates[key].asObservable();
8815
- };
8816
- /**
8817
- * Met à jour l'état de progression pour une clé spécifique
8818
- * @param key La clé de l'état de progression à mettre à jour
8819
- * @param state Le nouvel état de progression
8820
- */
8821
- this.set = (key, state) => {
8822
- if (this._progressStates[key]) {
8823
- this._progressStates[key].next(state);
8824
- }
8825
- else {
8826
- this._progressStates[key] = new BehaviorSubject(state);
8827
- }
8828
- };
8812
+ this.id = input(crypto.randomUUID(), /* @ts-ignore */
8813
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
8814
+ this.data = input.required(/* @ts-ignore */
8815
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
8816
+ this.layout = input('auto', /* @ts-ignore */
8817
+ ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
8818
+ this.page = model(1, /* @ts-ignore */
8819
+ ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
8820
+ this.pageSize = model(10, /* @ts-ignore */
8821
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
8829
8822
  }
8830
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8831
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, providedIn: 'root' }); }
8823
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSimpleTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8824
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: ChrSimpleTableComponent, isStandalone: true, selector: "chr-table", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", pageSize: "pageSizeChange" }, queries: [{ propertyName: "captionTemplate", first: true, predicate: ["caption"], descendants: true, read: TemplateRef }, { propertyName: "headerTemplate", first: true, predicate: ["header"], descendants: true, read: TemplateRef }, { propertyName: "rowTemplate", first: true, predicate: ["row"], descendants: true, read: TemplateRef }], ngImport: i0, template: "<table class=\"chr-table\" [attr.data-layout]=\"layout()\">\n @if (captionTemplate) {\n <caption>\n <ng-container [ngTemplateOutlet]=\"captionTemplate\"></ng-container>\n </caption>\n }\n @if (headerTemplate) {\n <thead>\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"></ng-container>\n </thead>\n }\n @if (rowTemplate) {\n <tbody>\n @for (entry of data() | paginate: { id: id(), itemsPerPage: pageSize(), currentPage: page() }; track $index) {\n <ng-container [ngTemplateOutlet]=\"rowTemplate\" [ngTemplateOutletContext]=\"{ $implicit: entry }\">\n </ng-container>\n } @empty {\n <tr>\n <td>Aucune donn\u00E9e disponible</td>\n </tr>\n }\n </tbody>\n }\n</table>\n<chr-paginator [id]=\"id()\" [(page)]=\"page\" [(pageSize)]=\"pageSize\"></chr-paginator>", styles: [":host{display:block}.chr-table{table-layout:auto;width:100%;border-collapse:collapse;border-radius:.5rem;overflow:hidden;box-shadow:0 .125rem .5rem #00000012}.chr-table[data-layout=fixed]{table-layout:fixed}.chr-table thead th{text-align:left}.chr-table thead th:last-child:not(:first-child){text-align:right}.chr-table tbody th{text-align:center}.chr-table tbody td{text-align:left}.chr-table tbody td:last-child:not(:first-child){text-align:right}.chr-table th,.chr-table td{padding:.75rem;text-align:left}.chr-table td{padding:.25rem .75rem}.chr-table caption{padding:.75rem;text-align:left;width:100%}.chr-table th,.chr-table caption{background-color:var(--primary-color);color:var(--primary-contrast-color);font-weight:600}.chr-table tr:nth-child(2n){background-color:var(--neutral-color)}.chr-table tr:hover{box-shadow:inset 0 0 0 .125rem color-mix(in srgb,var(--primary-color) 30%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: NgxPaginationModule }, { kind: "component", type: ChrPaginatorComponent, selector: "app-chr-paginator,chr-paginator, chr-paginator", inputs: ["page", "pageSize", "id", "allowSizeChange", "allowPageEdit", "isPageEditing"], outputs: ["pageChange", "pageSizeChange", "isPageEditingChange"] }, { kind: "pipe", type: i2.PaginatePipe, name: "paginate" }], encapsulation: i0.ViewEncapsulation.None }); }
8832
8825
  }
8833
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, decorators: [{
8834
- type: Injectable,
8835
- args: [{
8836
- providedIn: 'root',
8837
- }]
8838
- }] });
8826
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrSimpleTableComponent, decorators: [{
8827
+ type: Component,
8828
+ args: [{ selector: 'chr-table', imports: [CommonModule, NgxPaginationModule, ChrPaginatorComponent], encapsulation: ViewEncapsulation.None, template: "<table class=\"chr-table\" [attr.data-layout]=\"layout()\">\n @if (captionTemplate) {\n <caption>\n <ng-container [ngTemplateOutlet]=\"captionTemplate\"></ng-container>\n </caption>\n }\n @if (headerTemplate) {\n <thead>\n <ng-container [ngTemplateOutlet]=\"headerTemplate\"></ng-container>\n </thead>\n }\n @if (rowTemplate) {\n <tbody>\n @for (entry of data() | paginate: { id: id(), itemsPerPage: pageSize(), currentPage: page() }; track $index) {\n <ng-container [ngTemplateOutlet]=\"rowTemplate\" [ngTemplateOutletContext]=\"{ $implicit: entry }\">\n </ng-container>\n } @empty {\n <tr>\n <td>Aucune donn\u00E9e disponible</td>\n </tr>\n }\n </tbody>\n }\n</table>\n<chr-paginator [id]=\"id()\" [(page)]=\"page\" [(pageSize)]=\"pageSize\"></chr-paginator>", styles: [":host{display:block}.chr-table{table-layout:auto;width:100%;border-collapse:collapse;border-radius:.5rem;overflow:hidden;box-shadow:0 .125rem .5rem #00000012}.chr-table[data-layout=fixed]{table-layout:fixed}.chr-table thead th{text-align:left}.chr-table thead th:last-child:not(:first-child){text-align:right}.chr-table tbody th{text-align:center}.chr-table tbody td{text-align:left}.chr-table tbody td:last-child:not(:first-child){text-align:right}.chr-table th,.chr-table td{padding:.75rem;text-align:left}.chr-table td{padding:.25rem .75rem}.chr-table caption{padding:.75rem;text-align:left;width:100%}.chr-table th,.chr-table caption{background-color:var(--primary-color);color:var(--primary-contrast-color);font-weight:600}.chr-table tr:nth-child(2n){background-color:var(--neutral-color)}.chr-table tr:hover{box-shadow:inset 0 0 0 .125rem color-mix(in srgb,var(--primary-color) 30%,transparent)}\n"] }]
8829
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], captionTemplate: [{
8830
+ type: ContentChild,
8831
+ args: ['caption', { read: TemplateRef }]
8832
+ }], headerTemplate: [{
8833
+ type: ContentChild,
8834
+ args: ['header', { read: TemplateRef }]
8835
+ }], rowTemplate: [{
8836
+ type: ContentChild,
8837
+ args: ['row', { read: TemplateRef }]
8838
+ }] } });
8839
8839
 
8840
- class CookiesService {
8841
- constructor() { }
8842
- /**
8843
- * delete cookie
8844
- * @param name
8845
- */
8846
- deleteCookie(name) {
8847
- this.setCookie(name, "", -1);
8840
+ class TabToEnterHandlerDirective {
8841
+ constructor() {
8842
+ this.el = inject(ElementRef);
8848
8843
  }
8849
- /**
8850
- * get cookie
8851
- * @param {string} name
8852
- * @returns {string}
8853
- */
8854
- getCookie(name) {
8855
- const ca = decodeURIComponent(document.cookie).split(";");
8856
- const caLen = ca.length;
8857
- const cookieName = `${name}=`;
8858
- let c;
8859
- for (let i = 0; i < caLen; i += 1) {
8860
- c = ca[i].replace(/^\s+/g, "");
8861
- if (c.indexOf(cookieName) === 0) {
8862
- return c.substring(cookieName.length, c.length);
8863
- }
8844
+ handleTab(event) {
8845
+ if (event.key === 'Tab') {
8846
+ const enter = new KeyboardEvent('keyup', {
8847
+ ...event,
8848
+ key: 'Enter',
8849
+ code: 'Enter',
8850
+ bubbles: true,
8851
+ keyCode: 13,
8852
+ });
8853
+ event.target?.dispatchEvent(enter);
8864
8854
  }
8865
- return "";
8866
- }
8867
- /**
8868
- * set cookie
8869
- * @param {string} name
8870
- * @param {string} value
8871
- * @param {number} expireDays
8872
- * @param {string} path
8873
- */
8874
- setCookie(name, value, expireDays, path = "") {
8875
- const d = new Date();
8876
- d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000);
8877
- const expires = `expires=${d.toUTCString()}`;
8878
- const cpath = path ? `; path=${path}` : "";
8879
- document.cookie = `${name}=${value}; ${expires}${cpath}; SameSite=Lax`;
8880
8855
  }
8881
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8882
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, providedIn: "root" }); }
8856
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TabToEnterHandlerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8857
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: TabToEnterHandlerDirective, isStandalone: true, selector: "[tab-enter]", host: { listeners: { "keydown": "handleTab($event)" } }, ngImport: i0 }); }
8883
8858
  }
8884
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, decorators: [{
8885
- type: Injectable,
8859
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: TabToEnterHandlerDirective, decorators: [{
8860
+ type: Directive,
8886
8861
  args: [{
8887
- providedIn: "root",
8862
+ selector: '[tab-enter]',
8863
+ standalone: true,
8888
8864
  }]
8889
- }], ctorParameters: () => [] });
8890
-
8891
- const SHOW_SPINNER = new HttpContextToken(() => true);
8865
+ }], propDecorators: { handleTab: [{
8866
+ type: HostListener,
8867
+ args: ['keydown', ['$event']]
8868
+ }] } });
8892
8869
 
8893
- class TableConstraint {
8894
- constructor(data = {}) {
8895
- this.columns = [];
8896
- this.constraintCatalog = data.constraintCatalog || '';
8897
- this.constraintSchema = data.constraintSchema || '';
8898
- this.constraintName = data.constraintName || '';
8899
- this.constraintType = data.constraintType || '';
8900
- this.tableCatalog = data.tableCatalog || '';
8901
- this.tableName = data.tableName || '';
8902
- this.tableSchema = data.tableSchema || '';
8903
- this.columns = data.columns
8904
- ? data.columns.map((d) => new TableConstraintColumn(d))
8905
- : [];
8906
- }
8907
- fullTableName() {
8908
- return `${this.tableSchema}.${this.tableName}`;
8909
- }
8910
- fullConstraintName() {
8911
- return `${this.constraintSchema}.${this.constraintName}`;
8912
- }
8913
- isPrimaryKey() {
8914
- return this.constraintType === 'PRIMARY KEY';
8915
- }
8916
- isForeignKey() {
8917
- return this.constraintType === 'FOREIGN KEY';
8918
- }
8919
- isUnique() {
8920
- return this.constraintType === 'UNIQUE';
8921
- }
8922
- isCheck() {
8923
- return this.constraintType === 'CHECK';
8870
+ class ChrPreventReloadDirective {
8871
+ constructor() {
8872
+ this.host = inject(ElementRef);
8873
+ this.chrPreventReload = input(false, /* @ts-ignore */
8874
+ ...(ngDevMode ? [{ debugName: "chrPreventReload" }] : /* istanbul ignore next */ []));
8924
8875
  }
8876
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrPreventReloadDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8877
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrPreventReloadDirective, isStandalone: true, selector: "[chrPreventReload]", inputs: { chrPreventReload: { classPropertyName: "chrPreventReload", publicName: "chrPreventReload", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:beforeunload": "chrPreventReload() ? $event.preventDefault() : null" } }, ngImport: i0 }); }
8925
8878
  }
8879
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrPreventReloadDirective, decorators: [{
8880
+ type: Directive,
8881
+ args: [{
8882
+ selector: '[chrPreventReload]',
8883
+ host: {
8884
+ '(window:beforeunload)': 'chrPreventReload() ? $event.preventDefault() : null',
8885
+ },
8886
+ }]
8887
+ }], propDecorators: { chrPreventReload: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrPreventReload", required: false }] }] } });
8926
8888
 
8927
- class BaseTable {
8928
- addRow(row) {
8929
- this.rows.push(row);
8930
- this.count += 1;
8931
- return row;
8932
- }
8933
- updateRow(row) {
8934
- const index = this.rows.findIndex((r) => this.compareRows(r, row));
8935
- if (index !== -1) {
8936
- this.rows[index] = row;
8937
- return true;
8938
- }
8939
- return false;
8889
+ class ChrDebounceDirective {
8890
+ constructor() {
8891
+ this.host = inject(ElementRef);
8892
+ this.defaults = inject(CHR_DEBOUNCE_DEFAULTS);
8893
+ this.renderer = inject(Renderer2);
8894
+ this.subject = new Subject();
8895
+ this.chrDebounceTime = input(null, /* @ts-ignore */
8896
+ ...(ngDevMode ? [{ debugName: "chrDebounceTime" }] : /* istanbul ignore next */ []));
8897
+ this.debounceTimeValue = computed(() => {
8898
+ const time = this.chrDebounceTime();
8899
+ if (time !== null && !isNaN(Number(time))) {
8900
+ return Number(time);
8901
+ }
8902
+ return this.defaults.baseInputs;
8903
+ }, /* @ts-ignore */
8904
+ ...(ngDevMode ? [{ debugName: "debounceTimeValue" }] : /* istanbul ignore next */ []));
8905
+ this.subscription = of();
8906
+ this.isDispatching = false;
8940
8907
  }
8941
- removeRow(row) {
8942
- const index = this.rows.findIndex((r) => this.compareRows(r, row));
8943
- if (index !== -1) {
8944
- this.rows.splice(index, 1);
8945
- this.count -= 1;
8946
- return true;
8947
- }
8948
- return false;
8908
+ ngOnInit() {
8909
+ const element = this.host.nativeElement;
8910
+ this.subscription = this.subject.pipe(debounceTime$1(this.debounceTimeValue()));
8911
+ // Listener en phase CAPTURE pour intercepter AVANT tous les listeners Angular
8912
+ this.captureListener = this.renderer.listen(element, 'input', (event) => {
8913
+ if (this.isDispatching) {
8914
+ return;
8915
+ }
8916
+ event.preventDefault();
8917
+ event.stopImmediatePropagation();
8918
+ event.stopPropagation();
8919
+ this.subject.next(event);
8920
+ }, { capture: true });
8921
+ this.subscription.subscribe((event) => {
8922
+ this.isDispatching = true;
8923
+ const newEvent = new InputEvent('input', {
8924
+ bubbles: true,
8925
+ cancelable: true,
8926
+ data: event.data,
8927
+ inputType: event.inputType,
8928
+ });
8929
+ element.dispatchEvent(newEvent);
8930
+ setTimeout(() => (this.isDispatching = false), 0);
8931
+ });
8949
8932
  }
8950
- compareRows(row1, row2) {
8951
- return JSON.stringify(row1) === JSON.stringify(row2);
8933
+ ngOnDestroy() {
8934
+ this.captureListener?.();
8935
+ this.subject.complete();
8952
8936
  }
8953
- getColumnByName(columnName) {
8954
- return this.columnMetadatas.find((cmd) => cmd.columnName === columnName);
8937
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrDebounceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8938
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrDebounceDirective, isStandalone: true, selector: "[chrDebounce]", inputs: { chrDebounceTime: { classPropertyName: "chrDebounceTime", publicName: "chrDebounceTime", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
8939
+ }
8940
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrDebounceDirective, decorators: [{
8941
+ type: Directive,
8942
+ args: [{
8943
+ selector: '[chrDebounce]',
8944
+ }]
8945
+ }], propDecorators: { chrDebounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrDebounceTime", required: false }] }] } });
8946
+
8947
+ class ChrHoverDirective {
8948
+ constructor() {
8949
+ this.el = inject((ElementRef));
8950
+ this.overlay = inject(Overlay);
8951
+ this.vcr = inject(ViewContainerRef);
8952
+ this.renderer = inject(Renderer2);
8953
+ this.eventListeners = [];
8954
+ this.chrHoverContext = input({}, /* @ts-ignore */
8955
+ ...(ngDevMode ? [{ debugName: "chrHoverContext" }] : /* istanbul ignore next */ []));
8956
+ this.chrHoverSnap = input(false, /* @ts-ignore */
8957
+ ...(ngDevMode ? [{ debugName: "chrHoverSnap" }] : /* istanbul ignore next */ []));
8958
+ this.chrHoverPosition = input('right', /* @ts-ignore */
8959
+ ...(ngDevMode ? [{ debugName: "chrHoverPosition" }] : /* istanbul ignore next */ []));
8960
+ this.chrHoverCloseDelay = input(10, /* @ts-ignore */
8961
+ ...(ngDevMode ? [{ debugName: "chrHoverCloseDelay" }] : /* istanbul ignore next */ []));
8962
+ this.closeTimeout = null;
8963
+ this.overlayEventListeners = [];
8964
+ this.mousePositionStrategy = (mousemove) => {
8965
+ const positionOffset = { left: mousemove.clientX, top: mousemove.clientY };
8966
+ const componentSize = this.overlayRef?.overlayElement.getBoundingClientRect();
8967
+ switch (this.chrHoverPosition()) {
8968
+ case 'above':
8969
+ positionOffset.top -= (componentSize?.height || 0) + 20;
8970
+ positionOffset.left -= (componentSize?.width || 0) / 2;
8971
+ break;
8972
+ case 'below':
8973
+ positionOffset.top += 20;
8974
+ positionOffset.left -= (componentSize?.width || 0) / 2;
8975
+ break;
8976
+ case 'left':
8977
+ positionOffset.left -= (componentSize?.width || 0) + 20;
8978
+ positionOffset.top -= (componentSize?.height || 0) / 2;
8979
+ break;
8980
+ case 'right':
8981
+ positionOffset.left += 20;
8982
+ positionOffset.top -= (componentSize?.height || 0) / 2;
8983
+ break;
8984
+ }
8985
+ const positionStrategy = this.overlay
8986
+ .position()
8987
+ .global()
8988
+ .left(`${positionOffset.left}px`)
8989
+ .top(`${positionOffset.top}px`);
8990
+ return positionStrategy;
8991
+ };
8992
+ this.snappedStrategy = () => {
8993
+ const positionStrategy = this.overlay
8994
+ .position()
8995
+ .flexibleConnectedTo(this.el);
8996
+ switch (this.chrHoverPosition()) {
8997
+ case 'above':
8998
+ positionStrategy.withPositions([
8999
+ {
9000
+ originX: 'center',
9001
+ originY: 'top',
9002
+ overlayX: 'center',
9003
+ overlayY: 'bottom',
9004
+ },
9005
+ {
9006
+ originX: 'center',
9007
+ originY: 'bottom',
9008
+ overlayX: 'center',
9009
+ overlayY: 'top',
9010
+ },
9011
+ ]);
9012
+ break;
9013
+ case 'below':
9014
+ positionStrategy.withPositions([
9015
+ {
9016
+ originX: 'center',
9017
+ originY: 'bottom',
9018
+ overlayX: 'center',
9019
+ overlayY: 'top',
9020
+ },
9021
+ {
9022
+ originX: 'center',
9023
+ originY: 'top',
9024
+ overlayX: 'center',
9025
+ overlayY: 'bottom',
9026
+ },
9027
+ ]);
9028
+ break;
9029
+ case 'left':
9030
+ positionStrategy.withPositions([
9031
+ {
9032
+ originX: 'start',
9033
+ originY: 'center',
9034
+ overlayX: 'end',
9035
+ overlayY: 'center',
9036
+ },
9037
+ {
9038
+ originX: 'end',
9039
+ originY: 'center',
9040
+ overlayX: 'start',
9041
+ overlayY: 'center',
9042
+ },
9043
+ ]);
9044
+ break;
9045
+ case 'right':
9046
+ positionStrategy.withPositions([
9047
+ {
9048
+ originX: 'end',
9049
+ originY: 'center',
9050
+ overlayX: 'start',
9051
+ overlayY: 'center',
9052
+ },
9053
+ {
9054
+ originX: 'start',
9055
+ originY: 'center',
9056
+ overlayX: 'end',
9057
+ overlayY: 'center',
9058
+ },
9059
+ ]);
9060
+ break;
9061
+ }
9062
+ return positionStrategy;
9063
+ };
9064
+ const positionStrategy = this.snappedStrategy();
9065
+ this.overlayRef = this.overlay.create({
9066
+ positionStrategy,
9067
+ hasBackdrop: false,
9068
+ disposeOnNavigation: true,
9069
+ disableClose: true,
9070
+ });
8955
9071
  }
8956
- getConstraintsByType(type) {
8957
- return this.tableConstraints.filter((tc) => tc.constraintType === type);
9072
+ ngOnInit() {
9073
+ this.overlayRef.updatePositionStrategy(this.snappedStrategy());
9074
+ this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mouseenter', () => {
9075
+ this.clearCloseTimeout();
9076
+ this.open();
9077
+ }));
9078
+ this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mouseleave', () => {
9079
+ this.startCloseTimeout();
9080
+ }));
9081
+ this.eventListeners.push(this.renderer.listen(this.el.nativeElement, 'mousemove', (event) => {
9082
+ if (!this.chrHoverSnap()) {
9083
+ const positionStrategy = this.mousePositionStrategy(event);
9084
+ this.overlayRef.updatePositionStrategy(positionStrategy);
9085
+ }
9086
+ }));
8958
9087
  }
8959
- }
8960
- class AnonymousTable extends BaseTable {
8961
- constructor(data) {
8962
- super();
8963
- this.catalog = data.catalog;
8964
- this.schema = data.schema;
8965
- this.name = data.name;
8966
- this.description = data.description;
8967
- this.isReadOnly = data.isReadOnly;
8968
- this.rowNumberIdentity = data.rowNumberIdentity;
8969
- this.columnMetadatas = data.columnMetadatas
8970
- ? data.columnMetadatas
8971
- .map((d) => new ColumnMetadata(d))
8972
- .sort((a, b) => a.ordinalPosition - b.ordinalPosition)
8973
- : [];
8974
- this.tableConstraints = data.tableConstraints
8975
- ? data.tableConstraints.map((d) => new TableConstraint(d))
8976
- : [];
8977
- this.hasPrimaryKey =
8978
- data.hasPrimaryKey === undefined ? false : Boolean(data.hasPrimaryKey);
8979
- this.rows = data.rows || [];
8980
- this.count = data.count || 0;
8981
- this.total = data.total || 0;
9088
+ ngOnDestroy() {
9089
+ this.clearCloseTimeout();
9090
+ this.clearOverlayListeners();
9091
+ this.overlayRef.dispose();
9092
+ this.removeEventListeners();
8982
9093
  }
8983
- fullName() {
8984
- return `${this.schema}.${this.name}`;
9094
+ removeEventListeners() {
9095
+ this.eventListeners.forEach((unlisten) => unlisten());
9096
+ this.eventListeners = [];
8985
9097
  }
8986
- primaryKeyColumns() {
8987
- const pkColumns = this.tableConstraints.filter((tc) => tc.isPrimaryKey())?.[0]?.columns;
8988
- if (!pkColumns || pkColumns.length === 0) {
8989
- const rn = this.columnMetadatas.find((cmd) => cmd.columnName === 'ROW_NUMBER');
8990
- return rn ? [rn] : [];
8991
- }
8992
- return this.columnMetadatas.filter((cmd) => pkColumns?.some((pc) => pc.columnName === cmd.columnName));
9098
+ startCloseTimeout() {
9099
+ this.clearCloseTimeout();
9100
+ this.closeTimeout = window.setTimeout(() => this.close(), this.chrHoverCloseDelay());
8993
9101
  }
8994
- getRowIdentifier(row) {
8995
- if (!this.hasPrimaryKey) {
8996
- if (row[this.rowNumberIdentity] !== undefined) {
8997
- return `${row[this.rowNumberIdentity]}`;
8998
- }
8999
- }
9000
- const pkColumns = this.primaryKeyColumns();
9001
- if (this.hasPrimaryKey && pkColumns.length > 0) {
9002
- let key = '';
9003
- let index = 0;
9004
- for (const pc of pkColumns) {
9005
- if (row[pc.columnName] === undefined || row[pc.columnName] === null) {
9006
- if (row[this.rowNumberIdentity] !== undefined)
9007
- return `${row[this.rowNumberIdentity]}`;
9008
- else
9009
- break;
9010
- }
9011
- if (index == pkColumns.length - 1) {
9012
- key += `${pc.columnName}:${row[pc.columnName]}`;
9013
- }
9014
- else
9015
- key += `${pc.columnName}:${row[pc.columnName]}|`;
9016
- index++;
9017
- }
9018
- return key;
9102
+ clearCloseTimeout() {
9103
+ if (this.closeTimeout != null) {
9104
+ clearTimeout(this.closeTimeout);
9105
+ this.closeTimeout = null;
9019
9106
  }
9020
- return JSON.stringify(row);
9021
9107
  }
9022
- getRowNumber(row) {
9023
- return row[this.rowNumberIdentity];
9108
+ clearOverlayListeners() {
9109
+ this.overlayEventListeners.forEach((u) => u());
9110
+ this.overlayEventListeners = [];
9024
9111
  }
9025
- compareRows(row1, row2) {
9026
- if (!this.hasPrimaryKey) {
9027
- if (row1[this.rowNumberIdentity] !== undefined &&
9028
- row2[this.rowNumberIdentity] !== undefined) {
9029
- return row1[this.rowNumberIdentity] === row2[this.rowNumberIdentity];
9112
+ open() {
9113
+ if (!this.overlayRef.hasAttached()) {
9114
+ if (!this.portal) {
9115
+ this.portal = new TemplatePortal(this.template, this.vcr, this.chrHoverContext());
9030
9116
  }
9117
+ const ref = this.overlayRef.attach(this.portal);
9118
+ // keep overlay open when overlay itself is hovered
9119
+ const overlayEl = this.overlayRef.overlayElement;
9120
+ this.clearOverlayListeners();
9121
+ this.overlayEventListeners.push(this.renderer.listen(overlayEl, 'mouseenter', () => {
9122
+ this.clearCloseTimeout();
9123
+ }));
9124
+ this.overlayEventListeners.push(this.renderer.listen(overlayEl, 'mouseleave', () => {
9125
+ this.startCloseTimeout();
9126
+ }));
9031
9127
  }
9032
- const pkColumns = this.primaryKeyColumns();
9033
- if (this.hasPrimaryKey && pkColumns.length > 0) {
9034
- return pkColumns.every((pc) => row1[pc.columnName] === row2[pc.columnName]);
9035
- }
9036
- return super.compareRows(row1, row2);
9037
- }
9038
- addRow(row) {
9039
- row[this.rowNumberIdentity] = this.generateTempRowIdentifier();
9040
- return super.addRow(row);
9041
- }
9042
- generateTempRowIdentifier() {
9043
- return crypto.randomUUID();
9044
- }
9045
- }
9046
-
9047
- class MinFileNameLengthValidator {
9048
- constructor() {
9049
- this.max = input(0, /* @ts-ignore */
9050
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
9051
9128
  }
9052
- validate(control) {
9053
- const max = this.max();
9054
- return max ? minFileNameLength(max)(control) : null;
9129
+ close() {
9130
+ this.clearCloseTimeout();
9131
+ this.clearOverlayListeners();
9132
+ this.overlayRef.detach();
9055
9133
  }
9056
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
9057
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: MinFileNameLengthValidator, isStandalone: true, selector: "[app-min-file-name-length]", inputs: { max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
9134
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrHoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
9135
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: ChrHoverDirective, isStandalone: true, selector: "[chrHover]", inputs: { template: { classPropertyName: "template", publicName: "chrHover", isSignal: false, isRequired: false, transformFunction: null }, chrHoverContext: { classPropertyName: "chrHoverContext", publicName: "chrHoverContext", isSignal: true, isRequired: false, transformFunction: null }, chrHoverSnap: { classPropertyName: "chrHoverSnap", publicName: "chrHoverSnap", isSignal: true, isRequired: false, transformFunction: null }, chrHoverPosition: { classPropertyName: "chrHoverPosition", publicName: "chrHoverPosition", isSignal: true, isRequired: false, transformFunction: null }, chrHoverCloseDelay: { classPropertyName: "chrHoverCloseDelay", publicName: "chrHoverCloseDelay", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
9058
9136
  {
9059
- provide: NG_VALIDATORS,
9060
- useExisting: MinFileNameLengthValidator,
9061
- multi: true,
9137
+ provide: CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG,
9138
+ useFactory: () => {
9139
+ disableClose: true;
9140
+ },
9062
9141
  },
9063
9142
  ], ngImport: i0 }); }
9064
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, providedIn: 'root' }); }
9065
9143
  }
9066
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, decorators: [{
9067
- type: Injectable,
9068
- args: [{ providedIn: 'root' }]
9069
- }, {
9144
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ChrHoverDirective, decorators: [{
9070
9145
  type: Directive,
9071
9146
  args: [{
9072
- selector: '[app-min-file-name-length]',
9147
+ selector: '[chrHover]',
9073
9148
  providers: [
9074
9149
  {
9075
- provide: NG_VALIDATORS,
9076
- useExisting: MinFileNameLengthValidator,
9077
- multi: true,
9150
+ provide: CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG,
9151
+ useFactory: () => {
9152
+ disableClose: true;
9153
+ },
9078
9154
  },
9079
9155
  ],
9080
- standalone: true,
9081
9156
  }]
9082
- }], propDecorators: { max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }] } });
9083
- function minFileNameLength(minLength) {
9084
- return (control) => {
9085
- const value = control.value;
9086
- const files = Array.isArray(value) ? value : [value];
9087
- if (!value)
9088
- return null;
9089
- const errors = {};
9090
- for (let i = 0; i < files.length; i++) {
9091
- const fileNameLength = files[i].name?.length;
9092
- if (fileNameLength === undefined)
9093
- continue;
9094
- if (fileNameLength < minLength)
9095
- errors[i] = fileNameLength;
9096
- }
9097
- if (Object.keys(errors).length <= 0)
9098
- return null;
9099
- return {
9100
- minFileNameLength: {
9101
- minFileNameLength: minLength,
9102
- indexes: errors,
9103
- },
9104
- indexes: errors,
9105
- };
9106
- };
9107
- }
9157
+ }], ctorParameters: () => [], propDecorators: { template: [{
9158
+ type: Input,
9159
+ args: ['chrHover']
9160
+ }], chrHoverContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverContext", required: false }] }], chrHoverSnap: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverSnap", required: false }] }], chrHoverPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverPosition", required: false }] }], chrHoverCloseDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "chrHoverCloseDelay", required: false }] }] } });
9108
9161
 
9109
- class AutoDataTable extends BaseTable {
9110
- get columnMetadatas() {
9111
- return this._columnMetadatas.sort((a, b) => a.ordinalPosition - b.ordinalPosition);
9112
- }
9113
- set columnMetadatas(value) {
9114
- this._columnMetadatas = value;
9115
- }
9116
- get rows() {
9117
- return this._rows;
9118
- }
9119
- set rows(value) {
9120
- this.count = value.length;
9121
- this._rows = value.map((row, index) => {
9122
- if (!this.hasPrimaryKey && !row.hasOwnProperty(this.rowNumberIdentity)) {
9123
- return { [this.rowNumberIdentity]: index + 1, ...row };
9124
- }
9125
- return row;
9162
+ class CsvExporter {
9163
+ export(data, columns) {
9164
+ const csvRows = [];
9165
+ csvRows.push(columns.join(','));
9166
+ data.forEach((row) => {
9167
+ const values = columns.map((col) => {
9168
+ const cell = row[col];
9169
+ return typeof cell === 'string' ? cell.replace(/;/g, ',') : cell;
9170
+ });
9171
+ csvRows.push(values.join(','));
9126
9172
  });
9127
- this.deduceColumnTypes();
9173
+ const csvContent = csvRows.join('\n');
9174
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
9175
+ return URL.createObjectURL(blob);
9128
9176
  }
9129
- constructor(name, primaryKey = null, metadatas = [], rowIdentifier = 'ROW_NUMBER') {
9130
- super();
9131
- this.name = '';
9132
- this.schema = '';
9133
- this.total = 0;
9134
- this._columnMetadatas = [];
9135
- this.tableConstraints = [];
9136
- this._primaryKey = null;
9137
- this.hasPrimaryKey = this._primaryKey !== null;
9138
- this._rows = [];
9139
- this.count = 0;
9140
- this.getRowIdentifier = (row) => {
9141
- if (this._primaryKey && row.hasOwnProperty(this._primaryKey)) {
9142
- if (Array.isArray(this._primaryKey)) {
9143
- return this._primaryKey.map((key) => row[key]).join('_');
9144
- }
9145
- return row[this._primaryKey].toString();
9146
- }
9147
- return row[this.rowNumberIdentity].toString();
9148
- };
9149
- this.getRowNumber = (row) => {
9150
- return row[this.rowNumberIdentity];
9151
- };
9152
- this.primaryKeyColumns = () => {
9153
- if (!this._primaryKey) {
9154
- return [];
9155
- }
9156
- return this.columnMetadatas.filter((col) => col.columnName === this._primaryKey);
9157
- };
9158
- this.deduceColumnTypes = () => {
9159
- if (this.rows.length === 0) {
9160
- return;
9161
- }
9162
- if (this.columnMetadatas.length !== 0)
9163
- return;
9164
- for (const key of Object.keys(this.rows[0])) {
9165
- const inferredType = typeof this.rows[0][key];
9166
- const existingIndex = this.columnMetadatas.findIndex((md) => md.columnName === key);
9167
- const existingMetadata = this.columnMetadatas[existingIndex];
9168
- if (!existingMetadata) {
9169
- const md = {
9170
- ordinalPosition: this.columnMetadatas.length + 1,
9171
- length: 0,
9172
- tableName: this.name,
9173
- tableSchema: this.schema,
9174
- isIdentity: false,
9175
- isGenerated: false,
9176
- isUpdatable: false,
9177
- tooltip: `${inferredType} ${length ? `(${inferredType})` : ''}`,
9178
- columnName: key,
9179
- dataType: inferredType,
9180
- isNullable: false,
9181
- };
9182
- this.columnMetadatas.push(md);
9183
- }
9177
+ canExport(filetype) {
9178
+ const supportedTypes = ['.csv', 'text/csv', 'csv'];
9179
+ return supportedTypes.includes(filetype);
9180
+ }
9181
+ }
9182
+
9183
+ class JsonExporter {
9184
+ export(data, columns) {
9185
+ const jsonData = data.map((row) => {
9186
+ const obj = {};
9187
+ columns.forEach((col) => {
9188
+ obj[col] = row[col];
9189
+ });
9190
+ return obj;
9191
+ });
9192
+ const blob = new Blob([JSON.stringify(jsonData, null, 2)], {
9193
+ type: 'application/json;charset=utf-8;',
9194
+ });
9195
+ return URL.createObjectURL(blob);
9196
+ }
9197
+ canExport(filetype) {
9198
+ const supportedTypes = ['.json', 'application/json', 'json'];
9199
+ return supportedTypes.includes(filetype);
9200
+ }
9201
+ }
9202
+
9203
+ class DataExporterService {
9204
+ constructor() {
9205
+ this.exporter = [new CsvExporter(), new JsonExporter()];
9206
+ }
9207
+ setExporters(exporters) {
9208
+ this.exporter = exporters;
9209
+ }
9210
+ registerExporter(exporter) {
9211
+ this.exporter.push(exporter);
9212
+ }
9213
+ export(data, columns, filetype, filename = 'export', autodownload = false) {
9214
+ const exporter = this.exporter.find((exp) => exp.canExport(filetype));
9215
+ if (exporter) {
9216
+ const url = exporter.export(data, columns);
9217
+ if (autodownload) {
9218
+ const link = document.createElement('a');
9219
+ link.href = url;
9220
+ link.download = `${filename}.${filetype.split('.').pop()}`;
9221
+ document.body.appendChild(link);
9222
+ link.click();
9223
+ document.body.removeChild(link);
9184
9224
  }
9185
- };
9225
+ return url;
9226
+ }
9227
+ return null;
9228
+ }
9229
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9230
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, providedIn: 'root' }); }
9231
+ }
9232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: DataExporterService, decorators: [{
9233
+ type: Injectable,
9234
+ args: [{
9235
+ providedIn: 'root',
9236
+ }]
9237
+ }], ctorParameters: () => [] });
9238
+
9239
+ class CachingDefaults {
9240
+ constructor(init) {
9241
+ this.duration = 1000 * 60 * 5; // 5 minutes par défaut;
9242
+ Object.assign(this, init);
9243
+ }
9244
+ }
9245
+ const CHR_CACHING_DEFAULTS = new InjectionToken('CHR_CACHING_DEFAULTS', {
9246
+ factory: () => new CachingDefaults(),
9247
+ });
9248
+
9249
+ /**
9250
+ * Token de contexte HTTP mettre cache la réponse d'une requête via l'intercepteur CachingInterceptor.
9251
+ * La ressource doit être spécifiée pour identifier l'entrée de cache.
9252
+ * Vous êtes responsable de l'invalidation du cache via le token INVALIDATE_CACHE.
9253
+ */
9254
+ const ENABLE_CACHE = new HttpContextToken(() => null);
9255
+ /**
9256
+ * Token de contexte HTTP pour invalider une entrée de cache spécifique.
9257
+ * La ressource doit être spécifiée pour identifier l'entrée de cache à invalider.
9258
+ */
9259
+ const INVALIDATE_CACHE = new HttpContextToken(() => null);
9260
+ class CacheService {
9261
+ constructor() {
9262
+ this._default = inject(CHR_CACHING_DEFAULTS);
9263
+ this._cache = {};
9186
9264
  /**
9187
- * Retourne une copie de l'AutoDataTable
9265
+ * Récupère une valeur mise en cache par sa clé
9266
+ * @param key La clé de la valeur à récupérer
9267
+ * @returns La valeur mise en cache ou undefined si elle n'existe pas
9188
9268
  */
9189
- this.clone = () => {
9190
- const copy = new AutoDataTable(this.name, this._primaryKey, this.columnMetadatas, this.rowNumberIdentity);
9191
- for (const key of Object.keys(this)) {
9192
- if (Array.isArray(this[key])) {
9193
- copy[key] = [...this[key]];
9269
+ this.get = (key) => {
9270
+ const entry = this._cache[key];
9271
+ if (entry) {
9272
+ // Vérifie si l'entrée est expirée
9273
+ if (entry.status != 'expired' && Date.now() < entry.expiry) {
9274
+ return entry.value;
9194
9275
  }
9195
9276
  else {
9196
- copy[key] = this[key];
9277
+ // Marque l'entrée comme expirée
9278
+ entry.status = 'expired';
9279
+ return null;
9197
9280
  }
9198
9281
  }
9199
- return copy;
9282
+ return null;
9200
9283
  };
9201
- this.name = name;
9202
- this._primaryKey = primaryKey;
9203
- this.hasPrimaryKey = this._primaryKey !== null;
9204
- this.rowNumberIdentity = rowIdentifier;
9205
- this.columnMetadatas = metadatas.map((md, index) => ({
9206
- displayName: md.displayName || md.columnName,
9207
- ordinalPosition: index + 1,
9208
- length: 0,
9209
- tableName: this.name,
9210
- tableSchema: this.schema,
9211
- isIdentity: false,
9212
- isGenerated: false,
9213
- isUpdatable: false,
9214
- tooltip: `${md.dataType?.toUpperCase()} ${md.length ? `(${md.length})` : ''}`,
9215
- ...md,
9216
- }));
9217
- }
9218
- }
9219
-
9220
- class AutoDataSource {
9221
- constructor(config) {
9222
- this._subscriptions = [];
9223
- // Signaux internes
9224
- this._datagrid = signal(null, /* @ts-ignore */
9225
- ...(ngDevMode ? [{ debugName: "_datagrid" }] : /* istanbul ignore next */ []));
9226
- this._datagridElement = signal(null, /* @ts-ignore */
9227
- ...(ngDevMode ? [{ debugName: "_datagridElement" }] : /* istanbul ignore next */ []));
9228
- this._data = signal(undefined, /* @ts-ignore */
9229
- ...(ngDevMode ? [{ debugName: "_data" }] : /* istanbul ignore next */ []));
9230
- // protected _currentPage = signal(1);
9231
- // protected _pageSize = signal(10);
9232
- this._sort = signal(null, /* @ts-ignore */
9233
- ...(ngDevMode ? [{ debugName: "_sort" }] : /* istanbul ignore next */ []));
9234
- this._filter = signal({}, /* @ts-ignore */
9235
- ...(ngDevMode ? [{ debugName: "_filter" }] : /* istanbul ignore next */ []));
9236
- this._groupBy = signal(null, /* @ts-ignore */
9237
- ...(ngDevMode ? [{ debugName: "_groupBy" }] : /* istanbul ignore next */ []));
9238
- this._loading = signal(false, /* @ts-ignore */
9239
- ...(ngDevMode ? [{ debugName: "_loading" }] : /* istanbul ignore next */ []));
9240
- this._error = signal(null, /* @ts-ignore */
9241
- ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
9242
- // Données exposées (read-only)
9243
- this.data = this._data.asReadonly();
9244
- // public readonly currentPage: Signal<number> = this._currentPage.asReadonly();
9245
- // public readonly pageSize: Signal<number> = this._pageSize.asReadonly();
9246
- this.sort = this._sort.asReadonly();
9247
- this.filter = this._filter.asReadonly();
9248
- this.groupBy = this._groupBy.asReadonly();
9249
- this.loading = this._loading.asReadonly();
9250
- this.error = this._error.asReadonly();
9251
- // Données traitées
9252
- this.processedData = computed(() => {
9253
- let result = this._data();
9254
- if (!result)
9255
- return undefined;
9256
- result.total = result.rows.length ?? -1;
9257
- // Filtrage côté client
9258
- if (this.config.enableClientSideFiltering) {
9259
- result = this.applyFilters(result, this.filter());
9260
- }
9261
- // Tri côté client
9262
- if (this.config.enableClientSideSorting) {
9263
- result = this.applySort(result, this._sort());
9264
- }
9265
- // Pagination côté client
9266
- if (this.config.enableClientSidePagination) {
9267
- // Do nothing, it is handled by the data grid directly
9268
- }
9269
- result.count = result.rows.length;
9270
- return result;
9271
- }, /* @ts-ignore */
9272
- ...(ngDevMode ? [{ debugName: "processedData" }] : /* istanbul ignore next */ []));
9273
- this.config = {
9274
- initialData: config?.initialData,
9275
- pageSize: config?.pageSize ?? 10,
9276
- enableClientSidePagination: config?.enableClientSidePagination ?? true,
9277
- enableClientSideFiltering: config?.enableClientSideFiltering ?? true,
9278
- enableClientSideSorting: config?.enableClientSideSorting ?? true,
9284
+ /**
9285
+ * Met en cache une valeur avec une clé spécifique
9286
+ * @param key La clé sous laquelle stocker la valeur
9287
+ * @param value La valeur à mettre en cache
9288
+ */
9289
+ this.set = (key, value) => {
9290
+ this._cache[key] = {
9291
+ value: value,
9292
+ expiry: Date.now() + this._cacheValidityDuration,
9293
+ status: 'valid',
9294
+ };
9279
9295
  };
9280
- if (this.config.initialData) {
9281
- this._data.set(this.config.initialData);
9282
- }
9283
- // this._pageSize.set(this.config.pageSize);
9284
- effect(() => {
9285
- if (this.processedData() && this._datagrid()) {
9286
- this._datagrid().table.set(this.processedData());
9287
- this._datagrid().ngOnDestroy = () => {
9288
- this._datagrid()?.ngOnDestroy();
9289
- this._datagrid.set(null);
9290
- for (const sub of this._subscriptions) {
9291
- sub.unsubscribe();
9292
- }
9293
- };
9294
- }
9295
- });
9296
- }
9297
- // Méthode pour enregistrer la datagrid et écouter les événements de base
9298
- registerDataGrid(element) {
9299
- if (!element)
9300
- return;
9301
- this._datagridElement.set(element instanceof ElementRef ? element : null);
9302
- const datagrid = (element instanceof ElementRef ? element.nativeElement : element);
9303
- this._datagrid.set(datagrid);
9304
- this._subscriptions.push(
9305
- // datagrid.pageChange.subscribe((page: number) => {
9306
- // console.log(
9307
- // 'Page change event received:',
9308
- // page,
9309
- // this._datagridElement(),
9310
- // element,
9311
- // );
9312
- // // this._subscriptions.push(this.handlePageChange(page).subscribe());
9313
- // }),
9314
- // datagrid.pageSizeChange.subscribe((pageSize: number) => {
9315
- // // this._subscriptions
9316
- // // .push(
9317
- // // this.handlePageSizeChange(pageSize).subscribe(),
9318
- // // );
9319
- // }),
9320
- datagrid.sort.subscribe((sort) => {
9321
- this._subscriptions.push(this.handleSortChange(sort).subscribe());
9322
- }), datagrid.filter.subscribe((filter) => {
9323
- this._subscriptions.push(this.handleFilterChange(filter).subscribe());
9324
- }), datagrid.groupBy.subscribe((group) => {
9325
- this._subscriptions.push(this.handleGroupChange(group).subscribe());
9326
- }));
9327
- }
9328
- // Méthodes publiques pour gérer les événements de la datagrid
9329
- // public handlePageChange(page: number): Observable<void> {
9330
- // this._currentPage.set(page);
9331
- // return this.onPageChange(page);
9332
- // }
9333
- // public handlePageSizeChange(pageSize: number): Observable<void> {
9334
- // this._pageSize.set(pageSize);
9335
- // this._currentPage.set(1); // Reset à la première page
9336
- // return this.onPageSizeChange(pageSize);
9337
- // }
9338
- handleSortChange(sort) {
9339
- this._sort.set(sort);
9340
- return this.onSortChange(sort);
9341
- }
9342
- handleFilterChange(filter) {
9343
- this._filter.set(filter);
9344
- this._datagrid()?.currentPage.set(1); // Reset à la première page
9345
- return this.onFilterChange(filter);
9296
+ /**
9297
+ * Invalide une entrée de cache spécifique
9298
+ * @param key La clé de l'entrée à invalider
9299
+ */
9300
+ this.invalidate = (key) => {
9301
+ if (this._cache[key]) {
9302
+ this._cache[key].status = 'expired';
9303
+ }
9304
+ };
9305
+ /**
9306
+ * Mets à jour la durée de validité du cache
9307
+ * @param duration Durée en millisecondes
9308
+ */
9309
+ this.setCacheValidityDuration = (duration) => {
9310
+ this._cacheValidityDuration = duration;
9311
+ };
9312
+ this._cacheValidityDuration = this._default.duration;
9346
9313
  }
9347
- handleGroupChange(group) {
9348
- this._groupBy.set(group);
9349
- this._datagrid()?.currentPage.set(1); // Reset à la première page
9350
- return this.onGroupChange(group);
9314
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9315
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, providedIn: 'root' }); }
9316
+ }
9317
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CacheService, decorators: [{
9318
+ type: Injectable,
9319
+ args: [{
9320
+ providedIn: 'root',
9321
+ }]
9322
+ }], ctorParameters: () => [] });
9323
+
9324
+ const ENABLE_PROGRESS = new HttpContextToken(() => null);
9325
+ class ProgressService {
9326
+ constructor() {
9327
+ this._progressStates = {};
9328
+ /**
9329
+ * Récupère l'état de progression pour une clé spécifique
9330
+ * @param key La clé de l'état de progression à récupérer
9331
+ * @returns L'état de progression ou null si inexistant
9332
+ */
9333
+ this.get = (key) => {
9334
+ const obs = this._progressStates[key];
9335
+ if (!obs)
9336
+ return null;
9337
+ return this._progressStates[key].asObservable();
9338
+ };
9339
+ /**
9340
+ * Met à jour l'état de progression pour une clé spécifique
9341
+ * @param key La clé de l'état de progression à mettre à jour
9342
+ * @param state Le nouvel état de progression
9343
+ */
9344
+ this.set = (key, state) => {
9345
+ if (this._progressStates[key]) {
9346
+ this._progressStates[key].next(state);
9347
+ }
9348
+ else {
9349
+ this._progressStates[key] = new BehaviorSubject(state);
9350
+ }
9351
+ };
9351
9352
  }
9352
- refresh() {
9353
- return this.loadData();
9353
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9354
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, providedIn: 'root' }); }
9355
+ }
9356
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: ProgressService, decorators: [{
9357
+ type: Injectable,
9358
+ args: [{
9359
+ providedIn: 'root',
9360
+ }]
9361
+ }] });
9362
+
9363
+ class CookiesService {
9364
+ constructor() { }
9365
+ /**
9366
+ * delete cookie
9367
+ * @param name
9368
+ */
9369
+ deleteCookie(name) {
9370
+ this.setCookie(name, "", -1);
9354
9371
  }
9355
- setRows(rows) {
9356
- if (!this._data()) {
9357
- return;
9372
+ /**
9373
+ * get cookie
9374
+ * @param {string} name
9375
+ * @returns {string}
9376
+ */
9377
+ getCookie(name) {
9378
+ const ca = decodeURIComponent(document.cookie).split(";");
9379
+ const caLen = ca.length;
9380
+ const cookieName = `${name}=`;
9381
+ let c;
9382
+ for (let i = 0; i < caLen; i += 1) {
9383
+ c = ca[i].replace(/^\s+/g, "");
9384
+ if (c.indexOf(cookieName) === 0) {
9385
+ return c.substring(cookieName.length, c.length);
9386
+ }
9358
9387
  }
9359
- const updated = this._data().clone();
9360
- updated.rows = rows;
9361
- updated.total = rows.length;
9362
- this._data.set(updated);
9363
- }
9364
- setData(data) {
9365
- this._data.set(data);
9366
- }
9367
- // Méthodes virtuelles à surcharger pour créer une data source serveur-side
9368
- onPageChange(page) {
9369
- // Comportement par défaut : rien (pagination côté client)
9370
- return of(void 0);
9371
- }
9372
- onPageSizeChange(pageSize) {
9373
- // Comportement par défaut : rien (pagination côté client)
9374
- return of(void 0);
9375
- }
9376
- onSortChange(sort) {
9377
- // Comportement par défaut : rien (tri côté client)
9378
- return of(void 0);
9379
- }
9380
- onFilterChange(filter) {
9381
- // Comportement par défaut : rien (filtrage côté client)
9382
- return of(void 0);
9383
- }
9384
- onGroupChange(group) {
9385
- // Comportement par défaut : rien
9386
- return of(void 0);
9388
+ return "";
9387
9389
  }
9388
- loadData() {
9389
- // À surcharger pour charger les données
9390
- return of(void 0);
9390
+ /**
9391
+ * set cookie
9392
+ * @param {string} name
9393
+ * @param {string} value
9394
+ * @param {number} expireDays
9395
+ * @param {string} path
9396
+ */
9397
+ setCookie(name, value, expireDays, path = "") {
9398
+ const d = new Date();
9399
+ d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000);
9400
+ const expires = `expires=${d.toUTCString()}`;
9401
+ const cpath = path ? `; path=${path}` : "";
9402
+ document.cookie = `${name}=${value}; ${expires}${cpath}; SameSite=Lax`;
9391
9403
  }
9392
- // Méthodes de traitement côté client (peuvent être surchargées)
9393
- applyFilters(data, filters) {
9394
- if (Object.keys(filters).length === 0)
9395
- return data;
9396
- const filteredRows = data.rows.filter((row) => {
9397
- return Object.values(filters).every((filter) => {
9398
- const value = row[filter.column];
9399
- const filterValue = String(filter.value).toLowerCase();
9400
- const stringValue = String(value ?? '').toLowerCase();
9401
- switch (filter.mode) {
9402
- case DGFilterMode.CONTAINS:
9403
- return stringValue.includes(filterValue);
9404
- case DGFilterMode.STARTS_WITH:
9405
- return stringValue.startsWith(filterValue);
9406
- case DGFilterMode.ENDS_WITH:
9407
- return stringValue.endsWith(filterValue);
9408
- case DGFilterMode.EQUALS:
9409
- return stringValue === filterValue;
9410
- case DGFilterMode.NOT_EQUALS:
9411
- return stringValue !== filterValue;
9412
- case DGFilterMode.GREATER_OR_EQUAL:
9413
- return Number(value) >= Number(filter.value);
9414
- case DGFilterMode.LOWER_OR_EQUAL:
9415
- return Number(value) <= Number(filter.value);
9416
- default:
9417
- return true;
9418
- }
9419
- });
9420
- });
9421
- const cloned = data.clone();
9422
- cloned.rows = filteredRows;
9423
- return cloned;
9404
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9405
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, providedIn: "root" }); }
9406
+ }
9407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: CookiesService, decorators: [{
9408
+ type: Injectable,
9409
+ args: [{
9410
+ providedIn: "root",
9411
+ }]
9412
+ }], ctorParameters: () => [] });
9413
+
9414
+ const SHOW_SPINNER = new HttpContextToken(() => true);
9415
+
9416
+ class MinFileNameLengthValidator {
9417
+ constructor() {
9418
+ this.max = input(0, /* @ts-ignore */
9419
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
9424
9420
  }
9425
- applySort(data, sort) {
9426
- if (!sort)
9427
- return data;
9428
- const sortedRows = [...data.rows].sort((a, b) => {
9429
- const aValue = a[sort.column];
9430
- const bValue = b[sort.column];
9431
- if (aValue === bValue)
9432
- return 0;
9433
- if (aValue === null || aValue === undefined)
9434
- return 1;
9435
- if (bValue === null || bValue === undefined)
9436
- return -1;
9437
- const comparison = aValue > bValue ? 1 : -1;
9438
- return sort.isAsc ? comparison : -comparison;
9439
- });
9440
- const cloned = data.clone();
9441
- cloned.rows = sortedRows;
9442
- return cloned;
9421
+ validate(control) {
9422
+ const max = this.max();
9423
+ return max ? minFileNameLength(max)(control) : null;
9443
9424
  }
9425
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
9426
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: MinFileNameLengthValidator, isStandalone: true, selector: "[app-min-file-name-length]", inputs: { max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
9427
+ {
9428
+ provide: NG_VALIDATORS,
9429
+ useExisting: MinFileNameLengthValidator,
9430
+ multi: true,
9431
+ },
9432
+ ], ngImport: i0 }); }
9433
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, providedIn: 'root' }); }
9434
+ }
9435
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: MinFileNameLengthValidator, decorators: [{
9436
+ type: Injectable,
9437
+ args: [{ providedIn: 'root' }]
9438
+ }, {
9439
+ type: Directive,
9440
+ args: [{
9441
+ selector: '[app-min-file-name-length]',
9442
+ providers: [
9443
+ {
9444
+ provide: NG_VALIDATORS,
9445
+ useExisting: MinFileNameLengthValidator,
9446
+ multi: true,
9447
+ },
9448
+ ],
9449
+ standalone: true,
9450
+ }]
9451
+ }], propDecorators: { max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }] } });
9452
+ function minFileNameLength(minLength) {
9453
+ return (control) => {
9454
+ const value = control.value;
9455
+ const files = Array.isArray(value) ? value : [value];
9456
+ if (!value)
9457
+ return null;
9458
+ const errors = {};
9459
+ for (let i = 0; i < files.length; i++) {
9460
+ const fileNameLength = files[i].name?.length;
9461
+ if (fileNameLength === undefined)
9462
+ continue;
9463
+ if (fileNameLength < minLength)
9464
+ errors[i] = fileNameLength;
9465
+ }
9466
+ if (Object.keys(errors).length <= 0)
9467
+ return null;
9468
+ return {
9469
+ minFileNameLength: {
9470
+ minFileNameLength: minLength,
9471
+ indexes: errors,
9472
+ },
9473
+ indexes: errors,
9474
+ };
9475
+ };
9444
9476
  }
9445
9477
 
9446
9478
  const OldSpinnerInterceptor = (req, next) => {