ng-prime-tools 1.1.7 → 1.1.9

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.
@@ -12,19 +12,21 @@ import * as i5 from 'primeng/inputtext';
12
12
  import { InputTextModule } from 'primeng/inputtext';
13
13
  import * as i6 from 'primeng/button';
14
14
  import { ButtonModule } from 'primeng/button';
15
- import * as i7 from 'primeng/datepicker';
15
+ import * as i7 from 'primeng/contextmenu';
16
+ import { ContextMenuModule } from 'primeng/contextmenu';
17
+ import * as i8 from 'primeng/datepicker';
16
18
  import { DatePickerModule } from 'primeng/datepicker';
17
- import * as i8 from 'primeng/multiselect';
19
+ import * as i9 from 'primeng/multiselect';
18
20
  import { MultiSelectModule } from 'primeng/multiselect';
19
- import * as i9 from 'primeng/tag';
21
+ import * as i10 from 'primeng/tag';
20
22
  import { TagModule } from 'primeng/tag';
21
- import * as i10 from 'primeng/iconfield';
23
+ import * as i11 from 'primeng/iconfield';
22
24
  import { IconFieldModule } from 'primeng/iconfield';
23
- import * as i11 from 'primeng/inputicon';
25
+ import * as i12 from 'primeng/inputicon';
24
26
  import { InputIconModule } from 'primeng/inputicon';
25
- import * as i12 from 'primeng/tooltip';
27
+ import * as i13 from 'primeng/tooltip';
26
28
  import { TooltipModule } from 'primeng/tooltip';
27
- import * as i13 from 'primeng/progressbar';
29
+ import * as i14 from 'primeng/progressbar';
28
30
  import { ProgressBarModule } from 'primeng/progressbar';
29
31
  import { ProgressSpinnerModule } from 'primeng/progressspinner';
30
32
  import * as i8$1 from 'primeng/inputnumber';
@@ -200,6 +202,7 @@ class PTAdvancedPrimeTableComponent {
200
202
  this.selectionMode = null;
201
203
  this.selection = null;
202
204
  this.cellHeight = null;
205
+ this.hasContextMenu = true;
203
206
  this.selectionChange = new EventEmitter();
204
207
  this.rowSelect = new EventEmitter();
205
208
  this.rowUnselect = new EventEmitter();
@@ -212,6 +215,9 @@ class PTAdvancedPrimeTableComponent {
212
215
  this.onFilterColumn = new EventEmitter();
213
216
  this.rowReorderChange = new EventEmitter();
214
217
  this.filteredData = new EventEmitter();
218
+ this.contextMenuItems = [];
219
+ this.contextMenuRow = null;
220
+ this.editingRows = new Set();
215
221
  this.TableTypeEnum = TableTypeEnum;
216
222
  this.AlignEnum = AlignEnum;
217
223
  this.SeverityEnum = SeverityEnum;
@@ -299,9 +305,6 @@ class PTAdvancedPrimeTableComponent {
299
305
  this.resetToFirstPage();
300
306
  this.refreshTableData();
301
307
  }
302
- ngOnDestroy() {
303
- this.cleanupPointerDrag();
304
- }
305
308
  buildGlobalFilterFields() {
306
309
  const fields = [];
307
310
  this.columns.forEach((column) => {
@@ -1491,12 +1494,126 @@ class PTAdvancedPrimeTableComponent {
1491
1494
  }
1492
1495
  return styles;
1493
1496
  }
1497
+ onRowContextMenu(event, row) {
1498
+ if (!this.hasContextMenu) {
1499
+ return;
1500
+ }
1501
+ const items = this.buildContextMenuItems(row);
1502
+ if (items.length === 0) {
1503
+ return;
1504
+ }
1505
+ event.preventDefault();
1506
+ event.stopPropagation();
1507
+ this.contextMenuRow = row;
1508
+ this.contextMenuItems = items;
1509
+ this.contextMenu?.show(event);
1510
+ }
1511
+ onContextMenuHide() {
1512
+ this.contextMenuRow = null;
1513
+ this.contextMenuItems = [];
1514
+ }
1515
+ buildContextMenuItems(row) {
1516
+ const items = [];
1517
+ if (this.isDelete) {
1518
+ const deleteAction = this.actions.find((action) => action.code === 'delete');
1519
+ if (!deleteAction || this.isActionVisible(deleteAction, row)) {
1520
+ items.push({
1521
+ label: this.getActionTooltip(deleteAction, row) ||
1522
+ this.getDefaultActionTooltip('delete'),
1523
+ icon: deleteAction?.icon || 'pi pi-trash',
1524
+ disabled: deleteAction
1525
+ ? this.isActionDisabled(deleteAction, row)
1526
+ : false,
1527
+ command: () => this.Delete(row),
1528
+ });
1529
+ }
1530
+ }
1531
+ if (this.isEdit) {
1532
+ const editAction = this.actions.find((action) => action.code === 'edit');
1533
+ if (!editAction || this.isActionVisible(editAction, row)) {
1534
+ if (this.isRowEditing(row)) {
1535
+ items.push({
1536
+ label: this.getDefaultActionTooltip('save'),
1537
+ icon: 'pi pi-check',
1538
+ command: () => this.saveRowFromContextMenu(row),
1539
+ }, {
1540
+ label: this.getDefaultActionTooltip('cancel'),
1541
+ icon: 'pi pi-times',
1542
+ command: () => this.cancelRowFromContextMenu(row),
1543
+ });
1544
+ }
1545
+ else {
1546
+ items.push({
1547
+ label: this.getActionTooltip(editAction, row) ||
1548
+ this.getDefaultActionTooltip('edit'),
1549
+ icon: editAction?.icon || 'pi pi-pencil',
1550
+ disabled: editAction
1551
+ ? this.isActionDisabled(editAction, row)
1552
+ : false,
1553
+ command: () => this.startRowEdit(row),
1554
+ });
1555
+ }
1556
+ }
1557
+ }
1558
+ this.customActions.forEach((action) => {
1559
+ if (!this.isActionVisible(action, row)) {
1560
+ return;
1561
+ }
1562
+ items.push({
1563
+ label: this.getActionTooltip(action, row) || action.code,
1564
+ icon: action.icon || 'pi pi-ellipsis-h',
1565
+ disabled: this.isActionDisabled(action, row),
1566
+ styleClass: action.styleClass,
1567
+ command: () => this.onCustomActionClick(action, row),
1568
+ });
1569
+ });
1570
+ return items;
1571
+ }
1572
+ startRowEdit(row) {
1573
+ if (!row) {
1574
+ return;
1575
+ }
1576
+ this.dt?.initRowEdit(row);
1577
+ this.editingRows.add(this.getRowIdentity(row));
1578
+ this.initEditableRow(row);
1579
+ }
1580
+ saveRowEdit(row) {
1581
+ if (!row) {
1582
+ return;
1583
+ }
1584
+ this.saveEditableRow(row);
1585
+ this.editingRows.delete(this.getRowIdentity(row));
1586
+ }
1587
+ cancelRowEdit(row) {
1588
+ if (!row) {
1589
+ return;
1590
+ }
1591
+ this.cancelEditableRow(row);
1592
+ this.editingRows.delete(this.getRowIdentity(row));
1593
+ }
1594
+ saveRowFromContextMenu(row) {
1595
+ this.saveRowEdit(row);
1596
+ }
1597
+ cancelRowFromContextMenu(row) {
1598
+ this.dt?.cancelRowEdit(row);
1599
+ this.cancelRowEdit(row);
1600
+ }
1601
+ isRowEditing(row) {
1602
+ return this.editingRows.has(this.getRowIdentity(row));
1603
+ }
1604
+ ngOnDestroy() {
1605
+ this.cleanupPointerDrag();
1606
+ this.contextMenu?.hide();
1607
+ this.contextMenuItems = [];
1608
+ this.contextMenuRow = null;
1609
+ this.editingRows.clear();
1610
+ }
1494
1611
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTAdvancedPrimeTableComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
1495
- 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", showRowMoveArrows: "showRowMoveArrows", 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 }, { propertyName: "tableContainer", first: true, predicate: ["tableContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #tableContainer class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"tableData\"\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 [rowTrackBy]=\"rowTrackBy\"\n paginatorDropdownAppendTo=\"body\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n [selectionMode]=\"selectionMode\"\n [(selection)]=\"selection\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n (onFilter)=\"filterColumn($event)\"\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]=\"getRowReorderColumnWidth()\" />\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 type=\"button\"\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n title=\"Clear filters\"\n (click)=\"clear(dt)\"\n ></button>\n }\n\n @if (hasExportExcel) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to Excel\"\n (click)=\"exportExcel()\"\n ></button>\n }\n\n @if (hasExportPDF) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to PDF\"\n (click)=\"exportPdf()\"\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 placeholder=\"Search keyword\"\n (input)=\"filterGlobal($event)\"\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\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n <th\n [pSortableColumn]=\"col.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || col.isSortable === false\n \"\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 <div class=\"header-container\" [ngStyle]=\"getCellInnerStyle(col)\">\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n @if (\n (isSortable && col.isSortable !== false) ||\n (hasColumnFilter && col.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && col.isSortable !== false) {\n <p-sortIcon [field]=\"col.code\" />\n }\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 [showClearButton]=\"true\"\n [showMatchModes]=\"false\"\n [showOperator]=\"false\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n >\n @for (\n composedName of col.composedNames;\n track composedName\n ) {\n @if (\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n ) {\n <div>\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n filters[composedName]?.value ?? []\n \"\n [placeholder]=\"\n filters[composedName]?.placeholder\n \"\n display=\"chip\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event\n );\n filter($event ?? [])\n \"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n @if (item.image) {\n <img\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n }\n\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </div>\n }\n }\n </ng-template>\n </p-columnFilter>\n } @else {\n <p-columnFilter\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n col.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n @if (\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n ) {\n <ng-template pTemplate=\"filter\" let-value>\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value\n \"\n placeholder=\"Enter a number\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n />\n </ng-template>\n }\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-datepicker\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value ?? []\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event ?? [])\n \"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n }\n }\n </div>\n }\n </div>\n </th>\n } @else {\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getHeaderCellStyle(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n }\n }\n </tr>\n\n @if (hasGroupedColumns) {\n <tr>\n @if (canUseRowReorder()) {\n <th\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (col.children) {\n @for (child of col.children; track child) {\n <th\n [pSortableColumn]=\"child.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || child.isSortable === false\n \"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngStyle]=\"getHeaderCellStyle(child)\"\n [ngClass]=\"getHeaderAlignClass(child)\"\n >\n <div\n class=\"header-container\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n <span [ngClass]=\"getHeaderTitleClass(child)\">\n {{ child.title }}\n </span>\n\n @if (\n (isSortable && child.isSortable !== false) ||\n (hasColumnFilter && child.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && child.isSortable !== false) {\n <p-sortIcon [field]=\"child.code\" />\n }\n\n @if (hasColumnFilter && child.isFilter !== false) {\n <p-columnFilter\n display=\"menu\"\n [field]=\"child.code\"\n [type]=\"getColumnFilterType(child)\"\n [currency]=\"getCurrencySymbol(child)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n child.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(child.code!)\"\n ></p-columnFilter>\n }\n </div>\n }\n </div>\n </th>\n }\n }\n }\n </tr>\n }\n </ng-template>\n\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <td\n class=\"empty-message-cell\"\n [attr.colspan]=\"columns.length + (canUseRowReorder() ? 1 : 0)\"\n >\n <div class=\"empty-message-wrapper\">\n <div class=\"empty-message\">\n <i class=\"pi pi-info-circle\"></i>\n <p>No records available to display.</p>\n </div>\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template pTemplate=\"body\" let-data let-editing=\"editing\">\n @if (!loading) {\n <tr\n [pEditableRow]=\"isEdit ? data : null\"\n [pSelectableRow]=\"selectionMode ? data : null\"\n [attr.data-row-reorder-target]=\"canUseRowReorder() ? 'true' : null\"\n >\n @if (canUseRowReorder()) {\n <td\n class=\"row-reorder-column\"\n [style.width]=\"getRowReorderColumnWidth()\"\n [style.text-align]=\"'center'\"\n [ngStyle]=\"getStaticCellStyle()\"\n >\n <div class=\"row-reorder-actions\">\n <span\n class=\"row-drag-handle\"\n title=\"D\u00E9placer la ligne\"\n (pointerdown)=\"onRowPointerDown($event, data)\"\n >\n <i class=\"pi pi-bars\"></i>\n </span>\n\n @if (showRowMoveArrows) {\n <div class=\"row-reorder-arrow-buttons\">\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-up\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Monter la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowUp(data)\"\n (click)=\"moveRowUp(data); $event.stopPropagation()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-down\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Descendre la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowDown(data)\"\n (click)=\"moveRowDown(data); $event.stopPropagation()\"\n ></button>\n </div>\n }\n </div>\n </td>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n @if (isEditable(col.code!) && col.type !== TableTypeEnum.ACTION) {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isMultiSelect(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n [options]=\"optionValues\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-multiSelect>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n @for (\n rec of getMultiSelectValues(data[col.code!]);\n track rec\n ) {\n <p-tag [value]=\"rec\"></p-tag>\n }\n </div>\n </ng-template>\n </p-cellEditor>\n } @else if (isDatePicker(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-datepicker\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n [dateFormat]=\"'dd/mm/yy'\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n } @else {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"data[col.code!]\"\n (change)=\"onChange($event, data.id, col.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n @if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else {\n {{ data[col.code!] }}\n }\n </ng-template>\n </p-cellEditor>\n }\n </div>\n </td>\n } @else {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : '',\n ]\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n @if (col.type === TableTypeEnum.ACTION) {\n <div\n class=\"action-buttons-container\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isDelete) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"Delete(data)\"\n ></button>\n }\n\n @if (isEdit) {\n <div>\n @if (!editing) {\n <button\n pInitEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Modifier\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"initEditableRow(data)\"\n ></button>\n }\n\n @if (editing) {\n <button\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Enregistrer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"saveEditableRow(data)\"\n ></button>\n\n <button\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Annuler\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"cancelEditableRow(data)\"\n ></button>\n }\n </div>\n }\n\n @for (act of customActions; track act) {\n @if (isActionVisible(act, data)) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text\"\n [icon]=\"act.icon || 'pi pi-ellipsis-h'\"\n [ngClass]=\"act.styleClass\"\n [disabled]=\"isActionDisabled(act, data)\"\n [pTooltip]=\"act.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"onCustomActionClick(act, data)\"\n ></button>\n }\n }\n </div>\n } @else {\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (col.type === TableTypeEnum.COMPOSED) {\n <div class=\"composed-cell\">\n @for (\n composedName of col.composedNames;\n track composedName;\n let i = $index\n ) {\n @if (\n col.composedTypes?.[i] === TableTypeEnum.IMAGE\n ) {\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getMergedComposedImageStyle(\n col,\n composedName,\n data\n )\n \"\n />\n }\n\n @if (\n col.composedTypes?.[i] === TableTypeEnum.STRING\n ) {\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getMergedComposedTextStyle(\n col,\n composedName,\n data\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n }\n }\n </div>\n } @else if (col.type === TableTypeEnum.PROGRESS) {\n <div class=\"progress-cell\">\n <p-progressBar\n [value]=\"getProgressValue(col, data)\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'pt-progress-' + getProgressSeverity(col, data)\n \"\n ></p-progressBar>\n\n @if (isProgressShowValue(col)) {\n <span class=\"progress-cell-value\">\n {{ getProgressValue(col, data)\n }}{{ getProgressUnit(col) }}\n </span>\n }\n </div>\n } @else if (col.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(col, data)\"\n [severity]=\"$any(getTagSeverity(col, data))\"\n [icon]=\"getTagIcon(col, data)\"\n [rounded]=\"isTagRounded(col)\"\n ></p-tag>\n } @else if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else if (col.type === TableTypeEnum.NUMBER) {\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n } @else if (col.type === TableTypeEnum.DATE) {\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (col.type === TableTypeEnum.DATETIME) {\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT,\n ].includes(col.type!)\n ) {\n {{ data[col.code!] }}\n }\n </div>\n }\n </td>\n }\n } @else {\n @for (child of col.children; track child) {\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n [ngStyle]=\"getCellStyle(child, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(child)\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n @if (isEditable(child.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"child.code ? data[child.code] : null\"\n (change)=\"onChange($event, data.id, child.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n } @else if (child.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(child, data)\"\n [severity]=\"$any(getTagSeverity(child, data))\"\n [icon]=\"getTagIcon(child, data)\"\n [rounded]=\"isTagRounded(child)\"\n ></p-tag>\n } @else {\n {{ child.code ? data[child.code] : \"\" }}\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </ng-template>\n </p-table>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-advanced-prime-table{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{display:grid;align-items:center;justify-items:start;width:100%;min-width:0;height:5rem}.pt-advanced-prime-table .filter-container{display:flex;align-items:center;justify-content:space-between;gap:1rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .settings{display:flex;align-items:center;gap:1rem;min-width:0}.pt-advanced-prime-table .multi-select-container{display:flex;align-items:center;gap:.3rem;min-width:0}.pt-advanced-prime-table .table-container{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table ::ng-deep p-table{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper{display:block;width:100%;min-width:0;max-width:100%;overflow-x:auto!important;overflow-y:visible!important;overscroll-behavior-x:contain;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;scrollbar-width:thin}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{width:100%;min-width:var(--pt-advanced-table-min-width, 95rem);max-width:none;table-layout:fixed;border-collapse:separate;border-spacing:0}.pt-advanced-prime-table ::ng-deep .p-datatable-header,.pt-advanced-prime-table ::ng-deep .p-datatable-caption{width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-caption>.flex,.pt-advanced-prime-table ::ng-deep .p-datatable-header>.flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .custom-multiselect .p-hidden-accessible input{display:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column.p-highlight:hover{background:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column:focus{box-shadow:none;outline:0 none}.pt-advanced-prime-table ::ng-deep .header-container{position:relative;display:flex;align-items:center;gap:.25rem;width:100%!important;min-width:0;max-width:100%;margin:0!important;padding:0!important}.pt-advanced-prime-table ::ng-deep .header-title-left,.pt-advanced-prime-table ::ng-deep .header-title-center,.pt-advanced-prime-table ::ng-deep .header-title-right{display:block;flex:1 1 auto;width:auto;min-width:0;max-width:100%;padding:0;line-height:1.2;white-space:normal;overflow-wrap:break-word;word-break:normal}.pt-advanced-prime-table ::ng-deep .header-title-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-title-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-title-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-align-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-align-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-align-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-container>.icons{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:flex-end;flex:0 0 auto;gap:.15rem;width:auto!important;min-width:1.5rem;max-width:none;margin-left:auto;padding:0;transform:none!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .header-container>.icons p-sorticon,.pt-advanced-prime-table ::ng-deep .header-container>.icons p-columnfilter{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0}.pt-advanced-prime-table ::ng-deep .header-container>p-columnfilter:last-child{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0 0 0 auto!important;transform:none!important}.pt-advanced-prime-table ::ng-deep .p-sortable-column-icon,.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{flex:0 0 auto;margin:0!important}.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{width:1.75rem;height:1.75rem;padding:0}.pt-advanced-prime-table ::ng-deep p-sorticon{width:1.25rem;min-width:1.25rem}.pt-advanced-prime-table ::ng-deep p-columnfilter.p-element.ng-star-inserted{margin-top:0}.pt-advanced-prime-table .flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield{position:relative;display:inline-flex;align-items:center;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep p-inputicon,.pt-advanced-prime-table ::ng-deep .p-inputicon,.pt-advanced-prime-table ::ng-deep .p-input-icon{position:absolute;top:50%;left:.85rem;z-index:2;display:inline-flex;align-items:center;justify-content:center;margin:0;transform:translateY(-50%);pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-iconfield .p-inputtext{width:100%;max-width:100%;padding-left:2.5rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{margin-left:1rem;cursor:pointer}.pt-advanced-prime-table .table-row{display:flex;justify-content:center;gap:1rem;min-width:0;text-align:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{color:green;font-size:1.25em}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{color:red;font-size:1.25em}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:180px}.pt-advanced-prime-table .empty-message{color:#888;font-size:1.2rem;text-align:center}.pt-advanced-prime-table .empty-message i{display:block;margin-bottom:.5rem;font-size:2rem}.pt-advanced-prime-table th,.pt-advanced-prime-table td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th{vertical-align:middle}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{vertical-align:top}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right,.pt-advanced-prime-table ::ng-deep .composed-cell,.pt-advanced-prime-table ::ng-deep .composed-text{min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.filter-image{width:22px;height:14px;margin-right:5px}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-left{text-align:left!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-center{text-align:center!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-right{text-align:right!important}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right{display:flex;align-items:flex-start;width:100%;min-width:0}.pt-advanced-prime-table .cell-inner-left{justify-content:flex-start;text-align:left}.pt-advanced-prime-table .cell-inner-center{justify-content:center;text-align:center}.pt-advanced-prime-table .cell-inner-right{justify-content:flex-end;text-align:right}.pt-advanced-prime-table ::ng-deep .p-tag{padding:.2rem .55rem;font-size:.72rem;font-weight:800;line-height:1.2;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .p-tag .p-tag-icon{margin-right:.25rem}.pt-advanced-prime-table .progress-cell{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar{flex:1 1 auto;width:100%;min-width:0;height:.75rem!important;overflow:hidden;border-radius:999px;background:#e5e7eb!important}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar-value{display:block!important;height:100%!important;border-radius:999px}.pt-advanced-prime-table .progress-cell-value{flex:0 0 auto;min-width:38px;color:#334155;font-size:.72rem;font-weight:800;text-align:right}.pt-advanced-prime-table ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{align-items:center;justify-content:center;padding:0;overflow:hidden;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .action-buttons-container{display:flex;align-items:center!important;justify-content:center!important;flex-wrap:wrap;gap:.35rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{min-width:22px!important;height:22px!important;padding:.25rem!important}.pt-advanced-prime-table ::ng-deep .pt-progress-success .p-progressbar-value{background:#22c55e!important}.pt-advanced-prime-table ::ng-deep .pt-progress-warning .p-progressbar-value{background:#f59e0b!important}.pt-advanced-prime-table ::ng-deep .pt-progress-danger .p-progressbar-value{background:#ef4444!important}.pt-advanced-prime-table ::ng-deep .pt-progress-info .p-progressbar-value{background:#3b82f6!important}.pt-advanced-prime-table ::ng-deep .composed-cell{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .composed-image{display:block;flex:0 0 auto;object-fit:contain}.pt-advanced-prime-table ::ng-deep .composed-text{flex:1 1 auto;min-width:0}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{transition:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging{cursor:grabbing!important;opacity:.55}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:5;box-shadow:inset 0 0 0 1px #3b82f673}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before:before{position:absolute;z-index:30;top:-2px;right:0;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after:after{position:absolute;z-index:30;right:0;bottom:-2px;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td{border-top:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{border-bottom:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-drag-handle{display:inline-flex;align-items:center;justify-content:center;width:2rem;min-width:2rem;height:2rem;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pt-advanced-prime-table ::ng-deep .row-drag-handle:hover{background:#3b82f61a}.pt-advanced-prime-table ::ng-deep .row-drag-handle:active{cursor:grabbing}.pt-advanced-prime-table ::ng-deep .row-drag-handle .pi{color:#64748b;font-size:.95rem}.pt-advanced-prime-table ::ng-deep td.row-reorder-column,.pt-advanced-prime-table ::ng-deep th.row-reorder-column{width:7rem;min-width:7rem;padding:.25rem .4rem!important;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .row-reorder-actions{display:inline-flex;align-items:center;justify-content:center;gap:.15rem;width:100%}.pt-advanced-prime-table ::ng-deep .row-reorder-arrow-buttons{display:inline-flex;flex-direction:column;gap:0}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text{width:1.75rem!important;min-width:1.75rem!important;height:1.75rem!important;padding:0!important;border-radius:6px!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text .p-button-icon{font-size:.75rem}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:not(:disabled):hover{background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:disabled{cursor:not-allowed;opacity:.35}.pt-advanced-prime-table.dragging-active,.pt-advanced-prime-table.dragging-active *{-webkit-user-select:none!important;user-select:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:10}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-after>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar{width:10px;height:10px}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-track,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-advanced-table-scrollbar-track, #f1f5f9)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb{border:2px solid var(--pt-advanced-table-scrollbar-track, #f1f5f9);border-radius:999px;background:var(--pt-advanced-table-scrollbar-thumb, #94a3b8)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb:hover,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb:hover{background:var(--pt-advanced-table-scrollbar-thumb-hover, #64748b)}@media(max-width:768px){.pt-advanced-prime-table .filter-container,.pt-advanced-prime-table .flex{align-items:stretch;flex-direction:column}.pt-advanced-prime-table .settings{flex-wrap:wrap}.pt-advanced-prime-table .ml-auto{width:100%;margin-left:0}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield,.pt-advanced-prime-table ::ng-deep .p-inputtext{width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{min-width:var(--pt-advanced-table-mobile-min-width, 78rem)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i3.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i3.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "component", type: i3.CellEditor, selector: "p-cellEditor" }, { kind: "component", type: i3.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i3.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i3.InitEditableRow, selector: "[pInitEditableRow]" }, { kind: "directive", type: i3.SaveEditableRow, selector: "[pSaveEditableRow]" }, { kind: "directive", type: i3.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i3.ColumnFilter, selector: "p-columnFilter, p-column-filter, p-columnfilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "filterOn", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: i7.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: i8.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i10.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i11.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "directive", type: i12.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i13.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }, { kind: "pipe", type: CustomCurrencyPipe, name: "customCurrency" }, { kind: "pipe", type: CustomDatePipe, name: "customDate" }] }); }
1612
+ 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", showRowMoveArrows: "showRowMoveArrows", rowReorderIdField: "rowReorderIdField", rowOrderStartAt: "rowOrderStartAt", selectionDataKey: "selectionDataKey", selectionMode: "selectionMode", selection: "selection", cellHeight: "cellHeight", hasContextMenu: "hasContextMenu" }, 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 }, { propertyName: "tableContainer", first: true, predicate: ["tableContainer"], descendants: true }, { propertyName: "contextMenu", first: true, predicate: ["contextMenu"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #tableContainer class=\"pt-advanced-prime-table table-container\">\n @if (hasContextMenu) {\n <p-contextMenu\n #contextMenu\n [model]=\"contextMenuItems\"\n appendTo=\"body\"\n styleClass=\"pt-table-context-menu\"\n (onHide)=\"onContextMenuHide()\"\n ></p-contextMenu>\n }\n <p-table\n #dt\n [value]=\"tableData\"\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 [rowTrackBy]=\"rowTrackBy\"\n paginatorDropdownAppendTo=\"body\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n [selectionMode]=\"selectionMode\"\n [(selection)]=\"selection\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n (onFilter)=\"filterColumn($event)\"\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]=\"getRowReorderColumnWidth()\" />\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 type=\"button\"\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n title=\"Clear filters\"\n (click)=\"clear(dt)\"\n ></button>\n }\n\n @if (hasExportExcel) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to Excel\"\n (click)=\"exportExcel()\"\n ></button>\n }\n\n @if (hasExportPDF) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to PDF\"\n (click)=\"exportPdf()\"\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 placeholder=\"Search keyword\"\n (input)=\"filterGlobal($event)\"\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\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n <th\n [pSortableColumn]=\"col.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || col.isSortable === false\n \"\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 <div class=\"header-container\" [ngStyle]=\"getCellInnerStyle(col)\">\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n @if (\n (isSortable && col.isSortable !== false) ||\n (hasColumnFilter && col.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && col.isSortable !== false) {\n <p-sortIcon [field]=\"col.code\" />\n }\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 [showClearButton]=\"true\"\n [showMatchModes]=\"false\"\n [showOperator]=\"false\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n >\n @for (\n composedName of col.composedNames;\n track composedName\n ) {\n @if (\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n ) {\n <div>\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n filters[composedName]?.value ?? []\n \"\n [placeholder]=\"\n filters[composedName]?.placeholder\n \"\n display=\"chip\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event\n );\n filter($event ?? [])\n \"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n @if (item.image) {\n <img\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n }\n\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </div>\n }\n }\n </ng-template>\n </p-columnFilter>\n } @else {\n <p-columnFilter\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n col.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n @if (\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n ) {\n <ng-template pTemplate=\"filter\" let-value>\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value\n \"\n placeholder=\"Enter a number\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n />\n </ng-template>\n }\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-datepicker\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value ?? []\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event ?? [])\n \"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n }\n }\n </div>\n }\n </div>\n </th>\n } @else {\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getHeaderCellStyle(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n }\n }\n </tr>\n\n @if (hasGroupedColumns) {\n <tr>\n @if (canUseRowReorder()) {\n <th\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (col.children) {\n @for (child of col.children; track child) {\n <th\n [pSortableColumn]=\"child.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || child.isSortable === false\n \"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngStyle]=\"getHeaderCellStyle(child)\"\n [ngClass]=\"getHeaderAlignClass(child)\"\n >\n <div\n class=\"header-container\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n <span [ngClass]=\"getHeaderTitleClass(child)\">\n {{ child.title }}\n </span>\n\n @if (\n (isSortable && child.isSortable !== false) ||\n (hasColumnFilter && child.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && child.isSortable !== false) {\n <p-sortIcon [field]=\"child.code\" />\n }\n\n @if (hasColumnFilter && child.isFilter !== false) {\n <p-columnFilter\n display=\"menu\"\n [field]=\"child.code\"\n [type]=\"getColumnFilterType(child)\"\n [currency]=\"getCurrencySymbol(child)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n child.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(child.code!)\"\n ></p-columnFilter>\n }\n </div>\n }\n </div>\n </th>\n }\n }\n }\n </tr>\n }\n </ng-template>\n\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <td\n class=\"empty-message-cell\"\n [attr.colspan]=\"columns.length + (canUseRowReorder() ? 1 : 0)\"\n >\n <div class=\"empty-message-wrapper\">\n <div class=\"empty-message\">\n <i class=\"pi pi-info-circle\"></i>\n <p>No records available to display.</p>\n </div>\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template pTemplate=\"body\" let-data let-editing=\"editing\">\n @if (!loading) {\n <tr\n [pEditableRow]=\"isEdit ? data : null\"\n [pSelectableRow]=\"selectionMode ? data : null\"\n [attr.data-row-reorder-target]=\"canUseRowReorder() ? 'true' : null\"\n [class.pt-context-menu-row]=\"hasContextMenu\"\n (contextmenu)=\"onRowContextMenu($event, data)\"\n >\n @if (canUseRowReorder()) {\n <td\n class=\"row-reorder-column\"\n [style.width]=\"getRowReorderColumnWidth()\"\n [style.text-align]=\"'center'\"\n [ngStyle]=\"getStaticCellStyle()\"\n >\n <div class=\"row-reorder-actions\">\n <span\n class=\"row-drag-handle\"\n title=\"D\u00E9placer la ligne\"\n (pointerdown)=\"onRowPointerDown($event, data)\"\n >\n <i class=\"pi pi-bars\"></i>\n </span>\n\n @if (showRowMoveArrows) {\n <div class=\"row-reorder-arrow-buttons\">\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-up\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Monter la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowUp(data)\"\n (click)=\"moveRowUp(data); $event.stopPropagation()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-down\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Descendre la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowDown(data)\"\n (click)=\"moveRowDown(data); $event.stopPropagation()\"\n ></button>\n </div>\n }\n </div>\n </td>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n @if (isEditable(col.code!) && col.type !== TableTypeEnum.ACTION) {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isMultiSelect(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n [options]=\"optionValues\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-multiSelect>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n @for (\n rec of getMultiSelectValues(data[col.code!]);\n track rec\n ) {\n <p-tag [value]=\"rec\"></p-tag>\n }\n </div>\n </ng-template>\n </p-cellEditor>\n } @else if (isDatePicker(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-datepicker\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n [dateFormat]=\"'dd/mm/yy'\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n } @else {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"data[col.code!]\"\n (change)=\"onChange($event, data.id, col.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n @if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else {\n {{ data[col.code!] }}\n }\n </ng-template>\n </p-cellEditor>\n }\n </div>\n </td>\n } @else {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : '',\n ]\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n @if (col.type === TableTypeEnum.ACTION) {\n <div\n class=\"action-buttons-container\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isDelete) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"Delete(data); $event.stopPropagation()\"\n ></button>\n }\n\n @if (isEdit) {\n <div>\n @if (!editing) {\n <button\n pInitEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Modifier\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n startRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n }\n\n @if (editing) {\n <button\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Enregistrer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n saveRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n\n <button\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Annuler\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n cancelRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n }\n </div>\n }\n\n @for (act of customActions; track act) {\n @if (isActionVisible(act, data)) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text\"\n [icon]=\"act.icon || 'pi pi-ellipsis-h'\"\n [ngClass]=\"act.styleClass\"\n [disabled]=\"isActionDisabled(act, data)\"\n [pTooltip]=\"act.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n onCustomActionClick(act, data);\n $event.stopPropagation()\n \"\n ></button>\n }\n }\n </div>\n } @else {\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (col.type === TableTypeEnum.COMPOSED) {\n <div class=\"composed-cell\">\n @for (\n composedName of col.composedNames;\n track composedName;\n let i = $index\n ) {\n @if (\n col.composedTypes?.[i] === TableTypeEnum.IMAGE\n ) {\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getMergedComposedImageStyle(\n col,\n composedName,\n data\n )\n \"\n />\n }\n\n @if (\n col.composedTypes?.[i] === TableTypeEnum.STRING\n ) {\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getMergedComposedTextStyle(\n col,\n composedName,\n data\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n }\n }\n </div>\n } @else if (col.type === TableTypeEnum.PROGRESS) {\n <div class=\"progress-cell\">\n <p-progressBar\n [value]=\"getProgressValue(col, data)\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'pt-progress-' + getProgressSeverity(col, data)\n \"\n ></p-progressBar>\n\n @if (isProgressShowValue(col)) {\n <span class=\"progress-cell-value\">\n {{ getProgressValue(col, data)\n }}{{ getProgressUnit(col) }}\n </span>\n }\n </div>\n } @else if (col.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(col, data)\"\n [severity]=\"$any(getTagSeverity(col, data))\"\n [icon]=\"getTagIcon(col, data)\"\n [rounded]=\"isTagRounded(col)\"\n ></p-tag>\n } @else if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else if (col.type === TableTypeEnum.NUMBER) {\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n } @else if (col.type === TableTypeEnum.DATE) {\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (col.type === TableTypeEnum.DATETIME) {\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT,\n ].includes(col.type!)\n ) {\n {{ data[col.code!] }}\n }\n </div>\n }\n </td>\n }\n } @else {\n @for (child of col.children; track child) {\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n [ngStyle]=\"getCellStyle(child, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(child)\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n @if (isEditable(child.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"child.code ? data[child.code] : null\"\n (change)=\"onChange($event, data.id, child.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n } @else if (child.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(child, data)\"\n [severity]=\"$any(getTagSeverity(child, data))\"\n [icon]=\"getTagIcon(child, data)\"\n [rounded]=\"isTagRounded(child)\"\n ></p-tag>\n } @else {\n {{ child.code ? data[child.code] : \"\" }}\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </ng-template>\n </p-table>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-advanced-prime-table{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{display:grid;align-items:center;justify-items:start;width:100%;min-width:0;height:5rem}.pt-advanced-prime-table .filter-container{display:flex;align-items:center;justify-content:space-between;gap:1rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .settings{display:flex;align-items:center;gap:1rem;min-width:0}.pt-advanced-prime-table .multi-select-container{display:flex;align-items:center;gap:.3rem;min-width:0}.pt-advanced-prime-table .table-container{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table ::ng-deep p-table{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper{display:block;width:100%;min-width:0;max-width:100%;overflow-x:auto!important;overflow-y:visible!important;overscroll-behavior-x:contain;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;scrollbar-width:thin}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{width:100%;min-width:var(--pt-advanced-table-min-width, 95rem);max-width:none;table-layout:fixed;border-collapse:separate;border-spacing:0}.pt-advanced-prime-table ::ng-deep .p-datatable-header,.pt-advanced-prime-table ::ng-deep .p-datatable-caption{width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-caption>.flex,.pt-advanced-prime-table ::ng-deep .p-datatable-header>.flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .custom-multiselect .p-hidden-accessible input{display:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column.p-highlight:hover{background:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column:focus{box-shadow:none;outline:0 none}.pt-advanced-prime-table ::ng-deep .header-container{position:relative;display:flex;align-items:center;gap:.25rem;width:100%!important;min-width:0;max-width:100%;margin:0!important;padding:0!important}.pt-advanced-prime-table ::ng-deep .header-title-left,.pt-advanced-prime-table ::ng-deep .header-title-center,.pt-advanced-prime-table ::ng-deep .header-title-right{display:block;flex:1 1 auto;width:auto;min-width:0;max-width:100%;padding:0;line-height:1.2;white-space:normal;overflow-wrap:break-word;word-break:normal}.pt-advanced-prime-table ::ng-deep .header-title-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-title-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-title-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-align-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-align-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-align-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-container>.icons{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:flex-end;flex:0 0 auto;gap:.15rem;width:auto!important;min-width:1.5rem;max-width:none;margin-left:auto;padding:0;transform:none!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .header-container>.icons p-sorticon,.pt-advanced-prime-table ::ng-deep .header-container>.icons p-columnfilter{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0}.pt-advanced-prime-table ::ng-deep .header-container>p-columnfilter:last-child{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0 0 0 auto!important;transform:none!important}.pt-advanced-prime-table ::ng-deep .p-sortable-column-icon,.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{flex:0 0 auto;margin:0!important}.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{width:1.75rem;height:1.75rem;padding:0}.pt-advanced-prime-table ::ng-deep p-sorticon{width:1.25rem;min-width:1.25rem}.pt-advanced-prime-table ::ng-deep p-columnfilter.p-element.ng-star-inserted{margin-top:0}.pt-advanced-prime-table .flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield{position:relative;display:inline-flex;align-items:center;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep p-inputicon,.pt-advanced-prime-table ::ng-deep .p-inputicon,.pt-advanced-prime-table ::ng-deep .p-input-icon{position:absolute;top:50%;left:.85rem;z-index:2;display:inline-flex;align-items:center;justify-content:center;margin:0;transform:translateY(-50%);pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-iconfield .p-inputtext{width:100%;max-width:100%;padding-left:2.5rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{margin-left:1rem;cursor:pointer}.pt-advanced-prime-table .table-row{display:flex;justify-content:center;gap:1rem;min-width:0;text-align:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{color:green;font-size:1.25em}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{color:red;font-size:1.25em}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:180px}.pt-advanced-prime-table .empty-message{color:#888;font-size:1.2rem;text-align:center}.pt-advanced-prime-table .empty-message i{display:block;margin-bottom:.5rem;font-size:2rem}.pt-advanced-prime-table th,.pt-advanced-prime-table td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th{vertical-align:middle}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{vertical-align:top}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right,.pt-advanced-prime-table ::ng-deep .composed-cell,.pt-advanced-prime-table ::ng-deep .composed-text{min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.filter-image{width:22px;height:14px;margin-right:5px}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-left{text-align:left!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-center{text-align:center!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-right{text-align:right!important}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right{display:flex;align-items:flex-start;width:100%;min-width:0}.pt-advanced-prime-table .cell-inner-left{justify-content:flex-start;text-align:left}.pt-advanced-prime-table .cell-inner-center{justify-content:center;text-align:center}.pt-advanced-prime-table .cell-inner-right{justify-content:flex-end;text-align:right}.pt-advanced-prime-table ::ng-deep .p-tag{padding:.2rem .55rem;font-size:.72rem;font-weight:800;line-height:1.2;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .p-tag .p-tag-icon{margin-right:.25rem}.pt-advanced-prime-table .progress-cell{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar{flex:1 1 auto;width:100%;min-width:0;height:.75rem!important;overflow:hidden;border-radius:999px;background:#e5e7eb!important}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar-value{display:block!important;height:100%!important;border-radius:999px}.pt-advanced-prime-table .progress-cell-value{flex:0 0 auto;min-width:38px;color:#334155;font-size:.72rem;font-weight:800;text-align:right}.pt-advanced-prime-table ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{align-items:center;justify-content:center;padding:0;overflow:hidden;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .action-buttons-container{display:flex;align-items:center!important;justify-content:center!important;flex-wrap:wrap;gap:.35rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{min-width:22px!important;height:22px!important;padding:.25rem!important}.pt-advanced-prime-table ::ng-deep .pt-progress-success .p-progressbar-value{background:#22c55e!important}.pt-advanced-prime-table ::ng-deep .pt-progress-warning .p-progressbar-value{background:#f59e0b!important}.pt-advanced-prime-table ::ng-deep .pt-progress-danger .p-progressbar-value{background:#ef4444!important}.pt-advanced-prime-table ::ng-deep .pt-progress-info .p-progressbar-value{background:#3b82f6!important}.pt-advanced-prime-table ::ng-deep .composed-cell{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .composed-image{display:block;flex:0 0 auto;object-fit:contain}.pt-advanced-prime-table ::ng-deep .composed-text{flex:1 1 auto;min-width:0}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{transition:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging{cursor:grabbing!important;opacity:.55}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:5;box-shadow:inset 0 0 0 1px #3b82f673}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before:before{position:absolute;z-index:30;top:-2px;right:0;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after:after{position:absolute;z-index:30;right:0;bottom:-2px;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td{border-top:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{border-bottom:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-drag-handle{display:inline-flex;align-items:center;justify-content:center;width:2rem;min-width:2rem;height:2rem;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pt-advanced-prime-table ::ng-deep .row-drag-handle:hover{background:#3b82f61a}.pt-advanced-prime-table ::ng-deep .row-drag-handle:active{cursor:grabbing}.pt-advanced-prime-table ::ng-deep .row-drag-handle .pi{color:#64748b;font-size:.95rem}.pt-advanced-prime-table ::ng-deep td.row-reorder-column,.pt-advanced-prime-table ::ng-deep th.row-reorder-column{width:7rem;min-width:7rem;padding:.25rem .4rem!important;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .row-reorder-actions{display:inline-flex;align-items:center;justify-content:center;gap:.15rem;width:100%}.pt-advanced-prime-table ::ng-deep .row-reorder-arrow-buttons{display:inline-flex;flex-direction:column;gap:0}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text{width:1.75rem!important;min-width:1.75rem!important;height:1.75rem!important;padding:0!important;border-radius:6px!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text .p-button-icon{font-size:.75rem}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:not(:disabled):hover{background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:disabled{cursor:not-allowed;opacity:.35}.pt-advanced-prime-table.dragging-active,.pt-advanced-prime-table.dragging-active *{-webkit-user-select:none!important;user-select:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:10}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-after>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar{width:10px;height:10px}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-track,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-advanced-table-scrollbar-track, #f1f5f9)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb{border:2px solid var(--pt-advanced-table-scrollbar-track, #f1f5f9);border-radius:999px;background:var(--pt-advanced-table-scrollbar-thumb, #94a3b8)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb:hover,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb:hover{background:var(--pt-advanced-table-scrollbar-thumb-hover, #64748b)}@media(max-width:768px){.pt-advanced-prime-table .filter-container,.pt-advanced-prime-table .flex{align-items:stretch;flex-direction:column}.pt-advanced-prime-table .settings{flex-wrap:wrap}.pt-advanced-prime-table .ml-auto{width:100%;margin-left:0}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield,.pt-advanced-prime-table ::ng-deep .p-inputtext{width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{min-width:var(--pt-advanced-table-mobile-min-width, 78rem)}}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-context-menu-row{cursor:context-menu}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-context-menu-row:hover>td{background:#3b82f60a}::ng-deep .pt-table-context-menu{min-width:13rem}::ng-deep .pt-table-context-menu .p-contextmenu-item-link{gap:.65rem}::ng-deep .pt-table-context-menu .p-contextmenu-item-icon{width:1.1rem;text-align:center}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i3.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i3.SelectableRow, selector: "[pSelectableRow]", inputs: ["pSelectableRow", "pSelectableRowIndex", "pSelectableRowDisabled"] }, { kind: "component", type: i3.CellEditor, selector: "p-cellEditor" }, { kind: "component", type: i3.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i3.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i3.InitEditableRow, selector: "[pInitEditableRow]" }, { kind: "directive", type: i3.SaveEditableRow, selector: "[pSaveEditableRow]" }, { kind: "directive", type: i3.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i3.ColumnFilter, selector: "p-columnFilter, p-column-filter, p-columnfilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "filterOn", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: i7.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: i8.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: i9.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i10.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i11.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i12.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "directive", type: i13.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i14.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }, { kind: "pipe", type: CustomCurrencyPipe, name: "customCurrency" }, { kind: "pipe", type: CustomDatePipe, name: "customDate" }] }); }
1496
1613
  }
1497
1614
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTAdvancedPrimeTableComponent, decorators: [{
1498
1615
  type: Component,
1499
- args: [{ selector: 'pt-advanced-prime-table', standalone: false, template: "<div #tableContainer class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"tableData\"\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 [rowTrackBy]=\"rowTrackBy\"\n paginatorDropdownAppendTo=\"body\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n [selectionMode]=\"selectionMode\"\n [(selection)]=\"selection\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n (onFilter)=\"filterColumn($event)\"\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]=\"getRowReorderColumnWidth()\" />\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 type=\"button\"\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n title=\"Clear filters\"\n (click)=\"clear(dt)\"\n ></button>\n }\n\n @if (hasExportExcel) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to Excel\"\n (click)=\"exportExcel()\"\n ></button>\n }\n\n @if (hasExportPDF) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to PDF\"\n (click)=\"exportPdf()\"\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 placeholder=\"Search keyword\"\n (input)=\"filterGlobal($event)\"\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\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n <th\n [pSortableColumn]=\"col.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || col.isSortable === false\n \"\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 <div class=\"header-container\" [ngStyle]=\"getCellInnerStyle(col)\">\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n @if (\n (isSortable && col.isSortable !== false) ||\n (hasColumnFilter && col.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && col.isSortable !== false) {\n <p-sortIcon [field]=\"col.code\" />\n }\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 [showClearButton]=\"true\"\n [showMatchModes]=\"false\"\n [showOperator]=\"false\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n >\n @for (\n composedName of col.composedNames;\n track composedName\n ) {\n @if (\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n ) {\n <div>\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n filters[composedName]?.value ?? []\n \"\n [placeholder]=\"\n filters[composedName]?.placeholder\n \"\n display=\"chip\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event\n );\n filter($event ?? [])\n \"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n @if (item.image) {\n <img\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n }\n\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </div>\n }\n }\n </ng-template>\n </p-columnFilter>\n } @else {\n <p-columnFilter\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n col.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n @if (\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n ) {\n <ng-template pTemplate=\"filter\" let-value>\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value\n \"\n placeholder=\"Enter a number\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n />\n </ng-template>\n }\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-datepicker\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value ?? []\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event ?? [])\n \"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n }\n }\n </div>\n }\n </div>\n </th>\n } @else {\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getHeaderCellStyle(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n }\n }\n </tr>\n\n @if (hasGroupedColumns) {\n <tr>\n @if (canUseRowReorder()) {\n <th\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (col.children) {\n @for (child of col.children; track child) {\n <th\n [pSortableColumn]=\"child.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || child.isSortable === false\n \"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngStyle]=\"getHeaderCellStyle(child)\"\n [ngClass]=\"getHeaderAlignClass(child)\"\n >\n <div\n class=\"header-container\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n <span [ngClass]=\"getHeaderTitleClass(child)\">\n {{ child.title }}\n </span>\n\n @if (\n (isSortable && child.isSortable !== false) ||\n (hasColumnFilter && child.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && child.isSortable !== false) {\n <p-sortIcon [field]=\"child.code\" />\n }\n\n @if (hasColumnFilter && child.isFilter !== false) {\n <p-columnFilter\n display=\"menu\"\n [field]=\"child.code\"\n [type]=\"getColumnFilterType(child)\"\n [currency]=\"getCurrencySymbol(child)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n child.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(child.code!)\"\n ></p-columnFilter>\n }\n </div>\n }\n </div>\n </th>\n }\n }\n }\n </tr>\n }\n </ng-template>\n\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <td\n class=\"empty-message-cell\"\n [attr.colspan]=\"columns.length + (canUseRowReorder() ? 1 : 0)\"\n >\n <div class=\"empty-message-wrapper\">\n <div class=\"empty-message\">\n <i class=\"pi pi-info-circle\"></i>\n <p>No records available to display.</p>\n </div>\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template pTemplate=\"body\" let-data let-editing=\"editing\">\n @if (!loading) {\n <tr\n [pEditableRow]=\"isEdit ? data : null\"\n [pSelectableRow]=\"selectionMode ? data : null\"\n [attr.data-row-reorder-target]=\"canUseRowReorder() ? 'true' : null\"\n >\n @if (canUseRowReorder()) {\n <td\n class=\"row-reorder-column\"\n [style.width]=\"getRowReorderColumnWidth()\"\n [style.text-align]=\"'center'\"\n [ngStyle]=\"getStaticCellStyle()\"\n >\n <div class=\"row-reorder-actions\">\n <span\n class=\"row-drag-handle\"\n title=\"D\u00E9placer la ligne\"\n (pointerdown)=\"onRowPointerDown($event, data)\"\n >\n <i class=\"pi pi-bars\"></i>\n </span>\n\n @if (showRowMoveArrows) {\n <div class=\"row-reorder-arrow-buttons\">\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-up\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Monter la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowUp(data)\"\n (click)=\"moveRowUp(data); $event.stopPropagation()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-down\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Descendre la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowDown(data)\"\n (click)=\"moveRowDown(data); $event.stopPropagation()\"\n ></button>\n </div>\n }\n </div>\n </td>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n @if (isEditable(col.code!) && col.type !== TableTypeEnum.ACTION) {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isMultiSelect(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n [options]=\"optionValues\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-multiSelect>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n @for (\n rec of getMultiSelectValues(data[col.code!]);\n track rec\n ) {\n <p-tag [value]=\"rec\"></p-tag>\n }\n </div>\n </ng-template>\n </p-cellEditor>\n } @else if (isDatePicker(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-datepicker\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n [dateFormat]=\"'dd/mm/yy'\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n } @else {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"data[col.code!]\"\n (change)=\"onChange($event, data.id, col.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n @if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else {\n {{ data[col.code!] }}\n }\n </ng-template>\n </p-cellEditor>\n }\n </div>\n </td>\n } @else {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : '',\n ]\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n @if (col.type === TableTypeEnum.ACTION) {\n <div\n class=\"action-buttons-container\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isDelete) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"Delete(data)\"\n ></button>\n }\n\n @if (isEdit) {\n <div>\n @if (!editing) {\n <button\n pInitEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Modifier\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"initEditableRow(data)\"\n ></button>\n }\n\n @if (editing) {\n <button\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Enregistrer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"saveEditableRow(data)\"\n ></button>\n\n <button\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Annuler\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"cancelEditableRow(data)\"\n ></button>\n }\n </div>\n }\n\n @for (act of customActions; track act) {\n @if (isActionVisible(act, data)) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text\"\n [icon]=\"act.icon || 'pi pi-ellipsis-h'\"\n [ngClass]=\"act.styleClass\"\n [disabled]=\"isActionDisabled(act, data)\"\n [pTooltip]=\"act.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"onCustomActionClick(act, data)\"\n ></button>\n }\n }\n </div>\n } @else {\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (col.type === TableTypeEnum.COMPOSED) {\n <div class=\"composed-cell\">\n @for (\n composedName of col.composedNames;\n track composedName;\n let i = $index\n ) {\n @if (\n col.composedTypes?.[i] === TableTypeEnum.IMAGE\n ) {\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getMergedComposedImageStyle(\n col,\n composedName,\n data\n )\n \"\n />\n }\n\n @if (\n col.composedTypes?.[i] === TableTypeEnum.STRING\n ) {\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getMergedComposedTextStyle(\n col,\n composedName,\n data\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n }\n }\n </div>\n } @else if (col.type === TableTypeEnum.PROGRESS) {\n <div class=\"progress-cell\">\n <p-progressBar\n [value]=\"getProgressValue(col, data)\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'pt-progress-' + getProgressSeverity(col, data)\n \"\n ></p-progressBar>\n\n @if (isProgressShowValue(col)) {\n <span class=\"progress-cell-value\">\n {{ getProgressValue(col, data)\n }}{{ getProgressUnit(col) }}\n </span>\n }\n </div>\n } @else if (col.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(col, data)\"\n [severity]=\"$any(getTagSeverity(col, data))\"\n [icon]=\"getTagIcon(col, data)\"\n [rounded]=\"isTagRounded(col)\"\n ></p-tag>\n } @else if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else if (col.type === TableTypeEnum.NUMBER) {\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n } @else if (col.type === TableTypeEnum.DATE) {\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (col.type === TableTypeEnum.DATETIME) {\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT,\n ].includes(col.type!)\n ) {\n {{ data[col.code!] }}\n }\n </div>\n }\n </td>\n }\n } @else {\n @for (child of col.children; track child) {\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n [ngStyle]=\"getCellStyle(child, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(child)\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n @if (isEditable(child.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"child.code ? data[child.code] : null\"\n (change)=\"onChange($event, data.id, child.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n } @else if (child.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(child, data)\"\n [severity]=\"$any(getTagSeverity(child, data))\"\n [icon]=\"getTagIcon(child, data)\"\n [rounded]=\"isTagRounded(child)\"\n ></p-tag>\n } @else {\n {{ child.code ? data[child.code] : \"\" }}\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </ng-template>\n </p-table>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-advanced-prime-table{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{display:grid;align-items:center;justify-items:start;width:100%;min-width:0;height:5rem}.pt-advanced-prime-table .filter-container{display:flex;align-items:center;justify-content:space-between;gap:1rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .settings{display:flex;align-items:center;gap:1rem;min-width:0}.pt-advanced-prime-table .multi-select-container{display:flex;align-items:center;gap:.3rem;min-width:0}.pt-advanced-prime-table .table-container{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table ::ng-deep p-table{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper{display:block;width:100%;min-width:0;max-width:100%;overflow-x:auto!important;overflow-y:visible!important;overscroll-behavior-x:contain;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;scrollbar-width:thin}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{width:100%;min-width:var(--pt-advanced-table-min-width, 95rem);max-width:none;table-layout:fixed;border-collapse:separate;border-spacing:0}.pt-advanced-prime-table ::ng-deep .p-datatable-header,.pt-advanced-prime-table ::ng-deep .p-datatable-caption{width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-caption>.flex,.pt-advanced-prime-table ::ng-deep .p-datatable-header>.flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .custom-multiselect .p-hidden-accessible input{display:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column.p-highlight:hover{background:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column:focus{box-shadow:none;outline:0 none}.pt-advanced-prime-table ::ng-deep .header-container{position:relative;display:flex;align-items:center;gap:.25rem;width:100%!important;min-width:0;max-width:100%;margin:0!important;padding:0!important}.pt-advanced-prime-table ::ng-deep .header-title-left,.pt-advanced-prime-table ::ng-deep .header-title-center,.pt-advanced-prime-table ::ng-deep .header-title-right{display:block;flex:1 1 auto;width:auto;min-width:0;max-width:100%;padding:0;line-height:1.2;white-space:normal;overflow-wrap:break-word;word-break:normal}.pt-advanced-prime-table ::ng-deep .header-title-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-title-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-title-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-align-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-align-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-align-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-container>.icons{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:flex-end;flex:0 0 auto;gap:.15rem;width:auto!important;min-width:1.5rem;max-width:none;margin-left:auto;padding:0;transform:none!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .header-container>.icons p-sorticon,.pt-advanced-prime-table ::ng-deep .header-container>.icons p-columnfilter{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0}.pt-advanced-prime-table ::ng-deep .header-container>p-columnfilter:last-child{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0 0 0 auto!important;transform:none!important}.pt-advanced-prime-table ::ng-deep .p-sortable-column-icon,.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{flex:0 0 auto;margin:0!important}.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{width:1.75rem;height:1.75rem;padding:0}.pt-advanced-prime-table ::ng-deep p-sorticon{width:1.25rem;min-width:1.25rem}.pt-advanced-prime-table ::ng-deep p-columnfilter.p-element.ng-star-inserted{margin-top:0}.pt-advanced-prime-table .flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield{position:relative;display:inline-flex;align-items:center;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep p-inputicon,.pt-advanced-prime-table ::ng-deep .p-inputicon,.pt-advanced-prime-table ::ng-deep .p-input-icon{position:absolute;top:50%;left:.85rem;z-index:2;display:inline-flex;align-items:center;justify-content:center;margin:0;transform:translateY(-50%);pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-iconfield .p-inputtext{width:100%;max-width:100%;padding-left:2.5rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{margin-left:1rem;cursor:pointer}.pt-advanced-prime-table .table-row{display:flex;justify-content:center;gap:1rem;min-width:0;text-align:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{color:green;font-size:1.25em}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{color:red;font-size:1.25em}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:180px}.pt-advanced-prime-table .empty-message{color:#888;font-size:1.2rem;text-align:center}.pt-advanced-prime-table .empty-message i{display:block;margin-bottom:.5rem;font-size:2rem}.pt-advanced-prime-table th,.pt-advanced-prime-table td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th{vertical-align:middle}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{vertical-align:top}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right,.pt-advanced-prime-table ::ng-deep .composed-cell,.pt-advanced-prime-table ::ng-deep .composed-text{min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.filter-image{width:22px;height:14px;margin-right:5px}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-left{text-align:left!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-center{text-align:center!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-right{text-align:right!important}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right{display:flex;align-items:flex-start;width:100%;min-width:0}.pt-advanced-prime-table .cell-inner-left{justify-content:flex-start;text-align:left}.pt-advanced-prime-table .cell-inner-center{justify-content:center;text-align:center}.pt-advanced-prime-table .cell-inner-right{justify-content:flex-end;text-align:right}.pt-advanced-prime-table ::ng-deep .p-tag{padding:.2rem .55rem;font-size:.72rem;font-weight:800;line-height:1.2;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .p-tag .p-tag-icon{margin-right:.25rem}.pt-advanced-prime-table .progress-cell{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar{flex:1 1 auto;width:100%;min-width:0;height:.75rem!important;overflow:hidden;border-radius:999px;background:#e5e7eb!important}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar-value{display:block!important;height:100%!important;border-radius:999px}.pt-advanced-prime-table .progress-cell-value{flex:0 0 auto;min-width:38px;color:#334155;font-size:.72rem;font-weight:800;text-align:right}.pt-advanced-prime-table ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{align-items:center;justify-content:center;padding:0;overflow:hidden;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .action-buttons-container{display:flex;align-items:center!important;justify-content:center!important;flex-wrap:wrap;gap:.35rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{min-width:22px!important;height:22px!important;padding:.25rem!important}.pt-advanced-prime-table ::ng-deep .pt-progress-success .p-progressbar-value{background:#22c55e!important}.pt-advanced-prime-table ::ng-deep .pt-progress-warning .p-progressbar-value{background:#f59e0b!important}.pt-advanced-prime-table ::ng-deep .pt-progress-danger .p-progressbar-value{background:#ef4444!important}.pt-advanced-prime-table ::ng-deep .pt-progress-info .p-progressbar-value{background:#3b82f6!important}.pt-advanced-prime-table ::ng-deep .composed-cell{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .composed-image{display:block;flex:0 0 auto;object-fit:contain}.pt-advanced-prime-table ::ng-deep .composed-text{flex:1 1 auto;min-width:0}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{transition:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging{cursor:grabbing!important;opacity:.55}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:5;box-shadow:inset 0 0 0 1px #3b82f673}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before:before{position:absolute;z-index:30;top:-2px;right:0;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after:after{position:absolute;z-index:30;right:0;bottom:-2px;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td{border-top:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{border-bottom:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-drag-handle{display:inline-flex;align-items:center;justify-content:center;width:2rem;min-width:2rem;height:2rem;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pt-advanced-prime-table ::ng-deep .row-drag-handle:hover{background:#3b82f61a}.pt-advanced-prime-table ::ng-deep .row-drag-handle:active{cursor:grabbing}.pt-advanced-prime-table ::ng-deep .row-drag-handle .pi{color:#64748b;font-size:.95rem}.pt-advanced-prime-table ::ng-deep td.row-reorder-column,.pt-advanced-prime-table ::ng-deep th.row-reorder-column{width:7rem;min-width:7rem;padding:.25rem .4rem!important;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .row-reorder-actions{display:inline-flex;align-items:center;justify-content:center;gap:.15rem;width:100%}.pt-advanced-prime-table ::ng-deep .row-reorder-arrow-buttons{display:inline-flex;flex-direction:column;gap:0}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text{width:1.75rem!important;min-width:1.75rem!important;height:1.75rem!important;padding:0!important;border-radius:6px!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text .p-button-icon{font-size:.75rem}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:not(:disabled):hover{background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:disabled{cursor:not-allowed;opacity:.35}.pt-advanced-prime-table.dragging-active,.pt-advanced-prime-table.dragging-active *{-webkit-user-select:none!important;user-select:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:10}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-after>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar{width:10px;height:10px}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-track,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-advanced-table-scrollbar-track, #f1f5f9)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb{border:2px solid var(--pt-advanced-table-scrollbar-track, #f1f5f9);border-radius:999px;background:var(--pt-advanced-table-scrollbar-thumb, #94a3b8)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb:hover,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb:hover{background:var(--pt-advanced-table-scrollbar-thumb-hover, #64748b)}@media(max-width:768px){.pt-advanced-prime-table .filter-container,.pt-advanced-prime-table .flex{align-items:stretch;flex-direction:column}.pt-advanced-prime-table .settings{flex-wrap:wrap}.pt-advanced-prime-table .ml-auto{width:100%;margin-left:0}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield,.pt-advanced-prime-table ::ng-deep .p-inputtext{width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{min-width:var(--pt-advanced-table-mobile-min-width, 78rem)}}\n"] }]
1616
+ args: [{ selector: 'pt-advanced-prime-table', standalone: false, template: "<div #tableContainer class=\"pt-advanced-prime-table table-container\">\n @if (hasContextMenu) {\n <p-contextMenu\n #contextMenu\n [model]=\"contextMenuItems\"\n appendTo=\"body\"\n styleClass=\"pt-table-context-menu\"\n (onHide)=\"onContextMenuHide()\"\n ></p-contextMenu>\n }\n <p-table\n #dt\n [value]=\"tableData\"\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 [rowTrackBy]=\"rowTrackBy\"\n paginatorDropdownAppendTo=\"body\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n [selectionMode]=\"selectionMode\"\n [(selection)]=\"selection\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n (onFilter)=\"filterColumn($event)\"\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]=\"getRowReorderColumnWidth()\" />\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 type=\"button\"\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n title=\"Clear filters\"\n (click)=\"clear(dt)\"\n ></button>\n }\n\n @if (hasExportExcel) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to Excel\"\n (click)=\"exportExcel()\"\n ></button>\n }\n\n @if (hasExportPDF) {\n <button\n pButton\n type=\"button\"\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n title=\"Export to PDF\"\n (click)=\"exportPdf()\"\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 placeholder=\"Search keyword\"\n (input)=\"filterGlobal($event)\"\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\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n <th\n [pSortableColumn]=\"col.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || col.isSortable === false\n \"\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 <div class=\"header-container\" [ngStyle]=\"getCellInnerStyle(col)\">\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n @if (\n (isSortable && col.isSortable !== false) ||\n (hasColumnFilter && col.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && col.isSortable !== false) {\n <p-sortIcon [field]=\"col.code\" />\n }\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 [showClearButton]=\"true\"\n [showMatchModes]=\"false\"\n [showOperator]=\"false\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n >\n @for (\n composedName of col.composedNames;\n track composedName\n ) {\n @if (\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n ) {\n <div>\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n filters[composedName]?.value ?? []\n \"\n [placeholder]=\"\n filters[composedName]?.placeholder\n \"\n display=\"chip\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event\n );\n filter($event ?? [])\n \"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n @if (item.image) {\n <img\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n }\n\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </div>\n }\n }\n </ng-template>\n </p-columnFilter>\n } @else {\n <p-columnFilter\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n col.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n col.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n @if (\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n ) {\n <ng-template pTemplate=\"filter\" let-value>\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value\n \"\n placeholder=\"Enter a number\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n />\n </ng-template>\n }\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-datepicker\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filter=\"filterCallback\"\n let-value\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? value ?? []\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, null, $event);\n filter($event ?? [])\n \"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n }\n }\n </div>\n }\n </div>\n </th>\n } @else {\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngStyle]=\"getHeaderCellStyle(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n }\n }\n </tr>\n\n @if (hasGroupedColumns) {\n <tr>\n @if (canUseRowReorder()) {\n <th\n [style.width]=\"getRowReorderColumnWidth()\"\n [ngStyle]=\"getStaticCellStyle()\"\n ></th>\n }\n\n @for (col of columns; track col) {\n @if (col.children) {\n @for (child of col.children; track child) {\n <th\n [pSortableColumn]=\"child.code\"\n [pSortableColumnDisabled]=\"\n !isSortable || child.isSortable === false\n \"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngStyle]=\"getHeaderCellStyle(child)\"\n [ngClass]=\"getHeaderAlignClass(child)\"\n >\n <div\n class=\"header-container\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n <span [ngClass]=\"getHeaderTitleClass(child)\">\n {{ child.title }}\n </span>\n\n @if (\n (isSortable && child.isSortable !== false) ||\n (hasColumnFilter && child.isFilter !== false)\n ) {\n <div class=\"icons\">\n @if (isSortable && child.isSortable !== false) {\n <p-sortIcon [field]=\"child.code\" />\n }\n\n @if (hasColumnFilter && child.isFilter !== false) {\n <p-columnFilter\n display=\"menu\"\n [field]=\"child.code\"\n [type]=\"getColumnFilterType(child)\"\n [currency]=\"getCurrencySymbol(child)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n [showMatchModes]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [showOperator]=\"\n child.type !== TableTypeEnum.MULTISELECT\n \"\n [matchMode]=\"\n child.type === TableTypeEnum.MULTISELECT\n ? 'in'\n : undefined\n \"\n (onClear)=\"onFilterClear(child.code!)\"\n ></p-columnFilter>\n }\n </div>\n }\n </div>\n </th>\n }\n }\n }\n </tr>\n }\n </ng-template>\n\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <td\n class=\"empty-message-cell\"\n [attr.colspan]=\"columns.length + (canUseRowReorder() ? 1 : 0)\"\n >\n <div class=\"empty-message-wrapper\">\n <div class=\"empty-message\">\n <i class=\"pi pi-info-circle\"></i>\n <p>No records available to display.</p>\n </div>\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template pTemplate=\"body\" let-data let-editing=\"editing\">\n @if (!loading) {\n <tr\n [pEditableRow]=\"isEdit ? data : null\"\n [pSelectableRow]=\"selectionMode ? data : null\"\n [attr.data-row-reorder-target]=\"canUseRowReorder() ? 'true' : null\"\n [class.pt-context-menu-row]=\"hasContextMenu\"\n (contextmenu)=\"onRowContextMenu($event, data)\"\n >\n @if (canUseRowReorder()) {\n <td\n class=\"row-reorder-column\"\n [style.width]=\"getRowReorderColumnWidth()\"\n [style.text-align]=\"'center'\"\n [ngStyle]=\"getStaticCellStyle()\"\n >\n <div class=\"row-reorder-actions\">\n <span\n class=\"row-drag-handle\"\n title=\"D\u00E9placer la ligne\"\n (pointerdown)=\"onRowPointerDown($event, data)\"\n >\n <i class=\"pi pi-bars\"></i>\n </span>\n\n @if (showRowMoveArrows) {\n <div class=\"row-reorder-arrow-buttons\">\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-up\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Monter la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowUp(data)\"\n (click)=\"moveRowUp(data); $event.stopPropagation()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-chevron-down\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Descendre la ligne\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [disabled]=\"!canMoveRowDown(data)\"\n (click)=\"moveRowDown(data); $event.stopPropagation()\"\n ></button>\n </div>\n }\n </div>\n </td>\n }\n\n @for (col of columns; track col) {\n @if (!col.children) {\n @if (isEditable(col.code!) && col.type !== TableTypeEnum.ACTION) {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isMultiSelect(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n [options]=\"optionValues\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-multiSelect>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n @for (\n rec of getMultiSelectValues(data[col.code!]);\n track rec\n ) {\n <p-tag [value]=\"rec\"></p-tag>\n }\n </div>\n </ng-template>\n </p-cellEditor>\n } @else if (isDatePicker(col.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-datepicker\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n [dateFormat]=\"'dd/mm/yy'\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n ></p-datepicker>\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n } @else {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"data[col.code!]\"\n (change)=\"onChange($event, data.id, col.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n @if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else {\n {{ data[col.code!] }}\n }\n </ng-template>\n </p-cellEditor>\n }\n </div>\n </td>\n } @else {\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : '',\n ]\"\n [ngStyle]=\"getCellStyle(col, data)\"\n >\n @if (col.type === TableTypeEnum.ACTION) {\n <div\n class=\"action-buttons-container\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (isDelete) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Supprimer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"Delete(data); $event.stopPropagation()\"\n ></button>\n }\n\n @if (isEdit) {\n <div>\n @if (!editing) {\n <button\n pInitEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Modifier\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n startRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n }\n\n @if (editing) {\n <button\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Enregistrer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n saveRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n\n <button\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"p-button-rounded p-button-text\"\n pTooltip=\"Annuler\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n cancelRowEdit(data); $event.stopPropagation()\n \"\n ></button>\n }\n </div>\n }\n\n @for (act of customActions; track act) {\n @if (isActionVisible(act, data)) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text\"\n [icon]=\"act.icon || 'pi pi-ellipsis-h'\"\n [ngClass]=\"act.styleClass\"\n [disabled]=\"isActionDisabled(act, data)\"\n [pTooltip]=\"act.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n (click)=\"\n onCustomActionClick(act, data);\n $event.stopPropagation()\n \"\n ></button>\n }\n }\n </div>\n } @else {\n <div\n [ngClass]=\"getCellInnerAlignClass(col)\"\n [ngStyle]=\"getCellInnerStyle(col)\"\n >\n @if (col.type === TableTypeEnum.COMPOSED) {\n <div class=\"composed-cell\">\n @for (\n composedName of col.composedNames;\n track composedName;\n let i = $index\n ) {\n @if (\n col.composedTypes?.[i] === TableTypeEnum.IMAGE\n ) {\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getMergedComposedImageStyle(\n col,\n composedName,\n data\n )\n \"\n />\n }\n\n @if (\n col.composedTypes?.[i] === TableTypeEnum.STRING\n ) {\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getMergedComposedTextStyle(\n col,\n composedName,\n data\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n }\n }\n </div>\n } @else if (col.type === TableTypeEnum.PROGRESS) {\n <div class=\"progress-cell\">\n <p-progressBar\n [value]=\"getProgressValue(col, data)\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'pt-progress-' + getProgressSeverity(col, data)\n \"\n ></p-progressBar>\n\n @if (isProgressShowValue(col)) {\n <span class=\"progress-cell-value\">\n {{ getProgressValue(col, data)\n }}{{ getProgressUnit(col) }}\n </span>\n }\n </div>\n } @else if (col.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(col, data)\"\n [severity]=\"$any(getTagSeverity(col, data))\"\n [icon]=\"getTagIcon(col, data)\"\n [rounded]=\"isTagRounded(col)\"\n ></p-tag>\n } @else if (col.type === TableTypeEnum.AMOUNT) {\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n } @else if (col.type === TableTypeEnum.NUMBER) {\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n } @else if (col.type === TableTypeEnum.DATE) {\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (col.type === TableTypeEnum.DATETIME) {\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n } @else if (\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT,\n ].includes(col.type!)\n ) {\n {{ data[col.code!] }}\n }\n </div>\n }\n </td>\n }\n } @else {\n @for (child of col.children; track child) {\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n [ngStyle]=\"getCellStyle(child, data)\"\n >\n <div\n [ngClass]=\"getCellInnerAlignClass(child)\"\n [ngStyle]=\"getCellInnerStyle(child)\"\n >\n @if (isEditable(child.code)) {\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <input\n pInputText\n type=\"text\"\n [ngModel]=\"child.code ? data[child.code] : null\"\n (change)=\"onChange($event, data.id, child.code)\"\n />\n </ng-template>\n\n <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n } @else if (child.type === TableTypeEnum.TAG) {\n <p-tag\n [value]=\"getTagValue(child, data)\"\n [severity]=\"$any(getTagSeverity(child, data))\"\n [icon]=\"getTagIcon(child, data)\"\n [rounded]=\"isTagRounded(child)\"\n ></p-tag>\n } @else {\n {{ child.code ? data[child.code] : \"\" }}\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </ng-template>\n </p-table>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-advanced-prime-table{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{display:grid;align-items:center;justify-items:start;width:100%;min-width:0;height:5rem}.pt-advanced-prime-table .filter-container{display:flex;align-items:center;justify-content:space-between;gap:1rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .settings{display:flex;align-items:center;gap:1rem;min-width:0}.pt-advanced-prime-table .multi-select-container{display:flex;align-items:center;gap:.3rem;min-width:0}.pt-advanced-prime-table .table-container{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-advanced-prime-table ::ng-deep p-table{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable{display:block;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper{display:block;width:100%;min-width:0;max-width:100%;overflow-x:auto!important;overflow-y:visible!important;overscroll-behavior-x:contain;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;scrollbar-width:thin}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{width:100%;min-width:var(--pt-advanced-table-min-width, 95rem);max-width:none;table-layout:fixed;border-collapse:separate;border-spacing:0}.pt-advanced-prime-table ::ng-deep .p-datatable-header,.pt-advanced-prime-table ::ng-deep .p-datatable-caption{width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-caption>.flex,.pt-advanced-prime-table ::ng-deep .p-datatable-header>.flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep .custom-multiselect .p-hidden-accessible input{display:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column.p-highlight:hover{background:none}.pt-advanced-prime-table ::ng-deep .p-datatable .p-sortable-column:focus{box-shadow:none;outline:0 none}.pt-advanced-prime-table ::ng-deep .header-container{position:relative;display:flex;align-items:center;gap:.25rem;width:100%!important;min-width:0;max-width:100%;margin:0!important;padding:0!important}.pt-advanced-prime-table ::ng-deep .header-title-left,.pt-advanced-prime-table ::ng-deep .header-title-center,.pt-advanced-prime-table ::ng-deep .header-title-right{display:block;flex:1 1 auto;width:auto;min-width:0;max-width:100%;padding:0;line-height:1.2;white-space:normal;overflow-wrap:break-word;word-break:normal}.pt-advanced-prime-table ::ng-deep .header-title-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-title-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-title-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-align-left{text-align:left}.pt-advanced-prime-table ::ng-deep .header-align-center{text-align:center}.pt-advanced-prime-table ::ng-deep .header-align-right{text-align:right}.pt-advanced-prime-table ::ng-deep .header-container>.icons{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:flex-end;flex:0 0 auto;gap:.15rem;width:auto!important;min-width:1.5rem;max-width:none;margin-left:auto;padding:0;transform:none!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .header-container>.icons p-sorticon,.pt-advanced-prime-table ::ng-deep .header-container>.icons p-columnfilter{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0}.pt-advanced-prime-table ::ng-deep .header-container>p-columnfilter:last-child{position:static!important;top:auto!important;right:auto!important;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;margin:0 0 0 auto!important;transform:none!important}.pt-advanced-prime-table ::ng-deep .p-sortable-column-icon,.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{flex:0 0 auto;margin:0!important}.pt-advanced-prime-table ::ng-deep .p-column-filter-menu-button{width:1.75rem;height:1.75rem;padding:0}.pt-advanced-prime-table ::ng-deep p-sorticon{width:1.25rem;min-width:1.25rem}.pt-advanced-prime-table ::ng-deep p-columnfilter.p-element.ng-star-inserted{margin-top:0}.pt-advanced-prime-table .flex{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield{position:relative;display:inline-flex;align-items:center;width:100%;min-width:0;max-width:100%}.pt-advanced-prime-table ::ng-deep p-inputicon,.pt-advanced-prime-table ::ng-deep .p-inputicon,.pt-advanced-prime-table ::ng-deep .p-input-icon{position:absolute;top:50%;left:.85rem;z-index:2;display:inline-flex;align-items:center;justify-content:center;margin:0;transform:translateY(-50%);pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-iconfield .p-inputtext{width:100%;max-width:100%;padding-left:2.5rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{margin-left:1rem;cursor:pointer}.pt-advanced-prime-table .table-row{display:flex;justify-content:center;gap:1rem;min-width:0;text-align:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{color:green;font-size:1.25em}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{color:red;font-size:1.25em}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:180px}.pt-advanced-prime-table .empty-message{color:#888;font-size:1.2rem;text-align:center}.pt-advanced-prime-table .empty-message i{display:block;margin-bottom:.5rem;font-size:2rem}.pt-advanced-prime-table th,.pt-advanced-prime-table td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{min-width:0;overflow:hidden;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.pt-advanced-prime-table ::ng-deep .p-datatable-thead>tr>th{vertical-align:middle}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td{vertical-align:top}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right,.pt-advanced-prime-table ::ng-deep .composed-cell,.pt-advanced-prime-table ::ng-deep .composed-text{min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word}.filter-image{width:22px;height:14px;margin-right:5px}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-left{text-align:left!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-center{text-align:center!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr>td.cell-align-right{text-align:right!important}.pt-advanced-prime-table .cell-inner-left,.pt-advanced-prime-table .cell-inner-center,.pt-advanced-prime-table .cell-inner-right{display:flex;align-items:flex-start;width:100%;min-width:0}.pt-advanced-prime-table .cell-inner-left{justify-content:flex-start;text-align:left}.pt-advanced-prime-table .cell-inner-center{justify-content:center;text-align:center}.pt-advanced-prime-table .cell-inner-right{justify-content:flex-end;text-align:right}.pt-advanced-prime-table ::ng-deep .p-tag{padding:.2rem .55rem;font-size:.72rem;font-weight:800;line-height:1.2;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .p-tag .p-tag-icon{margin-right:.25rem}.pt-advanced-prime-table .progress-cell{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar{flex:1 1 auto;width:100%;min-width:0;height:.75rem!important;overflow:hidden;border-radius:999px;background:#e5e7eb!important}.pt-advanced-prime-table ::ng-deep .progress-cell .p-progressbar-value{display:block!important;height:100%!important;border-radius:999px}.pt-advanced-prime-table .progress-cell-value{flex:0 0 auto;min-width:38px;color:#334155;font-size:.72rem;font-weight:800;text-align:right}.pt-advanced-prime-table ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{align-items:center;justify-content:center;padding:0;overflow:hidden;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .action-buttons-container{display:flex;align-items:center!important;justify-content:center!important;flex-wrap:wrap;gap:.35rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{min-width:22px!important;height:22px!important;padding:.25rem!important}.pt-advanced-prime-table ::ng-deep .pt-progress-success .p-progressbar-value{background:#22c55e!important}.pt-advanced-prime-table ::ng-deep .pt-progress-warning .p-progressbar-value{background:#f59e0b!important}.pt-advanced-prime-table ::ng-deep .pt-progress-danger .p-progressbar-value{background:#ef4444!important}.pt-advanced-prime-table ::ng-deep .pt-progress-info .p-progressbar-value{background:#3b82f6!important}.pt-advanced-prime-table ::ng-deep .composed-cell{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;width:100%;min-width:0}.pt-advanced-prime-table ::ng-deep .composed-image{display:block;flex:0 0 auto;object-fit:contain}.pt-advanced-prime-table ::ng-deep .composed-text{flex:1 1 auto;min-width:0}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{transition:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging{cursor:grabbing!important;opacity:.55}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:5;box-shadow:inset 0 0 0 1px #3b82f673}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before:before{position:absolute;z-index:30;top:-2px;right:0;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after:after{position:absolute;z-index:30;right:0;bottom:-2px;left:0;height:3px;border-radius:999px;background:var(--primary-color, #3b82f6);content:\"\";pointer-events:none}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-before>td{border-top:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-drop-after>td{border-bottom:3px solid var(--primary-color, #3b82f6)!important;background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-drag-handle{display:inline-flex;align-items:center;justify-content:center;width:2rem;min-width:2rem;height:2rem;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pt-advanced-prime-table ::ng-deep .row-drag-handle:hover{background:#3b82f61a}.pt-advanced-prime-table ::ng-deep .row-drag-handle:active{cursor:grabbing}.pt-advanced-prime-table ::ng-deep .row-drag-handle .pi{color:#64748b;font-size:.95rem}.pt-advanced-prime-table ::ng-deep td.row-reorder-column,.pt-advanced-prime-table ::ng-deep th.row-reorder-column{width:7rem;min-width:7rem;padding:.25rem .4rem!important;text-align:center!important;white-space:nowrap}.pt-advanced-prime-table ::ng-deep .row-reorder-actions{display:inline-flex;align-items:center;justify-content:center;gap:.15rem;width:100%}.pt-advanced-prime-table ::ng-deep .row-reorder-arrow-buttons{display:inline-flex;flex-direction:column;gap:0}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text{width:1.75rem!important;min-width:1.75rem!important;height:1.75rem!important;padding:0!important;border-radius:6px!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text .p-button-icon{font-size:.75rem}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:not(:disabled):hover{background:#3b82f61a!important}.pt-advanced-prime-table ::ng-deep .row-reorder-actions .p-button.p-button-rounded.p-button-text:disabled{cursor:not-allowed;opacity:.35}.pt-advanced-prime-table.dragging-active,.pt-advanced-prime-table.dragging-active *{-webkit-user-select:none!important;user-select:none!important}.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-before,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table .p-datatable-tbody>tr.pt-row-drop-after{position:relative;z-index:10}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-before>td,.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-row-dragging.pt-row-drop-after>td{background:#3b82f60f!important}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar{width:10px;height:10px}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-track,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-advanced-table-scrollbar-track, #f1f5f9)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb{border:2px solid var(--pt-advanced-table-scrollbar-track, #f1f5f9);border-radius:999px;background:var(--pt-advanced-table-scrollbar-thumb, #94a3b8)}.pt-advanced-prime-table ::ng-deep .p-datatable-table-container::-webkit-scrollbar-thumb:hover,.pt-advanced-prime-table ::ng-deep .p-datatable-wrapper::-webkit-scrollbar-thumb:hover{background:var(--pt-advanced-table-scrollbar-thumb-hover, #64748b)}@media(max-width:768px){.pt-advanced-prime-table .filter-container,.pt-advanced-prime-table .flex{align-items:stretch;flex-direction:column}.pt-advanced-prime-table .settings{flex-wrap:wrap}.pt-advanced-prime-table .ml-auto{width:100%;margin-left:0}.pt-advanced-prime-table ::ng-deep p-iconfield,.pt-advanced-prime-table ::ng-deep .p-iconfield,.pt-advanced-prime-table ::ng-deep .p-inputtext{width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-table,.pt-advanced-prime-table ::ng-deep .p-datatable-scrollable-table{min-width:var(--pt-advanced-table-mobile-min-width, 78rem)}}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-context-menu-row{cursor:context-menu}.pt-advanced-prime-table ::ng-deep .p-datatable-tbody>tr.pt-context-menu-row:hover>td{background:#3b82f60a}::ng-deep .pt-table-context-menu{min-width:13rem}::ng-deep .pt-table-context-menu .p-contextmenu-item-link{gap:.65rem}::ng-deep .pt-table-context-menu .p-contextmenu-item-icon{width:1.1rem;text-align:center}\n"] }]
1500
1617
  }], ctorParameters: () => [{ type: i0.NgZone }], propDecorators: { data: [{
1501
1618
  type: Input
1502
1619
  }], columns: [{
@@ -1543,6 +1660,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1543
1660
  type: Input
1544
1661
  }], cellHeight: [{
1545
1662
  type: Input
1663
+ }], hasContextMenu: [{
1664
+ type: Input
1546
1665
  }], selectionChange: [{
1547
1666
  type: Output
1548
1667
  }], rowSelect: [{
@@ -1573,13 +1692,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1573
1692
  }], tableContainer: [{
1574
1693
  type: ViewChild,
1575
1694
  args: ['tableContainer', { static: false }]
1695
+ }], contextMenu: [{
1696
+ type: ViewChild,
1697
+ args: ['contextMenu', { static: false }]
1576
1698
  }] } });
1577
1699
 
1578
1700
  class PTMultiSelectComponent {
1579
1701
  constructor() {
1580
- /**
1581
- * Two-way standalone value.
1582
- */
1583
1702
  this.value = [];
1584
1703
  this.valueChange = new EventEmitter();
1585
1704
  this.selectionChange = new EventEmitter();
@@ -1609,7 +1728,8 @@ class PTMultiSelectComponent {
1609
1728
  return `pt-multiselect-${this.controlName}`;
1610
1729
  }
1611
1730
  get resolvedHidden() {
1612
- return this.formField?.hidden ?? this.config?.hidden ?? false;
1731
+ const hidden = this.formField?.hidden ?? this.config?.hidden;
1732
+ return this.resolveHiddenValue(hidden);
1613
1733
  }
1614
1734
  get resolvedLabel() {
1615
1735
  return this.formField?.label || this.config?.label;
@@ -1626,10 +1746,10 @@ class PTMultiSelectComponent {
1626
1746
  'Select options');
1627
1747
  }
1628
1748
  get resolvedOptionLabel() {
1629
- return this.config?.optionLabel || 'label';
1749
+ return this.formField?.optionLabel || this.config?.optionLabel || 'label';
1630
1750
  }
1631
1751
  get resolvedOptionValue() {
1632
- return this.config?.optionValue || 'value';
1752
+ return this.formField?.optionValue || this.config?.optionValue || 'value';
1633
1753
  }
1634
1754
  get resolvedRequired() {
1635
1755
  return this.formField?.required ?? this.config?.required ?? false;
@@ -1641,19 +1761,45 @@ class PTMultiSelectComponent {
1641
1761
  return this.formField?.errorText || this.config?.errorText;
1642
1762
  }
1643
1763
  get resolvedFilter() {
1644
- return this.config?.filter === true;
1764
+ return this.formField?.filter === true || this.config?.filter === true;
1645
1765
  }
1646
1766
  get resolvedFilterBy() {
1647
- return this.config?.filterBy || this.resolvedOptionLabel;
1767
+ return (this.formField?.filterBy ||
1768
+ this.config?.filterBy ||
1769
+ this.resolvedOptionLabel);
1648
1770
  }
1649
1771
  get resolvedDisplay() {
1650
- return this.config?.display || 'comma';
1772
+ return this.formField?.display || this.config?.display || 'comma';
1651
1773
  }
1652
1774
  get resolvedMaxSelectedLabels() {
1653
- return this.config?.maxSelectedLabels ?? 3;
1775
+ return (this.formField?.maxSelectedLabels ?? this.config?.maxSelectedLabels ?? 3);
1654
1776
  }
1655
1777
  get resolvedSelectedItemsLabel() {
1656
- return this.config?.selectedItemsLabel || '{0} items selected';
1778
+ return (this.formField?.selectedItemsLabel ||
1779
+ this.config?.selectedItemsLabel ||
1780
+ '{0} items selected');
1781
+ }
1782
+ getErrorMessage() {
1783
+ const control = this.activeFormGroup.get(this.controlName);
1784
+ if (!control?.errors) {
1785
+ return '';
1786
+ }
1787
+ const errorKey = Object.keys(control.errors)[0];
1788
+ if (!errorKey) {
1789
+ return '';
1790
+ }
1791
+ const customMessage = this.formField?.validationMessages?.[errorKey];
1792
+ if (customMessage) {
1793
+ return customMessage;
1794
+ }
1795
+ if (this.resolvedErrorText) {
1796
+ return this.resolvedErrorText;
1797
+ }
1798
+ const label = this.resolvedLabel || this.controlName;
1799
+ if (control.hasError('required')) {
1800
+ return `${label} is required`;
1801
+ }
1802
+ return `${label} is invalid`;
1657
1803
  }
1658
1804
  setupControl(forceValueFromConfig) {
1659
1805
  const name = this.controlName;
@@ -1662,23 +1808,35 @@ class PTMultiSelectComponent {
1662
1808
  }
1663
1809
  let control = this.activeFormGroup.get(name);
1664
1810
  if (!control) {
1665
- control = new FormControl(this.getInitialValue());
1811
+ control = new FormControl(this.getInitialValue(), {
1812
+ nonNullable: true,
1813
+ });
1666
1814
  this.activeFormGroup.addControl(name, control);
1667
1815
  }
1668
1816
  else if (forceValueFromConfig) {
1669
- control.setValue(this.getInitialValue(), { emitEvent: false });
1817
+ control.setValue(this.getInitialValue(), {
1818
+ emitEvent: false,
1819
+ });
1670
1820
  }
1671
1821
  else if (!this.formGroup && this.value !== undefined) {
1672
- control.setValue(this.normalizeValue(this.value), { emitEvent: false });
1822
+ control.setValue(this.normalizeValue(this.value), {
1823
+ emitEvent: false,
1824
+ });
1673
1825
  }
1674
1826
  control.setValidators(this.getValidators());
1675
1827
  if (this.resolvedDisabled) {
1676
- control.disable({ emitEvent: false });
1828
+ control.disable({
1829
+ emitEvent: false,
1830
+ });
1677
1831
  }
1678
1832
  else {
1679
- control.enable({ emitEvent: false });
1833
+ control.enable({
1834
+ emitEvent: false,
1835
+ });
1680
1836
  }
1681
- control.updateValueAndValidity({ emitEvent: false });
1837
+ control.updateValueAndValidity({
1838
+ emitEvent: false,
1839
+ });
1682
1840
  this.valueChangesSubscription?.unsubscribe();
1683
1841
  this.valueChangesSubscription = control.valueChanges.subscribe((selectedValues) => {
1684
1842
  const normalizedValues = this.normalizeValue(selectedValues);
@@ -1690,7 +1848,7 @@ class PTMultiSelectComponent {
1690
1848
  if (this.formField?.value !== undefined) {
1691
1849
  return this.normalizeValue(this.formField.value);
1692
1850
  }
1693
- if (this.value !== undefined && Array.isArray(this.value)) {
1851
+ if (Array.isArray(this.value)) {
1694
1852
  return this.normalizeValue(this.value);
1695
1853
  }
1696
1854
  return this.normalizeValue(this.config?.value);
@@ -1700,42 +1858,45 @@ class PTMultiSelectComponent {
1700
1858
  if (this.resolvedRequired) {
1701
1859
  validators.push(Validators.required);
1702
1860
  }
1861
+ if (Array.isArray(this.formField?.validators) &&
1862
+ this.formField.validators.length > 0) {
1863
+ validators.push(...this.formField.validators);
1864
+ }
1703
1865
  return validators;
1704
1866
  }
1705
- /**
1706
- * Le MultiSelect doit manipuler un tableau d'identifiants primitifs,
1707
- * pas des objets complets, pour éviter les problèmes de référence.
1708
- */
1709
1867
  normalizeValue(value) {
1710
- if (!Array.isArray(value)) {
1711
- return [];
1712
- }
1713
- if (value.length === 0) {
1868
+ if (!Array.isArray(value) || value.length === 0) {
1714
1869
  return [];
1715
1870
  }
1716
- if (typeof value[0] === 'number' || typeof value[0] === 'string') {
1717
- return value;
1871
+ if (value.every((item) => typeof item === 'number' ||
1872
+ typeof item === 'string' ||
1873
+ typeof item === 'boolean')) {
1874
+ return [...value];
1718
1875
  }
1876
+ const optionValue = this.resolvedOptionValue;
1719
1877
  return value
1720
1878
  .map((item) => {
1721
- const optionValue = this.resolvedOptionValue;
1722
- if (item?.[optionValue] !== undefined) {
1879
+ if (item === null || item === undefined) {
1880
+ return null;
1881
+ }
1882
+ if (typeof item !== 'object') {
1883
+ return item;
1884
+ }
1885
+ if (item[optionValue] !== undefined) {
1723
1886
  return item[optionValue];
1724
1887
  }
1725
- return item?.value ?? item?.id ?? null;
1888
+ return item.value ?? item.id ?? null;
1726
1889
  })
1727
1890
  .filter((item) => item !== null && item !== undefined);
1728
1891
  }
1729
- getErrorMessage() {
1730
- const control = this.activeFormGroup.get(this.controlName);
1731
- if (control?.hasError('required')) {
1732
- return (this.resolvedErrorText ||
1733
- `${this.resolvedLabel || this.controlName} is required`);
1892
+ resolveHiddenValue(hidden) {
1893
+ if (typeof hidden === 'function') {
1894
+ return Boolean(hidden(this.activeFormGroup.getRawValue()));
1734
1895
  }
1735
- return '';
1896
+ return Boolean(hidden);
1736
1897
  }
1737
1898
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1738
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTMultiSelectComponent, isStandalone: false, selector: "pt-multi-select", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-multiSelect\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [display]=\"resolvedDisplay\"\n [maxSelectedLabels]=\"resolvedMaxSelectedLabels\"\n [selectedItemsLabel]=\"resolvedSelectedItemsLabel\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{ baseZIndex: 11000 }\"\n ></p-multiSelect>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-multiselect{width:100%}.form-field ::ng-deep .p-multiselect.ng-invalid.ng-touched,.form-field ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i8.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }] }); }
1899
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTMultiSelectComponent, isStandalone: false, selector: "pt-multi-select", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-multiSelect\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [display]=\"resolvedDisplay\"\n [maxSelectedLabels]=\"resolvedMaxSelectedLabels\"\n [selectedItemsLabel]=\"resolvedSelectedItemsLabel\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{ baseZIndex: 11000 }\"\n ></p-multiSelect>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-multiselect{width:100%}.form-field ::ng-deep .p-multiselect.ng-invalid.ng-touched,.form-field ::ng-deep .p-multiselect.ng-invalid.ng-dirty{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i9.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }] }); }
1739
1900
  }
1740
1901
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTMultiSelectComponent, decorators: [{
1741
1902
  type: Component,
@@ -1976,6 +2137,7 @@ class PTAdvancedPrimeTableModule {
1976
2137
  TableModule,
1977
2138
  InputTextModule,
1978
2139
  ButtonModule,
2140
+ ContextMenuModule,
1979
2141
  DatePickerModule,
1980
2142
  MultiSelectModule,
1981
2143
  TagModule,
@@ -1992,6 +2154,7 @@ class PTAdvancedPrimeTableModule {
1992
2154
  TableModule,
1993
2155
  InputTextModule,
1994
2156
  ButtonModule,
2157
+ ContextMenuModule,
1995
2158
  DatePickerModule,
1996
2159
  MultiSelectModule,
1997
2160
  TagModule,
@@ -2013,6 +2176,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2013
2176
  TableModule,
2014
2177
  InputTextModule,
2015
2178
  ButtonModule,
2179
+ ContextMenuModule,
2016
2180
  DatePickerModule,
2017
2181
  MultiSelectModule,
2018
2182
  TagModule,
@@ -2298,7 +2462,7 @@ class MultiSearchCriteriaComponent {
2298
2462
  return input.decimalSeparator === 'comma' ? 'fr-FR' : 'en-US';
2299
2463
  }
2300
2464
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MultiSearchCriteriaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: MultiSearchCriteriaComponent, isStandalone: false, selector: "multi-search-criteria", inputs: { title: "title", criteria: "criteria", inputsPerRow: "inputsPerRow", data: "data", mode: "mode" }, outputs: { filteredData: "filteredData", searchCriteria: "searchCriteria" }, ngImport: i0, template: "<p-panel\n header=\"{{ title }}\"\n [toggleable]=\"true\"\n [collapsed]=\"true\"\n class=\"multi-search-criteria\"\n>\n <div\n class=\"criteria-container\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + inputsPerRow + ', 1fr)' }\"\n >\n @for (input of criteria; track input.title) {\n <div class=\"criteria-item\">\n <label class=\"bsc-label\">{{ input.title }}</label>\n\n @switch (input.type) {\n @case (SearchCriteriaTypeEnum.DATERANGE) {\n <p-datepicker\n [(ngModel)]=\"input.value\"\n selectionMode=\"range\"\n [dateFormat]=\"'dd/mm/yy'\"\n class=\"full-width-input\"\n [showIcon]=\"true\"\n (ngModelChange)=\"input.value = $event\"\n ></p-datepicker>\n }\n\n @case (SearchCriteriaTypeEnum.DATE) {\n <p-datepicker\n [ngModel]=\"input.value\"\n (ngModelChange)=\"input.value = $event\"\n [dateFormat]=\"'dd/mm/yy'\"\n [showIcon]=\"true\"\n class=\"full-width-input\"\n ></p-datepicker>\n }\n\n @case (SearchCriteriaTypeEnum.STRING) {\n <input\n type=\"text\"\n pInputText\n [(ngModel)]=\"input.value\"\n class=\"full-width-input\"\n />\n }\n\n @case (SearchCriteriaTypeEnum.AMOUNT) {\n <p-inputNumber\n type=\"number\"\n [(ngModel)]=\"input.value\"\n mode=\"decimal\"\n [minFractionDigits]=\"getMinFractionDigits(input)\"\n [maxFractionDigits]=\"getMaxFractionDigits(input)\"\n [step]=\"getStepValue(input)\"\n [placeholder]=\"getCurrencySymbol(input)\"\n [locale]=\"getDecimalSeparator(input)\"\n class=\"full-width-input\"\n ></p-inputNumber>\n }\n\n @case (SearchCriteriaTypeEnum.NUMBER) {\n <p-inputNumber\n [(ngModel)]=\"input.value\"\n mode=\"decimal\"\n [minFractionDigits]=\"getMinFractionDigits(input)\"\n [maxFractionDigits]=\"getMaxFractionDigits(input)\"\n [step]=\"getStepValue(input)\"\n [useGrouping]=\"false\"\n [locale]=\"getDecimalSeparator(input)\"\n class=\"full-width-input\"\n ></p-inputNumber>\n }\n\n @case (SearchCriteriaTypeEnum.MULTISELECT) {\n <p-multiSelect\n [options]=\"input.filterOptions\"\n [display]=\"'chip'\"\n placeholder=\"Select\"\n [selectAll]=\"selectAll\"\n [(ngModel)]=\"input.value\"\n optionLabel=\"label\"\n (onSelectAllChange)=\"onSelectAllChange($event, input)\"\n class=\"custom-multiselect full-width-input\"\n ></p-multiSelect>\n }\n }\n </div>\n }\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"footer-buttons\">\n <p-button\n label=\"Effacer\"\n icon=\"pi pi-times\"\n (click)=\"clear()\"\n class=\"footer-button\"\n ></p-button>\n\n <p-button\n label=\"Rechercher\"\n icon=\"pi pi-search\"\n (click)=\"search()\"\n class=\"footer-button\"\n ></p-button>\n </div>\n </ng-template>\n</p-panel>\n", styles: [".multi-search-criteria ::ng-deep .p-element .p-hidden-accessible input{display:none}.multi-search-criteria ::ng-deep .bsc-label{display:block;margin-bottom:5px;font-weight:700}.multi-search-criteria ::ng-deep .custom-multiselect{width:100%}.multi-search-criteria ::ng-deep .p-multiselect .p-multiselect-label{font-size:14px;color:#333}.multi-search-criteria ::ng-deep .p-inputtext{width:100%}.multi-search-criteria ::ng-deep .p-calendar .p-inputtext{width:100%}.multi-search-criteria ::ng-deep .p-inputnumber{width:100%}.multi-search-criteria .criteria-container{display:grid;gap:10px}.multi-search-criteria .criteria-item{margin-bottom:10px;box-sizing:border-box}.multi-search-criteria .full-width-input,::ng-deep .p-calendar,::ng-deep .p-multiselect{width:100%}.multi-search-criteria ::ng-deep .p-calendar .p-inputtext,::ng-deep .p-multiselect .p-multiselect-label{width:100%}.multi-search-criteria .footer-buttons{display:flex;justify-content:flex-end;gap:10px}.multi-search-criteria ::ng-deep .footer-button button{width:140px;cursor:pointer}.multi-search-criteria ::ng-deep .p-panel .p-panel-footer{background:#f8f9fa}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i7.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i8.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i9$1.Panel, selector: "p-panel", inputs: ["id", "toggleable", "header", "collapsed", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps", "motionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }] }); }
2465
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: MultiSearchCriteriaComponent, isStandalone: false, selector: "multi-search-criteria", inputs: { title: "title", criteria: "criteria", inputsPerRow: "inputsPerRow", data: "data", mode: "mode" }, outputs: { filteredData: "filteredData", searchCriteria: "searchCriteria" }, ngImport: i0, template: "<p-panel\n header=\"{{ title }}\"\n [toggleable]=\"true\"\n [collapsed]=\"true\"\n class=\"multi-search-criteria\"\n>\n <div\n class=\"criteria-container\"\n [ngStyle]=\"{ 'grid-template-columns': 'repeat(' + inputsPerRow + ', 1fr)' }\"\n >\n @for (input of criteria; track input.title) {\n <div class=\"criteria-item\">\n <label class=\"bsc-label\">{{ input.title }}</label>\n\n @switch (input.type) {\n @case (SearchCriteriaTypeEnum.DATERANGE) {\n <p-datepicker\n [(ngModel)]=\"input.value\"\n selectionMode=\"range\"\n [dateFormat]=\"'dd/mm/yy'\"\n class=\"full-width-input\"\n [showIcon]=\"true\"\n (ngModelChange)=\"input.value = $event\"\n ></p-datepicker>\n }\n\n @case (SearchCriteriaTypeEnum.DATE) {\n <p-datepicker\n [ngModel]=\"input.value\"\n (ngModelChange)=\"input.value = $event\"\n [dateFormat]=\"'dd/mm/yy'\"\n [showIcon]=\"true\"\n class=\"full-width-input\"\n ></p-datepicker>\n }\n\n @case (SearchCriteriaTypeEnum.STRING) {\n <input\n type=\"text\"\n pInputText\n [(ngModel)]=\"input.value\"\n class=\"full-width-input\"\n />\n }\n\n @case (SearchCriteriaTypeEnum.AMOUNT) {\n <p-inputNumber\n type=\"number\"\n [(ngModel)]=\"input.value\"\n mode=\"decimal\"\n [minFractionDigits]=\"getMinFractionDigits(input)\"\n [maxFractionDigits]=\"getMaxFractionDigits(input)\"\n [step]=\"getStepValue(input)\"\n [placeholder]=\"getCurrencySymbol(input)\"\n [locale]=\"getDecimalSeparator(input)\"\n class=\"full-width-input\"\n ></p-inputNumber>\n }\n\n @case (SearchCriteriaTypeEnum.NUMBER) {\n <p-inputNumber\n [(ngModel)]=\"input.value\"\n mode=\"decimal\"\n [minFractionDigits]=\"getMinFractionDigits(input)\"\n [maxFractionDigits]=\"getMaxFractionDigits(input)\"\n [step]=\"getStepValue(input)\"\n [useGrouping]=\"false\"\n [locale]=\"getDecimalSeparator(input)\"\n class=\"full-width-input\"\n ></p-inputNumber>\n }\n\n @case (SearchCriteriaTypeEnum.MULTISELECT) {\n <p-multiSelect\n [options]=\"input.filterOptions\"\n [display]=\"'chip'\"\n placeholder=\"Select\"\n [selectAll]=\"selectAll\"\n [(ngModel)]=\"input.value\"\n optionLabel=\"label\"\n (onSelectAllChange)=\"onSelectAllChange($event, input)\"\n class=\"custom-multiselect full-width-input\"\n ></p-multiSelect>\n }\n }\n </div>\n }\n </div>\n\n <ng-template pTemplate=\"footer\">\n <div class=\"footer-buttons\">\n <p-button\n label=\"Effacer\"\n icon=\"pi pi-times\"\n (click)=\"clear()\"\n class=\"footer-button\"\n ></p-button>\n\n <p-button\n label=\"Rechercher\"\n icon=\"pi pi-search\"\n (click)=\"search()\"\n class=\"footer-button\"\n ></p-button>\n </div>\n </ng-template>\n</p-panel>\n", styles: [".multi-search-criteria ::ng-deep .p-element .p-hidden-accessible input{display:none}.multi-search-criteria ::ng-deep .bsc-label{display:block;margin-bottom:5px;font-weight:700}.multi-search-criteria ::ng-deep .custom-multiselect{width:100%}.multi-search-criteria ::ng-deep .p-multiselect .p-multiselect-label{font-size:14px;color:#333}.multi-search-criteria ::ng-deep .p-inputtext{width:100%}.multi-search-criteria ::ng-deep .p-calendar .p-inputtext{width:100%}.multi-search-criteria ::ng-deep .p-inputnumber{width:100%}.multi-search-criteria .criteria-container{display:grid;gap:10px}.multi-search-criteria .criteria-item{margin-bottom:10px;box-sizing:border-box}.multi-search-criteria .full-width-input,::ng-deep .p-calendar,::ng-deep .p-multiselect{width:100%}.multi-search-criteria ::ng-deep .p-calendar .p-inputtext,::ng-deep .p-multiselect .p-multiselect-label{width:100%}.multi-search-criteria .footer-buttons{display:flex;justify-content:flex-end;gap:10px}.multi-search-criteria ::ng-deep .footer-button button{width:140px;cursor:pointer}.multi-search-criteria ::ng-deep .p-panel .p-panel-footer{background:#f8f9fa}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i8.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i9.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i6.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i9$1.Panel, selector: "p-panel", inputs: ["id", "toggleable", "header", "collapsed", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps", "motionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }] }); }
2302
2466
  }
2303
2467
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MultiSearchCriteriaComponent, decorators: [{
2304
2468
  type: Component,
@@ -2357,56 +2521,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2357
2521
  }]
2358
2522
  }] });
2359
2523
 
2524
+ // projects/ng-prime-tools/src/lib/pt-check-box-input/pt-check-box-input.component.ts
2360
2525
  class PTCheckBoxInputComponent {
2361
2526
  ngOnInit() {
2362
2527
  this.setupControl();
2363
2528
  }
2529
+ ngOnChanges(changes) {
2530
+ if (changes['formGroup'] || changes['formField']) {
2531
+ this.setupControl();
2532
+ }
2533
+ }
2364
2534
  get inputId() {
2365
2535
  return `pt-checkbox-${this.formField?.name || 'field'}`;
2366
2536
  }
2537
+ get errorId() {
2538
+ return `pt-checkbox-error-${this.formField?.name || 'field'}`;
2539
+ }
2540
+ get isInvalid() {
2541
+ const control = this.formGroup?.get(this.formField?.name);
2542
+ return Boolean(control &&
2543
+ control.enabled &&
2544
+ control.invalid &&
2545
+ (control.touched || control.dirty));
2546
+ }
2547
+ getErrorMessage() {
2548
+ const control = this.formGroup?.get(this.formField?.name);
2549
+ if (!control?.errors) {
2550
+ return '';
2551
+ }
2552
+ const errorKey = this.resolveFirstErrorKey(control.errors);
2553
+ if (!errorKey) {
2554
+ return '';
2555
+ }
2556
+ const customMessage = this.formField.validationMessages?.[errorKey];
2557
+ if (customMessage) {
2558
+ return customMessage;
2559
+ }
2560
+ if (this.formField.errorText) {
2561
+ return this.formField.errorText;
2562
+ }
2563
+ const label = this.formField.label?.trim() || this.formField.name;
2564
+ switch (errorKey) {
2565
+ case 'required':
2566
+ return `${label} is required`;
2567
+ default:
2568
+ return `${label} is invalid`;
2569
+ }
2570
+ }
2367
2571
  setupControl() {
2368
- let control = this.formGroup.get(this.formField.name);
2572
+ const name = this.formField?.name;
2573
+ if (!name || !this.formGroup) {
2574
+ return;
2575
+ }
2576
+ let control = this.formGroup.get(name);
2369
2577
  if (!control) {
2370
- control = new FormControl(null);
2371
- this.formGroup.addControl(this.formField.name, control);
2578
+ control = new FormControl(this.resolveInitialValue(), {
2579
+ nonNullable: true,
2580
+ });
2581
+ this.formGroup.addControl(name, control);
2372
2582
  }
2373
- const validators = this.getValidators();
2374
- control.setValidators(validators);
2583
+ control.setValidators(this.getValidators());
2375
2584
  if (this.formField.disabled) {
2376
- control.disable({ emitEvent: false });
2585
+ control.disable({
2586
+ emitEvent: false,
2587
+ });
2377
2588
  }
2378
2589
  else {
2379
- control.enable({ emitEvent: false });
2590
+ control.enable({
2591
+ emitEvent: false,
2592
+ });
2380
2593
  }
2381
- control.updateValueAndValidity({ emitEvent: false });
2594
+ control.updateValueAndValidity({
2595
+ emitEvent: false,
2596
+ });
2597
+ }
2598
+ resolveInitialValue() {
2599
+ const value = this.formField.value;
2600
+ if (typeof value === 'boolean') {
2601
+ return value;
2602
+ }
2603
+ return false;
2382
2604
  }
2383
2605
  getValidators() {
2384
2606
  const validators = [];
2385
2607
  if (this.formField.required) {
2386
- validators.push(this.requireChoiceValidator());
2608
+ validators.push(Validators.requiredTrue);
2609
+ }
2610
+ if (Array.isArray(this.formField.validators) &&
2611
+ this.formField.validators.length > 0) {
2612
+ validators.push(...this.formField.validators);
2387
2613
  }
2388
2614
  return validators;
2389
2615
  }
2390
- requireChoiceValidator() {
2391
- return (control) => {
2392
- return control.value !== null && control.value !== undefined
2393
- ? null
2394
- : { requiredChoice: true };
2395
- };
2396
- }
2397
- getErrorMessage() {
2398
- const control = this.formGroup.get(this.formField.name);
2399
- if (control?.errors?.['requiredChoice']) {
2400
- return this.formField.errorText || `${this.formField.label} is required`;
2401
- }
2402
- return '';
2616
+ resolveFirstErrorKey(errors) {
2617
+ const priority = ['required'];
2618
+ return (priority.find((key) => errors[key] !== undefined) ||
2619
+ Object.keys(errors)[0]);
2403
2620
  }
2404
2621
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTCheckBoxInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2405
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTCheckBoxInputComponent, isStandalone: false, selector: "pt-check-box-input", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n <div class=\"checkbox-container\">\n <p-checkbox\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n [binary]=\"true\"\n ></p-checkbox>\n\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"checkbox-label\">\n {{ formField.label }}\n </label>\n }\n </div>\n\n @if (\n formGroup.get(formField.name)?.errors &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.checkbox-container{display:flex;align-items:center;gap:.5rem;min-height:2.75rem}.checkbox-container .checkbox-label{display:inline-flex;align-items:center;margin:0;padding:0;font-weight:600;font-size:1rem;line-height:1;cursor:pointer}.checkbox-container ::ng-deep .p-checkbox{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}.checkbox-container ::ng-deep .p-checkbox-box{width:1.25rem;height:1.25rem}.error-container{margin-top:.25rem}.p-error{font-size:.8rem;color:#f44336}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }] }); }
2622
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTCheckBoxInputComponent, isStandalone: false, selector: "pt-check-box-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-check-box-input/pt-check-box-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n 'max-width': formField.width || '100%',\n }\"\n>\n <div class=\"checkbox-container\">\n <p-checkbox\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n [binary]=\"true\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-checkbox>\n\n @if (formField.label) {\n <label class=\"checkbox-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n </div>\n\n @if (isInvalid) {\n <div class=\"error-container\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"p-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.checkbox-container{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.checkbox-label{min-width:0;cursor:pointer;font-size:1rem;font-weight:600;overflow-wrap:anywhere}.required-indicator{margin-left:.2rem;color:#ef4444}.form-field-invalid ::ng-deep .p-checkbox-box{border-color:#ef4444!important}.error-container{display:flex;align-items:flex-start;gap:.35rem;width:100%;min-width:0;margin-top:.35rem;color:#ef4444}.error-container .pi{flex:0 0 auto;margin-top:.1rem;font-size:.8rem}.p-error{min-width:0;color:inherit;font-size:.8rem;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }] }); }
2406
2623
  }
2407
2624
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTCheckBoxInputComponent, decorators: [{
2408
2625
  type: Component,
2409
- args: [{ selector: 'pt-check-box-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n <div class=\"checkbox-container\">\n <p-checkbox\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n [binary]=\"true\"\n ></p-checkbox>\n\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"checkbox-label\">\n {{ formField.label }}\n </label>\n }\n </div>\n\n @if (\n formGroup.get(formField.name)?.errors &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.checkbox-container{display:flex;align-items:center;gap:.5rem;min-height:2.75rem}.checkbox-container .checkbox-label{display:inline-flex;align-items:center;margin:0;padding:0;font-weight:600;font-size:1rem;line-height:1;cursor:pointer}.checkbox-container ::ng-deep .p-checkbox{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}.checkbox-container ::ng-deep .p-checkbox-box{width:1.25rem;height:1.25rem}.error-container{margin-top:.25rem}.p-error{font-size:.8rem;color:#f44336}\n"] }]
2626
+ args: [{ selector: 'pt-check-box-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-check-box-input/pt-check-box-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n 'max-width': formField.width || '100%',\n }\"\n>\n <div class=\"checkbox-container\">\n <p-checkbox\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n [binary]=\"true\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-checkbox>\n\n @if (formField.label) {\n <label class=\"checkbox-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n </div>\n\n @if (isInvalid) {\n <div class=\"error-container\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"p-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.checkbox-container{display:flex;align-items:center;gap:.5rem;width:100%;min-width:0}.checkbox-label{min-width:0;cursor:pointer;font-size:1rem;font-weight:600;overflow-wrap:anywhere}.required-indicator{margin-left:.2rem;color:#ef4444}.form-field-invalid ::ng-deep .p-checkbox-box{border-color:#ef4444!important}.error-container{display:flex;align-items:flex-start;gap:.35rem;width:100%;min-width:0;margin-top:.35rem;color:#ef4444}.error-container .pi{flex:0 0 auto;margin-top:.1rem;font-size:.8rem}.p-error{min-width:0;color:inherit;font-size:.8rem;overflow-wrap:anywhere}\n"] }]
2410
2627
  }], propDecorators: { formGroup: [{
2411
2628
  type: Input
2412
2629
  }], formField: [{
@@ -2541,12 +2758,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2541
2758
  }]
2542
2759
  }], ctorParameters: () => [] });
2543
2760
 
2761
+ // projects/ng-prime-tools/src/lib/pt-date-input/pt-date-input.component.ts
2544
2762
  class PTDateInputComponent {
2545
2763
  constructor(dateService) {
2546
2764
  this.dateService = dateService;
2547
- /**
2548
- * Two-way standalone value.
2549
- */
2550
2765
  this.value = null;
2551
2766
  this.valueChange = new EventEmitter();
2552
2767
  this.dateChange = new EventEmitter();
@@ -2579,8 +2794,15 @@ class PTDateInputComponent {
2579
2794
  get inputId() {
2580
2795
  return `pt-date-${this.controlName}`;
2581
2796
  }
2797
+ get labelId() {
2798
+ return `pt-date-label-${this.controlName}`;
2799
+ }
2800
+ get errorId() {
2801
+ return `pt-date-error-${this.controlName}`;
2802
+ }
2582
2803
  get resolvedHidden() {
2583
- return this.formField?.hidden ?? this.config?.hidden ?? false;
2804
+ const hidden = this.formField?.hidden ?? this.config?.hidden;
2805
+ return this.resolveHiddenValue(hidden);
2584
2806
  }
2585
2807
  get resolvedLabel() {
2586
2808
  return this.formField?.label || this.config?.label;
@@ -2626,12 +2848,47 @@ class PTDateInputComponent {
2626
2848
  get resolvedErrorText() {
2627
2849
  return this.formField?.errorText || this.config?.errorText;
2628
2850
  }
2851
+ get isInvalid() {
2852
+ const control = this.activeFormGroup.get(this.controlName);
2853
+ return Boolean(control &&
2854
+ control.enabled &&
2855
+ control.invalid &&
2856
+ (control.touched || control.dirty));
2857
+ }
2858
+ getErrorMessage() {
2859
+ const control = this.activeFormGroup.get(this.controlName);
2860
+ if (!control?.errors) {
2861
+ return '';
2862
+ }
2863
+ const errorKey = this.resolveFirstErrorKey(control.errors);
2864
+ if (!errorKey) {
2865
+ return '';
2866
+ }
2867
+ const customMessage = this.formField?.validationMessages?.[errorKey];
2868
+ if (customMessage) {
2869
+ return customMessage;
2870
+ }
2871
+ if (this.resolvedErrorText) {
2872
+ return this.resolvedErrorText;
2873
+ }
2874
+ const label = this.resolvedLabel || this.controlName;
2875
+ switch (errorKey) {
2876
+ case 'required':
2877
+ return `${label} is required`;
2878
+ case 'minDate':
2879
+ return `${label} must be on or after ${this.resolvedMinValue}`;
2880
+ case 'maxDate':
2881
+ return `${label} must be on or before ${this.resolvedMaxValue}`;
2882
+ default:
2883
+ return `${label} is invalid`;
2884
+ }
2885
+ }
2629
2886
  initializeComponent() {
2630
2887
  this.defaultDateFormat = this.dateService.assignDefaultDateFormat(this.resolvedDateInputType, this.formField?.dateFormat || this.config?.dateFormat);
2631
- this.setupControl();
2888
+ this.defaultPlaceholder = getDefaultPlaceholder(this.resolvedDateInputType);
2632
2889
  this.initializeDateLimits();
2633
2890
  this.validateConfiguration();
2634
- this.defaultPlaceholder = getDefaultPlaceholder(this.resolvedDateInputType);
2891
+ this.setupControl();
2635
2892
  }
2636
2893
  setupControl() {
2637
2894
  const name = this.controlName;
@@ -2643,19 +2900,27 @@ class PTDateInputComponent {
2643
2900
  control = new FormControl(this.getInitialValue());
2644
2901
  this.activeFormGroup.addControl(name, control);
2645
2902
  }
2646
- if (!this.formGroup &&
2903
+ else if (!this.formGroup &&
2647
2904
  this.value !== undefined &&
2648
2905
  control.value !== this.value) {
2649
- control.setValue(this.value, { emitEvent: false });
2906
+ control.setValue(this.value, {
2907
+ emitEvent: false,
2908
+ });
2650
2909
  }
2651
2910
  control.setValidators(this.getValidators());
2652
2911
  if (this.resolvedDisabled) {
2653
- control.disable({ emitEvent: false });
2912
+ control.disable({
2913
+ emitEvent: false,
2914
+ });
2654
2915
  }
2655
2916
  else {
2656
- control.enable({ emitEvent: false });
2917
+ control.enable({
2918
+ emitEvent: false,
2919
+ });
2657
2920
  }
2658
- control.updateValueAndValidity({ emitEvent: false });
2921
+ control.updateValueAndValidity({
2922
+ emitEvent: false,
2923
+ });
2659
2924
  this.valueChangesSubscription?.unsubscribe();
2660
2925
  this.valueChangesSubscription = control.valueChanges.subscribe((selectedValue) => {
2661
2926
  this.valueChange.emit(selectedValue);
@@ -2686,10 +2951,9 @@ class PTDateInputComponent {
2686
2951
  catch (error) {
2687
2952
  if (error instanceof Error) {
2688
2953
  console.error(error.message);
2954
+ return;
2689
2955
  }
2690
- else {
2691
- console.error('An unknown error occurred.');
2692
- }
2956
+ console.error('An unknown error occurred.');
2693
2957
  }
2694
2958
  }
2695
2959
  getValidators() {
@@ -2697,22 +2961,90 @@ class PTDateInputComponent {
2697
2961
  if (this.resolvedRequired) {
2698
2962
  validators.push(Validators.required);
2699
2963
  }
2964
+ if (this.minDate) {
2965
+ validators.push(this.minDateValidator(this.minDate));
2966
+ }
2967
+ if (this.maxDate) {
2968
+ validators.push(this.maxDateValidator(this.maxDate));
2969
+ }
2970
+ if (Array.isArray(this.formField?.validators) &&
2971
+ this.formField.validators.length > 0) {
2972
+ validators.push(...this.formField.validators);
2973
+ }
2700
2974
  return validators;
2701
2975
  }
2702
- getErrorMessage() {
2703
- const control = this.activeFormGroup.get(this.controlName);
2704
- if (control?.hasError('required')) {
2705
- return (this.resolvedErrorText ||
2706
- `${this.resolvedLabel || this.controlName} is required`);
2976
+ minDateValidator(minDate) {
2977
+ return (control) => {
2978
+ const dates = this.extractDates(control.value);
2979
+ if (dates.length === 0) {
2980
+ return null;
2981
+ }
2982
+ const hasInvalidDate = dates.some((date) => date.getTime() < minDate.getTime());
2983
+ return hasInvalidDate
2984
+ ? {
2985
+ minDate: {
2986
+ requiredMinDate: minDate,
2987
+ actualValue: control.value,
2988
+ },
2989
+ }
2990
+ : null;
2991
+ };
2992
+ }
2993
+ maxDateValidator(maxDate) {
2994
+ return (control) => {
2995
+ const dates = this.extractDates(control.value);
2996
+ if (dates.length === 0) {
2997
+ return null;
2998
+ }
2999
+ const hasInvalidDate = dates.some((date) => date.getTime() > maxDate.getTime());
3000
+ return hasInvalidDate
3001
+ ? {
3002
+ maxDate: {
3003
+ requiredMaxDate: maxDate,
3004
+ actualValue: control.value,
3005
+ },
3006
+ }
3007
+ : null;
3008
+ };
3009
+ }
3010
+ extractDates(value) {
3011
+ if (value === null || value === undefined || value === '') {
3012
+ return [];
2707
3013
  }
2708
- return '';
3014
+ const values = Array.isArray(value) ? value : [value];
3015
+ return values
3016
+ .map((item) => this.convertToDate(item))
3017
+ .filter((date) => date !== null);
3018
+ }
3019
+ convertToDate(value) {
3020
+ if (value === null || value === undefined || value === '') {
3021
+ return null;
3022
+ }
3023
+ if (value instanceof Date) {
3024
+ return Number.isNaN(value.getTime()) ? null : value;
3025
+ }
3026
+ const parsedDate = parseDate(value, this.resolvedDateFormat);
3027
+ return parsedDate && !Number.isNaN(parsedDate.getTime())
3028
+ ? parsedDate
3029
+ : null;
3030
+ }
3031
+ resolveFirstErrorKey(errors) {
3032
+ const priority = ['required', 'minDate', 'maxDate'];
3033
+ const prioritizedError = priority.find((key) => errors[key] !== undefined);
3034
+ return prioritizedError || Object.keys(errors)[0];
3035
+ }
3036
+ resolveHiddenValue(hidden) {
3037
+ if (typeof hidden === 'function') {
3038
+ return Boolean(hidden(this.activeFormGroup.getRawValue()));
3039
+ }
3040
+ return Boolean(hidden);
2709
3041
  }
2710
3042
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDateInputComponent, deps: [{ token: PTDateService }], target: i0.ɵɵFactoryTarget.Component }); }
2711
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDateInputComponent, isStandalone: false, selector: "pt-date-input", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", dateChange: "dateChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-datepicker\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [placeholder]=\"resolvedPlaceholder\"\n [dateFormat]=\"resolvedDateFormat\"\n [showIcon]=\"resolvedShowIcon\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [showTime]=\"\n resolvedDateInputType === 'datetime' || resolvedDateInputType === 'time'\n \"\n [timeOnly]=\"resolvedDateInputType === 'time'\"\n [hourFormat]=\"resolvedHourFormat\"\n [selectionMode]=\"resolvedDateInputType === 'range' ? 'range' : 'single'\"\n appendTo=\"body\"\n [panelStyle]=\"{ zIndex: 99999 }\"\n ></p-datepicker>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem;overflow:visible}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-datepicker.ng-invalid.ng-touched .p-inputtext,.form-field ::ng-deep .p-datepicker.ng-invalid.ng-dirty .p-inputtext{border-color:#dc2626}.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i7.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }] }); }
3043
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDateInputComponent, isStandalone: false, selector: "pt-date-input", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", dateChange: "dateChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n class=\"form-field\"\n [formGroup]=\"activeFormGroup\"\n [ngClass]=\"{\n 'form-field-invalid':\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty),\n }\"\n [ngStyle]=\"{\n width: resolvedWidth,\n 'max-width': resolvedWidth,\n }\"\n >\n @if (resolvedLabel) {\n <label class=\"field-label\" [id]=\"labelId\" [for]=\"inputId\">\n {{ resolvedLabel }}\n\n @if (resolvedRequired) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-datepicker\n class=\"date-input\"\n styleClass=\"date-input-control\"\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [placeholder]=\"resolvedPlaceholder\"\n [dateFormat]=\"resolvedDateFormat\"\n [showIcon]=\"resolvedShowIcon\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [showTime]=\"\n resolvedDateInputType === 'datetime' || resolvedDateInputType === 'time'\n \"\n [timeOnly]=\"resolvedDateInputType === 'time'\"\n [hourFormat]=\"resolvedHourFormat\"\n [selectionMode]=\"resolvedDateInputType === 'range' ? 'range' : 'single'\"\n [readonlyInput]=\"false\"\n [showButtonBar]=\"true\"\n [appendTo]=\"'body'\"\n [panelStyle]=\"{\n 'z-index': 99999,\n }\"\n [attr.aria-labelledby]=\"resolvedLabel ? labelId : null\"\n [attr.aria-describedby]=\"\n activeFormGroup.get(controlName)?.invalid\n ? controlName + '-error'\n : null\n \"\n [attr.aria-invalid]=\"\n activeFormGroup.get(controlName)?.invalid ? 'true' : 'false'\n \"\n ></p-datepicker>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\" role=\"alert\" [id]=\"controlName + '-error'\">\n <i class=\"pi pi-exclamation-circle\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem;overflow:visible}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-datepicker.ng-invalid.ng-touched .p-inputtext,.form-field ::ng-deep .p-datepicker.ng-invalid.ng-dirty .p-inputtext{border-color:#dc2626}.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i8.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }] }); }
2712
3044
  }
2713
3045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDateInputComponent, decorators: [{
2714
3046
  type: Component,
2715
- args: [{ selector: 'pt-date-input', standalone: false, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-datepicker\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [placeholder]=\"resolvedPlaceholder\"\n [dateFormat]=\"resolvedDateFormat\"\n [showIcon]=\"resolvedShowIcon\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [showTime]=\"\n resolvedDateInputType === 'datetime' || resolvedDateInputType === 'time'\n \"\n [timeOnly]=\"resolvedDateInputType === 'time'\"\n [hourFormat]=\"resolvedHourFormat\"\n [selectionMode]=\"resolvedDateInputType === 'range' ? 'range' : 'single'\"\n appendTo=\"body\"\n [panelStyle]=\"{ zIndex: 99999 }\"\n ></p-datepicker>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem;overflow:visible}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-datepicker.ng-invalid.ng-touched .p-inputtext,.form-field ::ng-deep .p-datepicker.ng-invalid.ng-dirty .p-inputtext{border-color:#dc2626}.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"] }]
3047
+ args: [{ selector: 'pt-date-input', standalone: false, template: "@if (!resolvedHidden) {\n <div\n class=\"form-field\"\n [formGroup]=\"activeFormGroup\"\n [ngClass]=\"{\n 'form-field-invalid':\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty),\n }\"\n [ngStyle]=\"{\n width: resolvedWidth,\n 'max-width': resolvedWidth,\n }\"\n >\n @if (resolvedLabel) {\n <label class=\"field-label\" [id]=\"labelId\" [for]=\"inputId\">\n {{ resolvedLabel }}\n\n @if (resolvedRequired) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-datepicker\n class=\"date-input\"\n styleClass=\"date-input-control\"\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [placeholder]=\"resolvedPlaceholder\"\n [dateFormat]=\"resolvedDateFormat\"\n [showIcon]=\"resolvedShowIcon\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n [showTime]=\"\n resolvedDateInputType === 'datetime' || resolvedDateInputType === 'time'\n \"\n [timeOnly]=\"resolvedDateInputType === 'time'\"\n [hourFormat]=\"resolvedHourFormat\"\n [selectionMode]=\"resolvedDateInputType === 'range' ? 'range' : 'single'\"\n [readonlyInput]=\"false\"\n [showButtonBar]=\"true\"\n [appendTo]=\"'body'\"\n [panelStyle]=\"{\n 'z-index': 99999,\n }\"\n [attr.aria-labelledby]=\"resolvedLabel ? labelId : null\"\n [attr.aria-describedby]=\"\n activeFormGroup.get(controlName)?.invalid\n ? controlName + '-error'\n : null\n \"\n [attr.aria-invalid]=\"\n activeFormGroup.get(controlName)?.invalid ? 'true' : 'false'\n \"\n ></p-datepicker>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\" role=\"alert\" [id]=\"controlName + '-error'\">\n <i class=\"pi pi-exclamation-circle\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem;overflow:visible}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-datepicker.ng-invalid.ng-touched .p-inputtext,.form-field ::ng-deep .p-datepicker.ng-invalid.ng-dirty .p-inputtext{border-color:#dc2626}.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-datepicker-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"] }]
2716
3048
  }], ctorParameters: () => [{ type: PTDateService }], propDecorators: { formGroup: [{
2717
3049
  type: Input
2718
3050
  }], formField: [{
@@ -2727,18 +3059,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2727
3059
  type: Output
2728
3060
  }] } });
2729
3061
 
3062
+ // projects/ng-prime-tools/src/lib/pt-number-input/pt-number-input.component.ts
2730
3063
  class PTNumberInputComponent {
2731
3064
  ngOnInit() {
2732
3065
  this.setupControl();
2733
3066
  }
2734
3067
  ngOnChanges(changes) {
2735
- if (changes['formField'] && !changes['formField'].firstChange) {
3068
+ if (changes['formGroup'] || changes['formField']) {
2736
3069
  this.setupControl();
2737
3070
  }
2738
3071
  }
2739
3072
  get inputId() {
2740
3073
  return `pt-number-${this.formField?.name || 'field'}`;
2741
3074
  }
3075
+ get errorId() {
3076
+ return `pt-number-error-${this.formField?.name || 'field'}`;
3077
+ }
3078
+ get isInvalid() {
3079
+ const control = this.formGroup?.get(this.formField?.name);
3080
+ return Boolean(control &&
3081
+ control.enabled &&
3082
+ control.invalid &&
3083
+ (control.touched || control.dirty));
3084
+ }
3085
+ getErrorMessage() {
3086
+ const control = this.formGroup.get(this.formField.name);
3087
+ if (!control?.errors) {
3088
+ return '';
3089
+ }
3090
+ const errorKey = this.resolveFirstErrorKey(control.errors);
3091
+ if (!errorKey) {
3092
+ return '';
3093
+ }
3094
+ const customMessage = this.formField.validationMessages?.[errorKey];
3095
+ if (customMessage) {
3096
+ return customMessage;
3097
+ }
3098
+ if (this.formField.errorText) {
3099
+ return this.formField.errorText;
3100
+ }
3101
+ const label = this.formField.label?.trim() || this.formField.name;
3102
+ switch (errorKey) {
3103
+ case 'required':
3104
+ return `${label} is required`;
3105
+ case 'min': {
3106
+ const minValue = control.getError('min')?.min ?? this.formField.minValue;
3107
+ return `${label} must be at least ${minValue}`;
3108
+ }
3109
+ case 'max': {
3110
+ const maxValue = control.getError('max')?.max ?? this.formField.maxValue;
3111
+ return `${label} must be at most ${maxValue}`;
3112
+ }
3113
+ case 'pattern':
3114
+ return `${label} is invalid`;
3115
+ default:
3116
+ return `${label} is invalid`;
3117
+ }
3118
+ }
2742
3119
  setupControl() {
2743
3120
  const name = this.formField?.name;
2744
3121
  if (!name || !this.formGroup) {
@@ -2746,24 +3123,30 @@ class PTNumberInputComponent {
2746
3123
  }
2747
3124
  let control = this.formGroup.get(name);
2748
3125
  if (!control) {
2749
- control = new FormControl(this.formField?.value ?? null);
3126
+ control = new FormControl(this.formField.value ?? null);
2750
3127
  this.formGroup.addControl(name, control);
2751
3128
  }
2752
3129
  control.setValidators(this.getValidators());
2753
- if (this.formField?.disabled) {
2754
- control.disable({ emitEvent: false });
3130
+ if (this.formField.disabled) {
3131
+ control.disable({
3132
+ emitEvent: false,
3133
+ });
2755
3134
  }
2756
3135
  else {
2757
- control.enable({ emitEvent: false });
3136
+ control.enable({
3137
+ emitEvent: false,
3138
+ });
2758
3139
  }
2759
- control.updateValueAndValidity({ emitEvent: false });
3140
+ control.updateValueAndValidity({
3141
+ emitEvent: false,
3142
+ });
2760
3143
  }
2761
3144
  getValidators() {
2762
3145
  const validators = [];
2763
- if (this.formField?.required) {
3146
+ if (this.formField.required) {
2764
3147
  validators.push(Validators.required);
2765
3148
  }
2766
- if (this.formField?.minValue !== undefined &&
3149
+ if (this.formField.minValue !== undefined &&
2767
3150
  this.formField.minValue !== null) {
2768
3151
  const minValue = Number(this.formField.minValue);
2769
3152
  if (!Number.isNaN(minValue)) {
@@ -2773,7 +3156,7 @@ class PTNumberInputComponent {
2773
3156
  console.error(`Invalid minValue: ${this.formField.minValue} is not a number.`);
2774
3157
  }
2775
3158
  }
2776
- if (this.formField?.maxValue !== undefined &&
3159
+ if (this.formField.maxValue !== undefined &&
2777
3160
  this.formField.maxValue !== null) {
2778
3161
  const maxValue = Number(this.formField.maxValue);
2779
3162
  if (!Number.isNaN(maxValue)) {
@@ -2783,47 +3166,41 @@ class PTNumberInputComponent {
2783
3166
  console.error(`Invalid maxValue: ${this.formField.maxValue} is not a number.`);
2784
3167
  }
2785
3168
  }
2786
- if (this.formField?.inputValidation) {
3169
+ if (this.formField.inputValidation) {
2787
3170
  validators.push(this.validateWithInputValidation(this.formField.inputValidation));
2788
3171
  }
3172
+ else if (this.formField.pattern) {
3173
+ validators.push(Validators.pattern(this.formField.pattern));
3174
+ }
3175
+ if (Array.isArray(this.formField.validators) &&
3176
+ this.formField.validators.length > 0) {
3177
+ validators.push(...this.formField.validators);
3178
+ }
2789
3179
  return validators;
2790
3180
  }
2791
3181
  validateWithInputValidation(inputValidation) {
2792
3182
  return Validators.pattern(new RegExp(inputValidation));
2793
3183
  }
2794
- getErrorMessage() {
2795
- const control = this.formGroup.get(this.formField.name);
2796
- if (control?.hasError('required')) {
2797
- return this.formField.errorText || `${this.formField.label} is required`;
2798
- }
2799
- if (control?.hasError('min')) {
2800
- return `${this.formField.label} must be at least ${this.formField.minValue}`;
2801
- }
2802
- if (control?.hasError('max')) {
2803
- return `${this.formField.label} must be at most ${this.formField.maxValue}`;
2804
- }
2805
- if (control?.hasError('pattern')) {
2806
- return `${this.formField.label} is invalid`;
2807
- }
2808
- return '';
3184
+ resolveFirstErrorKey(errors) {
3185
+ const priority = ['required', 'min', 'max', 'pattern'];
3186
+ return (priority.find((key) => errors[key] !== undefined) ||
3187
+ Object.keys(errors)[0]);
2809
3188
  }
2810
3189
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNumberInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2811
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTNumberInputComponent, isStandalone: false, selector: "pt-number-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n <p-inputGroup>\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n\n <p-inputNumber\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits || 0\"\n [maxFractionDigits]=\"formField.decimalDigits || 0\"\n [placeholder]=\"formField.placeholder || ''\"\n ></p-inputNumber>\n }\n\n @if (formField.iconPosition === \"right\") {\n <p-inputNumber\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits || 0\"\n [maxFractionDigits]=\"formField.decimalDigits || 0\"\n [placeholder]=\"formField.placeholder || ''\"\n ></p-inputNumber>\n\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n @if (\n formGroup.get(formField.name)?.invalid &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-field ::ng-deep .p-inputnumber{width:100%}.form-field ::ng-deep .p-inputnumber-input{width:100%}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-inputnumber.ng-invalid.ng-touched .p-inputnumber-input,.form-field ::ng-deep .p-inputnumber.ng-invalid.ng-dirty .p-inputnumber-input{border-color:#dc2626}.form-field ::ng-deep .p-inputnumber-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-inputnumber-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$2.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "component", type: i4.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }, { kind: "component", type: i8$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }] }); }
3190
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTNumberInputComponent, isStandalone: false, selector: "pt-number-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-number-input/pt-number-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n 'max-width': formField.width || '100%',\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n <p-inputGroup class=\"number-input-group\">\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\" aria-hidden=\"true\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n\n <p-inputNumber\n class=\"number-input\"\n styleClass=\"number-input-control\"\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits ?? 0\"\n [maxFractionDigits]=\"formField.decimalDigits ?? 0\"\n [min]=\"\n formField.minValue !== undefined && formField.minValue !== null\n ? +formField.minValue\n : undefined\n \"\n [max]=\"\n formField.maxValue !== undefined && formField.maxValue !== null\n ? +formField.maxValue\n : undefined\n \"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-inputNumber>\n }\n\n @if (formField.iconPosition === \"right\") {\n <p-inputNumber\n class=\"number-input\"\n styleClass=\"number-input-control\"\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits ?? 0\"\n [maxFractionDigits]=\"formField.decimalDigits ?? 0\"\n [min]=\"\n formField.minValue !== undefined && formField.minValue !== null\n ? +formField.minValue\n : undefined\n \"\n [max]=\"\n formField.maxValue !== undefined && formField.maxValue !== null\n ? +formField.maxValue\n : undefined\n \"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-inputNumber>\n\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\" aria-hidden=\"true\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n @if (isInvalid) {\n <div class=\"error-container\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-size:1rem;font-weight:600}.required-indicator{margin-left:.2rem;color:#ef4444}.number-input-group{display:flex;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input-group.p-inputgroup,:host ::ng-deep .number-input-group .p-inputgroup{display:flex;width:100%;min-width:0;max-width:100%}.number-input{display:block;flex:1 1 auto;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input-control,:host ::ng-deep .number-input.p-inputnumber,:host ::ng-deep .number-input .p-inputnumber{flex:1 1 auto;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input .p-inputnumber-input{width:100%;min-width:0}:host ::ng-deep .p-inputgroup-addon{flex:0 0 auto}.form-field-invalid ::ng-deep .p-inputnumber-input{border-color:#ef4444!important}.error-container{display:flex;align-items:flex-start;gap:.35rem;margin-top:.35rem;color:#ef4444}.error-container .pi{flex:0 0 auto;margin-top:.1rem;font-size:.8rem}.field-error{min-width:0;color:inherit;font-size:.8rem;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$2.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "component", type: i4.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }, { kind: "component", type: i8$1.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }] }); }
2812
3191
  }
2813
3192
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNumberInputComponent, decorators: [{
2814
3193
  type: Component,
2815
- args: [{ selector: 'pt-number-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n <p-inputGroup>\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n\n <p-inputNumber\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits || 0\"\n [maxFractionDigits]=\"formField.decimalDigits || 0\"\n [placeholder]=\"formField.placeholder || ''\"\n ></p-inputNumber>\n }\n\n @if (formField.iconPosition === \"right\") {\n <p-inputNumber\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits || 0\"\n [maxFractionDigits]=\"formField.decimalDigits || 0\"\n [placeholder]=\"formField.placeholder || ''\"\n ></p-inputNumber>\n\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n @if (\n formGroup.get(formField.name)?.invalid &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-field ::ng-deep .p-inputnumber{width:100%}.form-field ::ng-deep .p-inputnumber-input{width:100%}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-inputnumber.ng-invalid.ng-touched .p-inputnumber-input,.form-field ::ng-deep .p-inputnumber.ng-invalid.ng-dirty .p-inputnumber-input{border-color:#dc2626}.form-field ::ng-deep .p-inputnumber-input.ng-invalid.ng-touched,.form-field ::ng-deep .p-inputnumber-input.ng-invalid.ng-dirty{border-color:#dc2626}\n"] }]
3194
+ args: [{ selector: 'pt-number-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-number-input/pt-number-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n 'max-width': formField.width || '100%',\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n <p-inputGroup class=\"number-input-group\">\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\" aria-hidden=\"true\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n\n <p-inputNumber\n class=\"number-input\"\n styleClass=\"number-input-control\"\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits ?? 0\"\n [maxFractionDigits]=\"formField.decimalDigits ?? 0\"\n [min]=\"\n formField.minValue !== undefined && formField.minValue !== null\n ? +formField.minValue\n : undefined\n \"\n [max]=\"\n formField.maxValue !== undefined && formField.maxValue !== null\n ? +formField.maxValue\n : undefined\n \"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-inputNumber>\n }\n\n @if (formField.iconPosition === \"right\") {\n <p-inputNumber\n class=\"number-input\"\n styleClass=\"number-input-control\"\n [inputId]=\"inputId\"\n [name]=\"formField.name\"\n [formControlName]=\"formField.name\"\n mode=\"decimal\"\n [locale]=\"formField.numberFormat || undefined\"\n [useGrouping]=\"!!formField.numberFormat\"\n [minFractionDigits]=\"formField.decimalDigits ?? 0\"\n [maxFractionDigits]=\"formField.decimalDigits ?? 0\"\n [min]=\"\n formField.minValue !== undefined && formField.minValue !== null\n ? +formField.minValue\n : undefined\n \"\n [max]=\"\n formField.maxValue !== undefined && formField.maxValue !== null\n ? +formField.maxValue\n : undefined\n \"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></p-inputNumber>\n\n @if (formField.iconClass || formField.currency) {\n <p-inputGroupAddon>\n @if (formField.iconClass) {\n <i [ngClass]=\"formField.iconClass\" aria-hidden=\"true\"></i>\n }\n\n @if (!formField.iconClass && formField.currency) {\n <span>\n {{ formField.currency }}\n </span>\n }\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n @if (isInvalid) {\n <div class=\"error-container\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.field-label{display:block;margin-bottom:.5rem;font-size:1rem;font-weight:600}.required-indicator{margin-left:.2rem;color:#ef4444}.number-input-group{display:flex;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input-group.p-inputgroup,:host ::ng-deep .number-input-group .p-inputgroup{display:flex;width:100%;min-width:0;max-width:100%}.number-input{display:block;flex:1 1 auto;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input-control,:host ::ng-deep .number-input.p-inputnumber,:host ::ng-deep .number-input .p-inputnumber{flex:1 1 auto;width:100%;min-width:0;max-width:100%}:host ::ng-deep .number-input .p-inputnumber-input{width:100%;min-width:0}:host ::ng-deep .p-inputgroup-addon{flex:0 0 auto}.form-field-invalid ::ng-deep .p-inputnumber-input{border-color:#ef4444!important}.error-container{display:flex;align-items:flex-start;gap:.35rem;margin-top:.35rem;color:#ef4444}.error-container .pi{flex:0 0 auto;margin-top:.1rem;font-size:.8rem}.field-error{min-width:0;color:inherit;font-size:.8rem;overflow-wrap:anywhere}\n"] }]
2816
3195
  }], propDecorators: { formGroup: [{
2817
3196
  type: Input
2818
3197
  }], formField: [{
2819
3198
  type: Input
2820
3199
  }] } });
2821
3200
 
3201
+ // projects/ng-prime-tools/src/lib/pt-switch-input/pt-switch-input.component.ts
2822
3202
  class PTSwitchInputComponent {
2823
3203
  constructor() {
2824
- /**
2825
- * Two-way standalone value.
2826
- */
2827
3204
  this.value = null;
2828
3205
  this.valueChange = new EventEmitter();
2829
3206
  this.switchChange = new EventEmitter();
@@ -2856,7 +3233,8 @@ class PTSwitchInputComponent {
2856
3233
  return `pt-switch-label-${this.controlName}`;
2857
3234
  }
2858
3235
  get resolvedHidden() {
2859
- return this.formField?.hidden ?? this.config?.hidden ?? false;
3236
+ const hidden = this.formField?.hidden ?? this.config?.hidden;
3237
+ return this.resolveHiddenValue(hidden);
2860
3238
  }
2861
3239
  get resolvedLabel() {
2862
3240
  return this.formField?.label || this.config?.label;
@@ -2873,6 +3251,28 @@ class PTSwitchInputComponent {
2873
3251
  get resolvedErrorText() {
2874
3252
  return this.formField?.errorText || this.config?.errorText;
2875
3253
  }
3254
+ getErrorMessage() {
3255
+ const control = this.activeFormGroup.get(this.controlName);
3256
+ if (!control?.errors) {
3257
+ return '';
3258
+ }
3259
+ const errorKey = Object.keys(control.errors)[0];
3260
+ if (!errorKey) {
3261
+ return '';
3262
+ }
3263
+ const customMessage = this.formField?.validationMessages?.[errorKey];
3264
+ if (customMessage) {
3265
+ return customMessage;
3266
+ }
3267
+ if (this.resolvedErrorText) {
3268
+ return this.resolvedErrorText;
3269
+ }
3270
+ const label = this.resolvedLabel || this.controlName;
3271
+ if (control.hasError('requiredChoice')) {
3272
+ return `${label} is required`;
3273
+ }
3274
+ return `${label} is invalid`;
3275
+ }
2876
3276
  setupControl() {
2877
3277
  const name = this.controlName;
2878
3278
  if (!name) {
@@ -2886,16 +3286,24 @@ class PTSwitchInputComponent {
2886
3286
  this.activeFormGroup.addControl(name, control);
2887
3287
  }
2888
3288
  else if (!this.formGroup && this.hasExplicitFieldValue(this.value)) {
2889
- control.setValue(this.value, { emitEvent: false });
3289
+ control.setValue(this.value, {
3290
+ emitEvent: false,
3291
+ });
2890
3292
  }
2891
3293
  control.setValidators(this.getValidators());
2892
3294
  if (this.resolvedDisabled) {
2893
- control.disable({ emitEvent: false });
3295
+ control.disable({
3296
+ emitEvent: false,
3297
+ });
2894
3298
  }
2895
3299
  else {
2896
- control.enable({ emitEvent: false });
3300
+ control.enable({
3301
+ emitEvent: false,
3302
+ });
2897
3303
  }
2898
- control.updateValueAndValidity({ emitEvent: false });
3304
+ control.updateValueAndValidity({
3305
+ emitEvent: false,
3306
+ });
2899
3307
  this.valueChangesSubscription?.unsubscribe();
2900
3308
  this.valueChangesSubscription = control.valueChanges.subscribe((selectedValue) => {
2901
3309
  this.valueChange.emit(selectedValue);
@@ -2919,22 +3327,26 @@ class PTSwitchInputComponent {
2919
3327
  if (this.resolvedRequired) {
2920
3328
  validators.push(this.requireChoiceValidator());
2921
3329
  }
3330
+ if (Array.isArray(this.formField?.validators) &&
3331
+ this.formField.validators.length > 0) {
3332
+ validators.push(...this.formField.validators);
3333
+ }
2922
3334
  return validators;
2923
3335
  }
2924
3336
  requireChoiceValidator() {
2925
3337
  return (control) => {
2926
3338
  return control.value !== null && control.value !== undefined
2927
3339
  ? null
2928
- : { requiredChoice: true };
3340
+ : {
3341
+ requiredChoice: true,
3342
+ };
2929
3343
  };
2930
3344
  }
2931
- getErrorMessage() {
2932
- const control = this.activeFormGroup.get(this.controlName);
2933
- if (control?.errors?.['requiredChoice']) {
2934
- return (this.resolvedErrorText ||
2935
- `${this.resolvedLabel || this.controlName} is required`);
3345
+ resolveHiddenValue(hidden) {
3346
+ if (typeof hidden === 'function') {
3347
+ return Boolean(hidden(this.activeFormGroup.getRawValue()));
2936
3348
  }
2937
- return '';
3349
+ return Boolean(hidden);
2938
3350
  }
2939
3351
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTSwitchInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2940
3352
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTSwitchInputComponent, isStandalone: false, selector: "pt-switch-input", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", switchChange: "switchChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n <div class=\"switch-container\">\n <p-toggleswitch\n [inputId]=\"inputId\"\n [formControlName]=\"controlName\"\n [ariaLabelledBy]=\"labelId\"\n ></p-toggleswitch>\n\n @if (resolvedLabel) {\n <label [id]=\"labelId\" [for]=\"inputId\" class=\"switch-label\">\n {{ resolvedLabel }}\n </label>\n }\n </div>\n\n @if (\n activeFormGroup.get(controlName)?.errors &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{margin-bottom:1rem}.switch-container{display:flex;align-items:center;gap:.5rem;min-height:2.75rem}.switch-label{display:inline-flex;align-items:center;margin:0;padding:0;font-weight:600;font-size:1rem;line-height:1;cursor:pointer}.error-container{margin-top:.25rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-toggleswitch.ng-invalid.ng-touched .p-toggleswitch-slider,.form-field ::ng-deep .p-toggleswitch.ng-invalid.ng-dirty .p-toggleswitch-slider{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$3.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }] }); }
@@ -2956,6 +3368,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
2956
3368
  type: Output
2957
3369
  }] } });
2958
3370
 
3371
+ // projects/ng-prime-tools/src/lib/pt-text-area-input/pt-text-area-input.component.ts
2959
3372
  class PTTextAreaInputComponent {
2960
3373
  constructor() {
2961
3374
  this.characterCount = 0;
@@ -2965,14 +3378,63 @@ class PTTextAreaInputComponent {
2965
3378
  this.updateCharacterCount();
2966
3379
  }
2967
3380
  ngOnChanges(changes) {
2968
- if (changes['formField'] && !changes['formField'].firstChange) {
3381
+ if (changes['formGroup'] || changes['formField']) {
2969
3382
  this.setupControl();
2970
3383
  this.updateCharacterCount();
2971
3384
  }
2972
3385
  }
3386
+ ngOnDestroy() {
3387
+ this.valueChangesSubscription?.unsubscribe();
3388
+ }
2973
3389
  get inputId() {
2974
3390
  return `pt-textarea-${this.formField?.name || 'field'}`;
2975
3391
  }
3392
+ get errorId() {
3393
+ return `pt-textarea-error-${this.formField?.name || 'field'}`;
3394
+ }
3395
+ get isInvalid() {
3396
+ const control = this.formGroup?.get(this.formField?.name);
3397
+ return Boolean(control &&
3398
+ control.enabled &&
3399
+ control.invalid &&
3400
+ (control.touched || control.dirty));
3401
+ }
3402
+ getErrorMessage() {
3403
+ const control = this.formGroup.get(this.formField.name);
3404
+ if (!control?.errors) {
3405
+ return '';
3406
+ }
3407
+ const errorKey = this.resolveFirstErrorKey(control.errors);
3408
+ if (!errorKey) {
3409
+ return '';
3410
+ }
3411
+ const customMessage = this.formField.validationMessages?.[errorKey];
3412
+ if (customMessage) {
3413
+ return customMessage;
3414
+ }
3415
+ if (this.formField.errorText) {
3416
+ return this.formField.errorText;
3417
+ }
3418
+ const label = this.formField.label?.trim() || this.formField.name;
3419
+ switch (errorKey) {
3420
+ case 'required':
3421
+ return `${label} is required`;
3422
+ case 'minlength': {
3423
+ const requiredLength = control.getError('minlength')?.requiredLength ??
3424
+ this.formField.minLength;
3425
+ return `${label} must be at least ${requiredLength} characters long`;
3426
+ }
3427
+ case 'maxlength': {
3428
+ const requiredLength = control.getError('maxlength')?.requiredLength ??
3429
+ this.formField.maxLength;
3430
+ return `${label} must be at most ${requiredLength} characters long`;
3431
+ }
3432
+ case 'pattern':
3433
+ return `${label} is invalid`;
3434
+ default:
3435
+ return `${label} is invalid`;
3436
+ }
3437
+ }
2976
3438
  setupControl() {
2977
3439
  const name = this.formField?.name;
2978
3440
  if (!name || !this.formGroup) {
@@ -2980,18 +3442,25 @@ class PTTextAreaInputComponent {
2980
3442
  }
2981
3443
  let control = this.formGroup.get(name);
2982
3444
  if (!control) {
2983
- control = new FormControl(this.formField?.value ?? null);
3445
+ control = new FormControl(this.formField.value ?? null);
2984
3446
  this.formGroup.addControl(name, control);
2985
3447
  }
2986
3448
  control.setValidators(this.getValidators());
2987
- if (this.formField?.disabled) {
2988
- control.disable({ emitEvent: false });
3449
+ if (this.formField.disabled) {
3450
+ control.disable({
3451
+ emitEvent: false,
3452
+ });
2989
3453
  }
2990
3454
  else {
2991
- control.enable({ emitEvent: false });
3455
+ control.enable({
3456
+ emitEvent: false,
3457
+ });
2992
3458
  }
2993
- control.updateValueAndValidity({ emitEvent: false });
2994
- control.valueChanges.subscribe(() => {
3459
+ control.updateValueAndValidity({
3460
+ emitEvent: false,
3461
+ });
3462
+ this.valueChangesSubscription?.unsubscribe();
3463
+ this.valueChangesSubscription = control.valueChanges.subscribe(() => {
2995
3464
  this.updateCharacterCount();
2996
3465
  });
2997
3466
  }
@@ -3002,55 +3471,55 @@ class PTTextAreaInputComponent {
3002
3471
  return;
3003
3472
  }
3004
3473
  const value = control.value;
3005
- this.characterCount = value ? String(value).length : 0;
3474
+ this.characterCount =
3475
+ value === null || value === undefined ? 0 : String(value).length;
3006
3476
  }
3007
3477
  getValidators() {
3008
3478
  const validators = [];
3009
- if (this.formField?.required) {
3479
+ if (this.formField.required) {
3010
3480
  validators.push(Validators.required);
3011
3481
  }
3012
- if (this.formField?.minLength !== undefined) {
3482
+ if (this.formField.minLength !== undefined &&
3483
+ this.formField.minLength !== null) {
3013
3484
  validators.push(Validators.minLength(this.formField.minLength));
3014
3485
  }
3015
- if (this.formField?.maxLength !== undefined) {
3486
+ if (this.formField.maxLength !== undefined &&
3487
+ this.formField.maxLength !== null) {
3016
3488
  validators.push(Validators.maxLength(this.formField.maxLength));
3017
3489
  }
3018
- if (this.formField?.inputValidation) {
3490
+ if (this.formField.inputValidation) {
3019
3491
  validators.push(this.validateWithInputValidation(this.formField.inputValidation));
3020
3492
  }
3493
+ else if (this.formField.pattern) {
3494
+ validators.push(Validators.pattern(this.formField.pattern));
3495
+ }
3496
+ if (Array.isArray(this.formField.validators) &&
3497
+ this.formField.validators.length > 0) {
3498
+ validators.push(...this.formField.validators);
3499
+ }
3021
3500
  return validators;
3022
3501
  }
3023
3502
  validateWithInputValidation(inputValidation) {
3024
3503
  return Validators.pattern(new RegExp(inputValidation));
3025
3504
  }
3026
- getErrorMessage() {
3027
- const control = this.formGroup.get(this.formField.name);
3028
- if (control?.hasError('required')) {
3029
- return this.formField.errorText || `${this.formField.label} is required`;
3030
- }
3031
- if (control?.hasError('minlength')) {
3032
- return `${this.formField.label} must be at least ${this.formField.minLength} characters long`;
3033
- }
3034
- if (control?.hasError('maxlength')) {
3035
- return `${this.formField.label} must be at most ${this.formField.maxLength} characters long`;
3036
- }
3037
- if (control?.hasError('pattern')) {
3038
- return `${this.formField.label} is invalid`;
3039
- }
3040
- return '';
3505
+ resolveFirstErrorKey(errors) {
3506
+ const priority = ['required', 'minlength', 'maxlength', 'pattern'];
3507
+ return (priority.find((key) => errors[key] !== undefined) ||
3508
+ Object.keys(errors)[0]);
3041
3509
  }
3042
3510
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTTextAreaInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3043
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTTextAreaInputComponent, isStandalone: false, selector: "pt-text-area-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\">{{ formField.label }}</label>\n }\n <p-inputGroup>\n @if (!formField.iconPosition || formField.iconPosition === 'left') {\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n <textarea\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [cols]=\"formField.cols || 30\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n ></textarea>\n }\n @if (formField.iconPosition === 'right') {\n <textarea\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [cols]=\"formField.cols || 30\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n ></textarea>\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n <div class=\"form-info-row\">\n @if (\n formGroup.get(formField.name)?.invalid &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <small\n class=\"p-error\"\n >\n {{ getErrorMessage() }}\n </small>\n }\n <div class=\"spacer\"></div>\n @if (!formField.disabled && formField.maxLength !== undefined) {\n <div\n class=\"character-counter\"\n >\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n </div>\n}\n", styles: [".form-field{position:relative;margin-bottom:1rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.form-info-row{display:flex;justify-content:space-between;align-items:center;margin-top:.5rem}.spacer{flex-grow:1}.character-counter{font-size:.8rem;color:#888;text-align:right;margin-left:auto}.p-error{font-size:.8rem;color:#f44336}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3$4.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i3$2.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "component", type: i4.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }] }); }
3511
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTTextAreaInputComponent, isStandalone: false, selector: "pt-text-area-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-text-area-input/pt-text-area-input.component.html -->\n\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n }\"\n>\n @if (formField.label) {\n <label [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-inputGroup class=\"textarea-input-group\">\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n\n <textarea\n class=\"textarea-control\"\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></textarea>\n }\n\n @if (formField.iconPosition === \"right\") {\n <textarea\n class=\"textarea-control\"\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></textarea>\n\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"p-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (!formField.disabled && formField.maxLength !== undefined) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}:host *,:host *:before,:host *:after{box-sizing:border-box}.form-field{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.form-field label{display:block;width:100%;margin-bottom:.5rem;font-size:1rem;font-weight:700}.textarea-input-group{display:flex;width:100%;min-width:0;max-width:100%}:host ::ng-deep .textarea-input-group.p-inputgroup,:host ::ng-deep .textarea-input-group .p-inputgroup{display:flex;width:100%;min-width:0;max-width:100%}.textarea-control{display:block;flex:1 1 auto;width:100%;min-width:0;max-width:100%;resize:vertical}:host ::ng-deep .textarea-control.p-textarea,:host ::ng-deep textarea.textarea-control{display:block;flex:1 1 auto;width:100%!important;min-width:0;max-width:100%}:host ::ng-deep .p-inputgroup textarea{flex:1 1 auto;width:100%!important;min-width:0}:host ::ng-deep .p-inputgroup-addon{flex:0 0 auto}.form-info-row{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem;width:100%;min-width:0;margin-top:.5rem}.spacer{flex:1 1 auto;min-width:0}.character-counter{flex:0 0 auto;margin-left:auto;color:#888;font-size:.8rem;text-align:right;white-space:nowrap}.p-error{min-width:0;color:#f44336;font-size:.8rem;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3$4.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i3$2.InputGroup, selector: "p-inputgroup, p-inputGroup, p-input-group", inputs: ["styleClass"] }, { kind: "component", type: i4.InputGroupAddon, selector: "p-inputgroup-addon, p-inputGroupAddon", inputs: ["style", "styleClass"] }] }); }
3044
3512
  }
3045
3513
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTTextAreaInputComponent, decorators: [{
3046
3514
  type: Component,
3047
- args: [{ selector: 'pt-text-area-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\">{{ formField.label }}</label>\n }\n <p-inputGroup>\n @if (!formField.iconPosition || formField.iconPosition === 'left') {\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n <textarea\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [cols]=\"formField.cols || 30\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n ></textarea>\n }\n @if (formField.iconPosition === 'right') {\n <textarea\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [cols]=\"formField.cols || 30\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n ></textarea>\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n <div class=\"form-info-row\">\n @if (\n formGroup.get(formField.name)?.invalid &&\n (formGroup.get(formField.name)?.touched ||\n formGroup.get(formField.name)?.dirty)\n ) {\n <small\n class=\"p-error\"\n >\n {{ getErrorMessage() }}\n </small>\n }\n <div class=\"spacer\"></div>\n @if (!formField.disabled && formField.maxLength !== undefined) {\n <div\n class=\"character-counter\"\n >\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n </div>\n}\n", styles: [".form-field{position:relative;margin-bottom:1rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.form-info-row{display:flex;justify-content:space-between;align-items:center;margin-top:.5rem}.spacer{flex-grow:1}.character-counter{font-size:.8rem;color:#888;text-align:right;margin-left:auto}.p-error{font-size:.8rem;color:#f44336}\n"] }]
3515
+ args: [{ selector: 'pt-text-area-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-text-area-input/pt-text-area-input.component.html -->\n\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n }\"\n>\n @if (formField.label) {\n <label [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-inputGroup class=\"textarea-input-group\">\n @if (!formField.iconPosition || formField.iconPosition === \"left\") {\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n\n <textarea\n class=\"textarea-control\"\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></textarea>\n }\n\n @if (formField.iconPosition === \"right\") {\n <textarea\n class=\"textarea-control\"\n [id]=\"inputId\"\n pTextarea\n [formControlName]=\"formField.name\"\n [rows]=\"formField.rows || 5\"\n [autoResize]=\"formField.autoResize || false\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [placeholder]=\"formField.placeholder || ''\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n ></textarea>\n\n @if (formField.iconClass) {\n <p-inputGroupAddon>\n <i [ngClass]=\"formField.iconClass\"></i>\n </p-inputGroupAddon>\n }\n }\n </p-inputGroup>\n\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"p-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (!formField.disabled && formField.maxLength !== undefined) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}:host *,:host *:before,:host *:after{box-sizing:border-box}.form-field{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.form-field label{display:block;width:100%;margin-bottom:.5rem;font-size:1rem;font-weight:700}.textarea-input-group{display:flex;width:100%;min-width:0;max-width:100%}:host ::ng-deep .textarea-input-group.p-inputgroup,:host ::ng-deep .textarea-input-group .p-inputgroup{display:flex;width:100%;min-width:0;max-width:100%}.textarea-control{display:block;flex:1 1 auto;width:100%;min-width:0;max-width:100%;resize:vertical}:host ::ng-deep .textarea-control.p-textarea,:host ::ng-deep textarea.textarea-control{display:block;flex:1 1 auto;width:100%!important;min-width:0;max-width:100%}:host ::ng-deep .p-inputgroup textarea{flex:1 1 auto;width:100%!important;min-width:0}:host ::ng-deep .p-inputgroup-addon{flex:0 0 auto}.form-info-row{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem;width:100%;min-width:0;margin-top:.5rem}.spacer{flex:1 1 auto;min-width:0}.character-counter{flex:0 0 auto;margin-left:auto;color:#888;font-size:.8rem;text-align:right;white-space:nowrap}.p-error{min-width:0;color:#f44336;font-size:.8rem;overflow-wrap:anywhere}\n"] }]
3048
3516
  }], propDecorators: { formGroup: [{
3049
3517
  type: Input
3050
3518
  }], formField: [{
3051
3519
  type: Input
3052
3520
  }] } });
3053
3521
 
3522
+ // projects/ng-prime-tools/src/lib/pt-text-input/pt-text-input.component.ts
3054
3523
  class PTTextInputComponent {
3055
3524
  constructor() {
3056
3525
  this.characterCount = 0;
@@ -3059,38 +3528,27 @@ class PTTextInputComponent {
3059
3528
  this.setupControl();
3060
3529
  this.updateCharacterCount();
3061
3530
  }
3531
+ ngOnChanges(changes) {
3532
+ if (changes['formGroup'] || changes['formField']) {
3533
+ this.setupControl();
3534
+ this.updateCharacterCount();
3535
+ }
3536
+ }
3062
3537
  ngOnDestroy() {
3063
3538
  this.valueChangesSubscription?.unsubscribe();
3064
3539
  }
3065
3540
  get inputId() {
3066
3541
  return `pt-input-${this.formField?.name || 'field'}`;
3067
3542
  }
3068
- setupControl() {
3069
- const control = this.formGroup.get(this.formField.name);
3070
- if (!control) {
3071
- return;
3072
- }
3073
- control.setValidators(this.getValidators());
3074
- this.valueChangesSubscription?.unsubscribe();
3075
- if (this.formField.disabled) {
3076
- control.disable({ emitEvent: false });
3077
- }
3078
- else {
3079
- control.enable({ emitEvent: false });
3080
- this.valueChangesSubscription = control.valueChanges.subscribe(() => {
3081
- this.updateCharacterCount();
3082
- });
3083
- }
3084
- control.updateValueAndValidity({ emitEvent: false });
3543
+ get errorId() {
3544
+ return `pt-input-error-${this.formField?.name || 'field'}`;
3085
3545
  }
3086
- updateCharacterCount() {
3087
- const control = this.formGroup.get(this.formField.name);
3088
- if (!control) {
3089
- this.characterCount = 0;
3090
- return;
3091
- }
3092
- const value = control.value;
3093
- this.characterCount = value != null ? String(value).length : 0;
3546
+ get isInvalid() {
3547
+ const control = this.formGroup?.get(this.formField?.name);
3548
+ return Boolean(control &&
3549
+ control.enabled &&
3550
+ control.invalid &&
3551
+ (control.touched || control.dirty));
3094
3552
  }
3095
3553
  getInputType() {
3096
3554
  switch (this.formField.type) {
@@ -3113,15 +3571,100 @@ class PTTextInputComponent {
3113
3571
  getPasswordFeedback() {
3114
3572
  return this.formField.feedback ?? false;
3115
3573
  }
3574
+ hasCharacterCounter() {
3575
+ return !this.formField.disabled && this.formField.maxLength !== undefined;
3576
+ }
3577
+ hasInfoRow() {
3578
+ return this.isInvalid || this.hasCharacterCounter();
3579
+ }
3580
+ getErrorMessage() {
3581
+ const control = this.formGroup?.get(this.formField?.name);
3582
+ if (!control?.errors) {
3583
+ return '';
3584
+ }
3585
+ const errorKey = this.resolveFirstErrorKey(control.errors);
3586
+ if (!errorKey) {
3587
+ return '';
3588
+ }
3589
+ const customMessage = this.formField.validationMessages?.[errorKey];
3590
+ if (customMessage) {
3591
+ return customMessage;
3592
+ }
3593
+ if (this.formField.errorText) {
3594
+ return this.formField.errorText;
3595
+ }
3596
+ const label = this.formField.label?.trim() || this.formField.name;
3597
+ switch (errorKey) {
3598
+ case 'required':
3599
+ return `${label} is required`;
3600
+ case 'email':
3601
+ return `${label} is not a valid email`;
3602
+ case 'minlength': {
3603
+ const requiredLength = control.getError('minlength')?.requiredLength ??
3604
+ this.formField.minLength;
3605
+ return `${label} must be at least ${requiredLength} characters`;
3606
+ }
3607
+ case 'maxlength': {
3608
+ const requiredLength = control.getError('maxlength')?.requiredLength ??
3609
+ this.formField.maxLength;
3610
+ return `${label} must be at most ${requiredLength} characters`;
3611
+ }
3612
+ case 'pattern':
3613
+ return `${label} is invalid`;
3614
+ default:
3615
+ return `${label} is invalid`;
3616
+ }
3617
+ }
3618
+ setupControl() {
3619
+ const name = this.formField?.name;
3620
+ if (!name || !this.formGroup) {
3621
+ return;
3622
+ }
3623
+ let control = this.formGroup.get(name);
3624
+ if (!control) {
3625
+ control = new FormControl(this.formField.value ?? null);
3626
+ this.formGroup.addControl(name, control);
3627
+ }
3628
+ control.setValidators(this.getValidators());
3629
+ if (this.formField.disabled) {
3630
+ control.disable({
3631
+ emitEvent: false,
3632
+ });
3633
+ }
3634
+ else {
3635
+ control.enable({
3636
+ emitEvent: false,
3637
+ });
3638
+ }
3639
+ control.updateValueAndValidity({
3640
+ emitEvent: false,
3641
+ });
3642
+ this.valueChangesSubscription?.unsubscribe();
3643
+ this.valueChangesSubscription = control.valueChanges.subscribe(() => {
3644
+ this.updateCharacterCount();
3645
+ });
3646
+ }
3647
+ updateCharacterCount() {
3648
+ const control = this.formGroup?.get(this.formField?.name);
3649
+ if (!control) {
3650
+ this.characterCount = 0;
3651
+ return;
3652
+ }
3653
+ const value = control.value;
3654
+ this.characterCount =
3655
+ value === null || value === undefined ? 0 : String(value).length;
3656
+ }
3116
3657
  getValidators() {
3117
3658
  const validators = [];
3118
3659
  if (this.formField.required) {
3119
3660
  validators.push(Validators.required);
3120
3661
  }
3121
- if (this.formField.minLength !== undefined) {
3662
+ if (this.formField.minLength !== undefined &&
3663
+ this.formField.minLength !== null) {
3122
3664
  validators.push(Validators.minLength(this.formField.minLength));
3123
3665
  }
3124
- if (this.formField.maxLength !== undefined) {
3666
+ if (this.formField.maxLength !== undefined &&
3667
+ this.formField.maxLength !== null) {
3125
3668
  validators.push(Validators.maxLength(this.formField.maxLength));
3126
3669
  }
3127
3670
  if (this.formField.type === FormInputTypeEnum.EMAIL) {
@@ -3130,46 +3673,29 @@ class PTTextInputComponent {
3130
3673
  if (this.formField.inputValidation) {
3131
3674
  validators.push(this.validateWithInputValidation(this.formField.inputValidation));
3132
3675
  }
3676
+ else if (this.formField.pattern) {
3677
+ validators.push(Validators.pattern(this.formField.pattern));
3678
+ }
3679
+ if (Array.isArray(this.formField.validators) &&
3680
+ this.formField.validators.length > 0) {
3681
+ validators.push(...this.formField.validators);
3682
+ }
3133
3683
  return validators;
3134
3684
  }
3135
3685
  validateWithInputValidation(inputValidation) {
3136
3686
  return Validators.pattern(new RegExp(inputValidation));
3137
3687
  }
3138
- getErrorMessage() {
3139
- const control = this.formGroup.get(this.formField.name);
3140
- if (control?.hasError('required')) {
3141
- return `${this.formField.label} is required`;
3142
- }
3143
- if (control?.hasError('email')) {
3144
- return `${this.formField.label} is not a valid email`;
3145
- }
3146
- if (control?.hasError('minlength')) {
3147
- return `${this.formField.label} must be at least ${this.formField.minLength} characters`;
3148
- }
3149
- if (control?.hasError('maxlength')) {
3150
- return `${this.formField.label} must be at most ${this.formField.maxLength} characters`;
3151
- }
3152
- if (control?.hasError('pattern')) {
3153
- return `${this.formField.label} is invalid`;
3154
- }
3155
- return '';
3156
- }
3157
- hasError() {
3158
- const control = this.formGroup.get(this.formField.name);
3159
- return !!(control?.invalid && (control.touched || control.dirty));
3160
- }
3161
- hasCharacterCounter() {
3162
- return !this.formField.disabled && this.formField.maxLength !== undefined;
3163
- }
3164
- hasInfoRow() {
3165
- return this.hasError() || this.hasCharacterCounter();
3688
+ resolveFirstErrorKey(errors) {
3689
+ const priority = ['required', 'email', 'minlength', 'maxlength', 'pattern'];
3690
+ return (priority.find((key) => errors[key] !== undefined) ||
3691
+ Object.keys(errors)[0]);
3166
3692
  }
3167
3693
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTTextInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3168
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTTextInputComponent, isStandalone: false, selector: "pt-text-input", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField [iconPosition]=\"formField.iconPosition || 'left'\">\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n pInputText\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n </p-iconField>\n } @else {\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n pInputText\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (hasError()) {\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}.form-field input.ng-invalid.ng-touched,.form-field input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input{width:100%!important;display:block!important}::ng-deep .pt-password-input .p-password{width:100%!important;display:block!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{width:100%!important;box-sizing:border-box}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i10.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i11.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "component", type: i6$1.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }] }); }
3694
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTTextInputComponent, isStandalone: false, selector: "pt-text-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-text-input/pt-text-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField\n class=\"text-icon-field\"\n [iconPosition]=\"formField.iconPosition || 'left'\"\n >\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n pInputText\n class=\"pt-text-input\"\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [ngStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n </p-iconField>\n } @else {\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n pInputText\n class=\"pt-text-input\"\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [ngStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"field-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{width:100%;min-width:0}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.required-indicator{margin-left:.2rem;color:#dc2626}.text-icon-field{display:block;width:100%;min-width:0}.pt-text-input{width:100%;min-width:0;box-sizing:border-box}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;min-width:0;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2;overflow-wrap:anywhere}.character-counter{flex:0 0 auto;color:#64748b;font-size:.8rem;font-weight:500;white-space:nowrap}.form-field-invalid .pt-text-input{border-color:#dc2626}:host ::ng-deep .text-icon-field.p-iconfield,:host ::ng-deep .text-icon-field .p-iconfield{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input.p-password,:host ::ng-deep .pt-password-input .p-password{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input .p-password-input,:host ::ng-deep .pt-password-input input{width:100%;min-width:0;box-sizing:border-box}.form-field-invalid ::ng-deep .pt-password-input .p-password-input{border-color:#dc2626}:host ::ng-deep .pt-password-input .p-password-toggle-mask-icon,:host ::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i11.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i12.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "component", type: i6$1.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }] }); }
3169
3695
  }
3170
3696
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTTextInputComponent, decorators: [{
3171
3697
  type: Component,
3172
- args: [{ selector: 'pt-text-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField [iconPosition]=\"formField.iconPosition || 'left'\">\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n pInputText\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n </p-iconField>\n } @else {\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n pInputText\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (hasError()) {\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}.form-field input.ng-invalid.ng-touched,.form-field input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input{width:100%!important;display:block!important}::ng-deep .pt-password-input .p-password{width:100%!important;display:block!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{width:100%!important;box-sizing:border-box}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"] }]
3698
+ args: [{ selector: 'pt-text-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-text-input/pt-text-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField\n class=\"text-icon-field\"\n [iconPosition]=\"formField.iconPosition || 'left'\"\n >\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n pInputText\n class=\"pt-text-input\"\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [ngStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n </p-iconField>\n } @else {\n @if (isPasswordInput()) {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n } @else {\n <input\n pInputText\n class=\"pt-text-input\"\n [id]=\"inputId\"\n [attr.name]=\"formField.name\"\n [type]=\"getInputType()\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [attr.minlength]=\"formField.minLength\"\n [attr.maxlength]=\"formField.maxLength\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [ngStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n />\n }\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"field-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{width:100%;min-width:0}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.required-indicator{margin-left:.2rem;color:#dc2626}.text-icon-field{display:block;width:100%;min-width:0}.pt-text-input{width:100%;min-width:0;box-sizing:border-box}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;min-width:0;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2;overflow-wrap:anywhere}.character-counter{flex:0 0 auto;color:#64748b;font-size:.8rem;font-weight:500;white-space:nowrap}.form-field-invalid .pt-text-input{border-color:#dc2626}:host ::ng-deep .text-icon-field.p-iconfield,:host ::ng-deep .text-icon-field .p-iconfield{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input.p-password,:host ::ng-deep .pt-password-input .p-password{display:block;width:100%;min-width:0}:host ::ng-deep .pt-password-input .p-password-input,:host ::ng-deep .pt-password-input input{width:100%;min-width:0;box-sizing:border-box}.form-field-invalid ::ng-deep .pt-password-input .p-password-input{border-color:#dc2626}:host ::ng-deep .pt-password-input .p-password-toggle-mask-icon,:host ::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"] }]
3173
3699
  }], propDecorators: { formGroup: [{
3174
3700
  type: Input
3175
3701
  }], formField: [{
@@ -3213,7 +3739,11 @@ class PTDropdownComponent {
3213
3739
  return `pt-dropdown-label-${this.controlName}`;
3214
3740
  }
3215
3741
  get resolvedHidden() {
3216
- return this.formField?.hidden ?? this.config?.hidden ?? false;
3742
+ const hidden = this.formField?.hidden;
3743
+ if (typeof hidden === 'function') {
3744
+ return Boolean(hidden(this.activeFormGroup.getRawValue()));
3745
+ }
3746
+ return hidden ?? this.config?.hidden ?? false;
3217
3747
  }
3218
3748
  get resolvedLabel() {
3219
3749
  return this.formField?.label || this.config?.label;
@@ -3425,11 +3955,11 @@ class PTDropdownComponent {
3425
3955
  return 'left';
3426
3956
  }
3427
3957
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3428
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDropdownComponent, isStandalone: false, selector: "pt-dropdown", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [id]=\"labelId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-select\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [optionDisabled]=\"resolvedOptionDisabled\"\n [dataKey]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{ baseZIndex: 11000 }\"\n [ariaLabelledBy]=\"labelId\"\n >\n <ng-template pTemplate=\"selectedItem\" let-option>\n @if (option) {\n <div class=\"pt-dropdown-item\">\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n } @else {\n <span>{{ resolvedPlaceholder }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-option>\n <div\n class=\"pt-dropdown-item\"\n [class.pt-dropdown-parent-option]=\"option?.isParent\"\n [class.pt-dropdown-child-option]=\"option?.isChild\"\n [class.pt-dropdown-disabled-option]=\"option?.disabled\"\n [ngStyle]=\"{ 'padding-left': option?.indent || '0rem' }\"\n >\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n </ng-template>\n </p-select>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.55rem}.pt-dropdown-label{display:inline-block}.pt-dropdown-parent-option{font-weight:700}.pt-dropdown-child-option{font-weight:400}.pt-dropdown-disabled-option{opacity:.65;cursor:not-allowed}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;text-align:center}.pt-dropdown-icon-image{object-fit:cover}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-select.ng-invalid.ng-touched{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }] }); }
3958
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDropdownComponent, isStandalone: false, selector: "pt-dropdown", inputs: { formGroup: "formGroup", formField: "formField", config: "config", value: "value" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (!resolvedHidden) {\n <div\n class=\"form-field\"\n [formGroup]=\"activeFormGroup\"\n [ngClass]=\"{\n 'form-field-invalid':\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty),\n }\"\n [ngStyle]=\"{\n width: resolvedWidth,\n 'max-width': resolvedWidth,\n }\"\n >\n @if (resolvedLabel) {\n <label class=\"field-label\" [id]=\"labelId\" [for]=\"inputId\">\n {{ resolvedLabel }}\n\n @if (resolvedRequired) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-select\n class=\"dropdown-input\"\n styleClass=\"dropdown-input-control\"\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [optionDisabled]=\"resolvedOptionDisabled\"\n [dataKey]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{\n baseZIndex: 11000,\n }\"\n [ariaLabelledBy]=\"resolvedLabel ? labelId : undefined\"\n [attr.aria-describedby]=\"\n activeFormGroup.get(controlName)?.invalid\n ? controlName + '-error'\n : null\n \"\n [attr.aria-invalid]=\"\n activeFormGroup.get(controlName)?.invalid ? 'true' : 'false'\n \"\n >\n <ng-template pTemplate=\"selectedItem\" let-option>\n @if (option) {\n <div class=\"pt-dropdown-item\">\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n class=\"pt-dropdown-icon-image\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n alt=\"\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n class=\"pt-dropdown-icon-font\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n } @else {\n <span class=\"pt-dropdown-placeholder\">\n {{ resolvedPlaceholder }}\n </span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-option>\n <div\n class=\"pt-dropdown-item\"\n [class.pt-dropdown-parent-option]=\"option?.isParent\"\n [class.pt-dropdown-child-option]=\"option?.isChild\"\n [class.pt-dropdown-disabled-option]=\"option?.[resolvedOptionDisabled]\"\n [ngStyle]=\"{\n 'padding-left': option?.indent || '0rem',\n }\"\n >\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n class=\"pt-dropdown-icon-image\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n alt=\"\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n class=\"pt-dropdown-icon-font\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n </ng-template>\n </p-select>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\" role=\"alert\" [id]=\"controlName + '-error'\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.55rem}.pt-dropdown-label{display:inline-block}.pt-dropdown-parent-option{font-weight:700}.pt-dropdown-child-option{font-weight:400}.pt-dropdown-disabled-option{opacity:.65;cursor:not-allowed}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;text-align:center}.pt-dropdown-icon-image{object-fit:cover}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-select.ng-invalid.ng-touched{border-color:#dc2626}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }] }); }
3429
3959
  }
3430
3960
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDropdownComponent, decorators: [{
3431
3961
  type: Component,
3432
- args: [{ selector: 'pt-dropdown', standalone: false, template: "@if (!resolvedHidden) {\n <div\n [formGroup]=\"activeFormGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: resolvedWidth }\"\n >\n @if (resolvedLabel) {\n <label [id]=\"labelId\" class=\"field-label\">\n {{ resolvedLabel }}\n </label>\n }\n\n <p-select\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [optionDisabled]=\"resolvedOptionDisabled\"\n [dataKey]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{ baseZIndex: 11000 }\"\n [ariaLabelledBy]=\"labelId\"\n >\n <ng-template pTemplate=\"selectedItem\" let-option>\n @if (option) {\n <div class=\"pt-dropdown-item\">\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n } @else {\n <span>{{ resolvedPlaceholder }}</span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-option>\n <div\n class=\"pt-dropdown-item\"\n [class.pt-dropdown-parent-option]=\"option?.isParent\"\n [class.pt-dropdown-child-option]=\"option?.isChild\"\n [class.pt-dropdown-disabled-option]=\"option?.disabled\"\n [ngStyle]=\"{ 'padding-left': option?.indent || '0rem' }\"\n >\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n </ng-template>\n </p-select>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\">\n <small class=\"field-error\">{{ getErrorMessage() }}</small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.55rem}.pt-dropdown-label{display:inline-block}.pt-dropdown-parent-option{font-weight:700}.pt-dropdown-child-option{font-weight:400}.pt-dropdown-disabled-option{opacity:.65;cursor:not-allowed}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;text-align:center}.pt-dropdown-icon-image{object-fit:cover}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-select.ng-invalid.ng-touched{border-color:#dc2626}\n"] }]
3962
+ args: [{ selector: 'pt-dropdown', standalone: false, template: "@if (!resolvedHidden) {\n <div\n class=\"form-field\"\n [formGroup]=\"activeFormGroup\"\n [ngClass]=\"{\n 'form-field-invalid':\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty),\n }\"\n [ngStyle]=\"{\n width: resolvedWidth,\n 'max-width': resolvedWidth,\n }\"\n >\n @if (resolvedLabel) {\n <label class=\"field-label\" [id]=\"labelId\" [for]=\"inputId\">\n {{ resolvedLabel }}\n\n @if (resolvedRequired) {\n <span class=\"required-indicator\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <p-select\n class=\"dropdown-input\"\n styleClass=\"dropdown-input-control\"\n [inputId]=\"inputId\"\n [name]=\"controlName\"\n [formControlName]=\"controlName\"\n [options]=\"resolvedOptions\"\n [placeholder]=\"resolvedPlaceholder\"\n [optionLabel]=\"resolvedOptionLabel\"\n [optionValue]=\"resolvedOptionValue\"\n [optionDisabled]=\"resolvedOptionDisabled\"\n [dataKey]=\"resolvedOptionValue\"\n [filter]=\"resolvedFilter\"\n [filterBy]=\"resolvedFilterBy\"\n [appendTo]=\"'body'\"\n [overlayOptions]=\"{\n baseZIndex: 11000,\n }\"\n [ariaLabelledBy]=\"resolvedLabel ? labelId : undefined\"\n [attr.aria-describedby]=\"\n activeFormGroup.get(controlName)?.invalid\n ? controlName + '-error'\n : null\n \"\n [attr.aria-invalid]=\"\n activeFormGroup.get(controlName)?.invalid ? 'true' : 'false'\n \"\n >\n <ng-template pTemplate=\"selectedItem\" let-option>\n @if (option) {\n <div class=\"pt-dropdown-item\">\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n class=\"pt-dropdown-icon-image\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n alt=\"\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n class=\"pt-dropdown-icon-font\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n } @else {\n <span class=\"pt-dropdown-placeholder\">\n {{ resolvedPlaceholder }}\n </span>\n }\n </ng-template>\n\n <ng-template pTemplate=\"item\" let-option>\n <div\n class=\"pt-dropdown-item\"\n [class.pt-dropdown-parent-option]=\"option?.isParent\"\n [class.pt-dropdown-child-option]=\"option?.isChild\"\n [class.pt-dropdown-disabled-option]=\"option?.[resolvedOptionDisabled]\"\n [ngStyle]=\"{\n 'padding-left': option?.indent || '0rem',\n }\"\n >\n @if (getResolvedIcon(option); as iconMeta) {\n @if (iconMeta.type === \"image\") {\n <img\n class=\"pt-dropdown-icon-image\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n alt=\"\"\n />\n }\n\n @if (iconMeta.type === \"font\") {\n <i\n class=\"pt-dropdown-icon-font\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n\n <span class=\"pt-dropdown-label\">\n {{ option[resolvedOptionLabel] }}\n </span>\n </div>\n </ng-template>\n </p-select>\n\n @if (\n activeFormGroup.get(controlName)?.invalid &&\n (activeFormGroup.get(controlName)?.touched ||\n activeFormGroup.get(controlName)?.dirty)\n ) {\n <div class=\"error-container\" role=\"alert\" [id]=\"controlName + '-error'\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.field-label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.55rem}.pt-dropdown-label{display:inline-block}.pt-dropdown-parent-option{font-weight:700}.pt-dropdown-child-option{font-weight:400}.pt-dropdown-disabled-option{opacity:.65;cursor:not-allowed}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;text-align:center}.pt-dropdown-icon-image{object-fit:cover}.error-container{margin-top:.35rem}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.form-field ::ng-deep .p-select.ng-invalid.ng-touched{border-color:#dc2626}\n"] }]
3433
3963
  }], propDecorators: { formGroup: [{
3434
3964
  type: Input
3435
3965
  }], formField: [{
@@ -3542,18 +4072,25 @@ class PTOtpInputComponent {
3542
4072
  control.setValue(normalizedValue, { emitEvent: false });
3543
4073
  }
3544
4074
  }
4075
+ get labelId() {
4076
+ return `pt-otp-label-${this.formField?.name || 'field'}`;
4077
+ }
4078
+ get errorId() {
4079
+ return `pt-otp-error-${this.formField?.name || 'field'}`;
4080
+ }
3545
4081
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTOtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3546
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTOtpInputComponent, isStandalone: false, selector: "pt-otp-input", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n <div class=\"otp-input-wrapper\">\n <p-inputOtp\n [formControlName]=\"formField.name\"\n [length]=\"resolvedLength\"\n [integerOnly]=\"resolvedIntegerOnly\"\n [mask]=\"resolvedMask\"\n [readonly]=\"formField.disabled ?? false\"\n styleClass=\"pt-otp-input\"\n ></p-inputOtp>\n </div>\n\n @if (hasError()) {\n <div class=\"form-info-row\">\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.85rem;color:#172554;font-size:1.1rem;font-weight:700;letter-spacing:.01em}.otp-input-wrapper{display:flex;width:100%;justify-content:center}.form-info-row{display:flex;min-height:1.35rem;margin-top:.65rem;justify-content:center}.field-error{color:#fca5a5;font-size:.82rem;font-weight:600;text-align:center}:host ::ng-deep .pt-otp-input{display:flex;width:100%;justify-content:center;gap:.7rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:3.7rem;height:4rem;border:2px solid rgba(255,255,255,.62);border-radius:.85rem;background:#fffffff0;box-shadow:0 6px 14px #0f172a29,inset 0 1px #ffffffe6;color:#172554;font-size:1.75rem;font-weight:800;text-align:center;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}:host ::ng-deep .pt-otp-input .p-inputotp-input:hover{border-color:#60a5fa;transform:translateY(-2px)}:host ::ng-deep .pt-otp-input .p-inputotp-input:focus{border-color:#2563eb;box-shadow:0 0 0 4px #2563eb33,0 8px 18px #0f172a33;outline:none}@media(max-width:480px){:host ::ng-deep .pt-otp-input{gap:.42rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:2.85rem;height:3.3rem;font-size:1.4rem}}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$6.InputOtp, selector: "p-inputOtp, p-inputotp, p-input-otp", inputs: ["readonly", "tabindex", "length", "styleClass", "mask", "integerOnly", "autofocus", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }] }); }
4082
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTOtpInputComponent, isStandalone: false, selector: "pt-otp-input", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-otp-input/pt-otp-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': hasError(),\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [id]=\"labelId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n <div class=\"otp-input-wrapper\">\n <p-inputOtp\n [formControlName]=\"formField.name\"\n [length]=\"resolvedLength\"\n [integerOnly]=\"resolvedIntegerOnly\"\n [mask]=\"resolvedMask\"\n [readonly]=\"formField.disabled ?? false\"\n [attr.aria-labelledby]=\"formField.label ? labelId : null\"\n [attr.aria-describedby]=\"hasError() ? errorId : null\"\n [attr.aria-invalid]=\"hasError() ? 'true' : 'false'\"\n styleClass=\"pt-otp-input\"\n ></p-inputOtp>\n </div>\n\n @if (hasError()) {\n <div class=\"form-info-row\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{width:100%;min-width:0}.field-label{display:block;margin-bottom:.85rem;color:#172554;font-size:1.1rem;font-weight:700;letter-spacing:.01em}.required-indicator{margin-left:.2rem;color:#ef4444}.otp-input-wrapper{display:flex;width:100%;min-width:0;justify-content:center}.form-info-row{display:flex;align-items:center;min-height:1.35rem;margin-top:.65rem;justify-content:center;gap:.35rem;color:#fca5a5}.form-info-row .pi{font-size:.8rem}.field-error{color:inherit;font-size:.82rem;font-weight:600;text-align:center}:host ::ng-deep .pt-otp-input{display:flex;width:100%;min-width:0;justify-content:center;gap:.7rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:3.7rem;height:4rem;border:2px solid rgba(255,255,255,.62);border-radius:.85rem;background:#fffffff0;box-shadow:0 6px 14px #0f172a29,inset 0 1px #ffffffe6;color:#172554;font-size:1.75rem;font-weight:800;text-align:center;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}:host ::ng-deep .pt-otp-input .p-inputotp-input:hover{border-color:#60a5fa;transform:translateY(-2px)}:host ::ng-deep .pt-otp-input .p-inputotp-input:focus{border-color:#2563eb;box-shadow:0 0 0 4px #2563eb33,0 8px 18px #0f172a33;outline:none}.form-field-invalid ::ng-deep .pt-otp-input .p-inputotp-input{border-color:#ef4444}@media(max-width:480px){:host ::ng-deep .pt-otp-input{gap:.42rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:2.85rem;height:3.3rem;font-size:1.4rem}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$6.InputOtp, selector: "p-inputOtp, p-inputotp, p-input-otp", inputs: ["readonly", "tabindex", "length", "styleClass", "mask", "integerOnly", "autofocus", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }] }); }
3547
4083
  }
3548
4084
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTOtpInputComponent, decorators: [{
3549
4085
  type: Component,
3550
- args: [{ selector: 'pt-otp-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n <div class=\"otp-input-wrapper\">\n <p-inputOtp\n [formControlName]=\"formField.name\"\n [length]=\"resolvedLength\"\n [integerOnly]=\"resolvedIntegerOnly\"\n [mask]=\"resolvedMask\"\n [readonly]=\"formField.disabled ?? false\"\n styleClass=\"pt-otp-input\"\n ></p-inputOtp>\n </div>\n\n @if (hasError()) {\n <div class=\"form-info-row\">\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.85rem;color:#172554;font-size:1.1rem;font-weight:700;letter-spacing:.01em}.otp-input-wrapper{display:flex;width:100%;justify-content:center}.form-info-row{display:flex;min-height:1.35rem;margin-top:.65rem;justify-content:center}.field-error{color:#fca5a5;font-size:.82rem;font-weight:600;text-align:center}:host ::ng-deep .pt-otp-input{display:flex;width:100%;justify-content:center;gap:.7rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:3.7rem;height:4rem;border:2px solid rgba(255,255,255,.62);border-radius:.85rem;background:#fffffff0;box-shadow:0 6px 14px #0f172a29,inset 0 1px #ffffffe6;color:#172554;font-size:1.75rem;font-weight:800;text-align:center;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}:host ::ng-deep .pt-otp-input .p-inputotp-input:hover{border-color:#60a5fa;transform:translateY(-2px)}:host ::ng-deep .pt-otp-input .p-inputotp-input:focus{border-color:#2563eb;box-shadow:0 0 0 4px #2563eb33,0 8px 18px #0f172a33;outline:none}@media(max-width:480px){:host ::ng-deep .pt-otp-input{gap:.42rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:2.85rem;height:3.3rem;font-size:1.4rem}}\n"] }]
4086
+ args: [{ selector: 'pt-otp-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-otp-input/pt-otp-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': hasError(),\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [id]=\"labelId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n <div class=\"otp-input-wrapper\">\n <p-inputOtp\n [formControlName]=\"formField.name\"\n [length]=\"resolvedLength\"\n [integerOnly]=\"resolvedIntegerOnly\"\n [mask]=\"resolvedMask\"\n [readonly]=\"formField.disabled ?? false\"\n [attr.aria-labelledby]=\"formField.label ? labelId : null\"\n [attr.aria-describedby]=\"hasError() ? errorId : null\"\n [attr.aria-invalid]=\"hasError() ? 'true' : 'false'\"\n styleClass=\"pt-otp-input\"\n ></p-inputOtp>\n </div>\n\n @if (hasError()) {\n <div class=\"form-info-row\" role=\"alert\" [id]=\"errorId\">\n <i class=\"pi pi-exclamation-circle\" aria-hidden=\"true\"></i>\n\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0}.form-field{width:100%;min-width:0}.field-label{display:block;margin-bottom:.85rem;color:#172554;font-size:1.1rem;font-weight:700;letter-spacing:.01em}.required-indicator{margin-left:.2rem;color:#ef4444}.otp-input-wrapper{display:flex;width:100%;min-width:0;justify-content:center}.form-info-row{display:flex;align-items:center;min-height:1.35rem;margin-top:.65rem;justify-content:center;gap:.35rem;color:#fca5a5}.form-info-row .pi{font-size:.8rem}.field-error{color:inherit;font-size:.82rem;font-weight:600;text-align:center}:host ::ng-deep .pt-otp-input{display:flex;width:100%;min-width:0;justify-content:center;gap:.7rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:3.7rem;height:4rem;border:2px solid rgba(255,255,255,.62);border-radius:.85rem;background:#fffffff0;box-shadow:0 6px 14px #0f172a29,inset 0 1px #ffffffe6;color:#172554;font-size:1.75rem;font-weight:800;text-align:center;transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}:host ::ng-deep .pt-otp-input .p-inputotp-input:hover{border-color:#60a5fa;transform:translateY(-2px)}:host ::ng-deep .pt-otp-input .p-inputotp-input:focus{border-color:#2563eb;box-shadow:0 0 0 4px #2563eb33,0 8px 18px #0f172a33;outline:none}.form-field-invalid ::ng-deep .pt-otp-input .p-inputotp-input{border-color:#ef4444}@media(max-width:480px){:host ::ng-deep .pt-otp-input{gap:.42rem}:host ::ng-deep .pt-otp-input .p-inputotp-input{width:2.85rem;height:3.3rem;font-size:1.4rem}}\n"] }]
3551
4087
  }], propDecorators: { formGroup: [{
3552
4088
  type: Input
3553
4089
  }], formField: [{
3554
4090
  type: Input
3555
4091
  }] } });
3556
4092
 
4093
+ // projects/ng-prime-tools/src/lib/pt-password-input/pt-password-input.component.ts
3557
4094
  class PTPasswordInputComponent {
3558
4095
  constructor() {
3559
4096
  this.characterCount = 0;
@@ -3562,96 +4099,154 @@ class PTPasswordInputComponent {
3562
4099
  this.setupControl();
3563
4100
  this.updateCharacterCount();
3564
4101
  }
4102
+ ngOnChanges(changes) {
4103
+ if (changes['formGroup'] || changes['formField']) {
4104
+ this.setupControl();
4105
+ this.updateCharacterCount();
4106
+ }
4107
+ }
3565
4108
  ngOnDestroy() {
3566
4109
  this.valueChangesSubscription?.unsubscribe();
3567
4110
  }
3568
4111
  get inputId() {
3569
4112
  return `pt-password-${this.formField?.name || 'field'}`;
3570
4113
  }
4114
+ get errorId() {
4115
+ return `pt-password-error-${this.formField?.name || 'field'}`;
4116
+ }
4117
+ get isInvalid() {
4118
+ const control = this.formGroup?.get(this.formField?.name);
4119
+ return Boolean(control &&
4120
+ control.enabled &&
4121
+ control.invalid &&
4122
+ (control.touched || control.dirty));
4123
+ }
4124
+ getPasswordToggleMask() {
4125
+ return this.formField?.toggleMask ?? true;
4126
+ }
4127
+ getPasswordFeedback() {
4128
+ return this.formField?.feedback ?? false;
4129
+ }
4130
+ hasCharacterCounter() {
4131
+ return !this.formField?.disabled && this.formField?.maxLength !== undefined;
4132
+ }
4133
+ hasInfoRow() {
4134
+ return this.isInvalid || this.hasCharacterCounter();
4135
+ }
4136
+ getErrorMessage() {
4137
+ const control = this.formGroup?.get(this.formField?.name);
4138
+ if (!control?.errors) {
4139
+ return '';
4140
+ }
4141
+ const errorKey = this.resolveFirstErrorKey(control.errors);
4142
+ if (!errorKey) {
4143
+ return '';
4144
+ }
4145
+ const customMessage = this.formField.validationMessages?.[errorKey];
4146
+ if (customMessage) {
4147
+ return customMessage;
4148
+ }
4149
+ if (this.formField.errorText) {
4150
+ return this.formField.errorText;
4151
+ }
4152
+ const label = this.formField.label?.trim() || this.formField.name;
4153
+ switch (errorKey) {
4154
+ case 'required':
4155
+ return `${label} is required`;
4156
+ case 'minlength': {
4157
+ const requiredLength = control.getError('minlength')?.requiredLength ??
4158
+ this.formField.minLength;
4159
+ return `${label} must be at least ${requiredLength} characters`;
4160
+ }
4161
+ case 'maxlength': {
4162
+ const requiredLength = control.getError('maxlength')?.requiredLength ??
4163
+ this.formField.maxLength;
4164
+ return `${label} must be at most ${requiredLength} characters`;
4165
+ }
4166
+ case 'pattern':
4167
+ return `${label} is invalid`;
4168
+ default:
4169
+ return `${label} is invalid`;
4170
+ }
4171
+ }
3571
4172
  setupControl() {
3572
- const control = this.formGroup.get(this.formField.name);
3573
- if (!control) {
4173
+ const name = this.formField?.name;
4174
+ if (!name || !this.formGroup) {
3574
4175
  return;
3575
4176
  }
4177
+ let control = this.formGroup.get(name);
4178
+ if (!control) {
4179
+ control = new FormControl(this.formField.value ?? null);
4180
+ this.formGroup.addControl(name, control);
4181
+ }
3576
4182
  control.setValidators(this.getValidators());
3577
- this.valueChangesSubscription?.unsubscribe();
3578
4183
  if (this.formField.disabled) {
3579
- control.disable({ emitEvent: false });
4184
+ control.disable({
4185
+ emitEvent: false,
4186
+ });
3580
4187
  }
3581
4188
  else {
3582
- control.enable({ emitEvent: false });
3583
- this.valueChangesSubscription = control.valueChanges.subscribe(() => {
3584
- this.updateCharacterCount();
4189
+ control.enable({
4190
+ emitEvent: false,
3585
4191
  });
3586
4192
  }
3587
- control.updateValueAndValidity({ emitEvent: false });
4193
+ control.updateValueAndValidity({
4194
+ emitEvent: false,
4195
+ });
4196
+ this.valueChangesSubscription?.unsubscribe();
4197
+ this.valueChangesSubscription = control.valueChanges.subscribe(() => {
4198
+ this.updateCharacterCount();
4199
+ });
3588
4200
  }
3589
4201
  updateCharacterCount() {
3590
- const control = this.formGroup.get(this.formField.name);
4202
+ const control = this.formGroup?.get(this.formField?.name);
3591
4203
  if (!control) {
3592
4204
  this.characterCount = 0;
3593
4205
  return;
3594
4206
  }
3595
4207
  const value = control.value;
3596
- this.characterCount = value != null ? String(value).length : 0;
3597
- }
3598
- getPasswordToggleMask() {
3599
- return this.formField.toggleMask ?? true;
3600
- }
3601
- getPasswordFeedback() {
3602
- return this.formField.feedback ?? false;
4208
+ this.characterCount =
4209
+ value === null || value === undefined ? 0 : String(value).length;
3603
4210
  }
3604
4211
  getValidators() {
3605
4212
  const validators = [];
3606
4213
  if (this.formField.required) {
3607
4214
  validators.push(Validators.required);
3608
4215
  }
3609
- if (this.formField.minLength !== undefined) {
4216
+ if (this.formField.minLength !== undefined &&
4217
+ this.formField.minLength !== null) {
3610
4218
  validators.push(Validators.minLength(this.formField.minLength));
3611
4219
  }
3612
- if (this.formField.maxLength !== undefined) {
4220
+ if (this.formField.maxLength !== undefined &&
4221
+ this.formField.maxLength !== null) {
3613
4222
  validators.push(Validators.maxLength(this.formField.maxLength));
3614
4223
  }
3615
4224
  if (this.formField.inputValidation) {
3616
4225
  validators.push(this.validateWithInputValidation(this.formField.inputValidation));
3617
- }
3618
- return validators;
3619
- }
3620
- validateWithInputValidation(inputValidation) {
3621
- return Validators.pattern(new RegExp(inputValidation));
3622
- }
3623
- getErrorMessage() {
3624
- const control = this.formGroup.get(this.formField.name);
3625
- if (control?.hasError('required')) {
3626
- return this.formField.errorText ?? `${this.formField.label} is required`;
3627
- }
3628
- if (control?.hasError('minlength')) {
3629
- return `${this.formField.label} must be at least ${this.formField.minLength} characters`;
3630
- }
3631
- if (control?.hasError('maxlength')) {
3632
- return `${this.formField.label} must be at most ${this.formField.maxLength} characters`;
3633
- }
3634
- if (control?.hasError('pattern')) {
3635
- return this.formField.errorText ?? `${this.formField.label} is invalid`;
3636
- }
3637
- return '';
3638
- }
3639
- hasError() {
3640
- const control = this.formGroup.get(this.formField.name);
3641
- return !!(control?.invalid && (control.touched || control.dirty));
4226
+ }
4227
+ else if (this.formField.pattern) {
4228
+ validators.push(Validators.pattern(this.formField.pattern));
4229
+ }
4230
+ if (Array.isArray(this.formField.validators) &&
4231
+ this.formField.validators.length > 0) {
4232
+ validators.push(...this.formField.validators);
4233
+ }
4234
+ return validators;
3642
4235
  }
3643
- hasCharacterCounter() {
3644
- return !this.formField.disabled && this.formField.maxLength !== undefined;
4236
+ validateWithInputValidation(inputValidation) {
4237
+ return Validators.pattern(new RegExp(inputValidation));
3645
4238
  }
3646
- hasInfoRow() {
3647
- return this.hasError() || this.hasCharacterCounter();
4239
+ resolveFirstErrorKey(errors) {
4240
+ const priority = ['required', 'minlength', 'maxlength', 'pattern'];
4241
+ return (priority.find((key) => errors[key] !== undefined) ||
4242
+ Object.keys(errors)[0]);
3648
4243
  }
3649
4244
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTPasswordInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3650
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTPasswordInputComponent, isStandalone: false, selector: "pt-password-input", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField [iconPosition]=\"formField.iconPosition || 'left'\">\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n </p-iconField>\n } @else {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (hasError()) {\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}::ng-deep .pt-password-input{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{box-sizing:border-box;width:100%!important}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i10.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i11.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "component", type: i6$1.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }] }); }
4245
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTPasswordInputComponent, isStandalone: false, selector: "pt-password-input", inputs: { formGroup: "formGroup", formField: "formField" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-password-input/pt-password-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField\n class=\"password-icon-field\"\n [iconPosition]=\"formField.iconPosition || 'left'\"\n >\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n </p-iconField>\n } @else {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"field-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}::ng-deep .pt-password-input{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{box-sizing:border-box;width:100%!important}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i11.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i12.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "component", type: i6$1.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }] }); }
3651
4246
  }
3652
4247
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTPasswordInputComponent, decorators: [{
3653
4248
  type: Component,
3654
- args: [{ selector: 'pt-password-input', standalone: false, template: "@if (!formField.hidden) {\n <div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n >\n @if (formField.label) {\n <label [for]=\"inputId\" class=\"field-label\">\n {{ formField.label }}\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField [iconPosition]=\"formField.iconPosition || 'left'\">\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n </p-iconField>\n } @else {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [inputStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (hasError()) {\n <small class=\"field-error\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}::ng-deep .pt-password-input{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{box-sizing:border-box;width:100%!important}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"] }]
4249
+ args: [{ selector: 'pt-password-input', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-password-input/pt-password-input.component.html -->\n\n<div\n class=\"form-field\"\n [formGroup]=\"formGroup\"\n [ngClass]=\"{\n 'form-field-invalid': isInvalid,\n }\"\n [ngStyle]=\"{\n width: formField.width || '100%',\n height: formField.height || 'auto',\n margin: formField.margin || null,\n }\"\n>\n @if (formField.label) {\n <label class=\"field-label\" [for]=\"inputId\">\n {{ formField.label }}\n\n @if (formField.required) {\n <span class=\"required-indicator\" aria-hidden=\"true\"> * </span>\n }\n </label>\n }\n\n @if (formField.iconClass) {\n <p-iconField\n class=\"password-icon-field\"\n [iconPosition]=\"formField.iconPosition || 'left'\"\n >\n <p-inputIcon [styleClass]=\"formField.iconClass\"></p-inputIcon>\n\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n </p-iconField>\n } @else {\n <p-password\n [inputId]=\"inputId\"\n [formControlName]=\"formField.name\"\n [placeholder]=\"formField.placeholder ?? ''\"\n [toggleMask]=\"getPasswordToggleMask()\"\n [feedback]=\"getPasswordFeedback()\"\n [attr.name]=\"formField.name\"\n [attr.aria-invalid]=\"isInvalid ? 'true' : 'false'\"\n [attr.aria-describedby]=\"isInvalid ? errorId : null\"\n [inputStyle]=\"{\n width: '100%',\n height: formField.height || 'auto',\n }\"\n styleClass=\"pt-password-input\"\n ></p-password>\n }\n\n @if (hasInfoRow()) {\n <div class=\"form-info-row\">\n @if (isInvalid) {\n <small class=\"field-error\" role=\"alert\" [id]=\"errorId\">\n {{ getErrorMessage() }}\n </small>\n }\n\n <div class=\"spacer\"></div>\n\n @if (hasCharacterCounter()) {\n <div class=\"character-counter\">\n {{ characterCount }}/{{ formField.maxLength }} characters\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.form-field{width:100%}.field-label{display:block;margin-bottom:.5rem;font-weight:700}.form-info-row{display:flex;align-items:center;gap:.5rem;min-height:1.25rem;margin-top:.35rem}.spacer{flex:1 1 auto}.field-error{display:block;color:#dc2626;font-size:.8rem;font-weight:500;line-height:1.2}.character-counter{font-size:.8rem;font-weight:500;color:#64748b;white-space:nowrap}::ng-deep .pt-password-input{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password{display:block!important;width:100%!important}::ng-deep .pt-password-input .p-password-input{width:100%!important}::ng-deep .pt-password-input input{box-sizing:border-box;width:100%!important}::ng-deep .pt-password-input.ng-invalid.ng-touched input,::ng-deep .pt-password-input.ng-invalid.ng-dirty input,::ng-deep .pt-password-input input.ng-invalid.ng-touched,::ng-deep .pt-password-input input.ng-invalid.ng-dirty{border-color:#dc2626}::ng-deep .pt-password-input .p-password-toggle-mask-icon,::ng-deep .pt-password-input .p-password-toggle-mask{right:1rem}\n"] }]
3655
4250
  }], propDecorators: { formGroup: [{
3656
4251
  type: Input
3657
4252
  }], formField: [{
@@ -3690,11 +4285,11 @@ class PTDynamicFormFieldComponent {
3690
4285
  return field;
3691
4286
  }
3692
4287
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDynamicFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3693
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDynamicFormFieldComponent, isStandalone: false, selector: "pt-dynamic-form-field", inputs: { field: "field", form: "form", inputWidth: "inputWidth" }, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-form-builder/pt-dynamic-form-field/pt-dynamic-form-field.component.html -->\n\n<div\n [formGroup]=\"form\"\n class=\"form-field\"\n [ngStyle]=\"{\n width:\n field.type !== FormInputTypeEnum.CHECKBOX\n ? field.width || inputWidth\n : 'auto',\n }\"\n>\n @switch (field.type) {\n <!-- TEXT -->\n @case (FormInputTypeEnum.TEXT) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- EMAIL -->\n @case (FormInputTypeEnum.EMAIL) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- PASSWORD -->\n @case (FormInputTypeEnum.PASSWORD) {\n <pt-password-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-password-input>\n }\n\n <!-- NUMBER -->\n @case (FormInputTypeEnum.NUMBER) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- AMOUNT -->\n @case (FormInputTypeEnum.AMOUNT) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- TEXTAREA -->\n @case (FormInputTypeEnum.TEXTAREA) {\n <pt-text-area-input\n [formGroup]=\"form\"\n [formField]=\"asTextAreaField(field)\"\n ></pt-text-area-input>\n }\n\n <!-- DATE -->\n @case (FormInputTypeEnum.DATE) {\n <pt-date-input\n [formGroup]=\"form\"\n [formField]=\"asDateField(field)\"\n ></pt-date-input>\n }\n\n <!-- MULTISELECT -->\n @case (FormInputTypeEnum.MULTISELECT) {\n <pt-multi-select\n [formGroup]=\"form\"\n [formField]=\"asMultiSelectField(field)\"\n ></pt-multi-select>\n }\n\n <!-- SELECT -->\n @case (FormInputTypeEnum.SELECT) {\n <pt-dropdown\n [formGroup]=\"form\"\n [formField]=\"asSelectField(field)\"\n ></pt-dropdown>\n }\n\n <!-- OTP -->\n @case (FormInputTypeEnum.OTP) {\n <pt-otp-input\n [formGroup]=\"form\"\n [formField]=\"asOtpField(field)\"\n ></pt-otp-input>\n }\n\n <!-- CHECKBOX -->\n @case (FormInputTypeEnum.CHECKBOX) {\n <pt-check-box-input\n [formGroup]=\"form\"\n [formField]=\"asCheckboxField(field)\"\n ></pt-check-box-input>\n }\n\n <!-- SWITCH -->\n @case (FormInputTypeEnum.SWITCH) {\n <pt-switch-input\n [formGroup]=\"form\"\n [formField]=\"asSwitchField(field)\"\n ></pt-switch-input>\n }\n }\n</div>\n", styles: [".form-field{margin-bottom:1rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: PTCheckBoxInputComponent, selector: "pt-check-box-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTDateInputComponent, selector: "pt-date-input", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "dateChange"] }, { kind: "component", type: PTNumberInputComponent, selector: "pt-number-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTSwitchInputComponent, selector: "pt-switch-input", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "switchChange"] }, { kind: "component", type: PTTextAreaInputComponent, selector: "pt-text-area-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTTextInputComponent, selector: "pt-text-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTDropdownComponent, selector: "pt-dropdown", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "selectionChange"] }, { kind: "component", type: PTMultiSelectComponent, selector: "pt-multi-select", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "selectionChange"] }, { kind: "component", type: PTOtpInputComponent, selector: "pt-otp-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTPasswordInputComponent, selector: "pt-password-input", inputs: ["formGroup", "formField"] }] }); }
4288
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTDynamicFormFieldComponent, isStandalone: false, selector: "pt-dynamic-form-field", inputs: { field: "field", form: "form", inputWidth: "inputWidth" }, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/pt-form-builder/pt-dynamic-form-field/pt-dynamic-form-field.component.html -->\n\n<div\n [formGroup]=\"form\"\n class=\"form-field\"\n [ngStyle]=\"{\n width:\n field.type !== FormInputTypeEnum.CHECKBOX\n ? field.width || inputWidth\n : 'auto',\n }\"\n>\n @switch (field.type) {\n <!-- TEXT -->\n @case (FormInputTypeEnum.TEXT) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- EMAIL -->\n @case (FormInputTypeEnum.EMAIL) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- PASSWORD -->\n @case (FormInputTypeEnum.PASSWORD) {\n <pt-password-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-password-input>\n }\n\n <!-- NUMBER -->\n @case (FormInputTypeEnum.NUMBER) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- AMOUNT -->\n @case (FormInputTypeEnum.AMOUNT) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- TEXTAREA -->\n @case (FormInputTypeEnum.TEXTAREA) {\n <pt-text-area-input\n [formGroup]=\"form\"\n [formField]=\"asTextAreaField(field)\"\n ></pt-text-area-input>\n }\n\n <!-- DATE -->\n @case (FormInputTypeEnum.DATE) {\n <pt-date-input\n [formGroup]=\"form\"\n [formField]=\"asDateField(field)\"\n ></pt-date-input>\n }\n\n <!-- MULTISELECT -->\n @case (FormInputTypeEnum.MULTISELECT) {\n <pt-multi-select\n [formGroup]=\"form\"\n [formField]=\"asMultiSelectField(field)\"\n ></pt-multi-select>\n }\n\n <!-- SELECT -->\n @case (FormInputTypeEnum.SELECT) {\n <pt-dropdown\n [formGroup]=\"form\"\n [formField]=\"asSelectField(field)\"\n ></pt-dropdown>\n }\n\n <!-- OTP -->\n @case (FormInputTypeEnum.OTP) {\n <pt-otp-input\n [formGroup]=\"form\"\n [formField]=\"asOtpField(field)\"\n ></pt-otp-input>\n }\n\n <!-- CHECKBOX -->\n @case (FormInputTypeEnum.CHECKBOX) {\n <pt-check-box-input\n [formGroup]=\"form\"\n [formField]=\"asCheckboxField(field)\"\n ></pt-check-box-input>\n }\n\n <!-- SWITCH -->\n @case (FormInputTypeEnum.SWITCH) {\n <pt-switch-input\n [formGroup]=\"form\"\n [formField]=\"asSwitchField(field)\"\n ></pt-switch-input>\n }\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.form-field{display:block;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.form-field>pt-text-input,.form-field>pt-password-input,.form-field>pt-number-input,.form-field>pt-text-area-input,.form-field>pt-date-input,.form-field>pt-multi-select,.form-field>pt-dropdown,.form-field>pt-otp-input,.form-field>pt-switch-input{display:block;width:100%;min-width:0;max-width:100%}.form-field label{display:block;margin-bottom:.5rem;font-weight:700}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: PTCheckBoxInputComponent, selector: "pt-check-box-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTDateInputComponent, selector: "pt-date-input", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "dateChange"] }, { kind: "component", type: PTNumberInputComponent, selector: "pt-number-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTSwitchInputComponent, selector: "pt-switch-input", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "switchChange"] }, { kind: "component", type: PTTextAreaInputComponent, selector: "pt-text-area-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTTextInputComponent, selector: "pt-text-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTDropdownComponent, selector: "pt-dropdown", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "selectionChange"] }, { kind: "component", type: PTMultiSelectComponent, selector: "pt-multi-select", inputs: ["formGroup", "formField", "config", "value"], outputs: ["valueChange", "selectionChange"] }, { kind: "component", type: PTOtpInputComponent, selector: "pt-otp-input", inputs: ["formGroup", "formField"] }, { kind: "component", type: PTPasswordInputComponent, selector: "pt-password-input", inputs: ["formGroup", "formField"] }] }); }
3694
4289
  }
3695
4290
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTDynamicFormFieldComponent, decorators: [{
3696
4291
  type: Component,
3697
- args: [{ selector: 'pt-dynamic-form-field', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-form-builder/pt-dynamic-form-field/pt-dynamic-form-field.component.html -->\n\n<div\n [formGroup]=\"form\"\n class=\"form-field\"\n [ngStyle]=\"{\n width:\n field.type !== FormInputTypeEnum.CHECKBOX\n ? field.width || inputWidth\n : 'auto',\n }\"\n>\n @switch (field.type) {\n <!-- TEXT -->\n @case (FormInputTypeEnum.TEXT) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- EMAIL -->\n @case (FormInputTypeEnum.EMAIL) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- PASSWORD -->\n @case (FormInputTypeEnum.PASSWORD) {\n <pt-password-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-password-input>\n }\n\n <!-- NUMBER -->\n @case (FormInputTypeEnum.NUMBER) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- AMOUNT -->\n @case (FormInputTypeEnum.AMOUNT) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- TEXTAREA -->\n @case (FormInputTypeEnum.TEXTAREA) {\n <pt-text-area-input\n [formGroup]=\"form\"\n [formField]=\"asTextAreaField(field)\"\n ></pt-text-area-input>\n }\n\n <!-- DATE -->\n @case (FormInputTypeEnum.DATE) {\n <pt-date-input\n [formGroup]=\"form\"\n [formField]=\"asDateField(field)\"\n ></pt-date-input>\n }\n\n <!-- MULTISELECT -->\n @case (FormInputTypeEnum.MULTISELECT) {\n <pt-multi-select\n [formGroup]=\"form\"\n [formField]=\"asMultiSelectField(field)\"\n ></pt-multi-select>\n }\n\n <!-- SELECT -->\n @case (FormInputTypeEnum.SELECT) {\n <pt-dropdown\n [formGroup]=\"form\"\n [formField]=\"asSelectField(field)\"\n ></pt-dropdown>\n }\n\n <!-- OTP -->\n @case (FormInputTypeEnum.OTP) {\n <pt-otp-input\n [formGroup]=\"form\"\n [formField]=\"asOtpField(field)\"\n ></pt-otp-input>\n }\n\n <!-- CHECKBOX -->\n @case (FormInputTypeEnum.CHECKBOX) {\n <pt-check-box-input\n [formGroup]=\"form\"\n [formField]=\"asCheckboxField(field)\"\n ></pt-check-box-input>\n }\n\n <!-- SWITCH -->\n @case (FormInputTypeEnum.SWITCH) {\n <pt-switch-input\n [formGroup]=\"form\"\n [formField]=\"asSwitchField(field)\"\n ></pt-switch-input>\n }\n }\n</div>\n", styles: [".form-field{margin-bottom:1rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700}\n"] }]
4292
+ args: [{ selector: 'pt-dynamic-form-field', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/pt-form-builder/pt-dynamic-form-field/pt-dynamic-form-field.component.html -->\n\n<div\n [formGroup]=\"form\"\n class=\"form-field\"\n [ngStyle]=\"{\n width:\n field.type !== FormInputTypeEnum.CHECKBOX\n ? field.width || inputWidth\n : 'auto',\n }\"\n>\n @switch (field.type) {\n <!-- TEXT -->\n @case (FormInputTypeEnum.TEXT) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- EMAIL -->\n @case (FormInputTypeEnum.EMAIL) {\n <pt-text-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-text-input>\n }\n\n <!-- PASSWORD -->\n @case (FormInputTypeEnum.PASSWORD) {\n <pt-password-input\n [formGroup]=\"form\"\n [formField]=\"asTextField(field)\"\n ></pt-password-input>\n }\n\n <!-- NUMBER -->\n @case (FormInputTypeEnum.NUMBER) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- AMOUNT -->\n @case (FormInputTypeEnum.AMOUNT) {\n <pt-number-input\n [formGroup]=\"form\"\n [formField]=\"asNumberField(field)\"\n ></pt-number-input>\n }\n\n <!-- TEXTAREA -->\n @case (FormInputTypeEnum.TEXTAREA) {\n <pt-text-area-input\n [formGroup]=\"form\"\n [formField]=\"asTextAreaField(field)\"\n ></pt-text-area-input>\n }\n\n <!-- DATE -->\n @case (FormInputTypeEnum.DATE) {\n <pt-date-input\n [formGroup]=\"form\"\n [formField]=\"asDateField(field)\"\n ></pt-date-input>\n }\n\n <!-- MULTISELECT -->\n @case (FormInputTypeEnum.MULTISELECT) {\n <pt-multi-select\n [formGroup]=\"form\"\n [formField]=\"asMultiSelectField(field)\"\n ></pt-multi-select>\n }\n\n <!-- SELECT -->\n @case (FormInputTypeEnum.SELECT) {\n <pt-dropdown\n [formGroup]=\"form\"\n [formField]=\"asSelectField(field)\"\n ></pt-dropdown>\n }\n\n <!-- OTP -->\n @case (FormInputTypeEnum.OTP) {\n <pt-otp-input\n [formGroup]=\"form\"\n [formField]=\"asOtpField(field)\"\n ></pt-otp-input>\n }\n\n <!-- CHECKBOX -->\n @case (FormInputTypeEnum.CHECKBOX) {\n <pt-check-box-input\n [formGroup]=\"form\"\n [formField]=\"asCheckboxField(field)\"\n ></pt-check-box-input>\n }\n\n <!-- SWITCH -->\n @case (FormInputTypeEnum.SWITCH) {\n <pt-switch-input\n [formGroup]=\"form\"\n [formField]=\"asSwitchField(field)\"\n ></pt-switch-input>\n }\n }\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.form-field{display:block;width:100%;min-width:0;max-width:100%;margin-bottom:1rem}.form-field>pt-text-input,.form-field>pt-password-input,.form-field>pt-number-input,.form-field>pt-text-area-input,.form-field>pt-date-input,.form-field>pt-multi-select,.form-field>pt-dropdown,.form-field>pt-otp-input,.form-field>pt-switch-input{display:block;width:100%;min-width:0;max-width:100%}.form-field label{display:block;margin-bottom:.5rem;font-weight:700}\n"] }]
3698
4293
  }], propDecorators: { field: [{
3699
4294
  type: Input
3700
4295
  }], form: [{
@@ -3708,7 +4303,10 @@ class PTFormBuilderComponent {
3708
4303
  constructor(fb, cdr) {
3709
4304
  this.fb = fb;
3710
4305
  this.cdr = cdr;
3711
- this.mainGroup = { fields: [], groups: [] };
4306
+ this.mainGroup = {
4307
+ fields: [],
4308
+ groups: [],
4309
+ };
3712
4310
  this.buttons = [];
3713
4311
  this.inputWidth = '100%';
3714
4312
  this.formWidth = '100%';
@@ -3722,8 +4320,12 @@ class PTFormBuilderComponent {
3722
4320
  required: 'is required',
3723
4321
  email: 'is not a valid email address',
3724
4322
  pattern: 'is invalid',
3725
- minlength: 'must be at least',
3726
- maxlength: 'must be at most',
4323
+ minlength: 'must contain at least',
4324
+ maxlength: 'must contain at most',
4325
+ min: 'must be greater than or equal to',
4326
+ max: 'must be less than or equal to',
4327
+ invalid: 'is invalid',
4328
+ characters: 'characters',
3727
4329
  },
3728
4330
  fr: {
3729
4331
  required: 'est requis',
@@ -3731,6 +4333,10 @@ class PTFormBuilderComponent {
3731
4333
  pattern: 'est invalide',
3732
4334
  minlength: 'doit contenir au moins',
3733
4335
  maxlength: 'doit contenir au plus',
4336
+ min: 'doit être supérieur ou égal à',
4337
+ max: 'doit être inférieur ou égal à',
4338
+ invalid: 'est invalide',
4339
+ characters: 'caractères',
3734
4340
  },
3735
4341
  };
3736
4342
  this.form = this.fb.group({});
@@ -3753,6 +4359,74 @@ class PTFormBuilderComponent {
3753
4359
  }
3754
4360
  return Boolean(hidden);
3755
4361
  }
4362
+ isInvalid(field) {
4363
+ const control = this.form.get(field.name);
4364
+ return Boolean(control &&
4365
+ control.enabled &&
4366
+ control.invalid &&
4367
+ (control.touched || control.dirty));
4368
+ }
4369
+ getErrorMessage(field) {
4370
+ const control = this.form.get(field.name);
4371
+ if (!control?.errors) {
4372
+ return '';
4373
+ }
4374
+ const errorKey = this.resolveFirstErrorKey(control);
4375
+ if (!errorKey) {
4376
+ return '';
4377
+ }
4378
+ const customMessage = field.validationMessages?.[errorKey];
4379
+ if (customMessage) {
4380
+ return customMessage;
4381
+ }
4382
+ if (field.errorText) {
4383
+ return field.errorText;
4384
+ }
4385
+ const fieldLabel = field.label?.trim() || field.name;
4386
+ const messages = this.errorMessages[this.language];
4387
+ switch (errorKey) {
4388
+ case 'required':
4389
+ return `${fieldLabel} ${messages.required}`;
4390
+ case 'email':
4391
+ return `${fieldLabel} ${messages.email}`;
4392
+ case 'pattern':
4393
+ return `${fieldLabel} ${messages.pattern}`;
4394
+ case 'minlength': {
4395
+ const requiredLength = control.getError('minlength')?.requiredLength ?? '';
4396
+ return `${fieldLabel} ${messages.minlength} ${requiredLength} ${messages.characters}`;
4397
+ }
4398
+ case 'maxlength': {
4399
+ const requiredLength = control.getError('maxlength')?.requiredLength ?? '';
4400
+ return `${fieldLabel} ${messages.maxlength} ${requiredLength} ${messages.characters}`;
4401
+ }
4402
+ case 'min': {
4403
+ const minimum = control.getError('min')?.min ?? '';
4404
+ return `${fieldLabel} ${messages.min} ${minimum}`;
4405
+ }
4406
+ case 'max': {
4407
+ const maximum = control.getError('max')?.max ?? '';
4408
+ return `${fieldLabel} ${messages.max} ${maximum}`;
4409
+ }
4410
+ default:
4411
+ return `${fieldLabel} ${messages.invalid}`;
4412
+ }
4413
+ }
4414
+ onSubmit() {
4415
+ this.form.markAllAsTouched();
4416
+ this.form.updateValueAndValidity({
4417
+ emitEvent: false,
4418
+ });
4419
+ if (this.form.invalid) {
4420
+ this.cdr.markForCheck();
4421
+ return;
4422
+ }
4423
+ this.formSubmit.emit(this.form.getRawValue());
4424
+ }
4425
+ onClear() {
4426
+ this.form.reset();
4427
+ this.formChange.emit(this.form.getRawValue());
4428
+ this.cdr.markForCheck();
4429
+ }
3756
4430
  rebuildForm() {
3757
4431
  this.formValueChangesSub?.unsubscribe();
3758
4432
  this.form = this.fb.group({});
@@ -3765,14 +4439,17 @@ class PTFormBuilderComponent {
3765
4439
  }
3766
4440
  buildFormGroup(group) {
3767
4441
  (group?.fields ?? []).forEach((field) => {
3768
- const validators = this.buildValidators(field);
3769
- const anyField = field;
3770
4442
  this.form.addControl(field.name, this.fb.control({
3771
4443
  value: this.resolveInitialValue(field),
3772
- disabled: Boolean(anyField.disabled),
3773
- }, validators));
4444
+ disabled: Boolean(field.disabled),
4445
+ }, {
4446
+ validators: this.buildValidators(field),
4447
+ updateOn: 'change',
4448
+ }));
4449
+ });
4450
+ (group?.groups ?? []).forEach((subGroup) => {
4451
+ this.buildFormGroup(subGroup);
3774
4452
  });
3775
- (group?.groups ?? []).forEach((subGroup) => this.buildFormGroup(subGroup));
3776
4453
  }
3777
4454
  resolveInitialValue(field) {
3778
4455
  if (field.value !== undefined && field.value !== null) {
@@ -3788,31 +4465,29 @@ class PTFormBuilderComponent {
3788
4465
  }
3789
4466
  buildValidators(field) {
3790
4467
  const validators = [];
3791
- const anyField = field;
3792
4468
  if (field.required) {
3793
4469
  validators.push(Validators.required);
3794
4470
  }
3795
- if (anyField.minLength != null) {
3796
- validators.push(Validators.minLength(Number(anyField.minLength)));
4471
+ if (field.minLength !== undefined && field.minLength !== null) {
4472
+ validators.push(Validators.minLength(Number(field.minLength)));
3797
4473
  }
3798
- if (anyField.maxLength != null) {
3799
- validators.push(Validators.maxLength(Number(anyField.maxLength)));
4474
+ if (field.maxLength !== undefined && field.maxLength !== null) {
4475
+ validators.push(Validators.maxLength(Number(field.maxLength)));
3800
4476
  }
3801
- if (anyField.inputValidation) {
3802
- validators.push(Validators.pattern(new RegExp(anyField.inputValidation)));
4477
+ if (field.inputValidation) {
4478
+ validators.push(Validators.pattern(new RegExp(field.inputValidation)));
3803
4479
  }
3804
- else if (anyField.pattern) {
3805
- validators.push(Validators.pattern(anyField.pattern));
4480
+ else if (field.pattern) {
4481
+ validators.push(Validators.pattern(field.pattern));
3806
4482
  }
3807
- switch (field.type) {
3808
- case FormInputTypeEnum.EMAIL:
3809
- validators.push(Validators.email);
3810
- break;
3811
- case FormInputTypeEnum.OTP:
3812
- validators.push(Validators.pattern(this.buildOtpPattern(field)));
3813
- break;
3814
- default:
3815
- break;
4483
+ if (field.type === FormInputTypeEnum.EMAIL) {
4484
+ validators.push(Validators.email);
4485
+ }
4486
+ if (field.type === FormInputTypeEnum.OTP) {
4487
+ validators.push(Validators.pattern(this.buildOtpPattern(field)));
4488
+ }
4489
+ if (Array.isArray(field.validators) && field.validators.length > 0) {
4490
+ validators.push(...field.validators);
3816
4491
  }
3817
4492
  return validators;
3818
4493
  }
@@ -3824,55 +4499,32 @@ class PTFormBuilderComponent {
3824
4499
  }
3825
4500
  return new RegExp(`^[A-Za-z0-9]{${length}}$`);
3826
4501
  }
3827
- isInvalid(field) {
3828
- const control = this.form.get(field.name);
3829
- return Boolean(control && control.invalid && (control.touched || control.dirty));
3830
- }
3831
- getErrorMessage(field) {
3832
- const control = this.form.get(field.name);
3833
- if (!control) {
3834
- return '';
3835
- }
3836
- const lang = this.language;
3837
- if (control.hasError('required')) {
3838
- return (field.errorText ||
3839
- `${field.label} ${this.errorMessages[lang].required}`);
3840
- }
3841
- if (control.hasError('email')) {
3842
- return (field.errorText ||
3843
- `${field.label} ${this.errorMessages[lang].email}`);
3844
- }
3845
- if (control.hasError('pattern')) {
3846
- return (field.errorText ||
3847
- `${field.label} ${this.errorMessages[lang].pattern}`);
3848
- }
3849
- if (control.hasError('minlength')) {
3850
- const requiredLength = control.getError('minlength')?.requiredLength;
3851
- return `${field.label} ${this.errorMessages[lang].minlength} ${requiredLength} ${lang === 'fr' ? 'caractères' : 'characters'}`;
3852
- }
3853
- if (control.hasError('maxlength')) {
3854
- const requiredLength = control.getError('maxlength')?.requiredLength;
3855
- return `${field.label} ${this.errorMessages[lang].maxlength} ${requiredLength} ${lang === 'fr' ? 'caractères' : 'characters'}`;
4502
+ resolveFirstErrorKey(control) {
4503
+ const errors = control.errors;
4504
+ if (!errors) {
4505
+ return undefined;
3856
4506
  }
3857
- return '';
3858
- }
3859
- onSubmit() {
3860
- this.form.markAllAsTouched();
3861
- if (this.form.valid) {
3862
- this.formSubmit.emit(this.form.getRawValue());
4507
+ const priority = [
4508
+ 'required',
4509
+ 'email',
4510
+ 'minlength',
4511
+ 'maxlength',
4512
+ 'min',
4513
+ 'max',
4514
+ 'pattern',
4515
+ ];
4516
+ const priorityError = priority.find((key) => errors[key] !== undefined);
4517
+ if (priorityError) {
4518
+ return priorityError;
3863
4519
  }
3864
- }
3865
- onClear() {
3866
- this.form.reset();
3867
- this.formChange.emit(this.form.getRawValue());
3868
- this.cdr.markForCheck();
4520
+ return Object.keys(errors)[0];
3869
4521
  }
3870
4522
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTFormBuilderComponent, deps: [{ token: i2.FormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3871
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTFormBuilderComponent, isStandalone: false, selector: "pt-form-builder", inputs: { mainGroup: "mainGroup", buttons: "buttons", title: "title", titleStyle: "titleStyle", inputWidth: "inputWidth", formWidth: "formWidth", language: "language" }, outputs: { formSubmit: "formSubmit", formReady: "formReady", formChange: "formChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/components/pt-form-builder/pt-form-builder.component.html -->\n\n<div\n class=\"pt-form-builder-wrapper\"\n [ngStyle]=\"{ width: formWidth, maxWidth: formWidth }\"\n>\n @if (title) {\n <div [ngStyle]=\"titleStyle\" class=\"form-title\">{{ title }}</div>\n }\n\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @for (field of mainGroup.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n\n @for (group of mainGroup.groups; track $index) {\n <div\n class=\"form-field-group\"\n [ngStyle]=\"{ width: group.width || '100%' }\"\n >\n @for (field of group.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n class=\"flex-item\"\n ></pt-dynamic-form-field>\n }\n }\n </div>\n }\n\n @if (buttons.length) {\n <div class=\"button-group\">\n @for (button of buttons; track button.text) {\n <button\n type=\"button\"\n pButton\n [label]=\"button.text\"\n [icon]=\"button.icon || ''\"\n [class]=\"button.color || ''\"\n [disabled]=\"button.disabled || false\"\n [ngStyle]=\"{\n color: button.fontColor || null,\n 'background-color': button.backgroundColor || null,\n 'border-color':\n button.borderColor || button.backgroundColor || null,\n }\"\n (click)=\"\n button.isSubmit\n ? onSubmit()\n : button.isClear\n ? onClear()\n : button.action\n ? button.action()\n : null\n \"\n ></button>\n }\n </div>\n }\n </form>\n</div>\n", styles: [".pt-form-builder-wrapper{width:100%;margin:0 auto;color:var(--pt-form-builder-text, inherit)}.form-title{text-align:center;margin-bottom:1rem;font-size:1.5rem;font-weight:700;color:var(--pt-form-builder-title, #333333)}.form-field{margin-bottom:1rem;color:var(--pt-form-builder-text, inherit)}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;color:var(--pt-form-builder-label, #333333)}::ng-deep .p-inputtext,::ng-deep .p-inputtextarea,::ng-deep .p-calendar,::ng-deep .p-inputnumber,::ng-deep .p-multiselect,::ng-deep .p-dropdown{width:100%!important}::ng-deep .p-inputnumber,::ng-deep p-inputnumber{display:flex!important}.button-group{display:flex;gap:1rem;margin-top:1rem;justify-content:space-between}.button-group button{flex:1;display:flex;align-items:center;justify-content:center}.button-group button i{margin-right:.5rem}.button-group button.p-button-primary{background-color:var(--pt-form-builder-button-primary-bg, #007bff);color:var(--pt-form-builder-button-primary-text, #ffffff)}.button-group button.p-button-secondary{background-color:var(--pt-form-builder-button-secondary-bg, #6c757d);color:var(--pt-form-builder-button-secondary-text, #ffffff)}.button-group button.p-button-success{background-color:var(--pt-form-builder-button-success-bg, #28a745);color:var(--pt-form-builder-button-success-text, #ffffff)}.button-group button:hover{opacity:.9}.form-field-group{display:flex;gap:1rem}.form-field-group .flex-item{flex:1}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: PTDynamicFormFieldComponent, selector: "pt-dynamic-form-field", inputs: ["field", "form", "inputWidth"] }] }); }
4523
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTFormBuilderComponent, isStandalone: false, selector: "pt-form-builder", inputs: { mainGroup: "mainGroup", buttons: "buttons", title: "title", titleStyle: "titleStyle", inputWidth: "inputWidth", formWidth: "formWidth", language: "language" }, outputs: { formSubmit: "formSubmit", formReady: "formReady", formChange: "formChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- projects/ng-prime-tools/src/lib/components/pt-form-builder/pt-form-builder.component.html -->\n\n<div\n class=\"pt-form-builder-wrapper\"\n [ngStyle]=\"{\n width: formWidth,\n 'max-width': formWidth,\n }\"\n>\n @if (title) {\n <div class=\"form-title\" [ngStyle]=\"titleStyle\">\n {{ title }}\n </div>\n }\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"onSubmit()\">\n @for (field of mainGroup.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n\n @for (group of mainGroup.groups; track $index) {\n <div\n class=\"form-field-group\"\n [ngStyle]=\"{\n width: group.width || '100%',\n }\"\n >\n @for (field of group.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n class=\"flex-item\"\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n </div>\n }\n\n @if (buttons.length > 0) {\n <div class=\"button-group\">\n @for (button of buttons; track button.text) {\n <button\n pButton\n [type]=\"button.isSubmit ? 'submit' : 'button'\"\n [label]=\"button.text\"\n [icon]=\"button.icon || ''\"\n [class]=\"button.color || ''\"\n [disabled]=\"button.disabled || false\"\n [ngStyle]=\"{\n color: button.fontColor || null,\n 'background-color': button.backgroundColor || null,\n 'border-color':\n button.borderColor || button.backgroundColor || null,\n }\"\n (click)=\"\n button.isSubmit\n ? null\n : button.isClear\n ? onClear()\n : button.action\n ? button.action()\n : null\n \"\n ></button>\n }\n </div>\n }\n </form>\n</div>\n", styles: [".pt-form-builder-wrapper{width:100%;margin:0 auto;color:var(--pt-form-builder-text, inherit)}.form-title{text-align:center;margin-bottom:1rem;font-size:1.5rem;font-weight:700;color:var(--pt-form-builder-title, #333333)}.form-field{margin-bottom:1rem;color:var(--pt-form-builder-text, inherit)}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;color:var(--pt-form-builder-label, #333333)}::ng-deep .p-inputtext,::ng-deep .p-inputtextarea,::ng-deep .p-calendar,::ng-deep .p-inputnumber,::ng-deep .p-multiselect,::ng-deep .p-dropdown{width:100%!important}::ng-deep .p-inputnumber,::ng-deep p-inputnumber{display:flex!important}.button-group{display:flex;gap:1rem;margin-top:1rem;justify-content:space-between}.button-group button{flex:1;display:flex;align-items:center;justify-content:center}.button-group button i{margin-right:.5rem}.button-group button.p-button-primary{background-color:var(--pt-form-builder-button-primary-bg, #007bff);color:var(--pt-form-builder-button-primary-text, #ffffff)}.button-group button.p-button-secondary{background-color:var(--pt-form-builder-button-secondary-bg, #6c757d);color:var(--pt-form-builder-button-secondary-text, #ffffff)}.button-group button.p-button-success{background-color:var(--pt-form-builder-button-success-bg, #28a745);color:var(--pt-form-builder-button-success-text, #ffffff)}.button-group button:hover{opacity:.9}.form-field-group{display:flex;gap:1rem}.form-field-group .flex-item{flex:1}\n"], dependencies: [{ kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: PTDynamicFormFieldComponent, selector: "pt-dynamic-form-field", inputs: ["field", "form", "inputWidth"] }] }); }
3872
4524
  }
3873
4525
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTFormBuilderComponent, decorators: [{
3874
4526
  type: Component,
3875
- args: [{ selector: 'pt-form-builder', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/components/pt-form-builder/pt-form-builder.component.html -->\n\n<div\n class=\"pt-form-builder-wrapper\"\n [ngStyle]=\"{ width: formWidth, maxWidth: formWidth }\"\n>\n @if (title) {\n <div [ngStyle]=\"titleStyle\" class=\"form-title\">{{ title }}</div>\n }\n\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @for (field of mainGroup.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n\n @for (group of mainGroup.groups; track $index) {\n <div\n class=\"form-field-group\"\n [ngStyle]=\"{ width: group.width || '100%' }\"\n >\n @for (field of group.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n class=\"flex-item\"\n ></pt-dynamic-form-field>\n }\n }\n </div>\n }\n\n @if (buttons.length) {\n <div class=\"button-group\">\n @for (button of buttons; track button.text) {\n <button\n type=\"button\"\n pButton\n [label]=\"button.text\"\n [icon]=\"button.icon || ''\"\n [class]=\"button.color || ''\"\n [disabled]=\"button.disabled || false\"\n [ngStyle]=\"{\n color: button.fontColor || null,\n 'background-color': button.backgroundColor || null,\n 'border-color':\n button.borderColor || button.backgroundColor || null,\n }\"\n (click)=\"\n button.isSubmit\n ? onSubmit()\n : button.isClear\n ? onClear()\n : button.action\n ? button.action()\n : null\n \"\n ></button>\n }\n </div>\n }\n </form>\n</div>\n", styles: [".pt-form-builder-wrapper{width:100%;margin:0 auto;color:var(--pt-form-builder-text, inherit)}.form-title{text-align:center;margin-bottom:1rem;font-size:1.5rem;font-weight:700;color:var(--pt-form-builder-title, #333333)}.form-field{margin-bottom:1rem;color:var(--pt-form-builder-text, inherit)}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;color:var(--pt-form-builder-label, #333333)}::ng-deep .p-inputtext,::ng-deep .p-inputtextarea,::ng-deep .p-calendar,::ng-deep .p-inputnumber,::ng-deep .p-multiselect,::ng-deep .p-dropdown{width:100%!important}::ng-deep .p-inputnumber,::ng-deep p-inputnumber{display:flex!important}.button-group{display:flex;gap:1rem;margin-top:1rem;justify-content:space-between}.button-group button{flex:1;display:flex;align-items:center;justify-content:center}.button-group button i{margin-right:.5rem}.button-group button.p-button-primary{background-color:var(--pt-form-builder-button-primary-bg, #007bff);color:var(--pt-form-builder-button-primary-text, #ffffff)}.button-group button.p-button-secondary{background-color:var(--pt-form-builder-button-secondary-bg, #6c757d);color:var(--pt-form-builder-button-secondary-text, #ffffff)}.button-group button.p-button-success{background-color:var(--pt-form-builder-button-success-bg, #28a745);color:var(--pt-form-builder-button-success-text, #ffffff)}.button-group button:hover{opacity:.9}.form-field-group{display:flex;gap:1rem}.form-field-group .flex-item{flex:1}\n"] }]
4527
+ args: [{ selector: 'pt-form-builder', standalone: false, template: "<!-- projects/ng-prime-tools/src/lib/components/pt-form-builder/pt-form-builder.component.html -->\n\n<div\n class=\"pt-form-builder-wrapper\"\n [ngStyle]=\"{\n width: formWidth,\n 'max-width': formWidth,\n }\"\n>\n @if (title) {\n <div class=\"form-title\" [ngStyle]=\"titleStyle\">\n {{ title }}\n </div>\n }\n\n <form [formGroup]=\"form\" novalidate (ngSubmit)=\"onSubmit()\">\n @for (field of mainGroup.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n\n @for (group of mainGroup.groups; track $index) {\n <div\n class=\"form-field-group\"\n [ngStyle]=\"{\n width: group.width || '100%',\n }\"\n >\n @for (field of group.fields; track field.name) {\n @if (!isFieldHidden(field)) {\n <pt-dynamic-form-field\n class=\"flex-item\"\n [field]=\"field\"\n [form]=\"form\"\n [inputWidth]=\"inputWidth\"\n ></pt-dynamic-form-field>\n }\n }\n </div>\n }\n\n @if (buttons.length > 0) {\n <div class=\"button-group\">\n @for (button of buttons; track button.text) {\n <button\n pButton\n [type]=\"button.isSubmit ? 'submit' : 'button'\"\n [label]=\"button.text\"\n [icon]=\"button.icon || ''\"\n [class]=\"button.color || ''\"\n [disabled]=\"button.disabled || false\"\n [ngStyle]=\"{\n color: button.fontColor || null,\n 'background-color': button.backgroundColor || null,\n 'border-color':\n button.borderColor || button.backgroundColor || null,\n }\"\n (click)=\"\n button.isSubmit\n ? null\n : button.isClear\n ? onClear()\n : button.action\n ? button.action()\n : null\n \"\n ></button>\n }\n </div>\n }\n </form>\n</div>\n", styles: [".pt-form-builder-wrapper{width:100%;margin:0 auto;color:var(--pt-form-builder-text, inherit)}.form-title{text-align:center;margin-bottom:1rem;font-size:1.5rem;font-weight:700;color:var(--pt-form-builder-title, #333333)}.form-field{margin-bottom:1rem;color:var(--pt-form-builder-text, inherit)}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;color:var(--pt-form-builder-label, #333333)}::ng-deep .p-inputtext,::ng-deep .p-inputtextarea,::ng-deep .p-calendar,::ng-deep .p-inputnumber,::ng-deep .p-multiselect,::ng-deep .p-dropdown{width:100%!important}::ng-deep .p-inputnumber,::ng-deep p-inputnumber{display:flex!important}.button-group{display:flex;gap:1rem;margin-top:1rem;justify-content:space-between}.button-group button{flex:1;display:flex;align-items:center;justify-content:center}.button-group button i{margin-right:.5rem}.button-group button.p-button-primary{background-color:var(--pt-form-builder-button-primary-bg, #007bff);color:var(--pt-form-builder-button-primary-text, #ffffff)}.button-group button.p-button-secondary{background-color:var(--pt-form-builder-button-secondary-bg, #6c757d);color:var(--pt-form-builder-button-secondary-text, #ffffff)}.button-group button.p-button-success{background-color:var(--pt-form-builder-button-success-bg, #28a745);color:var(--pt-form-builder-button-success-text, #ffffff)}.button-group button:hover{opacity:.9}.form-field-group{display:flex;gap:1rem}.form-field-group .flex-item{flex:1}\n"] }]
3876
4528
  }], ctorParameters: () => [{ type: i2.FormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { mainGroup: [{
3877
4529
  type: Input
3878
4530
  }], buttons: [{
@@ -8170,7 +8822,7 @@ class PTAccordionComponent {
8170
8822
  .filter(Boolean));
8171
8823
  }
8172
8824
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTAccordionComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
8173
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTAccordionComponent, isStandalone: false, selector: "pt-accordion", inputs: { config: "config", value: "value" }, outputs: { valueChange: "valueChange", panelOpen: "panelOpen", panelClose: "panelClose" }, queries: [{ propertyName: "panelTemplates", predicate: PTAccordionPanelDirective }], usesOnChanges: true, ngImport: i0, template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-accordion/pt-accordion.component.html -->\n\n<p-accordion\n [value]=\"value\"\n [multiple]=\"multiple\"\n [expandIcon]=\"expandIcon\"\n [collapseIcon]=\"collapseIcon\"\n [ngClass]=\"getRootClasses()\"\n (valueChange)=\"onValueChange($event)\"\n>\n @for (panel of visiblePanels; track trackPanel($index, panel)) {\n <p-accordion-panel\n [value]=\"panel.value\"\n [disabled]=\"panel.disabled === true\"\n [ngClass]=\"getPanelClasses(panel)\"\n >\n <p-accordion-header>\n <div [ngClass]=\"getHeaderClasses(panel)\">\n <div class=\"pt-accordion-header-main\">\n @if (\n getTitleIconClass(panel) && getTitleIconPosition(panel) === \"left\"\n ) {\n <span\n class=\"pt-accordion-title-icon\"\n [ngStyle]=\"getTitleIconStyles(panel)\"\n >\n <i\n class=\"pt-accordion-title-icon-code\"\n [class]=\"getTitleIconClass(panel)\"\n ></i>\n </span>\n }\n\n <div class=\"pt-accordion-header-text\">\n <span\n class=\"pt-accordion-title\"\n [ngStyle]=\"getTitleStyles(panel)\"\n >\n {{ getTitleText(panel) }}\n </span>\n\n @if (panel.description?.text) {\n <span\n class=\"pt-accordion-description\"\n [ngStyle]=\"getDescriptionStyles(panel)\"\n >\n {{ panel.description?.text }}\n </span>\n }\n </div>\n\n @if (\n getTitleIconClass(panel) &&\n getTitleIconPosition(panel) === \"right\"\n ) {\n <span\n class=\"pt-accordion-title-icon\"\n [ngStyle]=\"getTitleIconStyles(panel)\"\n >\n <i\n class=\"pt-accordion-title-icon-code\"\n [class]=\"getTitleIconClass(panel)\"\n ></i>\n </span>\n }\n </div>\n\n @if (isTagVisible(panel)) {\n <p-tag\n [value]=\"panel.tag?.value ?? ''\"\n [icon]=\"panel.tag?.icon\"\n [severity]=\"getTagSeverity(panel)\"\n [rounded]=\"panel.tag?.rounded === true\"\n [ngClass]=\"getTagClasses(panel)\"\n ></p-tag>\n }\n </div>\n </p-accordion-header>\n\n <p-accordion-content>\n @if (shouldRenderPanelContent(panel)) {\n <div\n [ngClass]=\"getContentClasses(panel)\"\n [ngStyle]=\"getContentStyles(panel)\"\n >\n <div\n class=\"pt-accordion-content-inner\"\n [ngStyle]=\"getContentInnerStyles(panel)\"\n >\n @if (resolvePanelTemplate(panel.value); as panelTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n panelTemplate;\n context: getTemplateContext(panel)\n \"\n ></ng-container>\n } @else {\n <div class=\"pt-accordion-empty-content\">\n Aucun contenu configur\u00E9.\n </div>\n }\n </div>\n </div>\n }\n </p-accordion-content>\n </p-accordion-panel>\n }\n</p-accordion>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-accordion{display:block;width:100%;min-width:0;max-width:100%}.pt-accordion-header-content{display:flex;align-items:center;justify-content:space-between;flex:1 1 auto;gap:1rem;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-accordion-header-main{display:flex;align-items:center;flex:1 1 auto;gap:.85rem;min-width:0;overflow:hidden}.pt-accordion-title-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 2.5rem;width:2.5rem;height:2.5rem;overflow:hidden}.pt-accordion-title-icon-code{display:inline-flex;align-items:center;justify-content:center;line-height:1}.pt-accordion-header-text{display:flex;flex:1 1 auto;flex-direction:column;gap:.25rem;min-width:0;overflow:hidden}.pt-accordion-title,.pt-accordion-description{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pt-accordion-title{line-height:1.25}.pt-accordion-description{line-height:1.35}.pt-accordion-tag{flex:0 0 auto;max-width:var(--pt-accordion-tag-max-width, 14rem);overflow:hidden}.pt-accordion-content,.pt-accordion-content-viewport{display:block;width:0;min-width:100%;max-width:100%;inline-size:0;min-inline-size:100%;max-inline-size:100%;min-height:0;min-block-size:0;overflow:hidden;box-sizing:border-box;contain:inline-size}.pt-accordion-content-inner{display:block;box-sizing:border-box}.pt-accordion-content-scroll-x{overflow-x:scroll!important;overflow-y:hidden!important;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable;scrollbar-width:auto;scrollbar-color:var(--pt-accordion-scrollbar-thumb, #64748b) var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-y{overflow-x:hidden!important;overflow-y:auto!important;overscroll-behavior-y:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable;scrollbar-width:auto;scrollbar-color:var(--pt-accordion-scrollbar-thumb, #64748b) var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-x.pt-accordion-content-scroll-y{overflow:auto!important;overscroll-behavior:contain}.pt-accordion-content-scroll-x::-webkit-scrollbar,.pt-accordion-content-scroll-y::-webkit-scrollbar{width:12px;height:12px}.pt-accordion-content-scroll-x::-webkit-scrollbar-track,.pt-accordion-content-scroll-y::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-x::-webkit-scrollbar-thumb,.pt-accordion-content-scroll-y::-webkit-scrollbar-thumb{min-width:3rem;min-height:3rem;border:2px solid var(--pt-accordion-scrollbar-track, #e2e8f0);border-radius:999px;background:var(--pt-accordion-scrollbar-thumb, #64748b)}.pt-accordion-content-scroll-x::-webkit-scrollbar-thumb:hover,.pt-accordion-content-scroll-y::-webkit-scrollbar-thumb:hover{background:var(--pt-accordion-scrollbar-thumb-hover, #475569)}.pt-accordion-empty-content{padding:1rem;color:#64748b;text-align:center}:host ::ng-deep .p-accordion{display:block;width:100%;min-width:0;max-width:100%}:host ::ng-deep .p-accordionpanel{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden;border:1px solid var(--pt-accordion-border-color, #e5e7eb);border-radius:var(--pt-accordion-border-radius, 12px);background:var(--pt-accordion-panel-background, #ffffff)}:host ::ng-deep .p-accordionpanel+.p-accordionpanel{margin-top:var(--pt-accordion-panel-gap, 1rem)}:host ::ng-deep .p-accordionheader{display:flex;align-items:center;width:100%;min-width:0;max-width:100%;padding:var(--pt-accordion-header-padding, 1rem);overflow:hidden;border:0;border-radius:var(--pt-accordion-border-radius, 12px);background:var(--pt-accordion-header-background, #ffffff);color:var(--pt-accordion-header-color, inherit);transition:background-color .2s ease,color .2s ease}:host ::ng-deep .p-accordionheader:hover{background:var(--pt-accordion-header-hover-background, #f8fafc)}:host ::ng-deep .p-accordionpanel-active .p-accordionheader{border-bottom-right-radius:0;border-bottom-left-radius:0}:host ::ng-deep .p-accordionheader-link,:host ::ng-deep .p-accordionheader-button{display:flex;align-items:center;flex:1 1 auto;width:100%;min-width:0;max-width:100%;overflow:hidden}:host ::ng-deep .p-accordionheader-toggle-icon{flex:0 0 auto;margin-left:.75rem}:host ::ng-deep .p-accordioncontent{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}:host ::ng-deep .p-accordioncontent-content{display:block;width:100%;min-width:0;max-width:100%;padding:var(--pt-accordion-content-padding, 1rem);overflow:hidden;border:0;background:var(--pt-accordion-content-background, #ffffff)}:host ::ng-deep .p-accordioncontent-content>*{min-width:0;max-width:100%}:host ::ng-deep .pt-accordion-panel-disabled .p-accordionheader{cursor:not-allowed;opacity:.65}@media(max-width:768px){.pt-accordion-header-content{gap:.5rem}.pt-accordion-header-main{gap:.65rem}.pt-accordion-title-icon{flex-basis:2.25rem;width:2.25rem;height:2.25rem}.pt-accordion-description{display:none}.pt-accordion-title{font-size:var(--pt-accordion-mobile-title-font-size, .95rem)!important}.pt-accordion-tag{max-width:var(--pt-accordion-mobile-tag-max-width, 9rem)}:host ::ng-deep .p-accordionheader,:host ::ng-deep .p-accordioncontent-content{padding:var(--pt-accordion-mobile-padding, .85rem)}:host ::ng-deep .p-accordionheader-toggle-icon{margin-left:.4rem}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2$4.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions", "motionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i2$4.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i2$4.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i2$4.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8825
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTAccordionComponent, isStandalone: false, selector: "pt-accordion", inputs: { config: "config", value: "value" }, outputs: { valueChange: "valueChange", panelOpen: "panelOpen", panelClose: "panelClose" }, queries: [{ propertyName: "panelTemplates", predicate: PTAccordionPanelDirective }], usesOnChanges: true, ngImport: i0, template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-accordion/pt-accordion.component.html -->\n\n<p-accordion\n [value]=\"value\"\n [multiple]=\"multiple\"\n [expandIcon]=\"expandIcon\"\n [collapseIcon]=\"collapseIcon\"\n [ngClass]=\"getRootClasses()\"\n (valueChange)=\"onValueChange($event)\"\n>\n @for (panel of visiblePanels; track trackPanel($index, panel)) {\n <p-accordion-panel\n [value]=\"panel.value\"\n [disabled]=\"panel.disabled === true\"\n [ngClass]=\"getPanelClasses(panel)\"\n >\n <p-accordion-header>\n <div [ngClass]=\"getHeaderClasses(panel)\">\n <div class=\"pt-accordion-header-main\">\n @if (\n getTitleIconClass(panel) && getTitleIconPosition(panel) === \"left\"\n ) {\n <span\n class=\"pt-accordion-title-icon\"\n [ngStyle]=\"getTitleIconStyles(panel)\"\n >\n <i\n class=\"pt-accordion-title-icon-code\"\n [class]=\"getTitleIconClass(panel)\"\n ></i>\n </span>\n }\n\n <div class=\"pt-accordion-header-text\">\n <span\n class=\"pt-accordion-title\"\n [ngStyle]=\"getTitleStyles(panel)\"\n >\n {{ getTitleText(panel) }}\n </span>\n\n @if (panel.description?.text) {\n <span\n class=\"pt-accordion-description\"\n [ngStyle]=\"getDescriptionStyles(panel)\"\n >\n {{ panel.description?.text }}\n </span>\n }\n </div>\n\n @if (\n getTitleIconClass(panel) &&\n getTitleIconPosition(panel) === \"right\"\n ) {\n <span\n class=\"pt-accordion-title-icon\"\n [ngStyle]=\"getTitleIconStyles(panel)\"\n >\n <i\n class=\"pt-accordion-title-icon-code\"\n [class]=\"getTitleIconClass(panel)\"\n ></i>\n </span>\n }\n </div>\n\n @if (isTagVisible(panel)) {\n <p-tag\n [value]=\"panel.tag?.value ?? ''\"\n [icon]=\"panel.tag?.icon\"\n [severity]=\"getTagSeverity(panel)\"\n [rounded]=\"panel.tag?.rounded === true\"\n [ngClass]=\"getTagClasses(panel)\"\n ></p-tag>\n }\n </div>\n </p-accordion-header>\n\n <p-accordion-content>\n @if (shouldRenderPanelContent(panel)) {\n <div\n [ngClass]=\"getContentClasses(panel)\"\n [ngStyle]=\"getContentStyles(panel)\"\n >\n <div\n class=\"pt-accordion-content-inner\"\n [ngStyle]=\"getContentInnerStyles(panel)\"\n >\n @if (resolvePanelTemplate(panel.value); as panelTemplate) {\n <ng-container\n *ngTemplateOutlet=\"\n panelTemplate;\n context: getTemplateContext(panel)\n \"\n ></ng-container>\n } @else {\n <div class=\"pt-accordion-empty-content\">\n Aucun contenu configur\u00E9.\n </div>\n }\n </div>\n </div>\n }\n </p-accordion-content>\n </p-accordion-panel>\n }\n</p-accordion>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-accordion{display:block;width:100%;min-width:0;max-width:100%}.pt-accordion-header-content{display:flex;align-items:center;justify-content:space-between;flex:1 1 auto;gap:1rem;width:100%;min-width:0;max-width:100%;overflow:hidden}.pt-accordion-header-main{display:flex;align-items:center;flex:1 1 auto;gap:.85rem;min-width:0;overflow:hidden}.pt-accordion-title-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 2.5rem;width:2.5rem;height:2.5rem;overflow:hidden}.pt-accordion-title-icon-code{display:inline-flex;align-items:center;justify-content:center;line-height:1}.pt-accordion-header-text{display:flex;flex:1 1 auto;flex-direction:column;gap:.25rem;min-width:0;overflow:hidden}.pt-accordion-title,.pt-accordion-description{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pt-accordion-title{line-height:1.25}.pt-accordion-description{line-height:1.35}.pt-accordion-tag{flex:0 0 auto;max-width:var(--pt-accordion-tag-max-width, 14rem);overflow:hidden}.pt-accordion-content,.pt-accordion-content-viewport{display:block;width:0;min-width:100%;max-width:100%;inline-size:0;min-inline-size:100%;max-inline-size:100%;min-height:0;min-block-size:0;overflow:hidden;box-sizing:border-box;contain:inline-size}.pt-accordion-content-inner{display:block;box-sizing:border-box}.pt-accordion-content-scroll-x{overflow-x:scroll!important;overflow-y:hidden!important;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable;scrollbar-width:auto;scrollbar-color:var(--pt-accordion-scrollbar-thumb, #64748b) var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-y{overflow-x:hidden!important;overflow-y:auto!important;overscroll-behavior-y:contain;-webkit-overflow-scrolling:touch;scrollbar-gutter:stable;scrollbar-width:auto;scrollbar-color:var(--pt-accordion-scrollbar-thumb, #64748b) var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-x.pt-accordion-content-scroll-y{overflow:auto!important;overscroll-behavior:contain}.pt-accordion-content-scroll-x::-webkit-scrollbar,.pt-accordion-content-scroll-y::-webkit-scrollbar{width:12px;height:12px}.pt-accordion-content-scroll-x::-webkit-scrollbar-track,.pt-accordion-content-scroll-y::-webkit-scrollbar-track{border-radius:999px;background:var(--pt-accordion-scrollbar-track, #e2e8f0)}.pt-accordion-content-scroll-x::-webkit-scrollbar-thumb,.pt-accordion-content-scroll-y::-webkit-scrollbar-thumb{min-width:3rem;min-height:3rem;border:2px solid var(--pt-accordion-scrollbar-track, #e2e8f0);border-radius:999px;background:var(--pt-accordion-scrollbar-thumb, #64748b)}.pt-accordion-content-scroll-x::-webkit-scrollbar-thumb:hover,.pt-accordion-content-scroll-y::-webkit-scrollbar-thumb:hover{background:var(--pt-accordion-scrollbar-thumb-hover, #475569)}.pt-accordion-empty-content{padding:1rem;color:#64748b;text-align:center}:host ::ng-deep .p-accordion{display:block;width:100%;min-width:0;max-width:100%}:host ::ng-deep .p-accordionpanel{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden;border:1px solid var(--pt-accordion-border-color, #e5e7eb);border-radius:var(--pt-accordion-border-radius, 12px);background:var(--pt-accordion-panel-background, #ffffff)}:host ::ng-deep .p-accordionpanel+.p-accordionpanel{margin-top:var(--pt-accordion-panel-gap, 1rem)}:host ::ng-deep .p-accordionheader{display:flex;align-items:center;width:100%;min-width:0;max-width:100%;padding:var(--pt-accordion-header-padding, 1rem);overflow:hidden;border:0;border-radius:var(--pt-accordion-border-radius, 12px);background:var(--pt-accordion-header-background, #ffffff);color:var(--pt-accordion-header-color, inherit);transition:background-color .2s ease,color .2s ease}:host ::ng-deep .p-accordionheader:hover{background:var(--pt-accordion-header-hover-background, #f8fafc)}:host ::ng-deep .p-accordionpanel-active .p-accordionheader{border-bottom-right-radius:0;border-bottom-left-radius:0}:host ::ng-deep .p-accordionheader-link,:host ::ng-deep .p-accordionheader-button{display:flex;align-items:center;flex:1 1 auto;width:100%;min-width:0;max-width:100%;overflow:hidden}:host ::ng-deep .p-accordionheader-toggle-icon{flex:0 0 auto;margin-left:.75rem}:host ::ng-deep .p-accordioncontent{display:block;width:100%;min-width:0;max-width:100%;overflow:hidden}:host ::ng-deep .p-accordioncontent-content{display:block;width:100%;min-width:0;max-width:100%;padding:var(--pt-accordion-content-padding, 1rem);overflow:hidden;border:0;background:var(--pt-accordion-content-background, #ffffff)}:host ::ng-deep .p-accordioncontent-content>*{min-width:0;max-width:100%}:host ::ng-deep .pt-accordion-panel-disabled .p-accordionheader{cursor:not-allowed;opacity:.65}@media(max-width:768px){.pt-accordion-header-content{gap:.5rem}.pt-accordion-header-main{gap:.65rem}.pt-accordion-title-icon{flex-basis:2.25rem;width:2.25rem;height:2.25rem}.pt-accordion-description{display:none}.pt-accordion-title{font-size:var(--pt-accordion-mobile-title-font-size, .95rem)!important}.pt-accordion-tag{max-width:var(--pt-accordion-mobile-tag-max-width, 9rem)}:host ::ng-deep .p-accordionheader,:host ::ng-deep .p-accordioncontent-content{padding:var(--pt-accordion-mobile-padding, .85rem)}:host ::ng-deep .p-accordionheader-toggle-icon{margin-left:.4rem}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2$4.Accordion, selector: "p-accordion", inputs: ["value", "multiple", "styleClass", "expandIcon", "collapseIcon", "selectOnFocus", "transitionOptions", "motionOptions"], outputs: ["valueChange", "onClose", "onOpen"] }, { kind: "component", type: i2$4.AccordionPanel, selector: "p-accordion-panel, p-accordionpanel", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i2$4.AccordionHeader, selector: "p-accordion-header, p-accordionheader" }, { kind: "component", type: i2$4.AccordionContent, selector: "p-accordion-content, p-accordioncontent" }, { kind: "component", type: i10.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8174
8826
  }
8175
8827
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTAccordionComponent, decorators: [{
8176
8828
  type: Component,
@@ -8683,7 +9335,7 @@ class PTMetricPanelComponent {
8683
9335
  this.actionMenuItems = [...items, ...this.menuItems];
8684
9336
  }
8685
9337
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTMetricPanelComponent, deps: [{ token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
8686
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTMetricPanelComponent, isStandalone: false, selector: "pt-metric-panel", inputs: { panelData: "panelData", cardConfig: "cardConfig", showRefresh: "showRefresh", showMenu: "showMenu", menuItems: "menuItems", loading: "loading" }, outputs: { refresh: "refresh" }, viewQueries: [{ propertyName: "actionMenu", first: true, predicate: ["actionMenu"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<pt-card [config]=\"cardConfig\">\n <div class=\"panel-header\">\n <div class=\"panel-title-container\" [ngStyle]=\"getTitleStyles()\">\n @if (getTitleIcon()) {\n <i\n class=\"panel-title-icon\"\n [ngClass]=\"getIconClass(getTitleIcon())\"\n [ngStyle]=\"getTitleIconStyles()\"\n ></i>\n }\n\n <span class=\"panel-title-text\">\n {{ getTitleText() }}\n </span>\n </div>\n\n @if (showRefresh || shouldShowMenu) {\n <div class=\"panel-actions\">\n @if (showRefresh) {\n <button\n type=\"button\"\n class=\"panel-action-button\"\n [class.panel-action-button-loading]=\"isLoading\"\n [disabled]=\"isLoading\"\n aria-label=\"Actualiser\"\n title=\"Actualiser\"\n (click)=\"onRefreshClick($event)\"\n >\n <i\n class=\"pi\"\n [ngClass]=\"isLoading ? 'pi-spin pi-spinner' : 'pi-refresh'\"\n ></i>\n </button>\n }\n\n @if (shouldShowMenu) {\n <button\n type=\"button\"\n class=\"panel-action-button\"\n [disabled]=\"isLoading\"\n aria-label=\"Plus d\u2019actions\"\n title=\"Plus d\u2019actions\"\n (click)=\"toggleMenu($event)\"\n >\n <i class=\"pi pi-ellipsis-v\"></i>\n </button>\n\n <p-menu\n #actionMenu\n [model]=\"actionMenuItems\"\n [popup]=\"true\"\n appendTo=\"body\"\n ></p-menu>\n }\n </div>\n }\n </div>\n\n <div class=\"panel-separator\"></div>\n\n <div class=\"metric-content\">\n @if (isLoading) {\n <div\n class=\"metric-loading-overlay\"\n aria-live=\"polite\"\n aria-label=\"Chargement des donn\u00E9es\"\n >\n <i class=\"pi pi-spinner pi-spin metric-loading-spinner\"></i>\n <span>Chargement des donn\u00E9es...</span>\n </div>\n }\n\n <div\n class=\"metric-list\"\n [class.metric-list-loading]=\"isLoading\"\n [attr.aria-busy]=\"isLoading\"\n >\n @for (item of panelData.indicators; track $index) {\n <div\n class=\"metric-item\"\n [class.metric-item-clickable]=\"!!item.url && !isLoading\"\n [pTooltip]=\"item.tooltip || ''\"\n tooltipPosition=\"top\"\n [tooltipDisabled]=\"!item.tooltip || isLoading\"\n (click)=\"!isLoading && handleClick(item.url)\"\n >\n <div class=\"metric-left\">\n @if (item.icon) {\n <span class=\"metric-icon-wrapper\">\n <i\n [ngClass]=\"getIconClass(item.icon.code)\"\n [ngStyle]=\"getIconStyles(item.icon)\"\n ></i>\n </span>\n }\n\n <span class=\"metric-title\">\n {{ isTitleStyle(item.title) ? item.title.text : item.title }}\n </span>\n </div>\n\n <span\n class=\"metric-value\"\n [class.metric-value-highlighted]=\"item.highlighted && !isLoading\"\n [ngStyle]=\"getValueStyles(item.value)\"\n >\n {{ isTitleStyle(item.value) ? item.value.text : item.value }}\n </span>\n </div>\n }\n </div>\n </div>\n</pt-card>\n", styles: [":host{display:block}.panel-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px}.panel-title-container{display:flex;align-items:center;min-width:0;gap:12px;font-weight:800;line-height:1.2}.panel-title-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;min-width:38px;min-height:38px;padding:10px;color:#6734c9;background:#f0e7ff;border-radius:12px;font-size:1.05rem}.panel-title-text{overflow:hidden;color:inherit;text-overflow:ellipsis;white-space:nowrap}.panel-actions{display:flex;align-items:center;flex:0 0 auto;gap:4px}.panel-action-button{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;cursor:pointer;border:1px solid transparent;border-radius:10px;color:#667085;background:transparent;transition:background-color .16s ease,border-color .16s ease,color .16s ease,transform .16s ease}.panel-action-button:hover:not(:disabled){border-color:#e3d8fb;color:#6734c9;background:#f7f3ff}.panel-action-button:active:not(:disabled){transform:scale(.95)}.panel-action-button:disabled{cursor:wait;opacity:.65}.panel-action-button-loading{color:#6734c9}.panel-action-button:focus-visible{outline:2px solid #8b5cf6;outline-offset:2px}.panel-separator{width:100%;height:1px;margin:0 0 16px;background:#e7eaf0}.metric-list{display:flex;flex-direction:column;gap:12px}.metric-item{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:68px;padding:12px 14px;background:#fff;border:1px solid #edf0f5;border-radius:14px;transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.metric-item:hover{border-color:#ddd2f8;box-shadow:0 5px 14px #4a209014}.metric-item-clickable{cursor:pointer}.metric-item-clickable:hover{transform:translateY(-1px)}.metric-left{display:flex;align-items:center;min-width:0;gap:12px}.metric-icon-wrapper{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}.metric-icon-wrapper i{display:inline-flex!important;align-items:center;justify-content:center;min-width:40px;min-height:40px;box-sizing:border-box}.metric-title{overflow:hidden;color:#344054;font-size:1rem;font-weight:700;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.metric-value{flex:0 0 auto;padding:7px 11px;color:#1f2937;background:#f8f9fc;border:1px solid #e8ebf1;border-radius:10px;font-size:.95rem;font-weight:800;line-height:1;text-align:right;white-space:nowrap}.metric-value-highlighted{color:#15803d!important;background:#dcfce7!important;border-color:#86efac!important;animation:metric-value-flash 1.8s ease-out}@keyframes metric-value-flash{0%{color:#166534;background:#bbf7d0;border-color:#4ade80;transform:scale(1.08)}to{color:#15803d;background:#dcfce7;border-color:#86efac;transform:scale(1)}}@media(max-width:576px){.panel-header,.panel-title-container{gap:8px}.panel-title-icon{min-width:34px;min-height:34px;padding:8px}.panel-action-button{width:32px;height:32px}.metric-item{min-height:60px;padding:10px}.metric-title{font-size:.92rem}.metric-value{padding:6px 8px;font-size:.85rem}}.metric-content{position:relative;min-height:120px}.metric-loading-overlay{position:absolute;z-index:2;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.7rem;min-height:120px;border-radius:14px;color:#6734c9;background:#ffffffd1;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);font-size:.9rem;font-weight:700}.metric-loading-spinner{font-size:1.5rem}.metric-list-loading{pointer-events:none;-webkit-user-select:none;user-select:none;opacity:.42;filter:blur(.4px)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: PTCardComponent, selector: "pt-card", inputs: ["config"] }, { kind: "directive", type: i12.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i5$1.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }] }); }
9338
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTMetricPanelComponent, isStandalone: false, selector: "pt-metric-panel", inputs: { panelData: "panelData", cardConfig: "cardConfig", showRefresh: "showRefresh", showMenu: "showMenu", menuItems: "menuItems", loading: "loading" }, outputs: { refresh: "refresh" }, viewQueries: [{ propertyName: "actionMenu", first: true, predicate: ["actionMenu"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<pt-card [config]=\"cardConfig\">\n <div class=\"panel-header\">\n <div class=\"panel-title-container\" [ngStyle]=\"getTitleStyles()\">\n @if (getTitleIcon()) {\n <i\n class=\"panel-title-icon\"\n [ngClass]=\"getIconClass(getTitleIcon())\"\n [ngStyle]=\"getTitleIconStyles()\"\n ></i>\n }\n\n <span class=\"panel-title-text\">\n {{ getTitleText() }}\n </span>\n </div>\n\n @if (showRefresh || shouldShowMenu) {\n <div class=\"panel-actions\">\n @if (showRefresh) {\n <button\n type=\"button\"\n class=\"panel-action-button\"\n [class.panel-action-button-loading]=\"isLoading\"\n [disabled]=\"isLoading\"\n aria-label=\"Actualiser\"\n title=\"Actualiser\"\n (click)=\"onRefreshClick($event)\"\n >\n <i\n class=\"pi\"\n [ngClass]=\"isLoading ? 'pi-spin pi-spinner' : 'pi-refresh'\"\n ></i>\n </button>\n }\n\n @if (shouldShowMenu) {\n <button\n type=\"button\"\n class=\"panel-action-button\"\n [disabled]=\"isLoading\"\n aria-label=\"Plus d\u2019actions\"\n title=\"Plus d\u2019actions\"\n (click)=\"toggleMenu($event)\"\n >\n <i class=\"pi pi-ellipsis-v\"></i>\n </button>\n\n <p-menu\n #actionMenu\n [model]=\"actionMenuItems\"\n [popup]=\"true\"\n appendTo=\"body\"\n ></p-menu>\n }\n </div>\n }\n </div>\n\n <div class=\"panel-separator\"></div>\n\n <div class=\"metric-content\">\n @if (isLoading) {\n <div\n class=\"metric-loading-overlay\"\n aria-live=\"polite\"\n aria-label=\"Chargement des donn\u00E9es\"\n >\n <i class=\"pi pi-spinner pi-spin metric-loading-spinner\"></i>\n <span>Chargement des donn\u00E9es...</span>\n </div>\n }\n\n <div\n class=\"metric-list\"\n [class.metric-list-loading]=\"isLoading\"\n [attr.aria-busy]=\"isLoading\"\n >\n @for (item of panelData.indicators; track $index) {\n <div\n class=\"metric-item\"\n [class.metric-item-clickable]=\"!!item.url && !isLoading\"\n [pTooltip]=\"item.tooltip || ''\"\n tooltipPosition=\"top\"\n [tooltipDisabled]=\"!item.tooltip || isLoading\"\n (click)=\"!isLoading && handleClick(item.url)\"\n >\n <div class=\"metric-left\">\n @if (item.icon) {\n <span class=\"metric-icon-wrapper\">\n <i\n [ngClass]=\"getIconClass(item.icon.code)\"\n [ngStyle]=\"getIconStyles(item.icon)\"\n ></i>\n </span>\n }\n\n <span class=\"metric-title\">\n {{ isTitleStyle(item.title) ? item.title.text : item.title }}\n </span>\n </div>\n\n <span\n class=\"metric-value\"\n [class.metric-value-highlighted]=\"item.highlighted && !isLoading\"\n [ngStyle]=\"getValueStyles(item.value)\"\n >\n {{ isTitleStyle(item.value) ? item.value.text : item.value }}\n </span>\n </div>\n }\n </div>\n </div>\n</pt-card>\n", styles: [":host{display:block}.panel-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px}.panel-title-container{display:flex;align-items:center;min-width:0;gap:12px;font-weight:800;line-height:1.2}.panel-title-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;min-width:38px;min-height:38px;padding:10px;color:#6734c9;background:#f0e7ff;border-radius:12px;font-size:1.05rem}.panel-title-text{overflow:hidden;color:inherit;text-overflow:ellipsis;white-space:nowrap}.panel-actions{display:flex;align-items:center;flex:0 0 auto;gap:4px}.panel-action-button{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;cursor:pointer;border:1px solid transparent;border-radius:10px;color:#667085;background:transparent;transition:background-color .16s ease,border-color .16s ease,color .16s ease,transform .16s ease}.panel-action-button:hover:not(:disabled){border-color:#e3d8fb;color:#6734c9;background:#f7f3ff}.panel-action-button:active:not(:disabled){transform:scale(.95)}.panel-action-button:disabled{cursor:wait;opacity:.65}.panel-action-button-loading{color:#6734c9}.panel-action-button:focus-visible{outline:2px solid #8b5cf6;outline-offset:2px}.panel-separator{width:100%;height:1px;margin:0 0 16px;background:#e7eaf0}.metric-list{display:flex;flex-direction:column;gap:12px}.metric-item{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:68px;padding:12px 14px;background:#fff;border:1px solid #edf0f5;border-radius:14px;transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.metric-item:hover{border-color:#ddd2f8;box-shadow:0 5px 14px #4a209014}.metric-item-clickable{cursor:pointer}.metric-item-clickable:hover{transform:translateY(-1px)}.metric-left{display:flex;align-items:center;min-width:0;gap:12px}.metric-icon-wrapper{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}.metric-icon-wrapper i{display:inline-flex!important;align-items:center;justify-content:center;min-width:40px;min-height:40px;box-sizing:border-box}.metric-title{overflow:hidden;color:#344054;font-size:1rem;font-weight:700;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.metric-value{flex:0 0 auto;padding:7px 11px;color:#1f2937;background:#f8f9fc;border:1px solid #e8ebf1;border-radius:10px;font-size:.95rem;font-weight:800;line-height:1;text-align:right;white-space:nowrap}.metric-value-highlighted{color:#15803d!important;background:#dcfce7!important;border-color:#86efac!important;animation:metric-value-flash 1.8s ease-out}@keyframes metric-value-flash{0%{color:#166534;background:#bbf7d0;border-color:#4ade80;transform:scale(1.08)}to{color:#15803d;background:#dcfce7;border-color:#86efac;transform:scale(1)}}@media(max-width:576px){.panel-header,.panel-title-container{gap:8px}.panel-title-icon{min-width:34px;min-height:34px;padding:8px}.panel-action-button{width:32px;height:32px}.metric-item{min-height:60px;padding:10px}.metric-title{font-size:.92rem}.metric-value{padding:6px 8px;font-size:.85rem}}.metric-content{position:relative;min-height:120px}.metric-loading-overlay{position:absolute;z-index:2;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.7rem;min-height:120px;border-radius:14px;color:#6734c9;background:#ffffffd1;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);font-size:.9rem;font-weight:700}.metric-loading-spinner{font-size:1.5rem}.metric-list-loading{pointer-events:none;-webkit-user-select:none;user-select:none;opacity:.42;filter:blur(.4px)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: PTCardComponent, selector: "pt-card", inputs: ["config"] }, { kind: "directive", type: i13.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: i5$1.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }] }); }
8687
9339
  }
8688
9340
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTMetricPanelComponent, decorators: [{
8689
9341
  type: Component,
@@ -9119,6 +9771,543 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9119
9771
  }]
9120
9772
  }] });
9121
9773
 
9774
+ var PTNewsTickerDirection;
9775
+ (function (PTNewsTickerDirection) {
9776
+ PTNewsTickerDirection["LEFT"] = "left";
9777
+ PTNewsTickerDirection["RIGHT"] = "right";
9778
+ })(PTNewsTickerDirection || (PTNewsTickerDirection = {}));
9779
+ var PTNewsTickerType;
9780
+ (function (PTNewsTickerType) {
9781
+ PTNewsTickerType["DEFAULT"] = "DEFAULT";
9782
+ PTNewsTickerType["INFO"] = "INFO";
9783
+ PTNewsTickerType["SUCCESS"] = "SUCCESS";
9784
+ PTNewsTickerType["WARNING"] = "WARNING";
9785
+ PTNewsTickerType["DANGER"] = "DANGER";
9786
+ PTNewsTickerType["FLASH"] = "FLASH";
9787
+ })(PTNewsTickerType || (PTNewsTickerType = {}));
9788
+
9789
+ class PTNewsTickerComponent {
9790
+ constructor(changeDetectorRef, zone) {
9791
+ this.changeDetectorRef = changeDetectorRef;
9792
+ this.zone = zone;
9793
+ this.news = [];
9794
+ this.speed = 30;
9795
+ this.defaultSpeed = 30;
9796
+ this.speedStep = 10;
9797
+ this.minSpeed = 5;
9798
+ this.maxSpeed = 200;
9799
+ this.direction = PTNewsTickerDirection.LEFT;
9800
+ this.autoplay = true;
9801
+ this.loop = true;
9802
+ this.pauseOnHover = true;
9803
+ this.showControls = false;
9804
+ this.showPlayPauseControl = true;
9805
+ this.showSpeedControls = true;
9806
+ this.showResetSpeedControl = true;
9807
+ this.showRemoveButton = true;
9808
+ this.showSpeedValue = false;
9809
+ this.height = '3.25rem';
9810
+ this.gap = '2rem';
9811
+ this.itemGap = '0.5rem';
9812
+ this.containerBackgroundColor = '#ffffff';
9813
+ this.containerTextColor = '#0f172a';
9814
+ this.borderColor = '#e2e8f0';
9815
+ this.borderWidth = '1px';
9816
+ this.borderRadius = '8px';
9817
+ this.controlsBackgroundColor = '#f8fafc';
9818
+ this.controlsBorderColor = '#e2e8f0';
9819
+ this.emptyMessage = 'Aucune actualité disponible';
9820
+ this.emptyIcon = 'pi pi-info-circle';
9821
+ this.ariaLabel = 'Ruban d’actualités';
9822
+ this.newsClick = new EventEmitter();
9823
+ this.newsRemove = new EventEmitter();
9824
+ this.newsChange = new EventEmitter();
9825
+ this.playStateChange = new EventEmitter();
9826
+ this.speedChange = new EventEmitter();
9827
+ this.PTNewsTickerType = PTNewsTickerType;
9828
+ this.internalNews = [];
9829
+ this.animationDurationSeconds = 20;
9830
+ this.playing = true;
9831
+ this.hovered = false;
9832
+ this.animationFrameId = null;
9833
+ this.viewInitialized = false;
9834
+ }
9835
+ ngOnChanges(changes) {
9836
+ if (changes['news']) {
9837
+ this.internalNews = this.normalizeNews(changes['news'].currentValue ?? []);
9838
+ this.scheduleAnimationRefresh();
9839
+ }
9840
+ if (changes['speed'] || changes['minSpeed'] || changes['maxSpeed']) {
9841
+ this.speed = this.normalizeSpeed(this.speed);
9842
+ this.scheduleAnimationRefresh();
9843
+ }
9844
+ if (changes['defaultSpeed']) {
9845
+ this.defaultSpeed = this.normalizeSpeed(this.defaultSpeed);
9846
+ }
9847
+ if (changes['autoplay']) {
9848
+ this.playing = this.autoplay;
9849
+ this.emitPlayStateChange();
9850
+ }
9851
+ if (changes['gap'] ||
9852
+ changes['height'] ||
9853
+ changes['direction'] ||
9854
+ changes['loop']) {
9855
+ this.scheduleAnimationRefresh();
9856
+ }
9857
+ }
9858
+ ngAfterViewInit() {
9859
+ this.viewInitialized = true;
9860
+ this.playing = this.autoplay;
9861
+ this.initializeResizeObserver();
9862
+ this.scheduleAnimationRefresh();
9863
+ this.changeDetectorRef.detectChanges();
9864
+ }
9865
+ ngOnDestroy() {
9866
+ this.resizeObserver?.disconnect();
9867
+ this.resizeObserver = undefined;
9868
+ if (this.animationFrameId !== null) {
9869
+ cancelAnimationFrame(this.animationFrameId);
9870
+ this.animationFrameId = null;
9871
+ }
9872
+ }
9873
+ addNews(item) {
9874
+ if (!item || item.id === null || item.id === undefined) {
9875
+ return;
9876
+ }
9877
+ const normalizedItem = this.normalizeItem(item);
9878
+ const existingIndex = this.internalNews.findIndex((newsItem) => newsItem.id === normalizedItem.id);
9879
+ if (existingIndex >= 0) {
9880
+ this.internalNews = this.internalNews.map((newsItem, index) => index === existingIndex ? normalizedItem : newsItem);
9881
+ }
9882
+ else {
9883
+ this.internalNews = [...this.internalNews, normalizedItem];
9884
+ }
9885
+ this.emitNewsChange({
9886
+ news: [...this.internalNews],
9887
+ addedItem: normalizedItem,
9888
+ });
9889
+ this.scheduleAnimationRefresh();
9890
+ }
9891
+ removeNews(itemOrId) {
9892
+ const id = typeof itemOrId === 'object' ? itemOrId?.id : itemOrId;
9893
+ const removedItem = this.internalNews.find((item) => item.id === id);
9894
+ if (!removedItem) {
9895
+ return;
9896
+ }
9897
+ this.internalNews = this.internalNews.filter((item) => item.id !== id);
9898
+ this.newsRemove.emit(removedItem);
9899
+ this.emitNewsChange({
9900
+ news: [...this.internalNews],
9901
+ removedItem,
9902
+ });
9903
+ this.scheduleAnimationRefresh();
9904
+ }
9905
+ clearNews() {
9906
+ this.internalNews = [];
9907
+ this.emitNewsChange({
9908
+ news: [],
9909
+ });
9910
+ this.scheduleAnimationRefresh();
9911
+ }
9912
+ replaceNews(news) {
9913
+ this.internalNews = this.normalizeNews(news);
9914
+ this.emitNewsChange({
9915
+ news: [...this.internalNews],
9916
+ });
9917
+ this.scheduleAnimationRefresh();
9918
+ }
9919
+ play() {
9920
+ if (this.playing) {
9921
+ return;
9922
+ }
9923
+ this.playing = true;
9924
+ this.emitPlayStateChange();
9925
+ }
9926
+ pause() {
9927
+ if (!this.playing) {
9928
+ return;
9929
+ }
9930
+ this.playing = false;
9931
+ this.emitPlayStateChange();
9932
+ }
9933
+ togglePlayState() {
9934
+ this.playing = !this.playing;
9935
+ this.emitPlayStateChange();
9936
+ }
9937
+ increaseSpeed() {
9938
+ this.setSpeed(this.speed + this.normalizeSpeedStep());
9939
+ }
9940
+ decreaseSpeed() {
9941
+ this.setSpeed(this.speed - this.normalizeSpeedStep());
9942
+ }
9943
+ resetSpeed() {
9944
+ this.setSpeed(this.defaultSpeed);
9945
+ }
9946
+ setSpeed(speed) {
9947
+ const previousSpeed = this.speed;
9948
+ const nextSpeed = this.normalizeSpeed(speed);
9949
+ if (nextSpeed === previousSpeed) {
9950
+ return;
9951
+ }
9952
+ this.speed = nextSpeed;
9953
+ this.speedChange.emit({
9954
+ previousSpeed,
9955
+ speed: nextSpeed,
9956
+ });
9957
+ this.emitPlayStateChange();
9958
+ this.scheduleAnimationRefresh();
9959
+ }
9960
+ onMouseEnter() {
9961
+ if (!this.pauseOnHover) {
9962
+ return;
9963
+ }
9964
+ this.hovered = true;
9965
+ }
9966
+ onMouseLeave() {
9967
+ this.hovered = false;
9968
+ }
9969
+ onItemClick(event, item) {
9970
+ if (item.disabled) {
9971
+ event.preventDefault();
9972
+ event.stopPropagation();
9973
+ return;
9974
+ }
9975
+ this.newsClick.emit(item);
9976
+ const link = item.link?.trim();
9977
+ if (!link) {
9978
+ return;
9979
+ }
9980
+ event.preventDefault();
9981
+ const target = item.target ?? '_self';
9982
+ if (target === '_blank') {
9983
+ window.open(link, '_blank', 'noopener,noreferrer');
9984
+ return;
9985
+ }
9986
+ window.location.assign(link);
9987
+ }
9988
+ hasItemLink(item) {
9989
+ return !!item.link?.trim() && !item.disabled;
9990
+ }
9991
+ onRemoveClick(event, item) {
9992
+ event.preventDefault();
9993
+ event.stopPropagation();
9994
+ if (item.disabled || item.removable === false) {
9995
+ return;
9996
+ }
9997
+ this.removeNews(item);
9998
+ }
9999
+ isItemRemovable(item) {
10000
+ return this.showRemoveButton && item.removable !== false && !item.disabled;
10001
+ }
10002
+ isItemFlash(item) {
10003
+ return item.flash === true || item.type === PTNewsTickerType.FLASH;
10004
+ }
10005
+ getTrackStyle() {
10006
+ return {
10007
+ '--pt-news-ticker-duration': `${this.animationDurationSeconds}s`,
10008
+ '--pt-news-ticker-gap': this.gap,
10009
+ animationDirection: this.direction === PTNewsTickerDirection.RIGHT ? 'reverse' : 'normal',
10010
+ animationPlayState: this.getAnimationPlayState(),
10011
+ };
10012
+ }
10013
+ getContainerStyle() {
10014
+ return {
10015
+ '--pt-news-ticker-height': this.height,
10016
+ '--pt-news-ticker-background': this.containerBackgroundColor,
10017
+ '--pt-news-ticker-text-color': this.containerTextColor,
10018
+ '--pt-news-ticker-border-color': this.borderColor,
10019
+ '--pt-news-ticker-border-width': this.borderWidth,
10020
+ '--pt-news-ticker-border-radius': this.borderRadius,
10021
+ '--pt-news-ticker-controls-background': this.controlsBackgroundColor,
10022
+ '--pt-news-ticker-controls-border-color': this.controlsBorderColor,
10023
+ };
10024
+ }
10025
+ getItemStyle(item) {
10026
+ const typeStyles = this.getTypeStyles(item.type);
10027
+ const backgroundColor = item.backgroundColor ?? typeStyles.backgroundColor;
10028
+ const textColor = item.textColor ?? typeStyles.textColor;
10029
+ const iconColor = item.iconColor ?? textColor ?? this.containerTextColor;
10030
+ const borderColor = item.borderColor ?? typeStyles.borderColor ?? 'transparent';
10031
+ const flashBackgroundColor = item.flashBackgroundColor ??
10032
+ item.backgroundColor ??
10033
+ typeStyles.backgroundColor ??
10034
+ '#dc2626';
10035
+ const flashAlternateBackgroundColor = item.flashAlternateBackgroundColor ?? '#7f1d1d';
10036
+ const flashTextColor = item.flashTextColor ?? item.textColor ?? '#ffffff';
10037
+ return {
10038
+ '--pt-news-item-background': backgroundColor ?? 'transparent',
10039
+ '--pt-news-item-text-color': textColor ?? this.containerTextColor,
10040
+ '--pt-news-item-icon-color': iconColor,
10041
+ '--pt-news-item-border-color': borderColor,
10042
+ '--pt-news-item-gap': this.itemGap,
10043
+ '--pt-news-flash-background': flashBackgroundColor,
10044
+ '--pt-news-flash-alternate-background': flashAlternateBackgroundColor,
10045
+ '--pt-news-flash-text-color': flashTextColor,
10046
+ '--pt-news-flash-duration': `${Math.max(100, Number(item.flashDuration ?? 700))}ms`,
10047
+ };
10048
+ }
10049
+ getItemClasses(item) {
10050
+ const classes = {
10051
+ 'pt-news-ticker__item--flash': this.isItemFlash(item),
10052
+ 'pt-news-ticker__item--disabled': item.disabled === true,
10053
+ 'pt-news-ticker__item--clickable': !item.disabled &&
10054
+ (this.hasItemLink(item) || this.newsClick.observers.length > 0),
10055
+ 'pt-news-ticker__item--link': this.hasItemLink(item),
10056
+ };
10057
+ if (item.styleClass?.trim()) {
10058
+ classes[item.styleClass.trim()] = true;
10059
+ }
10060
+ return classes;
10061
+ }
10062
+ getTrackById(index, item) {
10063
+ return `${item.id}-${index}`;
10064
+ }
10065
+ getDuplicateTrackById(index, item) {
10066
+ return `duplicate-${item.id}-${index}`;
10067
+ }
10068
+ getAnimationPlayState() {
10069
+ if (!this.playing) {
10070
+ return 'paused';
10071
+ }
10072
+ if (this.pauseOnHover && this.hovered) {
10073
+ return 'paused';
10074
+ }
10075
+ return 'running';
10076
+ }
10077
+ initializeResizeObserver() {
10078
+ if (typeof ResizeObserver === 'undefined' ||
10079
+ !this.tickerGroup?.nativeElement) {
10080
+ return;
10081
+ }
10082
+ this.zone.runOutsideAngular(() => {
10083
+ this.resizeObserver = new ResizeObserver(() => {
10084
+ this.zone.run(() => {
10085
+ this.calculateAnimationDuration();
10086
+ });
10087
+ });
10088
+ this.resizeObserver.observe(this.tickerGroup.nativeElement);
10089
+ if (this.tickerViewport?.nativeElement) {
10090
+ this.resizeObserver.observe(this.tickerViewport.nativeElement);
10091
+ }
10092
+ });
10093
+ }
10094
+ scheduleAnimationRefresh() {
10095
+ if (!this.viewInitialized) {
10096
+ return;
10097
+ }
10098
+ if (this.animationFrameId !== null) {
10099
+ cancelAnimationFrame(this.animationFrameId);
10100
+ }
10101
+ this.animationFrameId = requestAnimationFrame(() => {
10102
+ this.animationFrameId = null;
10103
+ this.calculateAnimationDuration();
10104
+ });
10105
+ }
10106
+ calculateAnimationDuration() {
10107
+ const groupElement = this.tickerGroup?.nativeElement;
10108
+ if (!groupElement || this.internalNews.length === 0) {
10109
+ this.animationDurationSeconds = 1;
10110
+ return;
10111
+ }
10112
+ const contentWidth = Math.max(groupElement.scrollWidth, groupElement.getBoundingClientRect().width, 1);
10113
+ const normalizedSpeed = this.normalizeSpeed(this.speed);
10114
+ this.animationDurationSeconds = Math.max(1, contentWidth / normalizedSpeed);
10115
+ this.changeDetectorRef.markForCheck();
10116
+ }
10117
+ normalizeNews(news) {
10118
+ if (!Array.isArray(news)) {
10119
+ return [];
10120
+ }
10121
+ return news
10122
+ .filter((item) => item &&
10123
+ item.id !== null &&
10124
+ item.id !== undefined &&
10125
+ String(item.text ?? '').trim().length > 0)
10126
+ .map((item) => this.normalizeItem(item));
10127
+ }
10128
+ normalizeItem(item) {
10129
+ return {
10130
+ ...item,
10131
+ text: String(item.text ?? '').trim(),
10132
+ type: item.type ?? PTNewsTickerType.DEFAULT,
10133
+ target: item.target ?? '_self',
10134
+ removable: item.removable ?? true,
10135
+ disabled: item.disabled ?? false,
10136
+ flashDuration: Math.max(100, Number(item.flashDuration ?? 700)),
10137
+ };
10138
+ }
10139
+ normalizeSpeed(speed) {
10140
+ const numericSpeed = Number(speed);
10141
+ const safeMinSpeed = Math.max(1, Number(this.minSpeed) || 1);
10142
+ const safeMaxSpeed = Math.max(safeMinSpeed, Number(this.maxSpeed) || safeMinSpeed);
10143
+ if (!Number.isFinite(numericSpeed)) {
10144
+ return safeMinSpeed;
10145
+ }
10146
+ return Math.min(safeMaxSpeed, Math.max(safeMinSpeed, numericSpeed));
10147
+ }
10148
+ normalizeSpeedStep() {
10149
+ const step = Number(this.speedStep);
10150
+ if (!Number.isFinite(step) || step <= 0) {
10151
+ return 5;
10152
+ }
10153
+ return step;
10154
+ }
10155
+ emitNewsChange(event) {
10156
+ this.newsChange.emit(event);
10157
+ }
10158
+ emitPlayStateChange() {
10159
+ this.playStateChange.emit({
10160
+ playing: this.playing,
10161
+ speed: this.speed,
10162
+ });
10163
+ }
10164
+ getTypeStyles(type) {
10165
+ switch (type) {
10166
+ case PTNewsTickerType.INFO:
10167
+ return {
10168
+ backgroundColor: '#dbeafe',
10169
+ textColor: '#1e3a8a',
10170
+ borderColor: '#93c5fd',
10171
+ };
10172
+ case PTNewsTickerType.SUCCESS:
10173
+ return {
10174
+ backgroundColor: '#dcfce7',
10175
+ textColor: '#14532d',
10176
+ borderColor: '#86efac',
10177
+ };
10178
+ case PTNewsTickerType.WARNING:
10179
+ return {
10180
+ backgroundColor: '#fef3c7',
10181
+ textColor: '#78350f',
10182
+ borderColor: '#fcd34d',
10183
+ };
10184
+ case PTNewsTickerType.DANGER:
10185
+ return {
10186
+ backgroundColor: '#fee2e2',
10187
+ textColor: '#7f1d1d',
10188
+ borderColor: '#fca5a5',
10189
+ };
10190
+ case PTNewsTickerType.FLASH:
10191
+ return {
10192
+ backgroundColor: '#dc2626',
10193
+ textColor: '#ffffff',
10194
+ borderColor: '#991b1b',
10195
+ };
10196
+ default:
10197
+ return {
10198
+ backgroundColor: '#f8fafc',
10199
+ textColor: '#0f172a',
10200
+ borderColor: '#e2e8f0',
10201
+ };
10202
+ }
10203
+ }
10204
+ getItemRole(item) {
10205
+ return this.hasItemLink(item) ? 'link' : null;
10206
+ }
10207
+ getItemTabIndex(item) {
10208
+ if (item.disabled) {
10209
+ return -1;
10210
+ }
10211
+ return this.hasItemLink(item) || this.newsClick.observers.length > 0
10212
+ ? 0
10213
+ : -1;
10214
+ }
10215
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
10216
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTNewsTickerComponent, isStandalone: false, selector: "pt-news-ticker", inputs: { news: "news", speed: "speed", defaultSpeed: "defaultSpeed", speedStep: "speedStep", minSpeed: "minSpeed", maxSpeed: "maxSpeed", direction: "direction", autoplay: "autoplay", loop: "loop", pauseOnHover: "pauseOnHover", showControls: "showControls", showPlayPauseControl: "showPlayPauseControl", showSpeedControls: "showSpeedControls", showResetSpeedControl: "showResetSpeedControl", showRemoveButton: "showRemoveButton", showSpeedValue: "showSpeedValue", height: "height", gap: "gap", itemGap: "itemGap", containerBackgroundColor: "containerBackgroundColor", containerTextColor: "containerTextColor", borderColor: "borderColor", borderWidth: "borderWidth", borderRadius: "borderRadius", controlsBackgroundColor: "controlsBackgroundColor", controlsBorderColor: "controlsBorderColor", emptyMessage: "emptyMessage", emptyIcon: "emptyIcon", ariaLabel: "ariaLabel" }, outputs: { newsClick: "newsClick", newsRemove: "newsRemove", newsChange: "newsChange", playStateChange: "playStateChange", speedChange: "speedChange" }, viewQueries: [{ propertyName: "tickerViewport", first: true, predicate: ["tickerViewport"], descendants: true }, { propertyName: "tickerGroup", first: true, predicate: ["tickerGroup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"pt-news-ticker\"\n [ngStyle]=\"getContainerStyle()\"\n [attr.aria-label]=\"ariaLabel\"\n role=\"region\"\n>\n @if (showControls) {\n <div class=\"pt-news-ticker__controls\">\n @if (showPlayPauseControl) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n [icon]=\"playing ? 'pi pi-pause' : 'pi pi-play'\"\n [pTooltip]=\"playing ? 'Mettre en pause' : 'Reprendre'\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-label]=\"\n playing ? 'Mettre le ruban en pause' : 'Reprendre le ruban'\n \"\n (click)=\"togglePlayState()\"\n ></button>\n }\n\n @if (showSpeedControls) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-minus\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Ralentir\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"Ralentir le ruban\"\n [disabled]=\"speed <= minSpeed\"\n (click)=\"decreaseSpeed()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-plus\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Acc\u00E9l\u00E9rer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"Acc\u00E9l\u00E9rer le ruban\"\n [disabled]=\"speed >= maxSpeed\"\n (click)=\"increaseSpeed()\"\n ></button>\n }\n\n @if (showResetSpeedControl) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-refresh\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"R\u00E9initialiser la vitesse\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"R\u00E9initialiser la vitesse\"\n [disabled]=\"speed === defaultSpeed\"\n (click)=\"resetSpeed()\"\n ></button>\n }\n\n @if (showSpeedValue) {\n <span\n class=\"pt-news-ticker__speed\"\n [attr.aria-label]=\"'Vitesse ' + speed + ' pixels par seconde'\"\n >\n {{ speed }} px/s\n </span>\n }\n </div>\n }\n\n <div\n #tickerViewport\n class=\"pt-news-ticker__viewport\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n >\n @if (internalNews.length > 0) {\n <div\n class=\"pt-news-ticker__track\"\n [class.pt-news-ticker__track--single]=\"!loop\"\n [ngStyle]=\"getTrackStyle()\"\n >\n <div\n #tickerGroup\n class=\"pt-news-ticker__group\"\n [attr.aria-hidden]=\"false\"\n >\n @for (item of internalNews; track getTrackById($index, item)) {\n <article\n class=\"pt-news-ticker__item\"\n [ngClass]=\"getItemClasses(item)\"\n [ngStyle]=\"getItemStyle(item)\"\n [pTooltip]=\"item.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-label]=\"\n hasItemLink(item) ? item.text + ', ouvrir le lien' : item.text\n \"\n [attr.role]=\"getItemRole(item)\"\n [attr.tabindex]=\"getItemTabIndex(item)\"\n (click)=\"onItemClick($event, item)\"\n (keydown.enter)=\"onItemClick($event, item)\"\n (keydown.space)=\"onItemClick($event, item)\"\n >\n @if (item.icon) {\n <i\n class=\"pt-news-ticker__item-icon\"\n [ngClass]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n\n <span class=\"pt-news-ticker__item-text\">\n {{ item.text }}\n </span>\n\n @if (hasItemLink(item)) {\n <i\n class=\"pt-news-ticker__link-icon pi pi-external-link\"\n aria-hidden=\"true\"\n ></i>\n }\n\n @if (isItemRemovable(item)) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"pt-news-ticker__remove-button p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Supprimer l\u2019actualit\u00E9\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-label]=\"'Supprimer : ' + item.text\"\n (click)=\"onRemoveClick($event, item)\"\n ></button>\n }\n </article>\n }\n </div>\n\n @if (loop) {\n <div class=\"pt-news-ticker__group\" aria-hidden=\"true\">\n @for (\n item of internalNews;\n track getDuplicateTrackById($index, item)\n ) {\n <article\n class=\"pt-news-ticker__item\"\n [ngClass]=\"getItemClasses(item)\"\n [ngStyle]=\"getItemStyle(item)\"\n >\n @if (item.icon) {\n <i\n class=\"pt-news-ticker__item-icon\"\n [ngClass]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n\n <span class=\"pt-news-ticker__item-text\">\n {{ item.text }}\n </span>\n\n @if (hasItemLink(item)) {\n <i\n class=\"pt-news-ticker__link-icon pi pi-external-link\"\n aria-hidden=\"true\"\n ></i>\n }\n\n @if (isItemRemovable(item)) {\n <span\n class=\"pt-news-ticker__duplicate-remove-placeholder\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-times\"></i>\n </span>\n }\n </article>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"pt-news-ticker__empty\">\n @if (emptyIcon) {\n <i [ngClass]=\"emptyIcon\" aria-hidden=\"true\"></i>\n }\n\n <span>{{ emptyMessage }}</span>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-news-ticker{display:flex;align-items:stretch;width:100%;min-width:0;max-width:100%;min-height:var(--pt-news-ticker-height, 3.25rem);overflow:hidden;border:var(--pt-news-ticker-border-width, 1px) solid var(--pt-news-ticker-border-color, #e2e8f0);border-radius:var(--pt-news-ticker-border-radius, 8px);background:var(--pt-news-ticker-background, #ffffff);color:var(--pt-news-ticker-text-color, #0f172a)}.pt-news-ticker__controls{position:relative;z-index:4;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;gap:.1rem;min-height:var(--pt-news-ticker-height, 3.25rem);padding:.25rem .4rem;border-right:1px solid var(--pt-news-ticker-controls-border-color, #e2e8f0);background:var(--pt-news-ticker-controls-background, #f8fafc)}.pt-news-ticker__controls ::ng-deep .p-button.p-button-rounded.p-button-text{width:1.9rem;min-width:1.9rem;height:1.9rem;padding:0}.pt-news-ticker__controls ::ng-deep .p-button .p-button-icon{font-size:.78rem}.pt-news-ticker__speed{min-width:3.5rem;padding:0 .35rem;color:#475569;font-size:.7rem;font-weight:700;text-align:center;white-space:nowrap}.pt-news-ticker__viewport{position:relative;display:flex;align-items:center;flex:1 1 auto;width:100%;min-width:0;min-height:var(--pt-news-ticker-height, 3.25rem);overflow:hidden}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{position:absolute;z-index:3;top:0;bottom:0;width:2.5rem;content:\"\";pointer-events:none}.pt-news-ticker__viewport:before{left:0;background:linear-gradient(to right,var(--pt-news-ticker-background, #ffffff),transparent)}.pt-news-ticker__viewport:after{right:0;background:linear-gradient(to left,var(--pt-news-ticker-background, #ffffff),transparent)}.pt-news-ticker__track{display:flex;align-items:center;flex:0 0 auto;width:max-content;min-width:max-content;animation-name:pt-news-ticker-scroll;animation-duration:var(--pt-news-ticker-duration, 20s);animation-timing-function:linear;animation-iteration-count:infinite;will-change:transform}.pt-news-ticker__track--single{animation-iteration-count:1;animation-fill-mode:forwards}.pt-news-ticker__group{display:inline-flex;align-items:center;flex:0 0 auto;gap:var(--pt-news-ticker-gap, 2rem);min-width:max-content;padding-right:var(--pt-news-ticker-gap, 2rem)}.pt-news-ticker__item{display:inline-flex;align-items:center;flex:0 0 auto;gap:var(--pt-news-item-gap, .5rem);min-height:2.25rem;max-width:36rem;padding:.35rem .65rem;overflow:hidden;border:1px solid var(--pt-news-item-border-color, transparent);border-radius:999px;outline:none;background:var(--pt-news-item-background, transparent);color:var( --pt-news-item-text-color, var(--pt-news-ticker-text-color, #0f172a) );line-height:1.25;white-space:nowrap;transition:box-shadow .15s ease,opacity .15s ease,transform .15s ease}.pt-news-ticker__item--clickable{cursor:pointer}.pt-news-ticker__item--clickable:hover{box-shadow:0 3px 10px #0f172a1f;transform:translateY(-1px)}.pt-news-ticker__item:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--primary-color, #3b82f6)}.pt-news-ticker__item--disabled{cursor:not-allowed;opacity:.5}.pt-news-ticker__item-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;color:var(--pt-news-item-icon-color, currentColor);font-size:.95rem}.pt-news-ticker__item-text{display:block;flex:1 1 auto;min-width:0;overflow:hidden;font-size:.85rem;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.pt-news-ticker__remove-button{flex:0 0 auto}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button{width:1.45rem;min-width:1.45rem;height:1.45rem;padding:0;color:currentColor}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button .p-button-icon{font-size:.65rem}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button:not(:disabled):hover{background:#0f172a1a}.pt-news-ticker__duplicate-remove-placeholder{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:1.45rem;min-width:1.45rem;height:1.45rem;opacity:0;pointer-events:none}.pt-news-ticker__duplicate-remove-placeholder .pi{font-size:.65rem}.pt-news-ticker__item--flash{border-color:var(--pt-news-item-border-color, #991b1b);color:var(--pt-news-flash-text-color, #ffffff);animation-name:pt-news-ticker-flash;animation-duration:var(--pt-news-flash-duration, .7s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-direction:alternate}.pt-news-ticker__item--flash .pt-news-ticker__item-icon{color:var(--pt-news-item-icon-color, #ffffff)}.pt-news-ticker__empty{display:flex;align-items:center;justify-content:center;gap:.5rem;width:100%;min-height:var(--pt-news-ticker-height, 3.25rem);padding:.75rem 1rem;color:#64748b;font-size:.85rem;font-weight:500;text-align:center}.pt-news-ticker__empty .pi{font-size:1rem}@keyframes pt-news-ticker-scroll{0%{transform:translateZ(0)}to{transform:translate3d(-50%,0,0)}}@keyframes pt-news-ticker-flash{0%{background-color:var(--pt-news-flash-background, #dc2626)}to{background-color:var(--pt-news-flash-alternate-background, #7f1d1d)}}@media(max-width:768px){.pt-news-ticker{min-height:3rem}.pt-news-ticker__controls{padding-right:.25rem;padding-left:.25rem}.pt-news-ticker__speed{display:none}.pt-news-ticker__item{max-width:22rem}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{width:1.25rem}}@media(max-width:480px){.pt-news-ticker__item{max-width:17rem}.pt-news-ticker__item-text{font-size:.78rem}}@media(prefers-reduced-motion:reduce){.pt-news-ticker__track{animation:none!important;transform:none!important}.pt-news-ticker__item--flash{animation:none!important;background-color:var(--pt-news-flash-background, #dc2626)!important}.pt-news-ticker__viewport{overflow-x:auto}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{display:none}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "directive", type: i13.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }] }); }
10217
+ }
10218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerComponent, decorators: [{
10219
+ type: Component,
10220
+ args: [{ selector: 'pt-news-ticker', standalone: false, template: "<div\n class=\"pt-news-ticker\"\n [ngStyle]=\"getContainerStyle()\"\n [attr.aria-label]=\"ariaLabel\"\n role=\"region\"\n>\n @if (showControls) {\n <div class=\"pt-news-ticker__controls\">\n @if (showPlayPauseControl) {\n <button\n pButton\n pRipple\n type=\"button\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n [icon]=\"playing ? 'pi pi-pause' : 'pi pi-play'\"\n [pTooltip]=\"playing ? 'Mettre en pause' : 'Reprendre'\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-label]=\"\n playing ? 'Mettre le ruban en pause' : 'Reprendre le ruban'\n \"\n (click)=\"togglePlayState()\"\n ></button>\n }\n\n @if (showSpeedControls) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-minus\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Ralentir\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"Ralentir le ruban\"\n [disabled]=\"speed <= minSpeed\"\n (click)=\"decreaseSpeed()\"\n ></button>\n\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-plus\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Acc\u00E9l\u00E9rer\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"Acc\u00E9l\u00E9rer le ruban\"\n [disabled]=\"speed >= maxSpeed\"\n (click)=\"increaseSpeed()\"\n ></button>\n }\n\n @if (showResetSpeedControl) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-refresh\"\n class=\"p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"R\u00E9initialiser la vitesse\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n aria-label=\"R\u00E9initialiser la vitesse\"\n [disabled]=\"speed === defaultSpeed\"\n (click)=\"resetSpeed()\"\n ></button>\n }\n\n @if (showSpeedValue) {\n <span\n class=\"pt-news-ticker__speed\"\n [attr.aria-label]=\"'Vitesse ' + speed + ' pixels par seconde'\"\n >\n {{ speed }} px/s\n </span>\n }\n </div>\n }\n\n <div\n #tickerViewport\n class=\"pt-news-ticker__viewport\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n >\n @if (internalNews.length > 0) {\n <div\n class=\"pt-news-ticker__track\"\n [class.pt-news-ticker__track--single]=\"!loop\"\n [ngStyle]=\"getTrackStyle()\"\n >\n <div\n #tickerGroup\n class=\"pt-news-ticker__group\"\n [attr.aria-hidden]=\"false\"\n >\n @for (item of internalNews; track getTrackById($index, item)) {\n <article\n class=\"pt-news-ticker__item\"\n [ngClass]=\"getItemClasses(item)\"\n [ngStyle]=\"getItemStyle(item)\"\n [pTooltip]=\"item.tooltip || ''\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.aria-label]=\"\n hasItemLink(item) ? item.text + ', ouvrir le lien' : item.text\n \"\n [attr.role]=\"getItemRole(item)\"\n [attr.tabindex]=\"getItemTabIndex(item)\"\n (click)=\"onItemClick($event, item)\"\n (keydown.enter)=\"onItemClick($event, item)\"\n (keydown.space)=\"onItemClick($event, item)\"\n >\n @if (item.icon) {\n <i\n class=\"pt-news-ticker__item-icon\"\n [ngClass]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n\n <span class=\"pt-news-ticker__item-text\">\n {{ item.text }}\n </span>\n\n @if (hasItemLink(item)) {\n <i\n class=\"pt-news-ticker__link-icon pi pi-external-link\"\n aria-hidden=\"true\"\n ></i>\n }\n\n @if (isItemRemovable(item)) {\n <button\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n class=\"pt-news-ticker__remove-button p-button-rounded p-button-text p-button-sm\"\n pTooltip=\"Supprimer l\u2019actualit\u00E9\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [attr.aria-label]=\"'Supprimer : ' + item.text\"\n (click)=\"onRemoveClick($event, item)\"\n ></button>\n }\n </article>\n }\n </div>\n\n @if (loop) {\n <div class=\"pt-news-ticker__group\" aria-hidden=\"true\">\n @for (\n item of internalNews;\n track getDuplicateTrackById($index, item)\n ) {\n <article\n class=\"pt-news-ticker__item\"\n [ngClass]=\"getItemClasses(item)\"\n [ngStyle]=\"getItemStyle(item)\"\n >\n @if (item.icon) {\n <i\n class=\"pt-news-ticker__item-icon\"\n [ngClass]=\"item.icon\"\n aria-hidden=\"true\"\n ></i>\n }\n\n <span class=\"pt-news-ticker__item-text\">\n {{ item.text }}\n </span>\n\n @if (hasItemLink(item)) {\n <i\n class=\"pt-news-ticker__link-icon pi pi-external-link\"\n aria-hidden=\"true\"\n ></i>\n }\n\n @if (isItemRemovable(item)) {\n <span\n class=\"pt-news-ticker__duplicate-remove-placeholder\"\n aria-hidden=\"true\"\n >\n <i class=\"pi pi-times\"></i>\n </span>\n }\n </article>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"pt-news-ticker__empty\">\n @if (emptyIcon) {\n <i [ngClass]=\"emptyIcon\" aria-hidden=\"true\"></i>\n }\n\n <span>{{ emptyMessage }}</span>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;min-width:0;max-width:100%;box-sizing:border-box}:host *,:host *:before,:host *:after{box-sizing:border-box}.pt-news-ticker{display:flex;align-items:stretch;width:100%;min-width:0;max-width:100%;min-height:var(--pt-news-ticker-height, 3.25rem);overflow:hidden;border:var(--pt-news-ticker-border-width, 1px) solid var(--pt-news-ticker-border-color, #e2e8f0);border-radius:var(--pt-news-ticker-border-radius, 8px);background:var(--pt-news-ticker-background, #ffffff);color:var(--pt-news-ticker-text-color, #0f172a)}.pt-news-ticker__controls{position:relative;z-index:4;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;gap:.1rem;min-height:var(--pt-news-ticker-height, 3.25rem);padding:.25rem .4rem;border-right:1px solid var(--pt-news-ticker-controls-border-color, #e2e8f0);background:var(--pt-news-ticker-controls-background, #f8fafc)}.pt-news-ticker__controls ::ng-deep .p-button.p-button-rounded.p-button-text{width:1.9rem;min-width:1.9rem;height:1.9rem;padding:0}.pt-news-ticker__controls ::ng-deep .p-button .p-button-icon{font-size:.78rem}.pt-news-ticker__speed{min-width:3.5rem;padding:0 .35rem;color:#475569;font-size:.7rem;font-weight:700;text-align:center;white-space:nowrap}.pt-news-ticker__viewport{position:relative;display:flex;align-items:center;flex:1 1 auto;width:100%;min-width:0;min-height:var(--pt-news-ticker-height, 3.25rem);overflow:hidden}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{position:absolute;z-index:3;top:0;bottom:0;width:2.5rem;content:\"\";pointer-events:none}.pt-news-ticker__viewport:before{left:0;background:linear-gradient(to right,var(--pt-news-ticker-background, #ffffff),transparent)}.pt-news-ticker__viewport:after{right:0;background:linear-gradient(to left,var(--pt-news-ticker-background, #ffffff),transparent)}.pt-news-ticker__track{display:flex;align-items:center;flex:0 0 auto;width:max-content;min-width:max-content;animation-name:pt-news-ticker-scroll;animation-duration:var(--pt-news-ticker-duration, 20s);animation-timing-function:linear;animation-iteration-count:infinite;will-change:transform}.pt-news-ticker__track--single{animation-iteration-count:1;animation-fill-mode:forwards}.pt-news-ticker__group{display:inline-flex;align-items:center;flex:0 0 auto;gap:var(--pt-news-ticker-gap, 2rem);min-width:max-content;padding-right:var(--pt-news-ticker-gap, 2rem)}.pt-news-ticker__item{display:inline-flex;align-items:center;flex:0 0 auto;gap:var(--pt-news-item-gap, .5rem);min-height:2.25rem;max-width:36rem;padding:.35rem .65rem;overflow:hidden;border:1px solid var(--pt-news-item-border-color, transparent);border-radius:999px;outline:none;background:var(--pt-news-item-background, transparent);color:var( --pt-news-item-text-color, var(--pt-news-ticker-text-color, #0f172a) );line-height:1.25;white-space:nowrap;transition:box-shadow .15s ease,opacity .15s ease,transform .15s ease}.pt-news-ticker__item--clickable{cursor:pointer}.pt-news-ticker__item--clickable:hover{box-shadow:0 3px 10px #0f172a1f;transform:translateY(-1px)}.pt-news-ticker__item:focus-visible{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--primary-color, #3b82f6)}.pt-news-ticker__item--disabled{cursor:not-allowed;opacity:.5}.pt-news-ticker__item-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;color:var(--pt-news-item-icon-color, currentColor);font-size:.95rem}.pt-news-ticker__item-text{display:block;flex:1 1 auto;min-width:0;overflow:hidden;font-size:.85rem;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.pt-news-ticker__remove-button{flex:0 0 auto}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button{width:1.45rem;min-width:1.45rem;height:1.45rem;padding:0;color:currentColor}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button .p-button-icon{font-size:.65rem}.pt-news-ticker__item ::ng-deep .pt-news-ticker__remove-button.p-button:not(:disabled):hover{background:#0f172a1a}.pt-news-ticker__duplicate-remove-placeholder{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:1.45rem;min-width:1.45rem;height:1.45rem;opacity:0;pointer-events:none}.pt-news-ticker__duplicate-remove-placeholder .pi{font-size:.65rem}.pt-news-ticker__item--flash{border-color:var(--pt-news-item-border-color, #991b1b);color:var(--pt-news-flash-text-color, #ffffff);animation-name:pt-news-ticker-flash;animation-duration:var(--pt-news-flash-duration, .7s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-direction:alternate}.pt-news-ticker__item--flash .pt-news-ticker__item-icon{color:var(--pt-news-item-icon-color, #ffffff)}.pt-news-ticker__empty{display:flex;align-items:center;justify-content:center;gap:.5rem;width:100%;min-height:var(--pt-news-ticker-height, 3.25rem);padding:.75rem 1rem;color:#64748b;font-size:.85rem;font-weight:500;text-align:center}.pt-news-ticker__empty .pi{font-size:1rem}@keyframes pt-news-ticker-scroll{0%{transform:translateZ(0)}to{transform:translate3d(-50%,0,0)}}@keyframes pt-news-ticker-flash{0%{background-color:var(--pt-news-flash-background, #dc2626)}to{background-color:var(--pt-news-flash-alternate-background, #7f1d1d)}}@media(max-width:768px){.pt-news-ticker{min-height:3rem}.pt-news-ticker__controls{padding-right:.25rem;padding-left:.25rem}.pt-news-ticker__speed{display:none}.pt-news-ticker__item{max-width:22rem}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{width:1.25rem}}@media(max-width:480px){.pt-news-ticker__item{max-width:17rem}.pt-news-ticker__item-text{font-size:.78rem}}@media(prefers-reduced-motion:reduce){.pt-news-ticker__track{animation:none!important;transform:none!important}.pt-news-ticker__item--flash{animation:none!important;background-color:var(--pt-news-flash-background, #dc2626)!important}.pt-news-ticker__viewport{overflow-x:auto}.pt-news-ticker__viewport:before,.pt-news-ticker__viewport:after{display:none}}\n"] }]
10221
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { news: [{
10222
+ type: Input
10223
+ }], speed: [{
10224
+ type: Input
10225
+ }], defaultSpeed: [{
10226
+ type: Input
10227
+ }], speedStep: [{
10228
+ type: Input
10229
+ }], minSpeed: [{
10230
+ type: Input
10231
+ }], maxSpeed: [{
10232
+ type: Input
10233
+ }], direction: [{
10234
+ type: Input
10235
+ }], autoplay: [{
10236
+ type: Input
10237
+ }], loop: [{
10238
+ type: Input
10239
+ }], pauseOnHover: [{
10240
+ type: Input
10241
+ }], showControls: [{
10242
+ type: Input
10243
+ }], showPlayPauseControl: [{
10244
+ type: Input
10245
+ }], showSpeedControls: [{
10246
+ type: Input
10247
+ }], showResetSpeedControl: [{
10248
+ type: Input
10249
+ }], showRemoveButton: [{
10250
+ type: Input
10251
+ }], showSpeedValue: [{
10252
+ type: Input
10253
+ }], height: [{
10254
+ type: Input
10255
+ }], gap: [{
10256
+ type: Input
10257
+ }], itemGap: [{
10258
+ type: Input
10259
+ }], containerBackgroundColor: [{
10260
+ type: Input
10261
+ }], containerTextColor: [{
10262
+ type: Input
10263
+ }], borderColor: [{
10264
+ type: Input
10265
+ }], borderWidth: [{
10266
+ type: Input
10267
+ }], borderRadius: [{
10268
+ type: Input
10269
+ }], controlsBackgroundColor: [{
10270
+ type: Input
10271
+ }], controlsBorderColor: [{
10272
+ type: Input
10273
+ }], emptyMessage: [{
10274
+ type: Input
10275
+ }], emptyIcon: [{
10276
+ type: Input
10277
+ }], ariaLabel: [{
10278
+ type: Input
10279
+ }], newsClick: [{
10280
+ type: Output
10281
+ }], newsRemove: [{
10282
+ type: Output
10283
+ }], newsChange: [{
10284
+ type: Output
10285
+ }], playStateChange: [{
10286
+ type: Output
10287
+ }], speedChange: [{
10288
+ type: Output
10289
+ }], tickerViewport: [{
10290
+ type: ViewChild,
10291
+ args: ['tickerViewport', { static: false }]
10292
+ }], tickerGroup: [{
10293
+ type: ViewChild,
10294
+ args: ['tickerGroup', { static: false }]
10295
+ }] } });
10296
+
10297
+ class PTNewsTickerModule {
10298
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10299
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerModule, declarations: [PTNewsTickerComponent], imports: [CommonModule, ButtonModule, TooltipModule], exports: [PTNewsTickerComponent] }); }
10300
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerModule, imports: [CommonModule, ButtonModule, TooltipModule] }); }
10301
+ }
10302
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTNewsTickerModule, decorators: [{
10303
+ type: NgModule,
10304
+ args: [{
10305
+ declarations: [PTNewsTickerComponent],
10306
+ imports: [CommonModule, ButtonModule, TooltipModule],
10307
+ exports: [PTNewsTickerComponent],
10308
+ }]
10309
+ }] });
10310
+
9122
10311
  const applyChangePasswordCardDefaults = (config) => {
9123
10312
  config.title = {
9124
10313
  text: 'Changer le mot de passe',
@@ -9349,7 +10538,7 @@ class PTChangePasswordCardComponent {
9349
10538
  };
9350
10539
  }
9351
10540
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTChangePasswordCardComponent, deps: [{ token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
9352
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTChangePasswordCardComponent, isStandalone: true, selector: "pt-change-password-card", inputs: { changePasswordPageConfig: "changePasswordPageConfig", changePasswordErrorMessage: "changePasswordErrorMessage" }, outputs: { passwordValueChange: "passwordValueChange", changePasswordSubmit: "changePasswordSubmit" }, usesOnChanges: true, ngImport: i0, template: "<pt-card [config]=\"changePasswordPageConfig.changePasswordCardConfig!\">\n @if (changePasswordPageConfig.logoUrl; as logoUrl) {\n @if (logoUrl.imageUrl) {\n <div class=\"logo-container\">\n <img\n [src]=\"logoUrl.imageUrl\"\n [alt]=\"logoUrl.altText || 'Logo'\"\n [style.width]=\"logoUrl.width || '100px'\"\n [style.height]=\"logoUrl.height || 'auto'\"\n />\n </div>\n }\n }\n\n <div class=\"title-container\">\n <h1\n [ngStyle]=\"{\n color: changePasswordPageConfig.title?.color || '#333',\n 'font-size': changePasswordPageConfig.title?.fontSize || '24px',\n }\"\n >\n {{ changePasswordPageConfig.title?.text || \"Changer le mot de passe\" }}\n </h1>\n </div>\n\n @if (changePasswordErrorMessage) {\n <div class=\"error-message\" role=\"alert\">\n {{ changePasswordErrorMessage }}\n </div>\n }\n\n <form class=\"form-container\" [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n @if (changePasswordPageConfig.errorMessage) {\n <div class=\"error-message\" role=\"alert\">\n {{ changePasswordPageConfig.errorMessage }}\n </div>\n }\n\n @if (changePasswordPageConfig.showCurrentPasswordField) {\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.currentPasswordField!\"\n ></pt-text-input>\n </div>\n }\n\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.newPasswordField!\"\n ></pt-text-input>\n </div>\n\n @if (changePasswordPageConfig.showPasswordStrength) {\n <div class=\"password-strength-section\">\n <div class=\"password-strength-header\">\n <span>\n {{\n changePasswordPageConfig.passwordStrengthLabel ||\n \"Robustesse du mot de passe\"\n }}\n </span>\n\n <span\n class=\"strength-label\"\n [ngClass]=\"\n 'strength-' +\n (changePasswordPageConfig.passwordStrengthSeverity || 'neutral')\n \"\n >\n {{\n changePasswordPageConfig.passwordStrengthText || \"Non renseign\u00E9\"\n }}\n </span>\n </div>\n\n <p-progressbar\n [value]=\"changePasswordPageConfig.passwordStrengthPercentage || 0\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'password-progressbar strength-' +\n (changePasswordPageConfig.passwordStrengthSeverity || 'neutral')\n \"\n ></p-progressbar>\n </div>\n }\n\n @if (visiblePasswordPolicyRules.length > 0) {\n <div class=\"password-policy-section\">\n <strong class=\"password-policy-title\">\n {{\n changePasswordPageConfig.passwordPolicyTitle || \"R\u00E8gles de s\u00E9curit\u00E9\"\n }}\n </strong>\n\n <ul class=\"password-policy-list\">\n @for (rule of visiblePasswordPolicyRules; track rule.code) {\n <li\n [class.valid]=\"rule.valid\"\n [class.invalid]=\"!rule.valid && !rule.backendOnly\"\n [class.backend-only]=\"rule.backendOnly\"\n >\n @if (rule.backendOnly) {\n <i class=\"pi pi-shield\"></i>\n } @else if (rule.valid) {\n <i class=\"pi pi-check-circle\"></i>\n } @else {\n <i class=\"pi pi-times-circle\"></i>\n }\n\n <span>{{ rule.label }}</span>\n </li>\n }\n </ul>\n </div>\n }\n\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.confirmationPasswordField!\"\n ></pt-text-input>\n\n @if (\n confirmationPasswordControl?.touched &&\n formGroup.hasError(\"passwordMismatch\")\n ) {\n <small class=\"field-error\">\n {{\n changePasswordPageConfig.passwordMismatchErrorMessage ||\n \"Les deux mots de passe ne correspondent pas.\"\n }}\n </small>\n }\n </div>\n\n <div class=\"submit-btn\">\n <pt-button\n [buttonConfig]=\"changePasswordPageConfig.buttonConfig!\"\n ></pt-button>\n </div>\n\n @if (visibleAdditionalContent.length > 0) {\n <div class=\"additional-content-list\">\n @for (item of visibleAdditionalContent; track item.id || $index) {\n <div\n class=\"additional-content-item\"\n [ngClass]=\"getAdditionalContentClass(item)\"\n [ngStyle]=\"item.style\"\n >\n @if (item.text) {\n <span class=\"additional-content-text\">\n {{ item.text }}\n </span>\n }\n </div>\n }\n </div>\n }\n </form>\n\n <div class=\"change-password-footer\">\n {{ changePasswordPageConfig.footer?.version }}\n\n <span>\n {{ changePasswordPageConfig.footer?.copyright }}\n </span>\n </div>\n</pt-card>\n", styles: [":host{display:block;width:100%;min-width:0}.logo-container{display:flex;align-items:center;justify-content:center;margin-bottom:1.25rem}.logo-container img{display:block;max-width:100%;object-fit:contain}.title-container{margin-bottom:1.25rem;text-align:center}.title-container h1{margin:0;line-height:1.25}.form-container{width:100%}.field{display:flex;flex-direction:column;width:100%;margin-bottom:1.25rem}.error-message{width:100%;box-sizing:border-box;margin-bottom:.75rem;padding:.75rem .875rem;color:var(--p-message-error-color, #b91c1c);background:var(--p-message-error-background, #fef2f2);border:1px solid var(--p-message-error-border-color, #fecaca);border-radius:var(--p-content-border-radius, .5rem);font-size:.875rem;line-height:1.4;text-align:center}.password-strength-section{width:100%;margin-top:-.25rem;margin-bottom:1.25rem}.password-strength-header{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:.5rem;color:var(--p-text-color, #1e293b);font-size:.875rem;font-weight:600}.strength-label{font-size:.82rem;font-weight:700;white-space:nowrap}.strength-neutral{color:var(--p-text-muted-color, #64748b)}.strength-danger{color:var(--p-red-500, #ef4444)}.strength-warn{color:var(--p-orange-500, #f97316)}.strength-success{color:var(--p-green-500, #22c55e)}:host ::ng-deep .password-progressbar{height:.65rem;overflow:hidden;border-radius:999px}:host ::ng-deep .password-progressbar .p-progressbar-value{transition:width .22s ease,background-color .22s ease}:host ::ng-deep .password-progressbar.strength-neutral .p-progressbar-value{background:var(--p-surface-400, #94a3b8)}:host ::ng-deep .password-progressbar.strength-danger .p-progressbar-value{background:var(--p-red-500, #ef4444)}:host ::ng-deep .password-progressbar.strength-warn .p-progressbar-value{background:var(--p-orange-500, #f97316)}:host ::ng-deep .password-progressbar.strength-success .p-progressbar-value{background:var(--p-green-500, #22c55e)}.password-policy-section{width:100%;box-sizing:border-box;margin-bottom:1.25rem;padding:1rem;background:var(--p-content-background, rgba(255, 255, 255, .12));border:1px solid var(--p-content-border-color, rgba(255, 255, 255, .25));border-radius:var(--p-content-border-radius, .5rem)}.password-policy-title{display:block;margin-bottom:.75rem;color:var(--p-text-color, #1e293b);font-size:.9rem;font-weight:700}.password-policy-list{display:flex;flex-direction:column;margin:0;padding:0;gap:.55rem;list-style:none}.password-policy-list li{display:flex;align-items:flex-start;gap:.6rem;color:var(--p-text-muted-color, #64748b);font-size:.84rem;line-height:1.35}.password-policy-list li i{width:1rem;margin-top:.1rem;text-align:center}.password-policy-list li.valid{color:var(--p-green-600, #16a34a)}.password-policy-list li.invalid{color:var(--p-red-500, #ef4444)}.password-policy-list li.backend-only{color:var(--p-text-muted-color, #64748b)}.password-policy-list li.backend-only i{color:var(--p-primary-color, #3b82f6)}.field-error{display:block;margin-top:.45rem;color:var(--p-message-error-color, #b91c1c);font-size:.78rem;line-height:1.35}.submit-btn{display:flex;justify-content:center;width:100%}:host ::ng-deep .submit-btn pt-button{display:block;width:100%}:host ::ng-deep .submit-btn p-button,:host ::ng-deep .submit-btn p-button button{width:100%}.additional-content-list{display:flex;flex-direction:column;width:100%;margin-top:1rem;gap:.55rem}.additional-content-item{display:flex;flex-wrap:wrap;align-items:baseline;width:100%;box-sizing:border-box;gap:.3rem;color:var(--p-text-muted-color, var(--text-color-secondary, #64748b));font-size:.875rem;line-height:1.45}.additional-content-left{justify-content:flex-start;text-align:left}.additional-content-center{justify-content:center;text-align:center}.additional-content-right{justify-content:flex-end;text-align:right}.additional-content-text{color:inherit}.change-password-footer{margin-top:1.25rem;color:var(--p-text-muted-color, var(--text-color-secondary, #64748b));font-size:.8rem;line-height:1.4;text-align:center}.change-password-footer span{display:block;margin-top:.25rem}:host-context(.p-dark) .error-message,:host-context(.app-dark) .error-message,:host-context(.dark) .error-message,:host-context(.dark-mode) .error-message,:host-context([data-theme=\"dark\"]) .error-message{color:var(--p-message-error-color, #fca5a5);background:var(--p-message-error-background, rgba(127, 29, 29, .25));border-color:var(--p-message-error-border-color, rgba(248, 113, 113, .4))}:host-context(.p-dark) .password-strength-header,:host-context(.app-dark) .password-strength-header,:host-context(.dark) .password-strength-header,:host-context(.dark-mode) .password-strength-header,:host-context([data-theme=\"dark\"]) .password-strength-header,:host-context(.p-dark) .password-policy-title,:host-context(.app-dark) .password-policy-title,:host-context(.dark) .password-policy-title,:host-context(.dark-mode) .password-policy-title,:host-context([data-theme=\"dark\"]) .password-policy-title{color:var(--p-text-color, #f8fafc)}:host-context(.p-dark) .password-policy-section,:host-context(.app-dark) .password-policy-section,:host-context(.dark) .password-policy-section,:host-context(.dark-mode) .password-policy-section,:host-context([data-theme=\"dark\"]) .password-policy-section{background:#0f172a59;border-color:#94a3b84d}:host-context(.p-dark) .additional-content-item,:host-context(.app-dark) .additional-content-item,:host-context(.dark) .additional-content-item,:host-context(.dark-mode) .additional-content-item,:host-context([data-theme=\"dark\"]) .additional-content-item,:host-context(.p-dark) .change-password-footer,:host-context(.app-dark) .change-password-footer,:host-context(.dark) .change-password-footer,:host-context(.dark-mode) .change-password-footer,:host-context([data-theme=\"dark\"]) .change-password-footer{color:var(--p-text-muted-color, var(--text-color-secondary, #cbd5e1))}@media(max-width:768px){:host ::ng-deep pt-card{width:100%;max-width:100%}.submit-btn{min-width:100%}.password-strength-header{align-items:flex-start;flex-direction:column;gap:.25rem}}@media(max-width:480px){.logo-container,.title-container,.field{margin-bottom:1rem}.additional-content-item{font-size:.82rem}.additional-content-list{gap:.5rem}.password-policy-section{padding:.85rem}.password-policy-list li{font-size:.8rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PTCardModule }, { kind: "component", type: PTCardComponent, selector: "pt-card", inputs: ["config"] }, { kind: "ngmodule", type: PTButtonModule }, { kind: "component", type: PTButtonComponent, selector: "pt-button", inputs: ["buttonConfig"] }, { kind: "ngmodule", type: PTTextInputModule }, { kind: "component", type: PTTextInputComponent, selector: "pt-text-input", inputs: ["formGroup", "formField"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i13.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }] }); }
10541
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PTChangePasswordCardComponent, isStandalone: true, selector: "pt-change-password-card", inputs: { changePasswordPageConfig: "changePasswordPageConfig", changePasswordErrorMessage: "changePasswordErrorMessage" }, outputs: { passwordValueChange: "passwordValueChange", changePasswordSubmit: "changePasswordSubmit" }, usesOnChanges: true, ngImport: i0, template: "<pt-card [config]=\"changePasswordPageConfig.changePasswordCardConfig!\">\n @if (changePasswordPageConfig.logoUrl; as logoUrl) {\n @if (logoUrl.imageUrl) {\n <div class=\"logo-container\">\n <img\n [src]=\"logoUrl.imageUrl\"\n [alt]=\"logoUrl.altText || 'Logo'\"\n [style.width]=\"logoUrl.width || '100px'\"\n [style.height]=\"logoUrl.height || 'auto'\"\n />\n </div>\n }\n }\n\n <div class=\"title-container\">\n <h1\n [ngStyle]=\"{\n color: changePasswordPageConfig.title?.color || '#333',\n 'font-size': changePasswordPageConfig.title?.fontSize || '24px',\n }\"\n >\n {{ changePasswordPageConfig.title?.text || \"Changer le mot de passe\" }}\n </h1>\n </div>\n\n @if (changePasswordErrorMessage) {\n <div class=\"error-message\" role=\"alert\">\n {{ changePasswordErrorMessage }}\n </div>\n }\n\n <form class=\"form-container\" [formGroup]=\"formGroup\" (ngSubmit)=\"onSubmit()\">\n @if (changePasswordPageConfig.errorMessage) {\n <div class=\"error-message\" role=\"alert\">\n {{ changePasswordPageConfig.errorMessage }}\n </div>\n }\n\n @if (changePasswordPageConfig.showCurrentPasswordField) {\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.currentPasswordField!\"\n ></pt-text-input>\n </div>\n }\n\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.newPasswordField!\"\n ></pt-text-input>\n </div>\n\n @if (changePasswordPageConfig.showPasswordStrength) {\n <div class=\"password-strength-section\">\n <div class=\"password-strength-header\">\n <span>\n {{\n changePasswordPageConfig.passwordStrengthLabel ||\n \"Robustesse du mot de passe\"\n }}\n </span>\n\n <span\n class=\"strength-label\"\n [ngClass]=\"\n 'strength-' +\n (changePasswordPageConfig.passwordStrengthSeverity || 'neutral')\n \"\n >\n {{\n changePasswordPageConfig.passwordStrengthText || \"Non renseign\u00E9\"\n }}\n </span>\n </div>\n\n <p-progressbar\n [value]=\"changePasswordPageConfig.passwordStrengthPercentage || 0\"\n [showValue]=\"false\"\n [ngClass]=\"\n 'password-progressbar strength-' +\n (changePasswordPageConfig.passwordStrengthSeverity || 'neutral')\n \"\n ></p-progressbar>\n </div>\n }\n\n @if (visiblePasswordPolicyRules.length > 0) {\n <div class=\"password-policy-section\">\n <strong class=\"password-policy-title\">\n {{\n changePasswordPageConfig.passwordPolicyTitle || \"R\u00E8gles de s\u00E9curit\u00E9\"\n }}\n </strong>\n\n <ul class=\"password-policy-list\">\n @for (rule of visiblePasswordPolicyRules; track rule.code) {\n <li\n [class.valid]=\"rule.valid\"\n [class.invalid]=\"!rule.valid && !rule.backendOnly\"\n [class.backend-only]=\"rule.backendOnly\"\n >\n @if (rule.backendOnly) {\n <i class=\"pi pi-shield\"></i>\n } @else if (rule.valid) {\n <i class=\"pi pi-check-circle\"></i>\n } @else {\n <i class=\"pi pi-times-circle\"></i>\n }\n\n <span>{{ rule.label }}</span>\n </li>\n }\n </ul>\n </div>\n }\n\n <div class=\"field\">\n <pt-text-input\n [formGroup]=\"formGroup\"\n [formField]=\"changePasswordPageConfig.confirmationPasswordField!\"\n ></pt-text-input>\n\n @if (\n confirmationPasswordControl?.touched &&\n formGroup.hasError(\"passwordMismatch\")\n ) {\n <small class=\"field-error\">\n {{\n changePasswordPageConfig.passwordMismatchErrorMessage ||\n \"Les deux mots de passe ne correspondent pas.\"\n }}\n </small>\n }\n </div>\n\n <div class=\"submit-btn\">\n <pt-button\n [buttonConfig]=\"changePasswordPageConfig.buttonConfig!\"\n ></pt-button>\n </div>\n\n @if (visibleAdditionalContent.length > 0) {\n <div class=\"additional-content-list\">\n @for (item of visibleAdditionalContent; track item.id || $index) {\n <div\n class=\"additional-content-item\"\n [ngClass]=\"getAdditionalContentClass(item)\"\n [ngStyle]=\"item.style\"\n >\n @if (item.text) {\n <span class=\"additional-content-text\">\n {{ item.text }}\n </span>\n }\n </div>\n }\n </div>\n }\n </form>\n\n <div class=\"change-password-footer\">\n {{ changePasswordPageConfig.footer?.version }}\n\n <span>\n {{ changePasswordPageConfig.footer?.copyright }}\n </span>\n </div>\n</pt-card>\n", styles: [":host{display:block;width:100%;min-width:0}.logo-container{display:flex;align-items:center;justify-content:center;margin-bottom:1.25rem}.logo-container img{display:block;max-width:100%;object-fit:contain}.title-container{margin-bottom:1.25rem;text-align:center}.title-container h1{margin:0;line-height:1.25}.form-container{width:100%}.field{display:flex;flex-direction:column;width:100%;margin-bottom:1.25rem}.error-message{width:100%;box-sizing:border-box;margin-bottom:.75rem;padding:.75rem .875rem;color:var(--p-message-error-color, #b91c1c);background:var(--p-message-error-background, #fef2f2);border:1px solid var(--p-message-error-border-color, #fecaca);border-radius:var(--p-content-border-radius, .5rem);font-size:.875rem;line-height:1.4;text-align:center}.password-strength-section{width:100%;margin-top:-.25rem;margin-bottom:1.25rem}.password-strength-header{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:.5rem;color:var(--p-text-color, #1e293b);font-size:.875rem;font-weight:600}.strength-label{font-size:.82rem;font-weight:700;white-space:nowrap}.strength-neutral{color:var(--p-text-muted-color, #64748b)}.strength-danger{color:var(--p-red-500, #ef4444)}.strength-warn{color:var(--p-orange-500, #f97316)}.strength-success{color:var(--p-green-500, #22c55e)}:host ::ng-deep .password-progressbar{height:.65rem;overflow:hidden;border-radius:999px}:host ::ng-deep .password-progressbar .p-progressbar-value{transition:width .22s ease,background-color .22s ease}:host ::ng-deep .password-progressbar.strength-neutral .p-progressbar-value{background:var(--p-surface-400, #94a3b8)}:host ::ng-deep .password-progressbar.strength-danger .p-progressbar-value{background:var(--p-red-500, #ef4444)}:host ::ng-deep .password-progressbar.strength-warn .p-progressbar-value{background:var(--p-orange-500, #f97316)}:host ::ng-deep .password-progressbar.strength-success .p-progressbar-value{background:var(--p-green-500, #22c55e)}.password-policy-section{width:100%;box-sizing:border-box;margin-bottom:1.25rem;padding:1rem;background:var(--p-content-background, rgba(255, 255, 255, .12));border:1px solid var(--p-content-border-color, rgba(255, 255, 255, .25));border-radius:var(--p-content-border-radius, .5rem)}.password-policy-title{display:block;margin-bottom:.75rem;color:var(--p-text-color, #1e293b);font-size:.9rem;font-weight:700}.password-policy-list{display:flex;flex-direction:column;margin:0;padding:0;gap:.55rem;list-style:none}.password-policy-list li{display:flex;align-items:flex-start;gap:.6rem;color:var(--p-text-muted-color, #64748b);font-size:.84rem;line-height:1.35}.password-policy-list li i{width:1rem;margin-top:.1rem;text-align:center}.password-policy-list li.valid{color:var(--p-green-600, #16a34a)}.password-policy-list li.invalid{color:var(--p-red-500, #ef4444)}.password-policy-list li.backend-only{color:var(--p-text-muted-color, #64748b)}.password-policy-list li.backend-only i{color:var(--p-primary-color, #3b82f6)}.field-error{display:block;margin-top:.45rem;color:var(--p-message-error-color, #b91c1c);font-size:.78rem;line-height:1.35}.submit-btn{display:flex;justify-content:center;width:100%}:host ::ng-deep .submit-btn pt-button{display:block;width:100%}:host ::ng-deep .submit-btn p-button,:host ::ng-deep .submit-btn p-button button{width:100%}.additional-content-list{display:flex;flex-direction:column;width:100%;margin-top:1rem;gap:.55rem}.additional-content-item{display:flex;flex-wrap:wrap;align-items:baseline;width:100%;box-sizing:border-box;gap:.3rem;color:var(--p-text-muted-color, var(--text-color-secondary, #64748b));font-size:.875rem;line-height:1.45}.additional-content-left{justify-content:flex-start;text-align:left}.additional-content-center{justify-content:center;text-align:center}.additional-content-right{justify-content:flex-end;text-align:right}.additional-content-text{color:inherit}.change-password-footer{margin-top:1.25rem;color:var(--p-text-muted-color, var(--text-color-secondary, #64748b));font-size:.8rem;line-height:1.4;text-align:center}.change-password-footer span{display:block;margin-top:.25rem}:host-context(.p-dark) .error-message,:host-context(.app-dark) .error-message,:host-context(.dark) .error-message,:host-context(.dark-mode) .error-message,:host-context([data-theme=\"dark\"]) .error-message{color:var(--p-message-error-color, #fca5a5);background:var(--p-message-error-background, rgba(127, 29, 29, .25));border-color:var(--p-message-error-border-color, rgba(248, 113, 113, .4))}:host-context(.p-dark) .password-strength-header,:host-context(.app-dark) .password-strength-header,:host-context(.dark) .password-strength-header,:host-context(.dark-mode) .password-strength-header,:host-context([data-theme=\"dark\"]) .password-strength-header,:host-context(.p-dark) .password-policy-title,:host-context(.app-dark) .password-policy-title,:host-context(.dark) .password-policy-title,:host-context(.dark-mode) .password-policy-title,:host-context([data-theme=\"dark\"]) .password-policy-title{color:var(--p-text-color, #f8fafc)}:host-context(.p-dark) .password-policy-section,:host-context(.app-dark) .password-policy-section,:host-context(.dark) .password-policy-section,:host-context(.dark-mode) .password-policy-section,:host-context([data-theme=\"dark\"]) .password-policy-section{background:#0f172a59;border-color:#94a3b84d}:host-context(.p-dark) .additional-content-item,:host-context(.app-dark) .additional-content-item,:host-context(.dark) .additional-content-item,:host-context(.dark-mode) .additional-content-item,:host-context([data-theme=\"dark\"]) .additional-content-item,:host-context(.p-dark) .change-password-footer,:host-context(.app-dark) .change-password-footer,:host-context(.dark) .change-password-footer,:host-context(.dark-mode) .change-password-footer,:host-context([data-theme=\"dark\"]) .change-password-footer{color:var(--p-text-muted-color, var(--text-color-secondary, #cbd5e1))}@media(max-width:768px){:host ::ng-deep pt-card{width:100%;max-width:100%}.submit-btn{min-width:100%}.password-strength-header{align-items:flex-start;flex-direction:column;gap:.25rem}}@media(max-width:480px){.logo-container,.title-container,.field{margin-bottom:1rem}.additional-content-item{font-size:.82rem}.additional-content-list{gap:.5rem}.password-policy-section{padding:.85rem}.password-policy-list li{font-size:.8rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PTCardModule }, { kind: "component", type: PTCardComponent, selector: "pt-card", inputs: ["config"] }, { kind: "ngmodule", type: PTButtonModule }, { kind: "component", type: PTButtonComponent, selector: "pt-button", inputs: ["buttonConfig"] }, { kind: "ngmodule", type: PTTextInputModule }, { kind: "component", type: PTTextInputComponent, selector: "pt-text-input", inputs: ["formGroup", "formField"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i14.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }] }); }
9353
10542
  }
9354
10543
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PTChangePasswordCardComponent, decorators: [{
9355
10544
  type: Component,
@@ -9461,6 +10650,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9461
10650
  }]
9462
10651
  }] });
9463
10652
 
10653
+ // projects/ng-prime-tools/src/lib/models/form-multi-select.model.ts
10654
+
9464
10655
  // projects/ng-prime-tools/src/lib/models/pt-accordion-config.model.ts
9465
10656
 
9466
10657
  // nav-bar-menu-config.model.ts
@@ -9525,5 +10716,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
9525
10716
  * Generated bundle index. Do not edit.
9526
10717
  */
9527
10718
 
9528
- export { AlignEnum, BadgeType, BadgeTypeStyles, ButtonColorEnum, DateUtilityService, FormInputTypeEnum, InputValidationEnum, MultiSearchCriteriaComponent, MultiSearchCriteriaModule, NgPrimeToolsModule, PTAccordionComponent, PTAccordionModule, PTAccordionPanelDirective, PTAdvancedPrimeTableComponent, PTAdvancedPrimeTableModule, PTBreadCrumbComponent, PTBreadCrumbModule, PTButtonComponent, PTButtonModule, PTCardComponent, PTCardModule, PTChangePasswordPageComponent, PTChangePasswordPageModule, PTChartComparisonComponent, PTChartComparisonModule, PTChartComponent, PTChartModule, PTCheckBoxInputComponent, PTCheckBoxInputModule, PTConfirmDialogComponent, PTConfirmDialogModule, PTDateInputComponent, PTDateInputModule, PTDialogComponent, PTDialogModule, PTDropdownComponent, PTDropdownModule, PTFooterComponent, PTFooterModule, PTFormBuilderComponent, PTFormBuilderModule, PTGroupComponent, PTGroupModule, PTLineChartComponent, PTLineChartModule, PTLoginPageComponent, PTLoginPageModule, PTMenuComponent, PTMenuFancyComponent, PTMenuFancyModule, PTMenuModule, PTMetricCardComponent, PTMetricCardGroupComponent, PTMetricCardGroupModule, PTMetricCardModule, PTMetricPanelComponent, PTMetricPanelModule, PTMultiSelectComponent, PTMultiSelectModule, PTNavbarMenuComponent, PTNavbarMenuModule, PTNumberInputComponent, PTNumberInputModule, PTOtpInputComponent, PTOtpInputModule, PTOtpPageComponent, PTOtpPageModule, PTPageSkeletonComponent, PTPageSkeletonModule, PTPasswordInputComponent, PTPasswordInputModule, PTSideBarMenuComponent, PTSideBarMenuModule, PTSwitchInputComponent, PTSwitchInputModule, PTTextAreaInputComponent, PTTextAreaInputModule, PTTextInputComponent, PTTextInputModule, PTToastNotifierComponent, PTToastNotifierModule, SearchCriteriaTypeEnum, SeverityEnum, TableTypeEnum };
10719
+ export { AlignEnum, BadgeType, BadgeTypeStyles, ButtonColorEnum, DateUtilityService, FormInputTypeEnum, InputValidationEnum, MultiSearchCriteriaComponent, MultiSearchCriteriaModule, NgPrimeToolsModule, PTAccordionComponent, PTAccordionModule, PTAccordionPanelDirective, PTAdvancedPrimeTableComponent, PTAdvancedPrimeTableModule, PTBreadCrumbComponent, PTBreadCrumbModule, PTButtonComponent, PTButtonModule, PTCardComponent, PTCardModule, PTChangePasswordPageComponent, PTChangePasswordPageModule, PTChartComparisonComponent, PTChartComparisonModule, PTChartComponent, PTChartModule, PTCheckBoxInputComponent, PTCheckBoxInputModule, PTConfirmDialogComponent, PTConfirmDialogModule, PTDateInputComponent, PTDateInputModule, PTDialogComponent, PTDialogModule, PTDropdownComponent, PTDropdownModule, PTFooterComponent, PTFooterModule, PTFormBuilderComponent, PTFormBuilderModule, PTGroupComponent, PTGroupModule, PTLineChartComponent, PTLineChartModule, PTLoginPageComponent, PTLoginPageModule, PTMenuComponent, PTMenuFancyComponent, PTMenuFancyModule, PTMenuModule, PTMetricCardComponent, PTMetricCardGroupComponent, PTMetricCardGroupModule, PTMetricCardModule, PTMetricPanelComponent, PTMetricPanelModule, PTMultiSelectComponent, PTMultiSelectModule, PTNavbarMenuComponent, PTNavbarMenuModule, PTNewsTickerComponent, PTNewsTickerDirection, PTNewsTickerModule, PTNewsTickerType, PTNumberInputComponent, PTNumberInputModule, PTOtpInputComponent, PTOtpInputModule, PTOtpPageComponent, PTOtpPageModule, PTPageSkeletonComponent, PTPageSkeletonModule, PTPasswordInputComponent, PTPasswordInputModule, PTSideBarMenuComponent, PTSideBarMenuModule, PTSwitchInputComponent, PTSwitchInputModule, PTTextAreaInputComponent, PTTextAreaInputModule, PTTextInputComponent, PTTextInputModule, PTToastNotifierComponent, PTToastNotifierModule, SearchCriteriaTypeEnum, SeverityEnum, TableTypeEnum };
9529
10720
  //# sourceMappingURL=ng-prime-tools.mjs.map