@fuentis/phoenix-ui 0.0.9-alpha.572 → 0.0.9-alpha.574
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/fesm2022/fuentis-phoenix-ui.mjs +185 -31
- package/fesm2022/fuentis-phoenix-ui.mjs.map +1 -1
- package/index.d.ts +0 -4
- package/package.json +1 -1
|
@@ -74,9 +74,9 @@ import * as i14 from 'primeng/floatlabel';
|
|
|
74
74
|
import { FloatLabelModule } from 'primeng/floatlabel';
|
|
75
75
|
import * as i2$4 from 'primeng/selectbutton';
|
|
76
76
|
import { SelectButtonModule } from 'primeng/selectbutton';
|
|
77
|
-
import jsPDF from 'jspdf';
|
|
78
|
-
import autoTable from 'jspdf-autotable';
|
|
79
77
|
import * as XLSX from 'xlsx';
|
|
78
|
+
import pdfMake from 'pdfmake/build/pdfmake';
|
|
79
|
+
import pdfFonts from 'pdfmake/build/vfs_fonts';
|
|
80
80
|
import * as i1$2 from 'primeng/dynamicdialog';
|
|
81
81
|
import { DialogService } from 'primeng/dynamicdialog';
|
|
82
82
|
import * as i2$6 from 'primeng/datepicker';
|
|
@@ -2407,11 +2407,16 @@ function getNestedValue(obj, path) {
|
|
|
2407
2407
|
* - Removes HTML tags
|
|
2408
2408
|
* - Replaces and non-breaking spaces
|
|
2409
2409
|
* - Normalizes Unicode (keeps German umlauts and Serbian letters)
|
|
2410
|
+
* - Removes emoji characters (since PDF fonts don't support them properly)
|
|
2410
2411
|
*/
|
|
2411
2412
|
function sanitizeText(v) {
|
|
2412
2413
|
const s = (v ?? '').toString();
|
|
2413
2414
|
const noHtml = s.replace(/<[^>]*>/g, '');
|
|
2414
|
-
|
|
2415
|
+
// Remove emoji characters - they don't render properly in PDF
|
|
2416
|
+
// Emoji ranges: U+1F300–U+1F9FF, U+2600–U+26FF, U+2700–U+27BF, U+FE00–U+FE0F, U+1F900–U+1F9FF, U+1F1E0–U+1F1FF
|
|
2417
|
+
const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{FE00}-\u{FE0F}\u{1F900}-\u{1F9FF}\u{1F1E0}-\u{1F1FF}]/gu;
|
|
2418
|
+
const noEmoji = noHtml.replace(emojiRegex, '');
|
|
2419
|
+
return noEmoji
|
|
2415
2420
|
.replace(/ /g, ' ')
|
|
2416
2421
|
.replace(/\u00A0/g, ' ')
|
|
2417
2422
|
.normalize('NFKC')
|
|
@@ -2495,20 +2500,52 @@ function collapseInlineSpaces(s) {
|
|
|
2495
2500
|
.trim();
|
|
2496
2501
|
}
|
|
2497
2502
|
/**
|
|
2498
|
-
*
|
|
2499
|
-
|
|
2503
|
+
* Loads Noto Sans font from CDN for better emoji support
|
|
2504
|
+
*/
|
|
2505
|
+
async function loadNotoSansFont() {
|
|
2506
|
+
try {
|
|
2507
|
+
// Noto Sans has better Unicode and emoji support than Roboto
|
|
2508
|
+
// Try jsDelivr CDN first (more reliable)
|
|
2509
|
+
const response = await fetch('https://cdn.jsdelivr.net/gh/google/fonts@main/ofl/notosans/NotoSans-Regular.ttf');
|
|
2510
|
+
if (!response.ok) {
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
2514
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
2515
|
+
let binary = '';
|
|
2516
|
+
const chunkSize = 8192;
|
|
2517
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
2518
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
2519
|
+
binary += String.fromCharCode.apply(null, Array.from(chunk));
|
|
2520
|
+
}
|
|
2521
|
+
return btoa(binary);
|
|
2522
|
+
}
|
|
2523
|
+
catch (error) {
|
|
2524
|
+
console.warn('Failed to load Noto Sans font from CDN:', error);
|
|
2525
|
+
return null;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Exports table rows to a PDF file using pdfmake.
|
|
2530
|
+
* - Uses pdfmake which has excellent Unicode support for Serbian characters (ć, č, đ, š, ž)
|
|
2500
2531
|
* - Respects only visible columns
|
|
2501
2532
|
* - Translates headers with provided translation function
|
|
2533
|
+
* - Attempts to load Noto Sans font for better emoji support
|
|
2502
2534
|
*/
|
|
2503
|
-
function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
|
|
2535
|
+
async function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileName = 'table.pdf', options = {}) {
|
|
2504
2536
|
if (!columns?.length)
|
|
2505
2537
|
return;
|
|
2506
2538
|
const locale = options.locale || 'en-US';
|
|
2507
|
-
|
|
2508
|
-
|
|
2539
|
+
/* =========================
|
|
2540
|
+
HEADERS
|
|
2541
|
+
========================= */
|
|
2542
|
+
const headers = columns.map((c) => sanitizeText(t(c.header)));
|
|
2543
|
+
/* =========================
|
|
2544
|
+
BODY ROWS
|
|
2545
|
+
========================= */
|
|
2546
|
+
const bodyRows = (rows ?? []).map((row) => columns.map((col) => {
|
|
2509
2547
|
const type = columnTypeMap[col.field] || col.columnType;
|
|
2510
2548
|
if (type === columnTypeEnum.LIST || type === columnTypeEnum.LIST_TAG) {
|
|
2511
|
-
// make each item its own line; keep items intact (no bullets)
|
|
2512
2549
|
const rawVal = getNestedValue(row, col.field);
|
|
2513
2550
|
const items = Array.isArray(rawVal)
|
|
2514
2551
|
? rawVal.map((x) => x?.name ?? x)
|
|
@@ -2516,33 +2553,148 @@ function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t, fileNa
|
|
|
2516
2553
|
const cleaned = items
|
|
2517
2554
|
.map((it) => collapseInlineSpaces(String(it)))
|
|
2518
2555
|
.filter(Boolean);
|
|
2519
|
-
return sanitizeText(cleaned.join(',\n'));
|
|
2556
|
+
return sanitizeText(cleaned.join(',\n'));
|
|
2520
2557
|
}
|
|
2521
2558
|
const raw = String(getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale));
|
|
2522
2559
|
return sanitizeText(collapseInlineSpaces(raw));
|
|
2523
2560
|
}));
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2561
|
+
/* =========================
|
|
2562
|
+
FONTS
|
|
2563
|
+
========================= */
|
|
2564
|
+
if (pdfFonts && pdfFonts.pdfMake) {
|
|
2565
|
+
pdfMake.vfs = pdfFonts.pdfMake.vfs;
|
|
2566
|
+
}
|
|
2567
|
+
else if (pdfFonts && pdfFonts.vfs) {
|
|
2568
|
+
pdfMake.vfs = pdfFonts.vfs;
|
|
2569
|
+
}
|
|
2570
|
+
else {
|
|
2571
|
+
pdfMake.vfs = pdfFonts || {};
|
|
2572
|
+
}
|
|
2573
|
+
try {
|
|
2574
|
+
const notoSansFont = await loadNotoSansFont();
|
|
2575
|
+
if (notoSansFont) {
|
|
2576
|
+
pdfMake.fonts ??= {};
|
|
2577
|
+
pdfMake.fonts.NotoSans = {
|
|
2578
|
+
normal: 'NotoSans-Regular.ttf',
|
|
2579
|
+
bold: 'NotoSans-Bold.ttf',
|
|
2580
|
+
italics: 'NotoSans-Italic.ttf',
|
|
2581
|
+
bolditalics: 'NotoSans-BoldItalic.ttf',
|
|
2582
|
+
};
|
|
2583
|
+
pdfMake.vfs['NotoSans-Regular.ttf'] = notoSansFont;
|
|
2584
|
+
pdfMake.vfs['NotoSans-Bold.ttf'] = notoSansFont;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
catch {
|
|
2588
|
+
console.warn('NotoSans not loaded, fallback to Roboto');
|
|
2589
|
+
}
|
|
2590
|
+
/* =========================
|
|
2591
|
+
FIXED TABLE WIDTH LOGIC
|
|
2592
|
+
========================= */
|
|
2593
|
+
const fixedTableWidth = 750;
|
|
2594
|
+
const listColumnWidth = 80;
|
|
2595
|
+
const minColumnWidth = 40;
|
|
2596
|
+
const listIndexes = [];
|
|
2597
|
+
const regularIndexes = [];
|
|
2598
|
+
columns.forEach((col, i) => {
|
|
2528
2599
|
const type = columnTypeMap[col.field] || col.columnType;
|
|
2529
2600
|
if (type === columnTypeEnum.LIST || type === columnTypeEnum.LIST_TAG) {
|
|
2530
|
-
|
|
2601
|
+
listIndexes.push(i);
|
|
2602
|
+
}
|
|
2603
|
+
else {
|
|
2604
|
+
regularIndexes.push(i);
|
|
2531
2605
|
}
|
|
2532
2606
|
});
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2607
|
+
const reservedWidth = listIndexes.length * listColumnWidth;
|
|
2608
|
+
const availableWidth = fixedTableWidth - reservedWidth;
|
|
2609
|
+
const baseRegularWidth = regularIndexes.length > 0
|
|
2610
|
+
? Math.max(minColumnWidth, availableWidth / regularIndexes.length)
|
|
2611
|
+
: minColumnWidth;
|
|
2612
|
+
let columnWidths = columns.map((_, i) => listIndexes.includes(i) ? listColumnWidth : baseRegularWidth);
|
|
2613
|
+
/* === NORMALIZACIJA (TAČNO 750px) === */
|
|
2614
|
+
const regularSum = regularIndexes.reduce((sum, i) => sum + columnWidths[i], 0);
|
|
2615
|
+
if (regularSum > 0) {
|
|
2616
|
+
const scaleFactor = availableWidth / regularSum;
|
|
2617
|
+
console.log(scaleFactor);
|
|
2618
|
+
// 1. skaliranje + floor
|
|
2619
|
+
regularIndexes.forEach((i) => {
|
|
2620
|
+
columnWidths[i] = Math.floor(columnWidths[i] * scaleFactor);
|
|
2621
|
+
});
|
|
2622
|
+
// 2. korekcija razlike (1px po koloni)
|
|
2623
|
+
let diff = fixedTableWidth - columnWidths.reduce((sum, w) => sum + w, 0);
|
|
2624
|
+
let idx = 0;
|
|
2625
|
+
while (diff !== 0 && regularIndexes.length) {
|
|
2626
|
+
const i = regularIndexes[idx % regularIndexes.length];
|
|
2627
|
+
columnWidths[i] += diff > 0 ? 1 : -1;
|
|
2628
|
+
diff += diff > 0 ? -1 : 1;
|
|
2629
|
+
idx++;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
/* =========================
|
|
2633
|
+
TABLE BODY
|
|
2634
|
+
========================= */
|
|
2635
|
+
const tableBody = [
|
|
2636
|
+
headers.map((header) => ({
|
|
2637
|
+
text: header,
|
|
2638
|
+
style: 'tableHeader',
|
|
2639
|
+
})),
|
|
2640
|
+
...bodyRows.map((row) => row.map((cell) => ({
|
|
2641
|
+
text: cell || '',
|
|
2642
|
+
style: 'tableCell',
|
|
2643
|
+
noWrap: false,
|
|
2644
|
+
}))),
|
|
2645
|
+
];
|
|
2646
|
+
console.log(columnWidths);
|
|
2647
|
+
/* =========================
|
|
2648
|
+
PDF DEFINITION
|
|
2649
|
+
========================= */
|
|
2650
|
+
const docDefinition = {
|
|
2651
|
+
pageOrientation: 'landscape',
|
|
2652
|
+
pageSize: 'A4',
|
|
2653
|
+
pageMargins: [25, 30, 15, 30],
|
|
2654
|
+
content: [
|
|
2655
|
+
{
|
|
2656
|
+
columns: [
|
|
2657
|
+
{
|
|
2658
|
+
width: fixedTableWidth,
|
|
2659
|
+
table: {
|
|
2660
|
+
headerRows: 1,
|
|
2661
|
+
widths: columnWidths,
|
|
2662
|
+
body: tableBody,
|
|
2663
|
+
},
|
|
2664
|
+
layout: {
|
|
2665
|
+
hLineWidth: () => 0,
|
|
2666
|
+
vLineWidth: () => 0,
|
|
2667
|
+
paddingLeft: () => 3,
|
|
2668
|
+
paddingRight: () => 3,
|
|
2669
|
+
paddingTop: () => 3,
|
|
2670
|
+
paddingBottom: () => 3,
|
|
2671
|
+
fillColor: (rowIndex) => rowIndex === 0
|
|
2672
|
+
? [41, 128, 186]
|
|
2673
|
+
: (rowIndex - 1) % 2 === 0
|
|
2674
|
+
? '#f5f5f5'
|
|
2675
|
+
: null,
|
|
2676
|
+
},
|
|
2677
|
+
},
|
|
2678
|
+
],
|
|
2679
|
+
},
|
|
2680
|
+
],
|
|
2536
2681
|
styles: {
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2682
|
+
tableHeader: {
|
|
2683
|
+
fontSize: 11,
|
|
2684
|
+
bold: true,
|
|
2685
|
+
color: '#ffffff',
|
|
2686
|
+
margin: [0, 5, 0, 5],
|
|
2687
|
+
},
|
|
2688
|
+
tableCell: {
|
|
2689
|
+
fontSize: 9,
|
|
2690
|
+
margin: [0, 5, 0, 5],
|
|
2691
|
+
},
|
|
2541
2692
|
},
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2693
|
+
defaultStyle: {
|
|
2694
|
+
font: pdfMake.fonts?.NotoSans ? 'NotoSans' : 'Roboto',
|
|
2695
|
+
},
|
|
2696
|
+
};
|
|
2697
|
+
pdfMake.createPdf(docDefinition).download(fileName);
|
|
2546
2698
|
}
|
|
2547
2699
|
/* -------------------------- Export: Excel -------------------------- */
|
|
2548
2700
|
/**
|
|
@@ -2750,10 +2902,10 @@ class TableComponent {
|
|
|
2750
2902
|
initialSortFieldApplied = null;
|
|
2751
2903
|
/** True while initial sort is running to avoid "flash" of unsorted data */
|
|
2752
2904
|
initialSortLoading = false;
|
|
2753
|
-
/** How many skeleton rows to render while initial sort/data is loading */
|
|
2754
|
-
skeletonRowCount = 8;
|
|
2755
|
-
/** Array used by Angular @for (must be iterable) */
|
|
2756
|
-
skeletonRows = Array.from({ length: this.skeletonRowCount });
|
|
2905
|
+
// /** How many skeleton rows to render while initial sort/data is loading */
|
|
2906
|
+
// skeletonRowCount = 8;
|
|
2907
|
+
// /** Array used by Angular @for (must be iterable) */
|
|
2908
|
+
// skeletonRows = Array.from({ length: this.skeletonRowCount });
|
|
2757
2909
|
createSortWorkerInline() {
|
|
2758
2910
|
const code = `
|
|
2759
2911
|
const compareStringsNatural = (a,b) => a.localeCompare(b, undefined, { numeric:true, sensitivity:'base' });
|
|
@@ -3269,7 +3421,9 @@ class TableComponent {
|
|
|
3269
3421
|
const file = this.tableConfiguration?.key
|
|
3270
3422
|
? buildFileName(this.tableConfiguration.key, 'pdf')
|
|
3271
3423
|
: buildFileName('table', 'pdf');
|
|
3272
|
-
exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file)
|
|
3424
|
+
exportRowsToPdf(this.selectedColumns, this.tableData, this.columnTypeMap, this.columnTypeEnum, (k) => this.translateService.instant(k), file).catch((error) => {
|
|
3425
|
+
console.error('Failed to export PDF:', error);
|
|
3426
|
+
});
|
|
3273
3427
|
return;
|
|
3274
3428
|
}
|
|
3275
3429
|
if (event?.action?.key === 'EXPORT_EXCEL') {
|