ng-prime-tools 1.0.43 → 1.0.44
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.
- package/esm2022/lib/enums/table-type.enum.mjs +2 -1
- package/esm2022/lib/models/table-column.model.mjs +1 -1
- package/esm2022/lib/pt-advanced-prime-table/pt-advanced-prime-table.component.mjs +192 -247
- package/esm2022/lib/pt-dropdown/pt-dropdown.component.mjs +2 -2
- package/fesm2022/ng-prime-tools.mjs +195 -249
- package/fesm2022/ng-prime-tools.mjs.map +1 -1
- package/lib/enums/table-type.enum.d.ts +1 -0
- package/lib/enums/table-type.enum.d.ts.map +1 -1
- package/lib/models/table-column.model.d.ts +1 -0
- package/lib/models/table-column.model.d.ts.map +1 -1
- package/lib/pt-advanced-prime-table/pt-advanced-prime-table.component.d.ts +13 -19
- package/lib/pt-advanced-prime-table/pt-advanced-prime-table.component.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -163,7 +163,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImpo
|
|
|
163
163
|
|
|
164
164
|
class PTAdvancedPrimeTableComponent {
|
|
165
165
|
constructor() {
|
|
166
|
-
// Inputs
|
|
167
166
|
this.data = [];
|
|
168
167
|
this.columns = [];
|
|
169
168
|
this.totalRecords = 0;
|
|
@@ -177,7 +176,6 @@ class PTAdvancedPrimeTableComponent {
|
|
|
177
176
|
this.isSortable = false;
|
|
178
177
|
this.loading = false;
|
|
179
178
|
this.maxHeight = null;
|
|
180
|
-
// Outputs
|
|
181
179
|
this.search = new EventEmitter();
|
|
182
180
|
this.exportExcelEvent = new EventEmitter();
|
|
183
181
|
this.exportPdfEvent = new EventEmitter();
|
|
@@ -187,17 +185,13 @@ class PTAdvancedPrimeTableComponent {
|
|
|
187
185
|
this.TableTypeEnum = TableTypeEnum;
|
|
188
186
|
this.AlignEnum = AlignEnum;
|
|
189
187
|
this.searchValue = '';
|
|
190
|
-
// used to store composed filter UI state (and options)
|
|
191
188
|
this.filters = {};
|
|
192
|
-
// store the last filter value per column field (for all filters)
|
|
193
189
|
this.latestFilterValues = {};
|
|
194
190
|
this.clearedFields = new Set();
|
|
195
191
|
this.validCurrencyCodes = ['USD', 'EUR', 'MAD'];
|
|
196
192
|
this.iconWidth = 77;
|
|
197
|
-
// Component state
|
|
198
193
|
this.rows = 0;
|
|
199
194
|
this.hasGroupedColumns = false;
|
|
200
|
-
// edit/delete flags
|
|
201
195
|
this.isDelete = false;
|
|
202
196
|
this.isEdit = false;
|
|
203
197
|
this.Delete = () => { };
|
|
@@ -205,7 +199,6 @@ class PTAdvancedPrimeTableComponent {
|
|
|
205
199
|
this.saveEditableRow = () => { };
|
|
206
200
|
this.cancelEditableRow = () => { };
|
|
207
201
|
this.customActions = [];
|
|
208
|
-
// Data management
|
|
209
202
|
this.dataMap = new Map();
|
|
210
203
|
this.map = new Map();
|
|
211
204
|
this.optionEntries = new Map();
|
|
@@ -228,18 +221,15 @@ class PTAdvancedPrimeTableComponent {
|
|
|
228
221
|
if (col.type === TableTypeEnum.COMPOSED) {
|
|
229
222
|
this.initializeComposedFilters(col);
|
|
230
223
|
}
|
|
231
|
-
if (col.isSortable === undefined)
|
|
224
|
+
if (col.isSortable === undefined)
|
|
232
225
|
col.isSortable = true;
|
|
233
|
-
|
|
234
|
-
if (col.isEditable === undefined) {
|
|
226
|
+
if (col.isEditable === undefined)
|
|
235
227
|
col.isEditable = true;
|
|
236
|
-
}
|
|
237
228
|
if (col.isFilter !== false && col.code !== undefined) {
|
|
238
229
|
this.globalFilterFields.push(col.code);
|
|
239
230
|
}
|
|
240
|
-
if (!col.width)
|
|
231
|
+
if (!col.width)
|
|
241
232
|
col.width = this.calculateColumnWidth(col);
|
|
242
|
-
}
|
|
243
233
|
});
|
|
244
234
|
}
|
|
245
235
|
// ---------- HEADER + BODY ALIGNMENT HELPERS ----------
|
|
@@ -288,9 +278,8 @@ class PTAdvancedPrimeTableComponent {
|
|
|
288
278
|
this.saveEditableRow = () => { };
|
|
289
279
|
this.cancelEditableRow = () => { };
|
|
290
280
|
this.customActions = [];
|
|
291
|
-
if (!this.actions || this.actions.length === 0)
|
|
281
|
+
if (!this.actions || this.actions.length === 0)
|
|
292
282
|
return;
|
|
293
|
-
}
|
|
294
283
|
this.actions.forEach((action) => {
|
|
295
284
|
switch (action.code) {
|
|
296
285
|
case 'delete':
|
|
@@ -318,16 +307,10 @@ class PTAdvancedPrimeTableComponent {
|
|
|
318
307
|
}
|
|
319
308
|
onCustomActionClick(action, row) {
|
|
320
309
|
if (action && typeof action.action === 'function') {
|
|
321
|
-
setTimeout(() =>
|
|
322
|
-
action.action(row);
|
|
323
|
-
}, 0);
|
|
310
|
+
setTimeout(() => action.action(row), 0);
|
|
324
311
|
}
|
|
325
312
|
}
|
|
326
313
|
// ---------- FILTERING / SORTING / PAGINATION ----------
|
|
327
|
-
/**
|
|
328
|
-
* For composed columns, we still register filters under filters[composedName]
|
|
329
|
-
* to keep options & placeholders.
|
|
330
|
-
*/
|
|
331
314
|
initializeComposedFilters(col) {
|
|
332
315
|
col.composedNames?.forEach((composedName) => {
|
|
333
316
|
const code = col.code || '';
|
|
@@ -350,23 +333,17 @@ class PTAdvancedPrimeTableComponent {
|
|
|
350
333
|
return undefined;
|
|
351
334
|
}
|
|
352
335
|
onComposedColumnClear(col) {
|
|
353
|
-
if (!col.code || !col.composedNames)
|
|
336
|
+
if (!col.code || !col.composedNames)
|
|
354
337
|
return;
|
|
355
|
-
}
|
|
356
338
|
col.composedNames.forEach((name) => {
|
|
357
339
|
const key = `${col.code}.${name}`;
|
|
358
340
|
delete this.latestFilterValues[key];
|
|
359
|
-
if (this.filters[name])
|
|
341
|
+
if (this.filters[name])
|
|
360
342
|
this.filters[name].value = [];
|
|
361
|
-
}
|
|
362
343
|
});
|
|
363
344
|
}
|
|
364
|
-
/**
|
|
365
|
-
* COMPOSED: cache UI values only, real filter applied on Apply/Clear.
|
|
366
|
-
*/
|
|
367
345
|
onComposedFilterValueChange(col, composedName, value, filterModel) {
|
|
368
346
|
const key = `${col.code}.${composedName}`;
|
|
369
|
-
// 1) sync with PrimeNG filter model so Apply/Clear work correctly
|
|
370
347
|
if (filterModel) {
|
|
371
348
|
filterModel.value = value;
|
|
372
349
|
if (Array.isArray(filterModel.constraints) &&
|
|
@@ -374,7 +351,6 @@ class PTAdvancedPrimeTableComponent {
|
|
|
374
351
|
filterModel.constraints[0].value = value;
|
|
375
352
|
}
|
|
376
353
|
}
|
|
377
|
-
// 2) keep your local UI model
|
|
378
354
|
if (!this.filters[composedName]) {
|
|
379
355
|
this.filters[composedName] = {
|
|
380
356
|
options: col.filterOptions,
|
|
@@ -383,40 +359,29 @@ class PTAdvancedPrimeTableComponent {
|
|
|
383
359
|
};
|
|
384
360
|
}
|
|
385
361
|
this.filters[composedName].value = value || [];
|
|
386
|
-
// 3) cache for server-side `filterColumn`
|
|
387
362
|
const isEmpty = !value || (Array.isArray(value) && value.length === 0);
|
|
388
|
-
if (isEmpty)
|
|
363
|
+
if (isEmpty)
|
|
389
364
|
delete this.latestFilterValues[key];
|
|
390
|
-
|
|
391
|
-
else {
|
|
365
|
+
else
|
|
392
366
|
this.latestFilterValues[key] = value;
|
|
393
|
-
}
|
|
394
367
|
}
|
|
395
368
|
onFilterClear(field) {
|
|
396
|
-
if (!field)
|
|
369
|
+
if (!field)
|
|
397
370
|
return;
|
|
398
|
-
}
|
|
399
|
-
// forget cached value for this field
|
|
400
371
|
delete this.latestFilterValues[field];
|
|
401
|
-
// mark this field as explicitly cleared so filterColumn
|
|
402
|
-
// doesn't reuse the cached value on the same event
|
|
403
372
|
this.clearedFields.add(field);
|
|
404
373
|
}
|
|
405
374
|
onFilterValueChange(field, filterModel, value) {
|
|
406
|
-
if (!field)
|
|
375
|
+
if (!field)
|
|
407
376
|
return;
|
|
408
|
-
}
|
|
409
377
|
const isEmpty = value === null ||
|
|
410
378
|
value === undefined ||
|
|
411
379
|
(Array.isArray(value) && value.length === 0);
|
|
412
|
-
if (isEmpty)
|
|
380
|
+
if (isEmpty)
|
|
413
381
|
delete this.latestFilterValues[field];
|
|
414
|
-
|
|
415
|
-
else {
|
|
382
|
+
else
|
|
416
383
|
this.latestFilterValues[field] = value;
|
|
417
|
-
}
|
|
418
384
|
if (filterModel) {
|
|
419
|
-
// update both places so PrimeNG emits it correctly
|
|
420
385
|
filterModel.value = value;
|
|
421
386
|
if (Array.isArray(filterModel.constraints) &&
|
|
422
387
|
filterModel.constraints.length > 0) {
|
|
@@ -425,25 +390,20 @@ class PTAdvancedPrimeTableComponent {
|
|
|
425
390
|
}
|
|
426
391
|
}
|
|
427
392
|
onNumberFilterChange(field, value) {
|
|
428
|
-
if (!field)
|
|
393
|
+
if (!field)
|
|
429
394
|
return;
|
|
430
|
-
}
|
|
431
395
|
const isEmpty = value === null ||
|
|
432
396
|
value === undefined ||
|
|
433
397
|
value === '' ||
|
|
434
398
|
(Array.isArray(value) && value.length === 0);
|
|
435
|
-
if (isEmpty)
|
|
399
|
+
if (isEmpty)
|
|
436
400
|
delete this.latestFilterValues[field];
|
|
437
|
-
|
|
438
|
-
else {
|
|
401
|
+
else
|
|
439
402
|
this.latestFilterValues[field] = value;
|
|
440
|
-
}
|
|
441
403
|
}
|
|
442
|
-
/** Helper: find column by field. Supports composed fields like "code.prop". */
|
|
443
404
|
findColumnByField(field) {
|
|
444
405
|
return this.columns.find((c) => c.code === field || (c.code && field.startsWith(c.code + '.')));
|
|
445
406
|
}
|
|
446
|
-
// Fired by PrimeNG when the user clicks Apply / Clear in any filter menu
|
|
447
407
|
filterColumn(event) {
|
|
448
408
|
const filters = event?.filters;
|
|
449
409
|
if (!filters) {
|
|
@@ -457,46 +417,33 @@ class PTAdvancedPrimeTableComponent {
|
|
|
457
417
|
const m = normalizeMeta(meta);
|
|
458
418
|
const col = this.findColumnByField(field);
|
|
459
419
|
const wasCleared = this.clearedFields.has(field);
|
|
460
|
-
|
|
461
|
-
if (!m && !col) {
|
|
420
|
+
if (!m && !col)
|
|
462
421
|
return;
|
|
463
|
-
|
|
464
|
-
// ----------------------------------------------------
|
|
465
|
-
// 🔹 SPECIAL CASE: COMPOSED COLUMN
|
|
466
|
-
// ----------------------------------------------------
|
|
422
|
+
// COMPOSED
|
|
467
423
|
if (col && col.type === TableTypeEnum.COMPOSED) {
|
|
468
424
|
const composedValues = {};
|
|
469
425
|
col.composedNames?.forEach((name) => {
|
|
470
426
|
const key = `${col.code}.${name}`;
|
|
471
427
|
const val = this.latestFilterValues[key];
|
|
472
428
|
const empty = isNullish(val) || (Array.isArray(val) && val.length === 0);
|
|
473
|
-
if (!empty)
|
|
429
|
+
if (!empty)
|
|
474
430
|
composedValues[name] = val;
|
|
475
|
-
}
|
|
476
431
|
});
|
|
477
|
-
// If nothing in cache, treat as "no filter"
|
|
478
432
|
if (Object.keys(composedValues).length === 0) {
|
|
479
433
|
if (m) {
|
|
480
434
|
m.value = null;
|
|
481
|
-
if (Array.isArray(m.constraints))
|
|
435
|
+
if (Array.isArray(m.constraints))
|
|
482
436
|
m.constraints.forEach((c) => (c.value = null));
|
|
483
|
-
}
|
|
484
437
|
}
|
|
485
438
|
}
|
|
486
439
|
else {
|
|
487
|
-
// send object { sub1: [...], sub2: [...] } to parent
|
|
488
440
|
m.value = composedValues;
|
|
489
441
|
}
|
|
490
|
-
const wasCleared = this.clearedFields.has(field);
|
|
491
|
-
return; // composed handled
|
|
492
|
-
}
|
|
493
|
-
// ----------------------------------------------------
|
|
494
|
-
// 🔹 GENERIC CASE: NON-COMPOSED COLUMNS
|
|
495
|
-
// ----------------------------------------------------
|
|
496
|
-
if (!m) {
|
|
497
442
|
return;
|
|
498
443
|
}
|
|
499
|
-
|
|
444
|
+
if (!m)
|
|
445
|
+
return;
|
|
446
|
+
// CLEAR
|
|
500
447
|
if (wasCleared) {
|
|
501
448
|
delete this.latestFilterValues[field];
|
|
502
449
|
m.value = null;
|
|
@@ -507,14 +454,11 @@ class PTAdvancedPrimeTableComponent {
|
|
|
507
454
|
return;
|
|
508
455
|
}
|
|
509
456
|
let value = m.value;
|
|
510
|
-
// PrimeNG often stores the real value in constraints[0].value
|
|
511
457
|
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
|
|
512
458
|
const cVal = m.constraints[0].value;
|
|
513
|
-
if (!isNullish(cVal))
|
|
459
|
+
if (!isNullish(cVal))
|
|
514
460
|
value = cVal;
|
|
515
|
-
}
|
|
516
461
|
}
|
|
517
|
-
// 🔴 NEW: fall back to cached value typed in the input
|
|
518
462
|
const cached = this.latestFilterValues[field];
|
|
519
463
|
const hasCached = !isNullish(cached) && (!Array.isArray(cached) || cached.length > 0);
|
|
520
464
|
if ((isNullish(value) || (Array.isArray(value) && value.length === 0)) &&
|
|
@@ -522,7 +466,6 @@ class PTAdvancedPrimeTableComponent {
|
|
|
522
466
|
value = cached;
|
|
523
467
|
}
|
|
524
468
|
const isEmpty = isNullish(value) || (Array.isArray(value) && value.length === 0);
|
|
525
|
-
// CLEAR => remove from cache, reset PrimeNG metadata
|
|
526
469
|
if (isEmpty) {
|
|
527
470
|
delete this.latestFilterValues[field];
|
|
528
471
|
m.value = null;
|
|
@@ -531,19 +474,22 @@ class PTAdvancedPrimeTableComponent {
|
|
|
531
474
|
}
|
|
532
475
|
return;
|
|
533
476
|
}
|
|
534
|
-
// NON-EMPTY => cache and normalize
|
|
535
477
|
this.latestFilterValues[field] = value;
|
|
536
478
|
let emitValue = value;
|
|
537
|
-
//
|
|
538
|
-
if (col &&
|
|
539
|
-
|
|
479
|
+
// ✅ DATE / DATETIME normalization for server-side filtering
|
|
480
|
+
if (col &&
|
|
481
|
+
(col.type === TableTypeEnum.DATE || col.type === TableTypeEnum.DATETIME)) {
|
|
482
|
+
const d = this.parseAnyDate(value);
|
|
483
|
+
emitValue =
|
|
484
|
+
col.type === TableTypeEnum.DATE
|
|
485
|
+
? this.formatDateWithColumn(d, col)
|
|
486
|
+
: this.formatDateTimeWithColumn(d, col);
|
|
540
487
|
}
|
|
541
488
|
m.value = emitValue;
|
|
542
489
|
if (Array.isArray(m.constraints) && m.constraints.length > 0) {
|
|
543
490
|
m.constraints[0].value = emitValue;
|
|
544
491
|
}
|
|
545
492
|
});
|
|
546
|
-
// send normalized filters to parent (server-side pagination)
|
|
547
493
|
this.onFilterColumn.emit(event);
|
|
548
494
|
}
|
|
549
495
|
changePage(event) {
|
|
@@ -553,93 +499,27 @@ class PTAdvancedPrimeTableComponent {
|
|
|
553
499
|
this.onPageChange.emit({ page, rows });
|
|
554
500
|
}
|
|
555
501
|
sortColumn(event) {
|
|
556
|
-
if (!this.isPaginated)
|
|
502
|
+
if (!this.isPaginated)
|
|
557
503
|
return;
|
|
558
|
-
}
|
|
559
504
|
let field = event.field;
|
|
560
505
|
const col = this.columns.find((c) => c.code === field);
|
|
561
506
|
if (col && col.type === TableTypeEnum.COMPOSED) {
|
|
562
507
|
let textProp;
|
|
563
508
|
if (col.composedNames && col.composedTypes) {
|
|
564
509
|
const idx = col.composedTypes.findIndex((t) => t === TableTypeEnum.STRING);
|
|
565
|
-
if (idx >= 0 && idx < col.composedNames.length)
|
|
510
|
+
if (idx >= 0 && idx < col.composedNames.length)
|
|
566
511
|
textProp = col.composedNames[idx];
|
|
567
|
-
}
|
|
568
512
|
}
|
|
569
513
|
if (!textProp && col.composedNames && col.composedNames.length > 0) {
|
|
570
514
|
textProp = col.composedNames[0];
|
|
571
515
|
}
|
|
572
|
-
if (textProp)
|
|
516
|
+
if (textProp)
|
|
573
517
|
field = `${field}.${textProp}`;
|
|
574
|
-
}
|
|
575
518
|
}
|
|
576
|
-
this.onSortColumn.emit({
|
|
577
|
-
...event,
|
|
578
|
-
field,
|
|
579
|
-
});
|
|
519
|
+
this.onSortColumn.emit({ ...event, field });
|
|
580
520
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (!filterValue) {
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
filterCallback(filterValue);
|
|
587
|
-
if (this.isPaginated) {
|
|
588
|
-
this.onFilterColumn.emit({
|
|
589
|
-
type: 'date',
|
|
590
|
-
columnCode,
|
|
591
|
-
value: filterValue,
|
|
592
|
-
});
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
const filterValueString = event ? this.formatDate(event) : '';
|
|
596
|
-
this.filterColumn({
|
|
597
|
-
filteredValue: this.data.filter((item) => {
|
|
598
|
-
const columnValue = item[columnCode];
|
|
599
|
-
if (columnValue) {
|
|
600
|
-
const itemDateString = this.formatDate(new Date(columnValue));
|
|
601
|
-
return itemDateString === filterValueString;
|
|
602
|
-
}
|
|
603
|
-
return false;
|
|
604
|
-
}),
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
// ---------- OTHER HELPERS ----------
|
|
608
|
-
filterComposedData(item, composedName, value) {
|
|
609
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
610
|
-
return value.some((filterValue) => item[composedName]?.toLowerCase().includes(filterValue.toLowerCase()));
|
|
611
|
-
}
|
|
612
|
-
return true;
|
|
613
|
-
}
|
|
614
|
-
calculateColumnWidth(col) {
|
|
615
|
-
const calculatedWidth = calculateTextWidth(col, col.title);
|
|
616
|
-
const totalWidth = calculatedWidth + this.iconWidth + 20;
|
|
617
|
-
return `${totalWidth}px`;
|
|
618
|
-
}
|
|
619
|
-
getHeaderWidth(col) {
|
|
620
|
-
if (col.width) {
|
|
621
|
-
return col.width;
|
|
622
|
-
}
|
|
623
|
-
return this.calculateColumnWidth(col);
|
|
624
|
-
}
|
|
625
|
-
clear(table) {
|
|
626
|
-
// Clear PrimeNG internal filters + UI
|
|
627
|
-
table.clear();
|
|
628
|
-
// Clear global search
|
|
629
|
-
this.searchValue = '';
|
|
630
|
-
// Clear cached values for normal & composed fields
|
|
631
|
-
this.latestFilterValues = {};
|
|
632
|
-
// Clear local models for composed filters
|
|
633
|
-
Object.keys(this.filters).forEach((key) => {
|
|
634
|
-
if (this.filters[key]) {
|
|
635
|
-
this.filters[key].value = [];
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
if (this.isPaginated) {
|
|
639
|
-
this.onFilterColumn.emit({ cleared: true });
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
parseDate(dateString) {
|
|
521
|
+
// ---------- EDIT HELPERS ----------
|
|
522
|
+
parseDate_ddMMyyyy(dateString) {
|
|
643
523
|
const parts = dateString.split('/');
|
|
644
524
|
if (parts.length === 3) {
|
|
645
525
|
const day = parseInt(parts[0], 10);
|
|
@@ -650,14 +530,6 @@ class PTAdvancedPrimeTableComponent {
|
|
|
650
530
|
}
|
|
651
531
|
return null;
|
|
652
532
|
}
|
|
653
|
-
initializePagination() {
|
|
654
|
-
if (this.isPaginated) {
|
|
655
|
-
if (!this.rowsPerPage || this.rowsPerPage.length === 0) {
|
|
656
|
-
this.rowsPerPage = [20, 30, 40];
|
|
657
|
-
}
|
|
658
|
-
this.rows = this.rowsPerPage[0];
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
533
|
onChange(event, id, key) {
|
|
662
534
|
const target = event.target;
|
|
663
535
|
this.changeHandler(id, key, target.value);
|
|
@@ -666,8 +538,7 @@ class PTAdvancedPrimeTableComponent {
|
|
|
666
538
|
const column = this.columns.find((item) => item.code === key);
|
|
667
539
|
if (!this.map.get(id)) {
|
|
668
540
|
if (column?.type === TableTypeEnum.DATE) {
|
|
669
|
-
|
|
670
|
-
this.dataMap.set(key, date);
|
|
541
|
+
this.dataMap.set(key, this.parseDate_ddMMyyyy(value));
|
|
671
542
|
}
|
|
672
543
|
else {
|
|
673
544
|
this.dataMap.set(key, value);
|
|
@@ -677,14 +548,14 @@ class PTAdvancedPrimeTableComponent {
|
|
|
677
548
|
else {
|
|
678
549
|
const mapItem = this.map.get(id);
|
|
679
550
|
if (column?.type === TableTypeEnum.DATE) {
|
|
680
|
-
|
|
681
|
-
mapItem.set(key, date);
|
|
551
|
+
mapItem.set(key, this.parseDate_ddMMyyyy(value));
|
|
682
552
|
}
|
|
683
553
|
else {
|
|
684
554
|
mapItem.set(key, value);
|
|
685
555
|
}
|
|
686
556
|
}
|
|
687
557
|
}
|
|
558
|
+
// ---------- TYPES ----------
|
|
688
559
|
getColumnFilterType(column) {
|
|
689
560
|
switch (column.type) {
|
|
690
561
|
case TableTypeEnum.STRING:
|
|
@@ -693,6 +564,7 @@ class PTAdvancedPrimeTableComponent {
|
|
|
693
564
|
case TableTypeEnum.NUMBER:
|
|
694
565
|
return 'numeric';
|
|
695
566
|
case TableTypeEnum.DATE:
|
|
567
|
+
case TableTypeEnum.DATETIME: // ✅ treat like date filter (calendar)
|
|
696
568
|
return 'date';
|
|
697
569
|
case TableTypeEnum.MULTISELECT:
|
|
698
570
|
return 'multiSelect';
|
|
@@ -724,22 +596,14 @@ class PTAdvancedPrimeTableComponent {
|
|
|
724
596
|
return false;
|
|
725
597
|
}
|
|
726
598
|
isDatePicker(key) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
dateConverter(value) {
|
|
731
|
-
return new Date(value).toLocaleDateString('en-US');
|
|
732
|
-
}
|
|
733
|
-
getCurrencySymbol(column) {
|
|
734
|
-
return column.type === TableTypeEnum.AMOUNT &&
|
|
735
|
-
column.currency &&
|
|
736
|
-
this.isValidCurrencyCode(column.currency)
|
|
737
|
-
? column.currency
|
|
738
|
-
: undefined;
|
|
599
|
+
const t = this.columns.find((item) => item.code === key)?.type;
|
|
600
|
+
return t === TableTypeEnum.DATE || t === TableTypeEnum.DATETIME; // ✅
|
|
739
601
|
}
|
|
740
|
-
|
|
741
|
-
|
|
602
|
+
isDateTimePicker(key) {
|
|
603
|
+
const t = this.columns.find((item) => item.code === key)?.type;
|
|
604
|
+
return t === TableTypeEnum.DATETIME;
|
|
742
605
|
}
|
|
606
|
+
// ---------- SEARCH ----------
|
|
743
607
|
filterGlobal(event) {
|
|
744
608
|
const target = event.target;
|
|
745
609
|
const value = target.value.toLowerCase();
|
|
@@ -750,23 +614,26 @@ class PTAdvancedPrimeTableComponent {
|
|
|
750
614
|
const filteredData = this.data.filter((item) => {
|
|
751
615
|
return this.globalFilterFields.some((field) => {
|
|
752
616
|
const column = this.columns.find((col) => col.code === field);
|
|
753
|
-
if (!column)
|
|
617
|
+
if (!column)
|
|
754
618
|
return false;
|
|
755
|
-
}
|
|
756
619
|
if (column.type === TableTypeEnum.DATE) {
|
|
757
|
-
|
|
758
|
-
|
|
620
|
+
return this.formatDateWithColumn(this.parseAnyDate(item[field]), column)
|
|
621
|
+
.toLowerCase()
|
|
622
|
+
.includes(value);
|
|
623
|
+
}
|
|
624
|
+
if (column.type === TableTypeEnum.DATETIME) {
|
|
625
|
+
return this.formatDateTimeWithColumn(this.parseAnyDate(item[field]), column)
|
|
626
|
+
.toLowerCase()
|
|
627
|
+
.includes(value);
|
|
759
628
|
}
|
|
760
|
-
|
|
629
|
+
if (column.type === TableTypeEnum.AMOUNT ||
|
|
761
630
|
column.type === TableTypeEnum.NUMBER) {
|
|
762
631
|
return (item[field] && item[field].toString().toLowerCase().includes(value));
|
|
763
632
|
}
|
|
764
|
-
|
|
633
|
+
if (column.type === TableTypeEnum.COMPOSED) {
|
|
765
634
|
return this.filterComposedColumn(item[field], value);
|
|
766
635
|
}
|
|
767
|
-
|
|
768
|
-
return (item[field] && item[field].toString().toLowerCase().includes(value));
|
|
769
|
-
}
|
|
636
|
+
return (item[field] && item[field].toString().toLowerCase().includes(value));
|
|
770
637
|
});
|
|
771
638
|
});
|
|
772
639
|
this.dt.value = filteredData;
|
|
@@ -775,33 +642,118 @@ class PTAdvancedPrimeTableComponent {
|
|
|
775
642
|
if (composedData) {
|
|
776
643
|
return Object.keys(composedData).some((key) => {
|
|
777
644
|
const cellValue = composedData[key];
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
return false;
|
|
645
|
+
return typeof cellValue === 'string'
|
|
646
|
+
? cellValue.toLowerCase().includes(value)
|
|
647
|
+
: false;
|
|
782
648
|
});
|
|
783
649
|
}
|
|
784
650
|
return false;
|
|
785
651
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if (
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
652
|
+
// ---------- ✅ DATE / DATETIME PARSING + FORMATTING ----------
|
|
653
|
+
/** Accepts Date | ISO string | timestamp | anything reasonable */
|
|
654
|
+
parseAnyDate(input) {
|
|
655
|
+
if (input === null || input === undefined || input === '')
|
|
656
|
+
return null;
|
|
657
|
+
if (input instanceof Date)
|
|
658
|
+
return isNaN(input.getTime()) ? null : input;
|
|
659
|
+
// numeric timestamp
|
|
660
|
+
if (typeof input === 'number') {
|
|
661
|
+
const d = new Date(input);
|
|
662
|
+
return isNaN(d.getTime()) ? null : d;
|
|
663
|
+
}
|
|
664
|
+
if (typeof input === 'string') {
|
|
665
|
+
const s = input.trim();
|
|
666
|
+
// if already dd/MM/yyyy[ ...], we keep it parseable
|
|
667
|
+
// (still try ISO first)
|
|
668
|
+
const isoTry = new Date(s);
|
|
669
|
+
if (!isNaN(isoTry.getTime()))
|
|
670
|
+
return isoTry;
|
|
671
|
+
// fallback: dd/MM/yyyy
|
|
672
|
+
if (s.includes('/')) {
|
|
673
|
+
const parts = s.split(' ')[0].split('/');
|
|
674
|
+
if (parts.length === 3) {
|
|
675
|
+
const day = parseInt(parts[0], 10);
|
|
676
|
+
const month = parseInt(parts[1], 10) - 1;
|
|
677
|
+
const year = parseInt(parts[2], 10);
|
|
678
|
+
const d = new Date(year, month, day);
|
|
679
|
+
return isNaN(d.getTime()) ? null : d;
|
|
680
|
+
}
|
|
799
681
|
}
|
|
800
|
-
|
|
801
|
-
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
formatWithPattern(d, pattern) {
|
|
686
|
+
if (!d)
|
|
687
|
+
return '';
|
|
688
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
689
|
+
const map = {
|
|
690
|
+
dd: pad2(d.getDate()),
|
|
691
|
+
MM: pad2(d.getMonth() + 1),
|
|
692
|
+
yyyy: String(d.getFullYear()),
|
|
693
|
+
HH: pad2(d.getHours()),
|
|
694
|
+
mm: pad2(d.getMinutes()),
|
|
695
|
+
ss: pad2(d.getSeconds()),
|
|
696
|
+
};
|
|
697
|
+
// Replace longer tokens first
|
|
698
|
+
return pattern
|
|
699
|
+
.replace(/yyyy/g, map.yyyy)
|
|
700
|
+
.replace(/dd/g, map.dd)
|
|
701
|
+
.replace(/MM/g, map.MM)
|
|
702
|
+
.replace(/HH/g, map.HH)
|
|
703
|
+
.replace(/mm/g, map.mm)
|
|
704
|
+
.replace(/ss/g, map.ss);
|
|
705
|
+
}
|
|
706
|
+
formatDate(date) {
|
|
707
|
+
// keep backward-compat default behavior but FIX ISO display
|
|
708
|
+
const d = this.parseAnyDate(date);
|
|
709
|
+
return this.formatWithPattern(d, 'dd/MM/yyyy');
|
|
710
|
+
}
|
|
711
|
+
formatDateWithColumn(d, col) {
|
|
712
|
+
const fmt = col.dateFormat?.trim() || 'dd/MM/yyyy';
|
|
713
|
+
return this.formatWithPattern(d, fmt);
|
|
714
|
+
}
|
|
715
|
+
formatDateTimeWithColumn(d, col) {
|
|
716
|
+
const fmt = col.dateTimeFormat?.trim() || 'dd/MM/yyyy HH:mm:ss';
|
|
717
|
+
return this.formatWithPattern(d, fmt);
|
|
718
|
+
}
|
|
719
|
+
// ---------- MISC ----------
|
|
720
|
+
calculateColumnWidth(col) {
|
|
721
|
+
const calculatedWidth = calculateTextWidth(col, col.title);
|
|
722
|
+
const totalWidth = calculatedWidth + this.iconWidth + 20;
|
|
723
|
+
return `${totalWidth}px`;
|
|
724
|
+
}
|
|
725
|
+
getHeaderWidth(col) {
|
|
726
|
+
return col.width ? col.width : this.calculateColumnWidth(col);
|
|
727
|
+
}
|
|
728
|
+
clear(table) {
|
|
729
|
+
table.clear();
|
|
730
|
+
this.searchValue = '';
|
|
731
|
+
this.latestFilterValues = {};
|
|
732
|
+
Object.keys(this.filters).forEach((key) => {
|
|
733
|
+
if (this.filters[key])
|
|
734
|
+
this.filters[key].value = [];
|
|
735
|
+
});
|
|
736
|
+
if (this.isPaginated) {
|
|
737
|
+
this.onFilterColumn.emit({ cleared: true });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
initializePagination() {
|
|
741
|
+
if (this.isPaginated) {
|
|
742
|
+
if (!this.rowsPerPage || this.rowsPerPage.length === 0) {
|
|
743
|
+
this.rowsPerPage = [20, 30, 40];
|
|
802
744
|
}
|
|
745
|
+
this.rows = this.rowsPerPage[0];
|
|
803
746
|
}
|
|
804
|
-
|
|
747
|
+
}
|
|
748
|
+
getCurrencySymbol(column) {
|
|
749
|
+
return column.type === TableTypeEnum.AMOUNT &&
|
|
750
|
+
column.currency &&
|
|
751
|
+
this.isValidCurrencyCode(column.currency)
|
|
752
|
+
? column.currency
|
|
753
|
+
: undefined;
|
|
754
|
+
}
|
|
755
|
+
isValidCurrencyCode(currencyCode) {
|
|
756
|
+
return this.validCurrencyCodes.includes(currencyCode);
|
|
805
757
|
}
|
|
806
758
|
exportExcel() {
|
|
807
759
|
this.exportExcelEvent.emit();
|
|
@@ -810,39 +762,32 @@ class PTAdvancedPrimeTableComponent {
|
|
|
810
762
|
this.exportPdfEvent.emit();
|
|
811
763
|
}
|
|
812
764
|
getImageStyle(style) {
|
|
813
|
-
if (style)
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (style.marginBottom) {
|
|
831
|
-
imageStyle.marginBottom = style.marginBottom;
|
|
832
|
-
}
|
|
833
|
-
return imageStyle;
|
|
834
|
-
}
|
|
835
|
-
return {};
|
|
765
|
+
if (!style)
|
|
766
|
+
return {};
|
|
767
|
+
const imageStyle = {
|
|
768
|
+
width: style.width || 'auto',
|
|
769
|
+
height: style.height || 'auto',
|
|
770
|
+
};
|
|
771
|
+
if (style.margin)
|
|
772
|
+
imageStyle.margin = style.margin;
|
|
773
|
+
if (style.marginLeft)
|
|
774
|
+
imageStyle.marginLeft = style.marginLeft;
|
|
775
|
+
if (style.marginRight)
|
|
776
|
+
imageStyle.marginRight = style.marginRight;
|
|
777
|
+
if (style.marginTop)
|
|
778
|
+
imageStyle.marginTop = style.marginTop;
|
|
779
|
+
if (style.marginBottom)
|
|
780
|
+
imageStyle.marginBottom = style.marginBottom;
|
|
781
|
+
return imageStyle;
|
|
836
782
|
}
|
|
837
783
|
getTitleStyle(style) {
|
|
838
|
-
if (style)
|
|
839
|
-
return {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
845
|
-
return {};
|
|
784
|
+
if (!style)
|
|
785
|
+
return {};
|
|
786
|
+
return {
|
|
787
|
+
color: style.color || 'inherit',
|
|
788
|
+
fontSize: style.fontSize || 'inherit',
|
|
789
|
+
textAlign: style.position || 'left',
|
|
790
|
+
};
|
|
846
791
|
}
|
|
847
792
|
formatNumber(value, decimalPlaces, thousandSeparator = 'comma', decimalSeparator = 'dot') {
|
|
848
793
|
if (value === null || value === undefined || isNaN(value))
|
|
@@ -861,12 +806,12 @@ class PTAdvancedPrimeTableComponent {
|
|
|
861
806
|
return formattedNumber;
|
|
862
807
|
}
|
|
863
808
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: PTAdvancedPrimeTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
864
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: PTAdvancedPrimeTableComponent, selector: "pt-advanced-prime-table", inputs: { data: "data", columns: "columns", totalRecords: "totalRecords", rowsPerPage: "rowsPerPage", hasSearchFilter: "hasSearchFilter", hasExportExcel: "hasExportExcel", hasExportPDF: "hasExportPDF", hasColumnFilter: "hasColumnFilter", isPaginated: "isPaginated", actions: "actions", isSortable: "isSortable", loading: "loading", maxHeight: "maxHeight" }, outputs: { search: "search", exportExcelEvent: "exportExcelEvent", exportPdfEvent: "exportPdfEvent", onPageChange: "onPageChange", onSortColumn: "onSortColumn", onFilterColumn: "onFilterColumn" }, viewQueries: [{ propertyName: "dt", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<div class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"data\"\n [loading]=\"loading\"\n [rows]=\"rows\"\n [paginator]=\"isPaginated\"\n [globalFilterFields]=\"globalFilterFields\"\n [rowsPerPageOptions]=\"rowsPerPage\"\n [totalRecords]=\"totalRecords\"\n [lazy]=\"isPaginated\"\n dataKey=\"id\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n (onFilter)=\"filterColumn($event)\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n >\n <!-- COLGROUP: sync header/body widths -->\n <ng-template pTemplate=\"colgroup\" let-columns>\n <colgroup>\n <col\n *ngFor=\"let col of columns\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n />\n </colgroup>\n </ng-template>\n\n <!-- CAPTION -->\n <ng-template pTemplate=\"caption\">\n <div class=\"flex\">\n <div>\n <h3>Total: {{ totalRecords }}</h3>\n </div>\n\n <div>\n <!-- Clear filters -->\n <button\n *ngIf=\"hasSearchFilter\"\n pButton\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"clear(dt)\"\n title=\"Clear filters\"\n ></button>\n\n <!-- Export to Excel Button -->\n <button\n *ngIf=\"hasExportExcel\"\n pButton\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportExcel()\"\n title=\"Export to Excel\"\n ></button>\n\n <!-- Export to PDF Button -->\n <button\n *ngIf=\"hasExportPDF\"\n pButton\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportPdf()\"\n title=\"Export to PDF\"\n ></button>\n </div>\n\n <div class=\"ml-auto\" *ngIf=\"hasSearchFilter\">\n <p-iconField iconPosition=\"left\" class=\"ml-auto\">\n <p-inputIcon>\n <i class=\"pi pi-search\"></i>\n </p-inputIcon>\n <input\n pInputText\n type=\"text\"\n [(ngModel)]=\"searchValue\"\n (input)=\"filterGlobal($event)\"\n placeholder=\"Search keyword\"\n />\n </p-iconField>\n </div>\n </div>\n </ng-template>\n\n <!-- HEADER -->\n <ng-template pTemplate=\"header\">\n <tr class=\"sticky-header\">\n <ng-container *ngFor=\"let col of columns\">\n <th\n *ngIf=\"!col.children; else groupHeader\"\n pSortableColumn=\"{{ col.code }}\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [ngClass]=\"[\n getHeaderAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n colspan=\"1\"\n >\n <!-- SORTABLE HEADER -->\n <ng-container\n *ngIf=\"isSortable && col.isSortable !== false; else noSortHeader\"\n >\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n <div\n class=\"icons d-flex align-items-center\"\n [style.width]=\"'77px'\"\n >\n <p-sortIcon field=\"{{ col.code }}\" />\n\n <!-- COLUMN FILTERS -->\n <ng-container\n *ngIf=\"hasColumnFilter && col.isFilter !== false\"\n >\n <!-- COMPOSED FILTER (uses built-in Apply/Clear) -->\n <p-columnFilter\n *ngIf=\"col.type === TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n type=\"text\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n let-filterModel=\"filterModel\"\n >\n <div *ngFor=\"let composedName of col.composedNames\">\n <ng-container\n *ngIf=\"\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n \"\n >\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n [ngModel]=\"filters[composedName]?.value\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event,\n filterModel\n )\n \"\n [placeholder]=\"filters[composedName]?.placeholder\"\n display=\"chip\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n <img\n *ngIf=\"item.image\"\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-container>\n </div>\n </ng-template>\n </p-columnFilter>\n\n <!-- OTHER TYPES -->\n <p-columnFilter\n *ngIf=\"col.type !== TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n <!-- NUMBER / AMOUNT: use text input so '.' works -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n \"\n let-value\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n placeholder=\"Enter a number\"\n />\n </ng-template>\n\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filterModel=\"filterModel\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filterModel=\"filterModel\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </div>\n </ng-container>\n\n <!-- NON-SORTABLE HEADER -->\n <ng-template #noSortHeader>\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n <ng-container *ngIf=\"hasColumnFilter && col.isFilter !== false\">\n <!-- AMOUNT behaves like NUMBER -->\n <p-columnFilter\n *ngIf=\"col.type === 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n placeholder=\"Enter an amount\"\n />\n </ng-template>\n </p-columnFilter>\n\n <p-columnFilter\n *ngIf=\"col.type !== 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'date'\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'multiSelect'\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Select\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </ng-template>\n </th>\n\n <!-- GROUPED HEADER -->\n <ng-template #groupHeader>\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n </ng-template>\n </ng-container>\n </tr>\n\n <!-- CHILD HEADERS -->\n <tr *ngIf=\"hasGroupedColumns\">\n <ng-container *ngFor=\"let col of columns\">\n <ng-container *ngIf=\"col.children\">\n <th\n *ngFor=\"let child of col.children\"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [style.padding]=\"'0px'\"\n ></th>\n </ng-container>\n </ng-container>\n </tr>\n </ng-template>\n\n <!-- EMPTY MESSAGE -->\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <!-- span all columns -->\n <td class=\"empty-message-cell\" [attr.colspan]=\"columns.length || 1\">\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 <!-- BODY -->\n <ng-template\n pTemplate=\"body\"\n let-data\n let-editing=\"editing\"\n let-ri=\"rowIndex\"\n >\n <tr *ngIf=\"!loading\" [pEditableRow]=\"isEdit ? data : null\">\n <ng-container *ngFor=\"let col of columns\">\n <!-- SIMPLE COLUMNS (no children) -->\n <ng-container *ngIf=\"!col.children; else childColumns\">\n <!-- EDITABLE CELL -->\n <td\n *ngIf=\"\n isEditable(col.code!) && col.type !== TableTypeEnum.ACTION;\n else normalTD\n \"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n >\n <!-- Editable input for NUMBER/DATE/STRING/MULTISELECT -->\n <ng-container *ngIf=\"isMultiSelect(col.code); else datePicker\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n (ngModelChange)=\"changeHandler(data.id, col.code, $event)\"\n [options]=\"optionValues\"\n ></p-multiSelect>\n </ng-template>\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n <ng-container *ngFor=\"let rec of data[col.code!]\">\n <p-tag [value]=\"rec\"></p-tag>\n </ng-container>\n </div>\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #datePicker>\n <ng-container *ngIf=\"isDatePicker(col.code); else normalInput\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-calendar\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n [dateFormat]=\"'dd/mm/yy'\"\n ></p-calendar>\n </ng-template>\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n </ng-template>\n\n <ng-template #normalInput>\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 <ng-template pTemplate=\"output\">\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.AMOUNT;\n else normalOutput\n \"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n <ng-template #normalOutput>\n {{ data[col.code!] }}\n </ng-template>\n </ng-template>\n </p-cellEditor>\n </ng-template>\n </td>\n\n <!-- NON-EDITABLE CELL (ALWAYS RENDERED) -->\n <ng-template #normalTD>\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n >\n <!-- ACTION column: built-in edit/delete + custom actions -->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.ACTION; else nonActionCell\"\n >\n <div class=\"action-buttons-container\">\n <!-- built-in delete -->\n <button\n *ngIf=\"isDelete\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n (click)=\"Delete(data.id)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n\n <!-- built-in inline edit -->\n <div *ngIf=\"isEdit\">\n <button\n pInitEditableRow\n *ngIf=\"!editing\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n (click)=\"initEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n (click)=\"saveEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n (click)=\"cancelEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n </div>\n\n <!-- custom actions -->\n <button\n *ngFor=\"let act of customActions\"\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 (click)=\"onCustomActionClick(act, data)\"\n ></button>\n </div>\n </ng-container>\n\n <!-- NON-ACTION cells -->\n <ng-template #nonActionCell>\n <!-- COMPOSED -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.COMPOSED;\n else nonComposed\n \"\n >\n <div class=\"composed-cell\">\n <ng-container\n *ngFor=\"\n let composedName of col.composedNames;\n let i = index\n \"\n >\n <!-- IMAGE -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.IMAGE\n \"\n >\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getImageStyle(col.composedStyles?.[composedName])\n \"\n />\n </ng-container>\n\n <!-- STRING -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.STRING\n \"\n >\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getTitleStyle(\n col.composedStyles?.[composedName]\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n </ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <ng-template #nonComposed>\n <!-- AMOUNT-->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.AMOUNT; else nonAmount\"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n\n <ng-template #nonAmount>\n <!-- NUMBER-->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER;\n else nonNumber\n \"\n >\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n </ng-container>\n\n <ng-template #nonNumber>\n <!-- DATE -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.DATE;\n else normalTypes\n \"\n >\n {{ formatDate(data[col.code!]) }}\n </ng-container>\n\n <ng-template #normalTypes>\n <!-- STRING, MULTISELECT-->\n <ng-container\n *ngIf=\"\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT\n ].includes(col.type!)\n \"\n >\n {{ data[col.code!] }}\n </ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </td>\n </ng-template>\n </ng-container>\n\n <!-- CHILD COLUMNS -->\n <ng-template #childColumns>\n <ng-container *ngFor=\"let child of col.children\">\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n >\n <ng-container\n *ngIf=\"isEditable(child.code); else childNormalTD\"\n >\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 <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #childNormalTD>\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </td>\n </ng-container>\n </ng-template>\n </ng-container>\n </tr>\n </ng-template>\n </p-table>\n</div>\n", styles: [".pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{width:100%;height:5rem;display:grid;justify-items:start;align-items:center}.pt-advanced-prime-table .filter-container{width:100%;display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .settings{display:flex;gap:1rem}.pt-advanced-prime-table .multi-select-container{display:flex;justify-content:center;align-items:center;gap:.3rem}.pt-advanced-prime-table ::ng-deep p-table{min-width:50rem}.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{display:flex;justify-content:space-between;align-items:center;width:100%}.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{flex:1}.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 p-columnfilter.p-element.ng-star-inserted{margin-top:4px}.pt-advanced-prime-table .flex{display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-inputicon{margin-right:-1.5rem;z-index:2;position:relative}.pt-advanced-prime-table ::ng-deep .p-inputtext{padding-left:1.7rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{cursor:pointer;margin-left:1rem}.pt-advanced-prime-table ::ng-deep .p-icon-field-left .p-input-icon:first-of-type{left:-1rem}.pt-advanced-prime-table .table-row{text-align:center;display:flex;gap:1rem;justify-content:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{font-size:1.25em;color:green}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{font-size:1.25em;color:red}.pt-advanced-prime-table .table-container{display:block;width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{width:100%;height:100%;min-height:180px;display:flex;align-items:center;justify-content:center}.pt-advanced-prime-table .empty-message{text-align:center;color:#888;font-size:1.2rem}.pt-advanced-prime-table .empty-message i{display:block;font-size:2rem;margin-bottom:.5rem}.pt-advanced-prime-table th{white-space:normal;word-wrap: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 ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{text-align:center!important;justify-content:center;align-items:center;overflow:hidden;white-space:nowrap;padding:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container{width:100%;display:flex;justify-content:center!important;align-items:center!important;gap:.35rem}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{padding:.25rem!important;min-width:22px!important;height:22px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "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", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll", "virtualRowHeight"], 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: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.CellEditor, selector: "p-cellEditor" }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i2.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i2.InitEditableRow, selector: "[pInitEditableRow]" }, { kind: "directive", type: i2.SaveEditableRow, selector: "[pSaveEditableRow]" }, { kind: "directive", type: i2.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i2.ColumnFilter, selector: "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", "useGrouping", "showButtons", "ariaLabel"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant"] }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain"] }, { kind: "component", type: i6.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepYearPicker", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8.MultiSelect, selector: "p-multiSelect", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "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", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i10.IconField, selector: "p-iconField", inputs: ["iconPosition"] }, { kind: "component", type: i11.InputIcon, selector: "p-inputIcon", inputs: ["styleClass"] }, { kind: "pipe", type: CustomCurrencyPipe, name: "customCurrency" }, { kind: "pipe", type: CustomDatePipe, name: "customDate" }] }); }
|
|
809
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: PTAdvancedPrimeTableComponent, selector: "pt-advanced-prime-table", inputs: { data: "data", columns: "columns", totalRecords: "totalRecords", rowsPerPage: "rowsPerPage", hasSearchFilter: "hasSearchFilter", hasExportExcel: "hasExportExcel", hasExportPDF: "hasExportPDF", hasColumnFilter: "hasColumnFilter", isPaginated: "isPaginated", actions: "actions", isSortable: "isSortable", loading: "loading", maxHeight: "maxHeight" }, outputs: { search: "search", exportExcelEvent: "exportExcelEvent", exportPdfEvent: "exportPdfEvent", onPageChange: "onPageChange", onSortColumn: "onSortColumn", onFilterColumn: "onFilterColumn" }, viewQueries: [{ propertyName: "dt", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<div class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"data\"\n [loading]=\"loading\"\n [rows]=\"rows\"\n [paginator]=\"isPaginated\"\n [globalFilterFields]=\"globalFilterFields\"\n [rowsPerPageOptions]=\"rowsPerPage\"\n [totalRecords]=\"totalRecords\"\n [lazy]=\"isPaginated\"\n dataKey=\"id\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n (onFilter)=\"filterColumn($event)\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n >\n <!-- COLGROUP: sync header/body widths -->\n <ng-template pTemplate=\"colgroup\" let-columns>\n <colgroup>\n <col\n *ngFor=\"let col of columns\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n />\n </colgroup>\n </ng-template>\n\n <!-- CAPTION -->\n <ng-template pTemplate=\"caption\">\n <div class=\"flex\">\n <div>\n <h3>Total: {{ totalRecords }}</h3>\n </div>\n\n <div>\n <!-- Clear filters -->\n <button\n *ngIf=\"hasSearchFilter\"\n pButton\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"clear(dt)\"\n title=\"Clear filters\"\n ></button>\n\n <!-- Export to Excel Button -->\n <button\n *ngIf=\"hasExportExcel\"\n pButton\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportExcel()\"\n title=\"Export to Excel\"\n ></button>\n\n <!-- Export to PDF Button -->\n <button\n *ngIf=\"hasExportPDF\"\n pButton\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportPdf()\"\n title=\"Export to PDF\"\n ></button>\n </div>\n\n <div class=\"ml-auto\" *ngIf=\"hasSearchFilter\">\n <p-iconField iconPosition=\"left\" class=\"ml-auto\">\n <p-inputIcon>\n <i class=\"pi pi-search\"></i>\n </p-inputIcon>\n <input\n pInputText\n type=\"text\"\n [(ngModel)]=\"searchValue\"\n (input)=\"filterGlobal($event)\"\n placeholder=\"Search keyword\"\n />\n </p-iconField>\n </div>\n </div>\n </ng-template>\n\n <!-- HEADER -->\n <ng-template pTemplate=\"header\">\n <tr class=\"sticky-header\">\n <ng-container *ngFor=\"let col of columns\">\n <th\n *ngIf=\"!col.children; else groupHeader\"\n pSortableColumn=\"{{ col.code }}\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [ngClass]=\"[\n getHeaderAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n colspan=\"1\"\n >\n <!-- SORTABLE HEADER -->\n <ng-container\n *ngIf=\"isSortable && col.isSortable !== false; else noSortHeader\"\n >\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n <div\n class=\"icons d-flex align-items-center\"\n [style.width]=\"'77px'\"\n >\n <p-sortIcon field=\"{{ col.code }}\" />\n\n <!-- COLUMN FILTERS -->\n <ng-container\n *ngIf=\"hasColumnFilter && col.isFilter !== false\"\n >\n <!-- COMPOSED FILTER (uses built-in Apply/Clear) -->\n <p-columnFilter\n *ngIf=\"col.type === TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n type=\"text\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n let-filterModel=\"filterModel\"\n >\n <div *ngFor=\"let composedName of col.composedNames\">\n <ng-container\n *ngIf=\"\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n \"\n >\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n [ngModel]=\"filters[composedName]?.value\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event,\n filterModel\n )\n \"\n [placeholder]=\"filters[composedName]?.placeholder\"\n display=\"chip\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n <img\n *ngIf=\"item.image\"\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-container>\n </div>\n </ng-template>\n </p-columnFilter>\n\n <!-- OTHER TYPES -->\n <p-columnFilter\n *ngIf=\"col.type !== TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n <!-- NUMBER / AMOUNT: use text input so '.' works -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n \"\n let-value\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n placeholder=\"Enter a number\"\n />\n </ng-template>\n\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filterModel=\"filterModel\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filterModel=\"filterModel\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </div>\n </ng-container>\n\n <!-- NON-SORTABLE HEADER -->\n <ng-template #noSortHeader>\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n <ng-container *ngIf=\"hasColumnFilter && col.isFilter !== false\">\n <!-- AMOUNT behaves like NUMBER -->\n <p-columnFilter\n *ngIf=\"col.type === 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n placeholder=\"Enter an amount\"\n />\n </ng-template>\n </p-columnFilter>\n\n <p-columnFilter\n *ngIf=\"col.type !== 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'date'\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'multiSelect'\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Select\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </ng-template>\n </th>\n\n <!-- GROUPED HEADER -->\n <ng-template #groupHeader>\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n </ng-template>\n </ng-container>\n </tr>\n\n <!-- CHILD HEADERS -->\n <tr *ngIf=\"hasGroupedColumns\">\n <ng-container *ngFor=\"let col of columns\">\n <ng-container *ngIf=\"col.children\">\n <th\n *ngFor=\"let child of col.children\"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [style.padding]=\"'0px'\"\n ></th>\n </ng-container>\n </ng-container>\n </tr>\n </ng-template>\n\n <!-- EMPTY MESSAGE -->\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <!-- span all columns -->\n <td class=\"empty-message-cell\" [attr.colspan]=\"columns.length || 1\">\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 <!-- BODY -->\n <ng-template\n pTemplate=\"body\"\n let-data\n let-editing=\"editing\"\n let-ri=\"rowIndex\"\n >\n <tr *ngIf=\"!loading\" [pEditableRow]=\"isEdit ? data : null\">\n <ng-container *ngFor=\"let col of columns\">\n <!-- SIMPLE COLUMNS (no children) -->\n <ng-container *ngIf=\"!col.children; else childColumns\">\n <!-- EDITABLE CELL -->\n <td\n *ngIf=\"\n isEditable(col.code!) && col.type !== TableTypeEnum.ACTION;\n else normalTD\n \"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n >\n <!-- Editable input for NUMBER/DATE/STRING/MULTISELECT -->\n <ng-container *ngIf=\"isMultiSelect(col.code); else datePicker\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n (ngModelChange)=\"changeHandler(data.id, col.code, $event)\"\n [options]=\"optionValues\"\n ></p-multiSelect>\n </ng-template>\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n <ng-container *ngFor=\"let rec of data[col.code!]\">\n <p-tag [value]=\"rec\"></p-tag>\n </ng-container>\n </div>\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #datePicker>\n <ng-container *ngIf=\"isDatePicker(col.code); else normalInput\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-calendar\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n [dateFormat]=\"'dd/mm/yy'\"\n ></p-calendar>\n </ng-template>\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n </ng-template>\n\n <ng-template #normalInput>\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 <ng-template pTemplate=\"output\">\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.AMOUNT;\n else normalOutput\n \"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n <ng-template #normalOutput>\n {{ data[col.code!] }}\n </ng-template>\n </ng-template>\n </p-cellEditor>\n </ng-template>\n </td>\n\n <!-- NON-EDITABLE CELL (ALWAYS RENDERED) -->\n <ng-template #normalTD>\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n >\n <!-- ACTION column: built-in edit/delete + custom actions -->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.ACTION; else nonActionCell\"\n >\n <div class=\"action-buttons-container\">\n <!-- built-in delete -->\n <button\n *ngIf=\"isDelete\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n (click)=\"Delete(data.id)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n\n <!-- built-in inline edit -->\n <div *ngIf=\"isEdit\">\n <button\n pInitEditableRow\n *ngIf=\"!editing\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n (click)=\"initEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n (click)=\"saveEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n (click)=\"cancelEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n </div>\n\n <!-- custom actions -->\n <button\n *ngFor=\"let act of customActions\"\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 (click)=\"onCustomActionClick(act, data)\"\n ></button>\n </div>\n </ng-container>\n\n <!-- NON-ACTION cells -->\n <ng-template #nonActionCell>\n <!-- COMPOSED -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.COMPOSED;\n else nonComposed\n \"\n >\n <div class=\"composed-cell\">\n <ng-container\n *ngFor=\"\n let composedName of col.composedNames;\n let i = index\n \"\n >\n <!-- IMAGE -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.IMAGE\n \"\n >\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getImageStyle(col.composedStyles?.[composedName])\n \"\n />\n </ng-container>\n\n <!-- STRING -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.STRING\n \"\n >\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getTitleStyle(\n col.composedStyles?.[composedName]\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n </ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <ng-template #nonComposed>\n <!-- AMOUNT-->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.AMOUNT; else nonAmount\"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n\n <ng-template #nonAmount>\n <!-- NUMBER-->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER;\n else nonNumber\n \"\n >\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n </ng-container>\n\n <ng-template #nonNumber>\n <!-- DATE -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.DATE ||\n col.type === TableTypeEnum.DATETIME;\n else normalTypes\n \"\n >\n <ng-container *ngIf=\"col.type === TableTypeEnum.DATE\">\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n </ng-container>\n\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.DATETIME\"\n >\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n </ng-container>\n </ng-container>\n\n <ng-template #normalTypes>\n <!-- STRING, MULTISELECT-->\n <ng-container\n *ngIf=\"\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT\n ].includes(col.type!)\n \"\n >\n {{ data[col.code!] }}\n </ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </td>\n </ng-template>\n </ng-container>\n\n <!-- CHILD COLUMNS -->\n <ng-template #childColumns>\n <ng-container *ngFor=\"let child of col.children\">\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n >\n <ng-container\n *ngIf=\"isEditable(child.code); else childNormalTD\"\n >\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 <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #childNormalTD>\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </td>\n </ng-container>\n </ng-template>\n </ng-container>\n </tr>\n </ng-template>\n </p-table>\n</div>\n", styles: [".pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{width:100%;height:5rem;display:grid;justify-items:start;align-items:center}.pt-advanced-prime-table .filter-container{width:100%;display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .settings{display:flex;gap:1rem}.pt-advanced-prime-table .multi-select-container{display:flex;justify-content:center;align-items:center;gap:.3rem}.pt-advanced-prime-table ::ng-deep p-table{min-width:50rem}.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{display:flex;justify-content:space-between;align-items:center;width:100%}.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{flex:1}.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 p-columnfilter.p-element.ng-star-inserted{margin-top:4px}.pt-advanced-prime-table .flex{display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-inputicon{margin-right:-1.5rem;z-index:2;position:relative}.pt-advanced-prime-table ::ng-deep .p-inputtext{padding-left:1.7rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{cursor:pointer;margin-left:1rem}.pt-advanced-prime-table ::ng-deep .p-icon-field-left .p-input-icon:first-of-type{left:-1rem}.pt-advanced-prime-table .table-row{text-align:center;display:flex;gap:1rem;justify-content:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{font-size:1.25em;color:green}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{font-size:1.25em;color:red}.pt-advanced-prime-table .table-container{display:block;width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{width:100%;height:100%;min-height:180px;display:flex;align-items:center;justify-content:center}.pt-advanced-prime-table .empty-message{text-align:center;color:#888;font-size:1.2rem}.pt-advanced-prime-table .empty-message i{display:block;font-size:2rem;margin-bottom:.5rem}.pt-advanced-prime-table th{white-space:normal;word-wrap: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 ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{text-align:center!important;justify-content:center;align-items:center;overflow:hidden;white-space:nowrap;padding:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container{width:100%;display:flex;justify-content:center!important;align-items:center!important;gap:.35rem}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{padding:.25rem!important;min-width:22px!important;height:22px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "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", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll", "virtualRowHeight"], 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: i2.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i2.CellEditor, selector: "p-cellEditor" }, { kind: "component", type: i2.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "directive", type: i2.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i2.InitEditableRow, selector: "[pInitEditableRow]" }, { kind: "directive", type: i2.SaveEditableRow, selector: "[pSaveEditableRow]" }, { kind: "directive", type: i2.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i2.ColumnFilter, selector: "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", "useGrouping", "showButtons", "ariaLabel"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant"] }, { kind: "directive", type: i3.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain"] }, { kind: "component", type: i6.Calendar, selector: "p-calendar", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepYearPicker", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: i2$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8.MultiSelect, selector: "p-multiSelect", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "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", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i10.IconField, selector: "p-iconField", inputs: ["iconPosition"] }, { kind: "component", type: i11.InputIcon, selector: "p-inputIcon", inputs: ["styleClass"] }, { kind: "pipe", type: CustomCurrencyPipe, name: "customCurrency" }, { kind: "pipe", type: CustomDatePipe, name: "customDate" }] }); }
|
|
865
810
|
}
|
|
866
811
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: PTAdvancedPrimeTableComponent, decorators: [{
|
|
867
812
|
type: Component,
|
|
868
|
-
args: [{ selector: 'pt-advanced-prime-table', template: "<div class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"data\"\n [loading]=\"loading\"\n [rows]=\"rows\"\n [paginator]=\"isPaginated\"\n [globalFilterFields]=\"globalFilterFields\"\n [rowsPerPageOptions]=\"rowsPerPage\"\n [totalRecords]=\"totalRecords\"\n [lazy]=\"isPaginated\"\n dataKey=\"id\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n (onFilter)=\"filterColumn($event)\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n >\n <!-- COLGROUP: sync header/body widths -->\n <ng-template pTemplate=\"colgroup\" let-columns>\n <colgroup>\n <col\n *ngFor=\"let col of columns\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n />\n </colgroup>\n </ng-template>\n\n <!-- CAPTION -->\n <ng-template pTemplate=\"caption\">\n <div class=\"flex\">\n <div>\n <h3>Total: {{ totalRecords }}</h3>\n </div>\n\n <div>\n <!-- Clear filters -->\n <button\n *ngIf=\"hasSearchFilter\"\n pButton\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"clear(dt)\"\n title=\"Clear filters\"\n ></button>\n\n <!-- Export to Excel Button -->\n <button\n *ngIf=\"hasExportExcel\"\n pButton\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportExcel()\"\n title=\"Export to Excel\"\n ></button>\n\n <!-- Export to PDF Button -->\n <button\n *ngIf=\"hasExportPDF\"\n pButton\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportPdf()\"\n title=\"Export to PDF\"\n ></button>\n </div>\n\n <div class=\"ml-auto\" *ngIf=\"hasSearchFilter\">\n <p-iconField iconPosition=\"left\" class=\"ml-auto\">\n <p-inputIcon>\n <i class=\"pi pi-search\"></i>\n </p-inputIcon>\n <input\n pInputText\n type=\"text\"\n [(ngModel)]=\"searchValue\"\n (input)=\"filterGlobal($event)\"\n placeholder=\"Search keyword\"\n />\n </p-iconField>\n </div>\n </div>\n </ng-template>\n\n <!-- HEADER -->\n <ng-template pTemplate=\"header\">\n <tr class=\"sticky-header\">\n <ng-container *ngFor=\"let col of columns\">\n <th\n *ngIf=\"!col.children; else groupHeader\"\n pSortableColumn=\"{{ col.code }}\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [ngClass]=\"[\n getHeaderAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n colspan=\"1\"\n >\n <!-- SORTABLE HEADER -->\n <ng-container\n *ngIf=\"isSortable && col.isSortable !== false; else noSortHeader\"\n >\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n <div\n class=\"icons d-flex align-items-center\"\n [style.width]=\"'77px'\"\n >\n <p-sortIcon field=\"{{ col.code }}\" />\n\n <!-- COLUMN FILTERS -->\n <ng-container\n *ngIf=\"hasColumnFilter && col.isFilter !== false\"\n >\n <!-- COMPOSED FILTER (uses built-in Apply/Clear) -->\n <p-columnFilter\n *ngIf=\"col.type === TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n type=\"text\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n let-filterModel=\"filterModel\"\n >\n <div *ngFor=\"let composedName of col.composedNames\">\n <ng-container\n *ngIf=\"\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n \"\n >\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n [ngModel]=\"filters[composedName]?.value\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event,\n filterModel\n )\n \"\n [placeholder]=\"filters[composedName]?.placeholder\"\n display=\"chip\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n <img\n *ngIf=\"item.image\"\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-container>\n </div>\n </ng-template>\n </p-columnFilter>\n\n <!-- OTHER TYPES -->\n <p-columnFilter\n *ngIf=\"col.type !== TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n <!-- NUMBER / AMOUNT: use text input so '.' works -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n \"\n let-value\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n placeholder=\"Enter a number\"\n />\n </ng-template>\n\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filterModel=\"filterModel\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filterModel=\"filterModel\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </div>\n </ng-container>\n\n <!-- NON-SORTABLE HEADER -->\n <ng-template #noSortHeader>\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n <ng-container *ngIf=\"hasColumnFilter && col.isFilter !== false\">\n <!-- AMOUNT behaves like NUMBER -->\n <p-columnFilter\n *ngIf=\"col.type === 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n placeholder=\"Enter an amount\"\n />\n </ng-template>\n </p-columnFilter>\n\n <p-columnFilter\n *ngIf=\"col.type !== 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'date'\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'multiSelect'\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Select\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </ng-template>\n </th>\n\n <!-- GROUPED HEADER -->\n <ng-template #groupHeader>\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n </ng-template>\n </ng-container>\n </tr>\n\n <!-- CHILD HEADERS -->\n <tr *ngIf=\"hasGroupedColumns\">\n <ng-container *ngFor=\"let col of columns\">\n <ng-container *ngIf=\"col.children\">\n <th\n *ngFor=\"let child of col.children\"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [style.padding]=\"'0px'\"\n ></th>\n </ng-container>\n </ng-container>\n </tr>\n </ng-template>\n\n <!-- EMPTY MESSAGE -->\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <!-- span all columns -->\n <td class=\"empty-message-cell\" [attr.colspan]=\"columns.length || 1\">\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 <!-- BODY -->\n <ng-template\n pTemplate=\"body\"\n let-data\n let-editing=\"editing\"\n let-ri=\"rowIndex\"\n >\n <tr *ngIf=\"!loading\" [pEditableRow]=\"isEdit ? data : null\">\n <ng-container *ngFor=\"let col of columns\">\n <!-- SIMPLE COLUMNS (no children) -->\n <ng-container *ngIf=\"!col.children; else childColumns\">\n <!-- EDITABLE CELL -->\n <td\n *ngIf=\"\n isEditable(col.code!) && col.type !== TableTypeEnum.ACTION;\n else normalTD\n \"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n >\n <!-- Editable input for NUMBER/DATE/STRING/MULTISELECT -->\n <ng-container *ngIf=\"isMultiSelect(col.code); else datePicker\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n (ngModelChange)=\"changeHandler(data.id, col.code, $event)\"\n [options]=\"optionValues\"\n ></p-multiSelect>\n </ng-template>\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n <ng-container *ngFor=\"let rec of data[col.code!]\">\n <p-tag [value]=\"rec\"></p-tag>\n </ng-container>\n </div>\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #datePicker>\n <ng-container *ngIf=\"isDatePicker(col.code); else normalInput\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-calendar\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n [dateFormat]=\"'dd/mm/yy'\"\n ></p-calendar>\n </ng-template>\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n </ng-template>\n\n <ng-template #normalInput>\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 <ng-template pTemplate=\"output\">\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.AMOUNT;\n else normalOutput\n \"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n <ng-template #normalOutput>\n {{ data[col.code!] }}\n </ng-template>\n </ng-template>\n </p-cellEditor>\n </ng-template>\n </td>\n\n <!-- NON-EDITABLE CELL (ALWAYS RENDERED) -->\n <ng-template #normalTD>\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n >\n <!-- ACTION column: built-in edit/delete + custom actions -->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.ACTION; else nonActionCell\"\n >\n <div class=\"action-buttons-container\">\n <!-- built-in delete -->\n <button\n *ngIf=\"isDelete\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n (click)=\"Delete(data.id)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n\n <!-- built-in inline edit -->\n <div *ngIf=\"isEdit\">\n <button\n pInitEditableRow\n *ngIf=\"!editing\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n (click)=\"initEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n (click)=\"saveEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n (click)=\"cancelEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n </div>\n\n <!-- custom actions -->\n <button\n *ngFor=\"let act of customActions\"\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 (click)=\"onCustomActionClick(act, data)\"\n ></button>\n </div>\n </ng-container>\n\n <!-- NON-ACTION cells -->\n <ng-template #nonActionCell>\n <!-- COMPOSED -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.COMPOSED;\n else nonComposed\n \"\n >\n <div class=\"composed-cell\">\n <ng-container\n *ngFor=\"\n let composedName of col.composedNames;\n let i = index\n \"\n >\n <!-- IMAGE -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.IMAGE\n \"\n >\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getImageStyle(col.composedStyles?.[composedName])\n \"\n />\n </ng-container>\n\n <!-- STRING -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.STRING\n \"\n >\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getTitleStyle(\n col.composedStyles?.[composedName]\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n </ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <ng-template #nonComposed>\n <!-- AMOUNT-->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.AMOUNT; else nonAmount\"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n\n <ng-template #nonAmount>\n <!-- NUMBER-->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER;\n else nonNumber\n \"\n >\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n </ng-container>\n\n <ng-template #nonNumber>\n <!-- DATE -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.DATE;\n else normalTypes\n \"\n >\n {{ formatDate(data[col.code!]) }}\n </ng-container>\n\n <ng-template #normalTypes>\n <!-- STRING, MULTISELECT-->\n <ng-container\n *ngIf=\"\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT\n ].includes(col.type!)\n \"\n >\n {{ data[col.code!] }}\n </ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </td>\n </ng-template>\n </ng-container>\n\n <!-- CHILD COLUMNS -->\n <ng-template #childColumns>\n <ng-container *ngFor=\"let child of col.children\">\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n >\n <ng-container\n *ngIf=\"isEditable(child.code); else childNormalTD\"\n >\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 <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #childNormalTD>\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </td>\n </ng-container>\n </ng-template>\n </ng-container>\n </tr>\n </ng-template>\n </p-table>\n</div>\n", styles: [".pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{width:100%;height:5rem;display:grid;justify-items:start;align-items:center}.pt-advanced-prime-table .filter-container{width:100%;display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .settings{display:flex;gap:1rem}.pt-advanced-prime-table .multi-select-container{display:flex;justify-content:center;align-items:center;gap:.3rem}.pt-advanced-prime-table ::ng-deep p-table{min-width:50rem}.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{display:flex;justify-content:space-between;align-items:center;width:100%}.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{flex:1}.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 p-columnfilter.p-element.ng-star-inserted{margin-top:4px}.pt-advanced-prime-table .flex{display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-inputicon{margin-right:-1.5rem;z-index:2;position:relative}.pt-advanced-prime-table ::ng-deep .p-inputtext{padding-left:1.7rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{cursor:pointer;margin-left:1rem}.pt-advanced-prime-table ::ng-deep .p-icon-field-left .p-input-icon:first-of-type{left:-1rem}.pt-advanced-prime-table .table-row{text-align:center;display:flex;gap:1rem;justify-content:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{font-size:1.25em;color:green}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{font-size:1.25em;color:red}.pt-advanced-prime-table .table-container{display:block;width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{width:100%;height:100%;min-height:180px;display:flex;align-items:center;justify-content:center}.pt-advanced-prime-table .empty-message{text-align:center;color:#888;font-size:1.2rem}.pt-advanced-prime-table .empty-message i{display:block;font-size:2rem;margin-bottom:.5rem}.pt-advanced-prime-table th{white-space:normal;word-wrap: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 ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{text-align:center!important;justify-content:center;align-items:center;overflow:hidden;white-space:nowrap;padding:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container{width:100%;display:flex;justify-content:center!important;align-items:center!important;gap:.35rem}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{padding:.25rem!important;min-width:22px!important;height:22px!important}\n"] }]
|
|
869
|
-
}],
|
|
813
|
+
args: [{ selector: 'pt-advanced-prime-table', template: "<div class=\"pt-advanced-prime-table table-container\">\n <p-table\n #dt\n [value]=\"data\"\n [loading]=\"loading\"\n [rows]=\"rows\"\n [paginator]=\"isPaginated\"\n [globalFilterFields]=\"globalFilterFields\"\n [rowsPerPageOptions]=\"rowsPerPage\"\n [totalRecords]=\"totalRecords\"\n [lazy]=\"isPaginated\"\n dataKey=\"id\"\n styleClass=\"p-datatable-gridlines p-datatable-striped\"\n [scrollable]=\"true\"\n [scrollHeight]=\"maxHeight !== null ? maxHeight : undefined\"\n (onFilter)=\"filterColumn($event)\"\n (onPage)=\"changePage($event)\"\n (onSort)=\"sortColumn($event)\"\n >\n <!-- COLGROUP: sync header/body widths -->\n <ng-template pTemplate=\"colgroup\" let-columns>\n <colgroup>\n <col\n *ngFor=\"let col of columns\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n />\n </colgroup>\n </ng-template>\n\n <!-- CAPTION -->\n <ng-template pTemplate=\"caption\">\n <div class=\"flex\">\n <div>\n <h3>Total: {{ totalRecords }}</h3>\n </div>\n\n <div>\n <!-- Clear filters -->\n <button\n *ngIf=\"hasSearchFilter\"\n pButton\n icon=\"pi pi-filter-slash\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"clear(dt)\"\n title=\"Clear filters\"\n ></button>\n\n <!-- Export to Excel Button -->\n <button\n *ngIf=\"hasExportExcel\"\n pButton\n icon=\"pi pi-file-excel\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportExcel()\"\n title=\"Export to Excel\"\n ></button>\n\n <!-- Export to PDF Button -->\n <button\n *ngIf=\"hasExportPDF\"\n pButton\n icon=\"pi pi-file-pdf\"\n class=\"p-button-rounded p-button-text\"\n (click)=\"exportPdf()\"\n title=\"Export to PDF\"\n ></button>\n </div>\n\n <div class=\"ml-auto\" *ngIf=\"hasSearchFilter\">\n <p-iconField iconPosition=\"left\" class=\"ml-auto\">\n <p-inputIcon>\n <i class=\"pi pi-search\"></i>\n </p-inputIcon>\n <input\n pInputText\n type=\"text\"\n [(ngModel)]=\"searchValue\"\n (input)=\"filterGlobal($event)\"\n placeholder=\"Search keyword\"\n />\n </p-iconField>\n </div>\n </div>\n </ng-template>\n\n <!-- HEADER -->\n <ng-template pTemplate=\"header\">\n <tr class=\"sticky-header\">\n <ng-container *ngFor=\"let col of columns\">\n <th\n *ngIf=\"!col.children; else groupHeader\"\n pSortableColumn=\"{{ col.code }}\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [ngClass]=\"[\n getHeaderAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n colspan=\"1\"\n >\n <!-- SORTABLE HEADER -->\n <ng-container\n *ngIf=\"isSortable && col.isSortable !== false; else noSortHeader\"\n >\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n <div\n class=\"icons d-flex align-items-center\"\n [style.width]=\"'77px'\"\n >\n <p-sortIcon field=\"{{ col.code }}\" />\n\n <!-- COLUMN FILTERS -->\n <ng-container\n *ngIf=\"hasColumnFilter && col.isFilter !== false\"\n >\n <!-- COMPOSED FILTER (uses built-in Apply/Clear) -->\n <p-columnFilter\n *ngIf=\"col.type === TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n type=\"text\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onComposedColumnClear(col)\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filter=\"filterCallback\"\n let-filterModel=\"filterModel\"\n >\n <div *ngFor=\"let composedName of col.composedNames\">\n <ng-container\n *ngIf=\"\n getComposedFieldType(col, composedName) ===\n TableTypeEnum.STRING\n \"\n >\n <p-multiSelect\n [options]=\"filters[composedName]?.options\"\n [ngModel]=\"filters[composedName]?.value\"\n (ngModelChange)=\"\n onComposedFilterValueChange(\n col,\n composedName,\n $event,\n filterModel\n )\n \"\n [placeholder]=\"filters[composedName]?.placeholder\"\n display=\"chip\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"custom-multiselect-item\">\n <img\n *ngIf=\"item.image\"\n [src]=\"item.image\"\n alt=\"icon\"\n class=\"filter-image\"\n />\n <span>{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-container>\n </div>\n </ng-template>\n </p-columnFilter>\n\n <!-- OTHER TYPES -->\n <p-columnFilter\n *ngIf=\"col.type !== TableTypeEnum.COMPOSED\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n (onClear)=\"onFilterClear(col.code!)\"\n >\n <!-- NUMBER / AMOUNT: use text input so '.' works -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER ||\n col.type === TableTypeEnum.AMOUNT\n \"\n let-value\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"latestFilterValues[col.code!] ?? value\"\n (ngModelChange)=\"\n onNumberFilterChange(col.code!, $event)\n \"\n placeholder=\"Enter a number\"\n />\n </ng-template>\n\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"col.type === TableTypeEnum.DATE\"\n let-filterModel=\"filterModel\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n placeholder=\"Choose a date\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n *ngIf=\"\n col.type === TableTypeEnum.MULTISELECT &&\n col.filterOptions &&\n col.filterOptions.length > 0\n \"\n let-filterModel=\"filterModel\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Choose option\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </div>\n </ng-container>\n\n <!-- NON-SORTABLE HEADER -->\n <ng-template #noSortHeader>\n <div\n class=\"header-container d-flex align-items-center justify-content-between\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [style.padding]=\"'0px'\"\n [style.margin]=\"'10px'\"\n >\n <span [ngClass]=\"getHeaderTitleClass(col)\">\n {{ col.title }}\n </span>\n\n <ng-container *ngIf=\"hasColumnFilter && col.isFilter !== false\">\n <!-- AMOUNT behaves like NUMBER -->\n <p-columnFilter\n *ngIf=\"col.type === 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [currency]=\"getCurrencySymbol(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n >\n <input\n pInputText\n type=\"text\"\n inputmode=\"decimal\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n placeholder=\"Enter an amount\"\n />\n </ng-template>\n </p-columnFilter>\n\n <p-columnFilter\n *ngIf=\"col.type !== 'AMOUNT'\"\n display=\"menu\"\n [field]=\"col.code\"\n [type]=\"getColumnFilterType(col)\"\n [showApplyButton]=\"true\"\n [showClearButton]=\"true\"\n >\n <!-- DATE -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'date'\"\n >\n <p-calendar\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n dateFormat=\"dd/mm/yy\"\n ></p-calendar>\n </ng-template>\n\n <!-- MULTISELECT -->\n <ng-template\n pTemplate=\"filter\"\n let-filterModel=\"filterModel\"\n *ngIf=\"getColumnFilterType(col) === 'multiSelect'\"\n >\n <p-multiSelect\n [options]=\"col.filterOptions\"\n [ngModel]=\"\n latestFilterValues[col.code!] ?? filterModel?.value\n \"\n (ngModelChange)=\"\n onFilterValueChange(col.code!, filterModel, $event)\n \"\n display=\"chip\"\n placeholder=\"Select\"\n class=\"custom-multiselect\"\n ></p-multiSelect>\n </ng-template>\n </p-columnFilter>\n </ng-container>\n </div>\n </ng-template>\n </th>\n\n <!-- GROUPED HEADER -->\n <ng-template #groupHeader>\n <th\n [attr.colspan]=\"col.children?.length\"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getHeaderAlignClass(col)\"\n >\n <span>{{ col.title }}</span>\n </th>\n </ng-template>\n </ng-container>\n </tr>\n\n <!-- CHILD HEADERS -->\n <tr *ngIf=\"hasGroupedColumns\">\n <ng-container *ngFor=\"let col of columns\">\n <ng-container *ngIf=\"col.children\">\n <th\n *ngFor=\"let child of col.children\"\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [style.padding]=\"'0px'\"\n ></th>\n </ng-container>\n </ng-container>\n </tr>\n </ng-template>\n\n <!-- EMPTY MESSAGE -->\n <ng-template pTemplate=\"emptymessage\">\n <tr class=\"p-datatable-emptymessage\">\n <!-- span all columns -->\n <td class=\"empty-message-cell\" [attr.colspan]=\"columns.length || 1\">\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 <!-- BODY -->\n <ng-template\n pTemplate=\"body\"\n let-data\n let-editing=\"editing\"\n let-ri=\"rowIndex\"\n >\n <tr *ngIf=\"!loading\" [pEditableRow]=\"isEdit ? data : null\">\n <ng-container *ngFor=\"let col of columns\">\n <!-- SIMPLE COLUMNS (no children) -->\n <ng-container *ngIf=\"!col.children; else childColumns\">\n <!-- EDITABLE CELL -->\n <td\n *ngIf=\"\n isEditable(col.code!) && col.type !== TableTypeEnum.ACTION;\n else normalTD\n \"\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"getDataAlignClass(col)\"\n >\n <!-- Editable input for NUMBER/DATE/STRING/MULTISELECT -->\n <ng-container *ngIf=\"isMultiSelect(col.code); else datePicker\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-multiSelect\n appendTo=\"body\"\n [ngModel]=\"data[col.code!]\"\n [style]=\"{ width: '100%' }\"\n (ngModelChange)=\"changeHandler(data.id, col.code, $event)\"\n [options]=\"optionValues\"\n ></p-multiSelect>\n </ng-template>\n <ng-template pTemplate=\"output\">\n <div class=\"multi-select-container\">\n <ng-container *ngFor=\"let rec of data[col.code!]\">\n <p-tag [value]=\"rec\"></p-tag>\n </ng-container>\n </div>\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #datePicker>\n <ng-container *ngIf=\"isDatePicker(col.code); else normalInput\">\n <p-cellEditor>\n <ng-template pTemplate=\"input\">\n <p-calendar\n [inputId]=\"data[col.code!]\"\n [ngModel]=\"data[col.code!]\"\n (ngModelChange)=\"\n changeHandler(data.id, col.code, $event)\n \"\n [dateFormat]=\"'dd/mm/yy'\"\n ></p-calendar>\n </ng-template>\n <ng-template pTemplate=\"output\">\n {{ data[col.code!] | customDate }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n </ng-template>\n\n <ng-template #normalInput>\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 <ng-template pTemplate=\"output\">\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.AMOUNT;\n else normalOutput\n \"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n <ng-template #normalOutput>\n {{ data[col.code!] }}\n </ng-template>\n </ng-template>\n </p-cellEditor>\n </ng-template>\n </td>\n\n <!-- NON-EDITABLE CELL (ALWAYS RENDERED) -->\n <ng-template #normalTD>\n <td\n [style.width]=\"col.width || getHeaderWidth(col)\"\n [ngClass]=\"[\n getDataAlignClass(col),\n col.type === TableTypeEnum.ACTION ? 'action-column' : ''\n ]\"\n >\n <!-- ACTION column: built-in edit/delete + custom actions -->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.ACTION; else nonActionCell\"\n >\n <div class=\"action-buttons-container\">\n <!-- built-in delete -->\n <button\n *ngIf=\"isDelete\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-trash\"\n (click)=\"Delete(data.id)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n\n <!-- built-in inline edit -->\n <div *ngIf=\"isEdit\">\n <button\n pInitEditableRow\n *ngIf=\"!editing\"\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-pencil\"\n (click)=\"initEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pSaveEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-check\"\n (click)=\"saveEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n <button\n *ngIf=\"editing\"\n pCancelEditableRow\n pButton\n pRipple\n type=\"button\"\n icon=\"pi pi-times\"\n (click)=\"cancelEditableRow(data)\"\n class=\"p-button-rounded p-button-text\"\n ></button>\n </div>\n\n <!-- custom actions -->\n <button\n *ngFor=\"let act of customActions\"\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 (click)=\"onCustomActionClick(act, data)\"\n ></button>\n </div>\n </ng-container>\n\n <!-- NON-ACTION cells -->\n <ng-template #nonActionCell>\n <!-- COMPOSED -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.COMPOSED;\n else nonComposed\n \"\n >\n <div class=\"composed-cell\">\n <ng-container\n *ngFor=\"\n let composedName of col.composedNames;\n let i = index\n \"\n >\n <!-- IMAGE -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.IMAGE\n \"\n >\n <img\n [src]=\"data[col.code!]?.[composedName]\"\n alt=\"composed-img\"\n class=\"composed-image\"\n [ngStyle]=\"\n getImageStyle(col.composedStyles?.[composedName])\n \"\n />\n </ng-container>\n\n <!-- STRING -->\n <ng-container\n *ngIf=\"\n col.composedTypes &&\n col.composedTypes[i] === TableTypeEnum.STRING\n \"\n >\n <span\n class=\"composed-text\"\n [ngStyle]=\"\n getTitleStyle(\n col.composedStyles?.[composedName]\n )\n \"\n >\n {{ data[col.code!]?.[composedName] }}\n </span>\n </ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <ng-template #nonComposed>\n <!-- AMOUNT-->\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.AMOUNT; else nonAmount\"\n >\n {{\n data[col.code!]\n | customCurrency\n : getCurrencySymbol(col)\n : col.decimalPlaces\n : col.thousandSeparator\n : col.decimalSeparator\n }}\n </ng-container>\n\n <ng-template #nonAmount>\n <!-- NUMBER-->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.NUMBER;\n else nonNumber\n \"\n >\n {{\n formatNumber(\n data[col.code!],\n col.decimalPlaces,\n col.thousandSeparator,\n col.decimalSeparator\n )\n }}\n </ng-container>\n\n <ng-template #nonNumber>\n <!-- DATE -->\n <ng-container\n *ngIf=\"\n col.type === TableTypeEnum.DATE ||\n col.type === TableTypeEnum.DATETIME;\n else normalTypes\n \"\n >\n <ng-container *ngIf=\"col.type === TableTypeEnum.DATE\">\n {{\n formatDateWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n </ng-container>\n\n <ng-container\n *ngIf=\"col.type === TableTypeEnum.DATETIME\"\n >\n {{\n formatDateTimeWithColumn(\n parseAnyDate(data[col.code!]),\n col\n )\n }}\n </ng-container>\n </ng-container>\n\n <ng-template #normalTypes>\n <!-- STRING, MULTISELECT-->\n <ng-container\n *ngIf=\"\n [\n TableTypeEnum.STRING,\n TableTypeEnum.MULTISELECT\n ].includes(col.type!)\n \"\n >\n {{ data[col.code!] }}\n </ng-container>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </ng-template>\n </td>\n </ng-template>\n </ng-container>\n\n <!-- CHILD COLUMNS -->\n <ng-template #childColumns>\n <ng-container *ngFor=\"let child of col.children\">\n <td\n [style.width]=\"child.width || getHeaderWidth(child)\"\n [ngClass]=\"getDataAlignClass(child)\"\n >\n <ng-container\n *ngIf=\"isEditable(child.code); else childNormalTD\"\n >\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 <ng-template pTemplate=\"output\">\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </p-cellEditor>\n </ng-container>\n\n <ng-template #childNormalTD>\n {{ child.code ? data[child.code] : \"\" }}\n </ng-template>\n </td>\n </ng-container>\n </ng-template>\n </ng-container>\n </tr>\n </ng-template>\n </p-table>\n</div>\n", styles: [".pt-advanced-prime-table .bread-crumb{margin-bottom:15px}.pt-advanced-prime-table .date{width:100%;height:5rem;display:grid;justify-items:start;align-items:center}.pt-advanced-prime-table .filter-container{width:100%;display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .settings{display:flex;gap:1rem}.pt-advanced-prime-table .multi-select-container{display:flex;justify-content:center;align-items:center;gap:.3rem}.pt-advanced-prime-table ::ng-deep p-table{min-width:50rem}.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{display:flex;justify-content:space-between;align-items:center;width:100%}.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{flex:1}.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 p-columnfilter.p-element.ng-star-inserted{margin-top:4px}.pt-advanced-prime-table .flex{display:flex;justify-content:space-between;align-items:center}.pt-advanced-prime-table .ml-auto{margin-left:auto}.pt-advanced-prime-table ::ng-deep p-inputicon{margin-right:-1.5rem;z-index:2;position:relative}.pt-advanced-prime-table ::ng-deep .p-inputtext{padding-left:1.7rem}.pt-advanced-prime-table ::ng-deep .bt-filter-btn button{cursor:pointer;margin-left:1rem}.pt-advanced-prime-table ::ng-deep .p-icon-field-left .p-input-icon:first-of-type{left:-1rem}.pt-advanced-prime-table .table-row{text-align:center;display:flex;gap:1rem;justify-content:center}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-excel{font-size:1.25em;color:green}.pt-advanced-prime-table ::ng-deep span.p-button-icon.pi.pi-file-pdf{font-size:1.25em;color:red}.pt-advanced-prime-table .table-container{display:block;width:100%}.pt-advanced-prime-table ::ng-deep .p-datatable-emptymessage>td.empty-message-cell{padding:0!important}.pt-advanced-prime-table .empty-message-wrapper{width:100%;height:100%;min-height:180px;display:flex;align-items:center;justify-content:center}.pt-advanced-prime-table .empty-message{text-align:center;color:#888;font-size:1.2rem}.pt-advanced-prime-table .empty-message i{display:block;font-size:2rem;margin-bottom:.5rem}.pt-advanced-prime-table th{white-space:normal;word-wrap: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 ::ng-deep th.action-column,.pt-advanced-prime-table ::ng-deep td.action-column{text-align:center!important;justify-content:center;align-items:center;overflow:hidden;white-space:nowrap;padding:0}.pt-advanced-prime-table ::ng-deep .action-buttons-container{width:100%;display:flex;justify-content:center!important;align-items:center!important;gap:.35rem}.pt-advanced-prime-table ::ng-deep .action-buttons-container .p-button.p-button-rounded.p-button-text{padding:.25rem!important;min-width:22px!important;height:22px!important}\n"] }]
|
|
814
|
+
}], propDecorators: { data: [{
|
|
870
815
|
type: Input
|
|
871
816
|
}], columns: [{
|
|
872
817
|
type: Input
|
|
@@ -1198,6 +1143,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImpo
|
|
|
1198
1143
|
var TableTypeEnum;
|
|
1199
1144
|
(function (TableTypeEnum) {
|
|
1200
1145
|
TableTypeEnum["DATE"] = "DATE";
|
|
1146
|
+
TableTypeEnum["DATETIME"] = "DATETIME";
|
|
1201
1147
|
TableTypeEnum["STRING"] = "STRING";
|
|
1202
1148
|
TableTypeEnum["AMOUNT"] = "AMOUNT";
|
|
1203
1149
|
TableTypeEnum["NUMBER"] = "NUMBER";
|
|
@@ -2185,11 +2131,11 @@ class PTDropdownComponent {
|
|
|
2185
2131
|
return 'left';
|
|
2186
2132
|
}
|
|
2187
2133
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: PTDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2188
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: PTDropdownComponent, selector: "pt-dropdown", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-dropdown/pt-dropdown.component.html -->\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n *ngIf=\"!formField.hidden\"\n>\n <label *ngIf=\"formField.label\">{{ formField.label }}</label>\n\n <p-dropdown\n [formControlName]=\"formField.name\"\n [options]=\"formField.options\"\n [placeholder]=\"formField.placeholder || 'Select an option'\"\n optionLabel=\"label\"\n >\n <!-- Selected value template -->\n <ng-template pTemplate=\"selectedItem\" let-option>\n <ng-container *ngIf=\"option; else placeholderTpl\">\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <!-- IMAGE ICON -->\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <!-- FONT ICON (PrimeNG / FontAwesome) -->\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-container>\n </ng-template>\n\n <!-- Placeholder when no value selected -->\n <ng-template #placeholderTpl>\n <span>{{ formField.placeholder || \"Select an option\" }}</span>\n </ng-template>\n\n <!-- Items template -->\n <ng-template pTemplate=\"item\" let-option>\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-template>\n </p-dropdown>\n\n <div\n *ngIf=\"\n formGroup.get(formField.name)?.invalid &&\n formGroup.get(formField.name)?.touched\n \"\n >\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n</div>\n", styles: [".form-field{display:flex;flex-direction:column;gap:.25rem}.pt-dropdown-item{display:flex;align-items:center;gap:.5rem}.pt-dropdown-icon-image{object-fit:cover}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center}.pt-dropdown-label{display:inline-block}\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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$5.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }] }); }
|
|
2134
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: PTDropdownComponent, selector: "pt-dropdown", inputs: { formGroup: "formGroup", formField: "formField" }, ngImport: i0, template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-dropdown/pt-dropdown.component.html -->\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n *ngIf=\"!formField.hidden\"\n>\n <label *ngIf=\"formField.label\">{{ formField.label }}</label>\n\n <p-dropdown\n [formControlName]=\"formField.name\"\n [options]=\"formField.options\"\n [placeholder]=\"formField.placeholder || 'Select an option'\"\n optionLabel=\"label\"\n >\n <!-- Selected value template -->\n <ng-template pTemplate=\"selectedItem\" let-option>\n <ng-container *ngIf=\"option; else placeholderTpl\">\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <!-- IMAGE ICON -->\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <!-- FONT ICON (PrimeNG / FontAwesome) -->\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-container>\n </ng-template>\n\n <!-- Placeholder when no value selected -->\n <ng-template #placeholderTpl>\n <span>{{ formField.placeholder || \"Select an option\" }}</span>\n </ng-template>\n\n <!-- Items template -->\n <ng-template pTemplate=\"item\" let-option>\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-template>\n </p-dropdown>\n\n <div\n *ngIf=\"\n formGroup.get(formField.name)?.invalid &&\n formGroup.get(formField.name)?.touched\n \"\n >\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n</div>\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.5rem}.pt-dropdown-icon-image{object-fit:cover}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center}.pt-dropdown-label{display:inline-block}\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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3$5.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }] }); }
|
|
2189
2135
|
}
|
|
2190
2136
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: PTDropdownComponent, decorators: [{
|
|
2191
2137
|
type: Component,
|
|
2192
|
-
args: [{ selector: 'pt-dropdown', template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-dropdown/pt-dropdown.component.html -->\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n *ngIf=\"!formField.hidden\"\n>\n <label *ngIf=\"formField.label\">{{ formField.label }}</label>\n\n <p-dropdown\n [formControlName]=\"formField.name\"\n [options]=\"formField.options\"\n [placeholder]=\"formField.placeholder || 'Select an option'\"\n optionLabel=\"label\"\n >\n <!-- Selected value template -->\n <ng-template pTemplate=\"selectedItem\" let-option>\n <ng-container *ngIf=\"option; else placeholderTpl\">\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <!-- IMAGE ICON -->\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <!-- FONT ICON (PrimeNG / FontAwesome) -->\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-container>\n </ng-template>\n\n <!-- Placeholder when no value selected -->\n <ng-template #placeholderTpl>\n <span>{{ formField.placeholder || \"Select an option\" }}</span>\n </ng-template>\n\n <!-- Items template -->\n <ng-template pTemplate=\"item\" let-option>\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-template>\n </p-dropdown>\n\n <div\n *ngIf=\"\n formGroup.get(formField.name)?.invalid &&\n formGroup.get(formField.name)?.touched\n \"\n >\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n</div>\n", styles: [".form-field{display:flex;flex-direction:column;gap:.25rem}.pt-dropdown-item{display:flex;align-items:center;gap:.5rem}.pt-dropdown-icon-image{object-fit:cover}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center}.pt-dropdown-label{display:inline-block}\n"] }]
|
|
2138
|
+
args: [{ selector: 'pt-dropdown', template: "<!-- ng-prime-tools/projects/ng-prime-tools/src/lib/pt-dropdown/pt-dropdown.component.html -->\n<div\n [formGroup]=\"formGroup\"\n class=\"form-field\"\n [ngStyle]=\"{ width: formField.width || '100%' }\"\n *ngIf=\"!formField.hidden\"\n>\n <label *ngIf=\"formField.label\">{{ formField.label }}</label>\n\n <p-dropdown\n [formControlName]=\"formField.name\"\n [options]=\"formField.options\"\n [placeholder]=\"formField.placeholder || 'Select an option'\"\n optionLabel=\"label\"\n >\n <!-- Selected value template -->\n <ng-template pTemplate=\"selectedItem\" let-option>\n <ng-container *ngIf=\"option; else placeholderTpl\">\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <!-- IMAGE ICON -->\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <!-- FONT ICON (PrimeNG / FontAwesome) -->\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-container>\n </ng-template>\n\n <!-- Placeholder when no value selected -->\n <ng-template #placeholderTpl>\n <span>{{ formField.placeholder || \"Select an option\" }}</span>\n </ng-template>\n\n <!-- Items template -->\n <ng-template pTemplate=\"item\" let-option>\n <div class=\"pt-dropdown-item\">\n <ng-container *ngIf=\"getResolvedIcon(option) as iconMeta\">\n <img\n *ngIf=\"iconMeta.type === 'image'\"\n [src]=\"iconMeta.imageUrl\"\n [ngStyle]=\"iconMeta.imageStyle\"\n class=\"pt-dropdown-icon-image\"\n />\n\n <i\n *ngIf=\"iconMeta.type === 'font'\"\n [ngClass]=\"iconMeta.fontIconClass\"\n [ngStyle]=\"iconMeta.fontIconStyle\"\n class=\"pt-dropdown-icon-font\"\n ></i>\n </ng-container>\n\n <span class=\"pt-dropdown-label\">{{ option.label }}</span>\n </div>\n </ng-template>\n </p-dropdown>\n\n <div\n *ngIf=\"\n formGroup.get(formField.name)?.invalid &&\n formGroup.get(formField.name)?.touched\n \"\n >\n <small class=\"p-error\">{{ getErrorMessage() }}</small>\n </div>\n</div>\n", styles: [".form-field{display:flex;flex-direction:column;margin-bottom:1rem;gap:.25rem}.form-field label{display:block;margin-bottom:.5rem;font-weight:700;font-size:1rem}.pt-dropdown-item{display:flex;align-items:center;gap:.5rem}.pt-dropdown-icon-image{object-fit:cover}.pt-dropdown-icon-font{display:inline-flex;align-items:center;justify-content:center}.pt-dropdown-label{display:inline-block}\n"] }]
|
|
2193
2139
|
}], propDecorators: { formGroup: [{
|
|
2194
2140
|
type: Input
|
|
2195
2141
|
}], formField: [{
|