ng-prime-tools
Version:
An advanced PrimeNG table for Angular
1,191 lines (1,187 loc) • 726 kB
JavaScript
import * as i0 from '@angular/core';
import { Pipe, EventEmitter, ViewChild, Output, Input, Component, NgModule, Injectable, Inject, HostListener, ContentChild, HostBinding } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule, DOCUMENT } from '@angular/common';
import * as i2 from '@angular/forms';
import { FormGroup, FormControl, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
import * as i3 from 'primeng/table';
import { TableModule } from 'primeng/table';
import * as i1$1 from 'primeng/api';
import { ConfirmationService, MessageService } from 'primeng/api';
import * as i5 from 'primeng/inputtext';
import { InputTextModule } from 'primeng/inputtext';
import * as i6 from 'primeng/button';
import { ButtonModule } from 'primeng/button';
import * as i7 from 'primeng/datepicker';
import { DatePickerModule } from 'primeng/datepicker';
import * as i8 from 'primeng/multiselect';
import { MultiSelectModule } from 'primeng/multiselect';
import * as i9 from 'primeng/tag';
import { TagModule } from 'primeng/tag';
import * as i10 from 'primeng/iconfield';
import { IconFieldModule } from 'primeng/iconfield';
import * as i11 from 'primeng/inputicon';
import { InputIconModule } from 'primeng/inputicon';
import * as i12 from 'primeng/tooltip';
import { TooltipModule } from 'primeng/tooltip';
import * as i13 from 'primeng/progressbar';
import { ProgressBarModule } from 'primeng/progressbar';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import * as i8$1 from 'primeng/inputnumber';
import { InputNumberModule } from 'primeng/inputnumber';
import * as i9$1 from 'primeng/panel';
import { PanelModule } from 'primeng/panel';
import * as i3$1 from 'primeng/checkbox';
import { CheckboxModule } from 'primeng/checkbox';
import * as i3$2 from 'primeng/inputgroup';
import { InputGroupModule } from 'primeng/inputgroup';
import * as i4 from 'primeng/inputgroupaddon';
import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
import * as i3$3 from 'primeng/toggleswitch';
import { ToggleSwitchModule } from 'primeng/toggleswitch';
import * as i3$4 from 'primeng/textarea';
import { TextareaModule } from 'primeng/textarea';
import * as i6$1 from 'primeng/password';
import { PasswordModule } from 'primeng/password';
import * as i3$5 from 'primeng/select';
import { SelectModule } from 'primeng/select';
import * as i3$6 from 'primeng/inputotp';
import { InputOtpModule } from 'primeng/inputotp';
import { Chart, registerables } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { Subject, interval, BehaviorSubject } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import * as i1$2 from '@angular/router';
import { RouterModule, NavigationEnd } from '@angular/router';
import * as i2$1 from 'primeng/breadcrumb';
import { BreadcrumbModule } from 'primeng/breadcrumb';
import * as i3$7 from 'primeng/confirmdialog';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import * as i2$2 from 'primeng/toast';
import { ToastModule } from 'primeng/toast';
import * as i2$3 from 'primeng/dialog';
import { DialogModule } from 'primeng/dialog';
/**
* Calculates the width required for a column based on the header text (column title).
* It uses the Canvas API to measure text width dynamically.
*
* @param {TableColumn} col - The column metadata containing the header title and code.
* @param {string} [font='16px Arial'] - The font to use for measurement (defaults to '16px Arial').
* @returns {number} - The calculated width of the column in pixels.
*/
function calculateTextWidth(col, font = '16px Arial') {
// Create a canvas context for measuring text
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) {
return 100; // Fallback width if canvas context is not available
}
// Set the font to match the provided font or the document body font
context.font = font || getComputedStyle(document.body).font;
// Measure the header text width
const headerWidth = context.measureText(col.title).width;
// Return the width with some padding
return Math.ceil(headerWidth + 20); // Add padding for extra space
}
class CustomCurrencyPipe {
transform(value, currency, decimalPlaces, thousandSeparator = 'comma', decimalSeparator = 'dot') {
let formattedValue;
if (decimalPlaces !== undefined) {
formattedValue = value.toFixed(decimalPlaces);
}
else {
formattedValue = value.toString();
}
const thousandSeparatorChar = thousandSeparator === 'space' ? ' ' : ',';
const decimalSeparatorChar = decimalSeparator === 'comma' ? ',' : '.';
formattedValue = formattedValue.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparatorChar);
if (decimalSeparatorChar === ',') {
formattedValue = formattedValue.replace('.', ',');
}
if (currency) {
let formattedCurrency;
switch (currency) {
case 'MAD':
formattedCurrency = `${formattedValue} DH`;
break;
case 'USD':
formattedCurrency = `$${formattedValue}`;
break;
default:
formattedCurrency = `${formattedValue} ${currency}`;
}
return formattedCurrency;
}
return formattedValue;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CustomCurrencyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CustomCurrencyPipe, isStandalone: true, name: "customCurrency" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CustomCurrencyPipe, decorators: [{
type: Pipe,
args: [{
name: 'customCurrency',
standalone: true,
}]
}] });
class CustomDatePipe {
transform(value, format = 'dd/MM/yyyy') {
if (!value)
return null;
if (typeof value === 'string') {
const parts = value.split('/');
if (parts.length === 3) {
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
const date = new Date(year, month, day);
if (isNaN(date.getTime()))
return null;
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
};
return new Intl.DateTimeFormat('en-GB', options).format(date);
}
}
else if (value instanceof Date) {
// If the value is already a Date object
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
};
return new Intl.DateTimeFormat('en-GB', options).format(value);
}
return null;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CustomDatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CustomDatePipe, isStandalone: false, name: "customDate" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CustomDatePipe, decorators: [{
type: Pipe,
args: [{
name: 'customDate',
standalone: false
}]
}] });
class PTAdvancedPrimeTableComponent {
constructor() {
this.data = [];
this.columns = [];
this.totalRecords = 0;
this.rowsPerPage = [10, 20, 30];
this.hasSearchFilter = false;
this.hasExportExcel = false;
this.hasExportPDF = false;
this.hasColumnFilter = false;
this.cellPadding = '0.5rem 0.75rem';
this.isPaginated = true;
this.isLazy = false;
this.actions = [];
this.isSortable = false;
this.loading = false;
this.maxHeight = null;
this.isRowReorderable = false;
this.rowReorderIdField = 'id';
this.rowOrderStartAt = 1;
this.selectionDataKey = 'id';
this.selectionMode = null;
this.selection = null;
this.cellHeight = null;
this.selectionChange = new EventEmitter();
this.rowSelect = new EventEmitter();
this.rowUnselect = new EventEmitter();
this.lazyLoad = new EventEmitter();
this.search = new EventEmitter();
this.exportExcelEvent = new EventEmitter();
this.exportPdfEvent = new EventEmitter();
this.onPageChange = new EventEmitter();
this.onSortColumn = new EventEmitter();
this.onFilterColumn = new EventEmitter();
this.rowReorderChange = new EventEmitter();
this.filteredData = new EventEmitter();
this.TableTypeEnum = TableTypeEnum;
this.AlignEnum = AlignEnum;
this.SeverityEnum = SeverityEnum;
this.searchValue = '';
this.filters = {};
this.latestFilterValues = {};
this.clearedFields = new Set();
this.validCurrencyCodes = ['USD', 'EUR', 'MAD'];
this.iconWidth = 77;
this.rows = 0;
this.first = 0;
this.currentPage = 0;
this.currentSortField = null;
this.currentSortOrder = null;
this.hasGroupedColumns = false;
this.isDelete = false;
this.isEdit = false;
this.Delete = () => { };
this.initEditableRow = () => { };
this.saveEditableRow = () => { };
this.cancelEditableRow = () => { };
this.customActions = [];
this.dataMap = new Map();
this.map = new Map();
this.optionEntries = new Map();
this.optionValues = [];
this.globalFilterFields = [];
}
ngOnInit() {
this.hasGroupedColumns = this.columns.some((col) => col.children && col.children.length > 0);
this.globalFilterFields = this.columns
.filter((col) => col.code !== undefined && col.isFilter !== false)
.map((col) => col.code);
this.initializePagination();
this.initializeActions();
this.columns.forEach((col) => {
if (col.type === TableTypeEnum.ACTION) {
col.isEditable = false;
col.isFilter = false;
col.isSortable = false;
}
if (col.type === TableTypeEnum.TAG ||
col.type === TableTypeEnum.PROGRESS) {
col.isEditable = false;
}
if (col.type === TableTypeEnum.COMPOSED) {
this.initializeComposedFilters(col);
}
if (col.isSortable === undefined)
col.isSortable = true;
if (col.isEditable === undefined)
col.isEditable = true;
if (col.isFilter !== false && col.code !== undefined) {
if (!this.globalFilterFields.includes(col.code)) {
this.globalFilterFields.push(col.code);
}
}
if (!col.width)
col.width = this.calculateColumnWidth(col);
});
if (!this.isLazy) {
this.totalRecords = this.data?.length ?? 0;
}
}
ngOnChanges(changes) {
if (changes['data'] && !this.isLazy) {
this.totalRecords = this.data?.length ?? 0;
if (this.dt)
this.dt.first = 0;
}
}
emitLazyLoad() {
const payload = {
page: this.currentPage,
rows: this.rows,
first: this.first,
search: this.searchValue?.trim() || undefined,
sortField: this.currentSortField,
sortOrder: this.currentSortOrder,
filters: { ...this.latestFilterValues },
};
this.lazyLoad.emit(payload);
}
resetToFirstPage() {
this.currentPage = 0;
this.first = 0;
if (this.dt) {
this.dt.first = 0;
}
}
canUseRowReorder() {
return this.isRowReorderable && !this.isLazy;
}
buildRowOrdering(data) {
const idField = this.rowReorderIdField || 'id';
return (data ?? []).map((row, index) => ({
id: row?.[idField],
order: this.rowOrderStartAt + index,
}));
}
onRowReorder(event) {
const oldIndex = event.dragIndex ?? -1;
const newIndex = event.dropIndex ?? -1;
if (oldIndex < 0 || newIndex < 0) {
return;
}
const reorderedData = [...(this.data ?? [])];
if (!reorderedData.length || newIndex >= reorderedData.length) {
return;
}
const movedRow = reorderedData[newIndex] ?? null;
this.data = reorderedData;
if (!this.isLazy) {
this.totalRecords = this.data.length;
}
this.rowReorderChange.emit({
oldIndex,
newIndex,
movedRow,
data: [...this.data],
ordering: this.buildRowOrdering(this.data),
});
}
getHeaderTitleClass(col) {
const align = col.headerAlign ?? AlignEnum.LEFT;
switch (align) {
case AlignEnum.CENTER:
return 'header-title-center';
case AlignEnum.RIGHT:
return 'header-title-right';
default:
return 'header-title-left';
}
}
getHeaderAlignClass(col) {
const align = col.headerAlign ?? AlignEnum.LEFT;
switch (align) {
case AlignEnum.CENTER:
return 'header-align-center';
case AlignEnum.RIGHT:
return 'header-align-right';
default:
return 'header-align-left';
}
}
getDataAlignClass(col) {
const effectiveAlign = col.dataAlign ??
(col.type === TableTypeEnum.NUMBER || col.type === TableTypeEnum.AMOUNT
? AlignEnum.RIGHT
: AlignEnum.LEFT);
switch (effectiveAlign) {
case AlignEnum.CENTER:
return 'cell-align-center';
case AlignEnum.RIGHT:
return 'cell-align-right';
default:
return 'cell-align-left';
}
}
getCellInnerAlignClass(col) {
const effectiveAlign = col.dataAlign ??
(col.type === TableTypeEnum.NUMBER || col.type === TableTypeEnum.AMOUNT
? AlignEnum.RIGHT
: AlignEnum.LEFT);
switch (effectiveAlign) {
case AlignEnum.CENTER:
return 'cell-inner-center';
case AlignEnum.RIGHT:
return 'cell-inner-right';
default:
return 'cell-inner-left';
}
}
initializeActions() {
this.isDelete = false;
this.isEdit = false;
this.Delete = () => { };
this.initEditableRow = () => { };
this.saveEditableRow = () => { };
this.cancelEditableRow = () => { };
this.customActions = [];
if (!this.actions || this.actions.length === 0)
return;
this.actions.forEach((action) => {
switch (action.code) {
case 'delete':
this.isDelete = true;
this.Delete = (row) => action.action(row);
break;
case 'edit':
this.initializeEditActions(action);
break;
default:
this.customActions.push(action);
break;
}
});
}
initializeEditActions(action) {
this.isEdit = true;
this.initEditableRow = (data) => action.action.init(data);
this.saveEditableRow = (data) => {
const record = this.map.get(data.id);
action.action.save(data, record);
this.dataMap.clear();
};
this.cancelEditableRow = (item) => console.log(item);
}
onCustomActionClick(action, row) {
if (!this.isActionVisible(action, row))
return;
if (this.isActionDisabled(action, row))
return;
if (action && typeof action.action === 'function') {
setTimeout(() => action.action(row), 0);
}
}
initializeComposedFilters(col) {
col.composedNames?.forEach((composedName) => {
const code = col.code || '';
const composedCode = code + '.' + composedName;
if (!this.globalFilterFields.includes(composedCode)) {
this.globalFilterFields.push(composedCode);
}
this.filters[composedName] = {
options: col.filterOptions,
value: [],
label: 'Filter by ' + composedName,
placeholder: 'Select option',
};
});
}
getComposedFieldType(col, composedName) {
if (col.composedNames && col.composedTypes) {
const index = col.composedNames.indexOf(composedName);
if (index >= 0 && index < col.composedTypes.length) {
return col.composedTypes[index];
}
}
return undefined;
}
onComposedColumnClear(col) {
if (!col.code || !col.composedNames)
return;
col.composedNames.forEach((name) => {
const key = `${col.code}.${name}`;
delete this.latestFilterValues[key];
if (this.filters[name]) {
this.filters[name].value = [];
}
this.clearedFields.add(key);
});
}
onComposedFilterValueChange(col, composedName, value, filterModel) {
const key = `${col.code}.${composedName}`;
if (filterModel) {
filterModel.value = value;
if (Array.isArray(filterModel.constraints) &&
filterModel.constraints.length > 0) {
filterModel.constraints[0].value = value;
}
}
if (!this.filters[composedName]) {
this.filters[composedName] = {
options: col.filterOptions,
value: [],
placeholder: 'Select option',
};
}
this.filters[composedName].value = value || [];
const isEmpty = !value || (Array.isArray(value) && value.length === 0);
if (isEmpty) {
delete this.latestFilterValues[key];
}
else {
this.latestFilterValues[key] = value;
}
}
onFilterClear(field) {
if (!field)
return;
delete this.latestFilterValues[field];
this.clearedFields.add(field);
}
onFilterValueChange(field, filterModel, value) {
if (!field)
return;
const isEmpty = value === null ||
value === undefined ||
value === '' ||
(Array.isArray(value) && value.length === 0);
if (isEmpty) {
delete this.latestFilterValues[field];
}
else {
this.latestFilterValues[field] = value;
}
if (filterModel) {
filterModel.value = value;
if (Array.isArray(filterModel.constraints) &&
filterModel.constraints.length > 0) {
filterModel.constraints[0].value = value;
}
}
}
onNumberFilterChange(field, value) {
if (!field)
return;
const isEmpty = value === null ||
value === undefined ||
value === '' ||
(Array.isArray(value) && value.length === 0);
if (isEmpty) {
delete this.latestFilterValues[field];
}
else {
this.latestFilterValues[field] = value;
}
}
findColumnByField(field) {
return this.columns.find((c) => c.code === field || (c.code && field.startsWith(c.code + '.')));
}
filterColumn(event) {
const filters = event?.filters;
if (!filters) {
if (this.isLazy) {
this.resetToFirstPage();
this.onFilterColumn.emit(event);
this.emitLazyLoad();
}
return;
}
const isNullish = (v) => v === null || v === undefined || v === '';
const nextFilterValues = {
...this.latestFilterValues,
};
Object.keys(filters).forEach((field) => {
const meta = filters[field];
const normalizeMeta = (m) => Array.isArray(m) && m.length > 0 ? m[0] : m;
const m = normalizeMeta(meta);
const col = this.findColumnByField(field);
const wasCleared = this.clearedFields.has(field);
if (!m && !col)
return;
if (wasCleared) {
delete nextFilterValues[field];
if (col?.type === TableTypeEnum.COMPOSED &&
col.code &&
col.composedNames) {
col.composedNames.forEach((name) => {
delete nextFilterValues[`${col.code}.${name}`];
});
}
if (m) {
m.value = null;
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
m.constraints.forEach((c) => (c.value = null));
}
}
this.clearedFields.delete(field);
return;
}
if (col && col.type === TableTypeEnum.COMPOSED) {
const composedValues = {};
col.composedNames?.forEach((name) => {
const key = `${col.code}.${name}`;
if (this.clearedFields.has(key)) {
delete nextFilterValues[key];
this.clearedFields.delete(key);
return;
}
const val = nextFilterValues[key];
const empty = isNullish(val) || (Array.isArray(val) && val.length === 0);
if (!empty) {
composedValues[name] = val;
}
});
if (Object.keys(composedValues).length === 0) {
if (m) {
m.value = null;
if (Array.isArray(m.constraints)) {
m.constraints.forEach((c) => (c.value = null));
}
}
}
else {
m.value = composedValues;
}
return;
}
if (!m)
return;
let value = m.value;
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
const cVal = m.constraints[0].value;
if (!isNullish(cVal)) {
value = cVal;
}
}
const cached = nextFilterValues[field];
const hasCached = !isNullish(cached) && (!Array.isArray(cached) || cached.length > 0);
if ((isNullish(value) || (Array.isArray(value) && value.length === 0)) &&
hasCached) {
value = cached;
}
const isEmpty = isNullish(value) || (Array.isArray(value) && value.length === 0);
if (isEmpty) {
delete nextFilterValues[field];
m.value = null;
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
m.constraints[0].value = null;
}
return;
}
let emitValue = value;
if (col &&
(col.type === TableTypeEnum.DATE || col.type === TableTypeEnum.DATETIME)) {
const d = this.parseAnyDate(value);
emitValue =
col.type === TableTypeEnum.DATE
? this.formatDateWithColumn(d, col)
: this.formatDateTimeWithColumn(d, col);
}
nextFilterValues[field] = emitValue;
m.value = emitValue;
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
m.constraints[0].value = emitValue;
}
});
this.latestFilterValues = nextFilterValues;
if (this.isLazy) {
this.resetToFirstPage();
this.onFilterColumn.emit(event);
this.emitLazyLoad();
}
else if (this.dt) {
const current = (this.dt.filteredValue ?? this.dt.value ?? []);
this.totalRecords = current.length;
this.dt.first = 0;
this.filteredData.emit([...current]);
}
}
changePage(event) {
const page = event.page ?? Math.floor((event.first || 0) / event.rows);
const rows = event.rows;
const first = event.first ?? page * rows;
this.rows = rows;
this.currentPage = page;
this.first = first;
if (this.isLazy) {
this.onPageChange.emit({ page, rows });
this.emitLazyLoad();
}
}
sortColumn(event) {
if (!this.isLazy)
return;
let field = event.field;
const col = this.columns.find((c) => c.code === field);
if (col && col.type === TableTypeEnum.COMPOSED) {
let textProp;
if (col.composedNames && col.composedTypes) {
const idx = col.composedTypes.findIndex((t) => t === TableTypeEnum.STRING);
if (idx >= 0 && idx < col.composedNames.length) {
textProp = col.composedNames[idx];
}
}
if (!textProp && col.composedNames?.length) {
textProp = col.composedNames[0];
}
if (textProp) {
field = `${field}.${textProp}`;
}
}
this.currentSortField = field ?? null;
this.currentSortOrder = event.order ?? null;
this.resetToFirstPage();
this.onSortColumn.emit({ ...event, field });
this.emitLazyLoad();
}
parseDate_ddMMyyyy(dateString) {
const parts = dateString.split('/');
if (parts.length === 3) {
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
const date = new Date(year, month, day);
return isNaN(date.getTime()) ? null : date;
}
return null;
}
onChange(event, id, key) {
const target = event.target;
this.changeHandler(id, key, target.value);
}
changeHandler(id, key, value) {
const column = this.columns.find((item) => item.code === key);
if (!this.map.get(id)) {
if (column?.type === TableTypeEnum.DATE) {
this.dataMap.set(key, this.parseDate_ddMMyyyy(value));
}
else {
this.dataMap.set(key, value);
}
this.map.set(id, new Map(this.dataMap));
}
else {
const mapItem = this.map.get(id);
if (column?.type === TableTypeEnum.DATE) {
mapItem.set(key, this.parseDate_ddMMyyyy(value));
}
else {
mapItem.set(key, value);
}
}
}
getColumnFilterType(column) {
switch (column.type) {
case TableTypeEnum.STRING:
return 'text';
case TableTypeEnum.AMOUNT:
case TableTypeEnum.NUMBER:
return 'numeric';
case TableTypeEnum.DATE:
case TableTypeEnum.DATETIME:
return 'date';
case TableTypeEnum.MULTISELECT:
return 'multiSelect';
case TableTypeEnum.BOOLEAN:
return 'boolean';
case TableTypeEnum.COMPOSED:
return 'composed';
default:
return 'text';
}
}
isEditable(key) {
if (!key)
return false;
const column = this.columns.find((item) => item.code === key);
return column?.isEditable !== false;
}
isMultiSelect(key) {
const column = this.columns.find((item) => item.code === key);
if (column?.type === TableTypeEnum.MULTISELECT &&
column.options &&
column.code !== undefined) {
this.optionEntries = new Map([
[column.code, Object.values(column.options)],
]);
this.optionValues = this.optionEntries.get(key) || [];
return true;
}
return false;
}
isDatePicker(key) {
const t = this.columns.find((item) => item.code === key)?.type;
return t === TableTypeEnum.DATE || t === TableTypeEnum.DATETIME;
}
isDateTimePicker(key) {
const t = this.columns.find((item) => item.code === key)?.type;
return t === TableTypeEnum.DATETIME;
}
filterGlobal(event) {
const target = event.target;
const value = (target.value || '').toLowerCase();
if (this.isLazy) {
this.searchValue = value;
this.resetToFirstPage();
this.search.emit(value);
this.emitLazyLoad();
return;
}
if (!value) {
const allData = [...(this.data ?? [])];
this.dt.value = allData;
this.totalRecords = allData.length;
this.dt.first = 0;
this.filteredData.emit(allData);
return;
}
const filteredData = (this.data ?? []).filter((item) => (this.globalFilterFields ?? []).some((field) => {
const column = this.columns?.find((col) => col.code === field);
if (!column)
return false;
const cell = item?.[field];
if (column.type === TableTypeEnum.DATE) {
return this.formatDateWithColumn(this.parseAnyDate(cell), column)
.toLowerCase()
.includes(value);
}
if (column.type === TableTypeEnum.DATETIME) {
return this.formatDateTimeWithColumn(this.parseAnyDate(cell), column)
.toLowerCase()
.includes(value);
}
if (column.type === TableTypeEnum.AMOUNT ||
column.type === TableTypeEnum.NUMBER) {
return String(cell ?? '')
.toLowerCase()
.includes(value);
}
if (column.type === TableTypeEnum.COMPOSED) {
return this.filterComposedColumn(cell, value);
}
return String(cell ?? '')
.toLowerCase()
.includes(value);
}));
this.dt.value = filteredData;
this.totalRecords = filteredData.length;
this.dt.first = 0;
this.filteredData.emit([...filteredData]);
}
filterComposedColumn(composedData, value) {
if (!composedData)
return false;
return Object.keys(composedData).some((key) => {
const cellValue = composedData[key];
return typeof cellValue === 'string'
? cellValue.toLowerCase().includes(value)
: false;
});
}
parseAnyDate(input) {
if (input === null || input === undefined || input === '')
return null;
if (input instanceof Date)
return isNaN(input.getTime()) ? null : input;
if (typeof input === 'number') {
const d = new Date(input);
return isNaN(d.getTime()) ? null : d;
}
if (typeof input === 'string') {
const s = input.trim();
const isoTry = new Date(s);
if (!isNaN(isoTry.getTime()))
return isoTry;
if (s.includes('/')) {
const parts = s.split(' ')[0].split('/');
if (parts.length === 3) {
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
const d = new Date(year, month, day);
return isNaN(d.getTime()) ? null : d;
}
}
}
return null;
}
formatWithPattern(d, pattern) {
if (!d)
return '';
const pad2 = (n) => String(n).padStart(2, '0');
const map = {
dd: pad2(d.getDate()),
MM: pad2(d.getMonth() + 1),
yyyy: String(d.getFullYear()),
HH: pad2(d.getHours()),
mm: pad2(d.getMinutes()),
ss: pad2(d.getSeconds()),
};
return pattern
.replace(/yyyy/g, map.yyyy)
.replace(/dd/g, map.dd)
.replace(/MM/g, map.MM)
.replace(/HH/g, map.HH)
.replace(/mm/g, map.mm)
.replace(/ss/g, map.ss);
}
formatDate(date) {
const d = this.parseAnyDate(date);
return this.formatWithPattern(d, 'dd/MM/yyyy');
}
formatDateWithColumn(d, col) {
const fmt = col.dateFormat?.trim() || 'dd/MM/yyyy';
return this.formatWithPattern(d, fmt);
}
formatDateTimeWithColumn(d, col) {
const fmt = col.dateTimeFormat?.trim() || 'dd/MM/yyyy HH:mm:ss';
return this.formatWithPattern(d, fmt);
}
calculateColumnWidth(col) {
const calculatedWidth = calculateTextWidth(col, col.title);
const totalWidth = calculatedWidth + this.iconWidth + 20;
return `${totalWidth}px`;
}
getHeaderWidth(col) {
return col.width ? col.width : this.calculateColumnWidth(col);
}
clear(table) {
table.clear();
this.searchValue = '';
this.latestFilterValues = {};
this.clearedFields.clear();
this.currentSortField = null;
this.currentSortOrder = null;
Object.keys(this.filters).forEach((key) => {
if (this.filters[key])
this.filters[key].value = [];
});
if (this.isLazy) {
this.resetToFirstPage();
this.onFilterColumn.emit({ cleared: true });
this.emitLazyLoad();
return;
}
if (this.dt) {
const allData = [...(this.data ?? [])];
this.dt.value = allData;
this.totalRecords = allData.length;
this.dt.first = 0;
this.filteredData.emit(allData);
}
}
initializePagination() {
if (this.isPaginated) {
if (!this.rowsPerPage || this.rowsPerPage.length === 0) {
this.rowsPerPage = [10, 20, 30];
}
this.rows = this.rowsPerPage[0];
this.currentPage = 0;
this.first = 0;
}
}
getCurrencySymbol(column) {
return column.type === TableTypeEnum.AMOUNT &&
column.currency &&
this.isValidCurrencyCode(column.currency)
? column.currency
: undefined;
}
isValidCurrencyCode(currencyCode) {
return this.validCurrencyCodes.includes(currencyCode);
}
exportExcel() {
this.exportExcelEvent.emit();
}
exportPdf() {
this.exportPdfEvent.emit();
}
getImageStyle(style) {
if (!style)
return {};
const imageStyle = {
width: style.width || 'auto',
height: style.height || 'auto',
};
if (style.margin) {
imageStyle.margin = style.margin;
}
if (style.marginLeft) {
imageStyle.marginLeft = style.marginLeft;
}
if (style.marginRight) {
imageStyle.marginRight = style.marginRight;
}
if (style.marginTop) {
imageStyle.marginTop = style.marginTop;
}
if (style.marginBottom) {
imageStyle.marginBottom = style.marginBottom;
}
return imageStyle;
}
getTitleStyle(style) {
if (!style)
return {};
return {
color: style.color || 'inherit',
fontSize: style.fontSize || 'inherit',
textAlign: style.position || 'left',
};
}
formatNumber(value, decimalPlaces, thousandSeparator = 'comma', decimalSeparator = 'dot') {
if (value === null || value === undefined || isNaN(value))
return '';
let formattedNumber = decimalPlaces !== undefined
? value.toFixed(decimalPlaces)
: value.toString();
if (decimalSeparator === 'comma') {
formattedNumber = formattedNumber.replace('.', ',');
}
if (thousandSeparator && Math.abs(value) >= 1000) {
const parts = formattedNumber.split(decimalSeparator === 'comma' ? ',' : '.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator === 'comma' ? ',' : ' ');
formattedNumber = parts.join(decimalSeparator === 'comma' ? ',' : '.');
}
return formattedNumber;
}
isActionVisible(action, row) {
const v = action?.visible;
if (v === undefined || v === null)
return true;
return typeof v === 'function' ? !!v(row) : !!v;
}
isActionDisabled(action, row) {
const d = action.disabled;
if (d === undefined || d === null)
return false;
return typeof d === 'function' ? !!d(row) : !!d;
}
getComposedCellStyle(col, composedName, row) {
const extendedCol = col;
const styleConfig = extendedCol?.composedCellStyles?.[composedName];
if (!styleConfig) {
return {};
}
return typeof styleConfig === 'function'
? (styleConfig(row) ?? {})
: styleConfig;
}
mergeStyles(...styles) {
return styles.reduce((acc, style) => {
if (!style) {
return acc;
}
return {
...acc,
...style,
};
}, {});
}
getMergedComposedTextStyle(col, composedName, row) {
return this.mergeStyles(this.getTitleStyle(col.composedStyles?.[composedName]), this.getComposedCellStyle(col, composedName, row));
}
getMergedComposedImageStyle(col, composedName, row) {
return this.mergeStyles(this.getImageStyle(col.composedStyles?.[composedName]), this.getComposedCellStyle(col, composedName, row));
}
getTagValue(col, row) {
const extendedCol = col;
const tagValue = extendedCol.tagValue;
if (typeof tagValue === 'function') {
return tagValue(row) ?? '';
}
if (tagValue !== undefined && tagValue !== null) {
return String(tagValue);
}
return String(col.code ? row?.[col.code] : '');
}
getTagSeverity(col, row) {
const extendedCol = col;
const tagSeverity = extendedCol.tagSeverity;
if (typeof tagSeverity === 'function') {
return tagSeverity(row) ?? SeverityEnum.INFO;
}
return tagSeverity ?? SeverityEnum.INFO;
}
getTagIcon(col, row) {
const extendedCol = col;
const tagIcon = extendedCol.tagIcon;
if (typeof tagIcon === 'function') {
return tagIcon(row) || undefined;
}
return tagIcon || undefined;
}
isTagRounded(col) {
return col.tagRounded !== false;
}
getActionTooltip(action, row) {
if (!action?.tooltip) {
return '';
}
return action.tooltip;
}
getDefaultActionTooltip(code) {
switch (code) {
case 'delete':
return 'Supprimer';
case 'edit':
return 'Modifier';
case 'save':
return 'Enregistrer';
case 'cancel':
return 'Annuler';
default:
return '';
}
}
getProgressValue(col, row) {
const extendedCol = col;
const progressValue = extendedCol.progressValue;
let value;
if (typeof progressValue === 'function') {
value = Number(progressValue(row) ?? 0);
}
else if (progressValue !== undefined && progressValue !== null) {
value = Number(progressValue);
}
else {
value = Number(col.code ? row?.[col.code] : 0);
}
if (!Number.isFinite(value)) {
return 0;
}
return Math.max(0, Math.min(100, value));
}
isProgressShowValue(col) {
return col.progressShowValue !== false;
}
getProgressUnit(col) {
return col.progressUnit ?? '%';
}
getProgressSeverity(col, row) {
const extendedCol = col;
const severity = extendedCol.progressSeverity;
if (typeof severity === 'function') {
return severity(row) ?? SeverityEnum.INFO;
}
return severity ?? SeverityEnum.INFO;
}
getMultiSelectValues(value) {
if (Array.isArray(value)) {
return value;
}
if (value === null || value === undefined || value === '') {
return [];
}
return [value];
}
getCellHeight() {
return this.cellHeight?.trim() || null;
}
getColumnCellPadding(col) {
return col.cellPadding?.trim() || this.cellPadding?.trim() || '0px';
}
getColumnCellMargin(col) {
return col.cellMargin?.trim() || '0px';
}
getCellInnerStyle(col, defaultMargin = '0px') {
return {
margin: col.cellMargin?.trim() || defaultMargin,
minHeight: '0',
boxSizing: 'border-box',
};
}
getCellStyle(col, row) {
const styles = {};
if (col.cellStyle) {
const customStyle = typeof col.cellStyle === 'function'
? col.cellStyle(row)
: col.cellStyle;
Object.assign(styles, customStyle);
}
const height = this.getCellHeight();
if (height) {
styles['height'] = height;
styles['max-height'] = height;
styles['line-height'] = height;
}
styles['padding'] = this.getColumnCellPadding(col);
styles['box-sizing'] = 'border-box';
return styles;
}
getHeaderCellStyle(col) {
const styles = {
padding: this.getColumnCellPadding(col),
boxSizing: 'border-box',
};
const height = this.getCellHeight();
if (height) {
styles['height'] = height;
styles['max-height'] = height;
styles['line-height'] = height;
}
return styles;
}
getStaticCellStyle() {
const styles = {
padding: this.cellPadding?.trim() || '0px',
boxSizing: 'border-box',
};
const height = this.getCellHeight();
if (height) {
styles['height'] = height;
styles['max-height'] = height;
styles['line-height'] = height;
}
return styles;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTAdvancedPrimeTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTAdvancedPrimeTableComponent, isStandalone: false, selector: "pt-advanced-prime-table", inputs: { data: "data", columns: "columns", totalRecords: "totalRecords", rowsPerPage: "rowsPerPage", hasSearchFilter: "hasSearchFilter", hasExportExcel: "hasExportExcel", hasExportPDF: "hasExportPDF", hasColumnFilter: "hasColumnFilter", cellPadding: "cellPadding", isPaginated: "isPaginated", isLazy: "isLazy", actions: "actions", isSortable: "isSortable", loading: "loading", maxHeight: "maxHeight", isRowReorderable: "isRowReorderable", rowReorderIdField: "rowReorderIdField", rowOrderStartAt: "rowOrderStartAt", selectionDataKey: "selectionDataKey", selectionMode: "selectionMode", selection: "selection", cellHeight: "cellHeight" }, outputs: { selectionChange: "selectionChange", rowSelect: "rowSelect", rowUnselect: "rowUnselect", lazyLoad: "lazyLoad", search: "search", exportExcelEvent: "exportExcelEvent", exportPdfEvent: "exportPdfEvent", onPageChange: "onPageChange", onSortColumn: "onSortColumn", onFilterColumn: "onFilterColumn", rowReorderChange: "rowReorderChange", filteredData: "filteredData" }, viewQueries: [{ propertyName: "dt", first: true, predicate: ["dt"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"data\"\n [loading]=\"loading\"\n [rows]=\"rows\"\n [paginator]=\"isPaginated\"\n [globalFilterFields]=\"globalFilterFields\"\n [rowsPerPageOptions]=\"rowsPerPage\"\n [totalRecords]=\"totalRecords\"\n [lazy]=\"isLazy\"\n [filterDelay]=\"0\"\n [dataKey]=\"selectionDataKey || rowReorderIdField || 'id'\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n (onRowReorder)=\"onRowReorder($event)\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n (onFilter)=\"filterColumn($event)\"\n [selectionMode]=\"selectionMode\"\n [(selection)]=\"selection\"\n (selectionChange)=\"selectionChange.emit($event)\"\n (onRowSelect)=\"rowSelect.emit($event.data)\"\n (onRowUnselect)=\"rowUnselect.emit($event.data)\"\n >\n <ng-template pTemplate=\"colgroup\" let-columns>\n <colgroup>\n @if (canUseRowReorder()) {\n <col style=\"width: 3rem\" />\n }\n\n @for (col of columns; track col) {\n <col [style.width]=\"col.width || getHeaderWidth(col)\" />\n }\n </colgroup>\n </ng-template>\n\n <ng-template pTemplate=\"caption\">\n <div class=\"flex\">\n <div>\n <h3>Total: {{ totalRecords }}</h3>\n </div>\n\n <div>\n @if (hasSearchFilter) {\n <button\n pButton\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"clear(dt)\"\n title=\"Clear filters\"\n ></button>\n }\n\n @if (hasExportExcel) {\n <button\n pButton\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportExcel()\"\n title=\"Export to Excel\"\n ></button>\n }\n\n @if (hasExportPDF) {\n <button\n pButton\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportPdf()\"\n title=\"Export to PDF\"\n ></button>\n }\n </div>\n\n @if (hasSearchFilter) {\n <div class=\"ml-auto\">\n <p-iconField iconPosition=\"left\" class=\"ml-auto\">\n <p-inputIcon>\n <i class=\"pi pi-search\"></i>\n </p-inputIcon>\n\n <input\n pInputText\n type=\"text\"\n [(ngModel)]=\"searchValue\"\n (input)=\"filterGlobal($event)\"\n placeholder=\"Search keyword\"\n />\n </p-iconField>\n </div>\n }\n </div>\n </ng-template>\n\n <ng-template pTemplate=\"header\">\n <tr class=\"sticky-header\">\n @if (canUseRowReorder()) {\n <th [style.width]=\"'3rem'\" [ngStyle]=\"getStaticCellStyle()\"></th>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n <th\n [pSortableColumn]=\"col.code\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getHeaderCellStyle(col)\"\n [ngClass]=\"[\n getHeaderAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : '',\n ]\"\n colspan=\"1\"\n >\n @if (isSortable && col.isSortable !== false) {\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getCellInnerStyle(col, '10px')\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n <div\n class=\"icons d-flex align-items-center\"\n [style.width]=\"'77px'\"\n >\n <p-sortIcon [field]=\"col.code\" />\n\n @if (hasColumnFilter && col.isFilter !== false) {\n @if (col.type === TableTypeEnum.COMPOSED) {\n <p-columnFilter\n display=\"menu\"\n [field]=\"col.code\"\n type=\"text\"\n [showApplyButton]=\"true\"\n [showClea