@fuentis/phoenix-ui 0.0.9-alpha.573 → 0.0.9-alpha.576

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.
@@ -2536,13 +2536,16 @@ async function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t,
2536
2536
  if (!columns?.length)
2537
2537
  return;
2538
2538
  const locale = options.locale || 'en-US';
2539
- // Prepare headers
2539
+ /* =========================
2540
+ HEADERS
2541
+ ========================= */
2540
2542
  const headers = columns.map((c) => sanitizeText(t(c.header)));
2541
- // Prepare body rows
2543
+ /* =========================
2544
+ BODY ROWS
2545
+ ========================= */
2542
2546
  const bodyRows = (rows ?? []).map((row) => columns.map((col) => {
2543
2547
  const type = columnTypeMap[col.field] || col.columnType;
2544
2548
  if (type === columnTypeEnum.LIST || type === columnTypeEnum.LIST_TAG) {
2545
- // make each item its own line; keep items intact (no bullets)
2546
2549
  const rawVal = getNestedValue(row, col.field);
2547
2550
  const items = Array.isArray(rawVal)
2548
2551
  ? rawVal.map((x) => x?.name ?? x)
@@ -2550,13 +2553,14 @@ async function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t,
2550
2553
  const cleaned = items
2551
2554
  .map((it) => collapseInlineSpaces(String(it)))
2552
2555
  .filter(Boolean);
2553
- return sanitizeText(cleaned.join(',\n')); // hard break after commas
2556
+ return sanitizeText(cleaned.join(',\n'));
2554
2557
  }
2555
2558
  const raw = String(getDisplayValue(row, col, columnTypeMap, columnTypeEnum, t, locale));
2556
2559
  return sanitizeText(collapseInlineSpaces(raw));
2557
2560
  }));
2558
- // Set up pdfmake fonts - pdfmake has built-in support for Unicode characters
2559
- // pdfFonts contains the vfs object directly
2561
+ /* =========================
2562
+ FONTS
2563
+ ========================= */
2560
2564
  if (pdfFonts && pdfFonts.pdfMake) {
2561
2565
  pdfMake.vfs = pdfFonts.pdfMake.vfs;
2562
2566
  }
@@ -2564,89 +2568,114 @@ async function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t,
2564
2568
  pdfMake.vfs = pdfFonts.vfs;
2565
2569
  }
2566
2570
  else {
2567
- // Fallback: try to access vfs directly
2568
2571
  pdfMake.vfs = pdfFonts || {};
2569
2572
  }
2570
- // Try to load Noto Sans font which has better emoji support
2571
- // This is optional - if it fails, we'll use Roboto
2572
2573
  try {
2573
2574
  const notoSansFont = await loadNotoSansFont();
2574
2575
  if (notoSansFont) {
2575
- // Add Noto Sans to pdfmake fonts
2576
- if (!pdfMake.fonts) {
2577
- pdfMake.fonts = {};
2578
- }
2576
+ pdfMake.fonts ??= {};
2579
2577
  pdfMake.fonts.NotoSans = {
2580
2578
  normal: 'NotoSans-Regular.ttf',
2581
2579
  bold: 'NotoSans-Bold.ttf',
2582
2580
  italics: 'NotoSans-Italic.ttf',
2583
2581
  bolditalics: 'NotoSans-BoldItalic.ttf',
2584
2582
  };
2585
- // Add font file to vfs
2586
2583
  pdfMake.vfs['NotoSans-Regular.ttf'] = notoSansFont;
2587
- pdfMake.vfs['NotoSans-Bold.ttf'] = notoSansFont; // Use same font for bold
2584
+ pdfMake.vfs['NotoSans-Bold.ttf'] = notoSansFont;
2588
2585
  }
2589
2586
  }
2590
- catch (error) {
2591
- console.warn('Failed to load Noto Sans font, using default Roboto:', error);
2592
- }
2593
- // Use 'auto' widths for pdfmake - it will automatically fit columns to page width
2594
- // pdfmake works well with Serbian characters and handles auto-sizing better
2595
- const columnWidths = columns.map((col) => {
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) => {
2596
2599
  const type = columnTypeMap[col.field] || col.columnType;
2597
2600
  if (type === columnTypeEnum.LIST || type === columnTypeEnum.LIST_TAG) {
2598
- return 100; // Fixed width for LIST columns
2601
+ listIndexes.push(i);
2602
+ }
2603
+ else {
2604
+ regularIndexes.push(i);
2599
2605
  }
2600
- return 'auto'; // Auto width for other columns - pdfmake will fit them
2601
2606
  });
2602
- // Build table body for pdfmake
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
+ ========================= */
2603
2635
  const tableBody = [
2604
- // Header row
2605
2636
  headers.map((header) => ({
2606
2637
  text: header,
2607
2638
  style: 'tableHeader',
2608
- bold: true,
2609
- })),
2610
- // Data rows
2611
- ...bodyRows.map((row) => row.map((cell, cellIdx) => {
2612
- const col = columns[cellIdx];
2613
- const type = columnTypeMap[col?.field] || col?.columnType;
2614
- return {
2615
- text: cell || '',
2616
- style: 'tableCell',
2617
- // Enable text wrapping for all cells
2618
- noWrap: false,
2619
- };
2620
2639
  })),
2640
+ ...bodyRows.map((row) => row.map((cell) => ({
2641
+ text: cell || '',
2642
+ style: 'tableCell',
2643
+ noWrap: false,
2644
+ }))),
2621
2645
  ];
2622
- // PDF document definition
2646
+ console.log(columnWidths);
2647
+ /* =========================
2648
+ PDF DEFINITION
2649
+ ========================= */
2623
2650
  const docDefinition = {
2624
2651
  pageOrientation: 'landscape',
2625
2652
  pageSize: 'A4',
2653
+ pageMargins: [25, 30, 15, 30],
2626
2654
  content: [
2627
2655
  {
2628
- table: {
2629
- headerRows: 1,
2630
- widths: columnWidths,
2631
- body: tableBody,
2632
- },
2633
- layout: {
2634
- hLineWidth: () => 0, // Remove horizontal lines between rows
2635
- vLineWidth: () => 0, // Remove vertical lines between columns
2636
- paddingLeft: () => 3,
2637
- paddingRight: () => 3,
2638
- paddingTop: () => 3,
2639
- paddingBottom: () => 3,
2640
- // Alternating row colors: grey, white, grey, white...
2641
- fillColor: (rowIndex, node, columnIndex) => {
2642
- // Header row stays blue RGB(41, 128, 186)
2643
- if (rowIndex === 0) {
2644
- return [41, 128, 186];
2645
- }
2646
- // Data rows alternate: grey for odd rows (1, 3, 5...), white for even rows (2, 4, 6...)
2647
- return (rowIndex - 1) % 2 === 0 ? '#f5f5f5' : null; // grey for odd rows, white (null) for even rows
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
+ },
2648
2677
  },
2649
- },
2678
+ ],
2650
2679
  },
2651
2680
  ],
2652
2681
  styles: {
@@ -2654,26 +2683,17 @@ async function exportRowsToPdf(columns, rows, columnTypeMap, columnTypeEnum, t,
2654
2683
  fontSize: 11,
2655
2684
  bold: true,
2656
2685
  color: '#ffffff',
2657
- fillColor: [41, 128, 186], // RGB(41, 128, 186)
2658
- alignment: 'left',
2659
2686
  margin: [0, 5, 0, 5],
2660
2687
  },
2661
2688
  tableCell: {
2662
2689
  fontSize: 9,
2663
- alignment: 'left',
2664
2690
  margin: [0, 5, 0, 5],
2665
- // Enable text wrapping
2666
- noWrap: false,
2667
2691
  },
2668
2692
  },
2669
2693
  defaultStyle: {
2670
- // Use NotoSans if loaded, otherwise fallback to Roboto
2671
2694
  font: pdfMake.fonts?.NotoSans ? 'NotoSans' : 'Roboto',
2672
- // NotoSans has better Unicode and emoji support than Roboto
2673
2695
  },
2674
- pageMargins: [15, 30, 15, 30], // Reduced margins to give more space
2675
2696
  };
2676
- // Generate and download PDF
2677
2697
  pdfMake.createPdf(docDefinition).download(fileName);
2678
2698
  }
2679
2699
  /* -------------------------- Export: Excel -------------------------- */