@tekkare/romulus 0.1.166 → 0.1.167
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/dist/module.json +1 -1
- package/dist/runtime/composables/useRmExport.js +253 -92
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -55,6 +55,16 @@ function xlsxColumnsOf(sheet, keep) {
|
|
|
55
55
|
}
|
|
56
56
|
}));
|
|
57
57
|
}
|
|
58
|
+
function readableText(value, lang) {
|
|
59
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
60
|
+
return value.toLocaleString(lang === "en" ? "en-US" : "fr-FR", { maximumFractionDigits: 2 });
|
|
61
|
+
}
|
|
62
|
+
return cellText(value, lang);
|
|
63
|
+
}
|
|
64
|
+
function isYearColumn(values) {
|
|
65
|
+
const seen = values.filter((value) => value !== null && value !== void 0 && value !== "");
|
|
66
|
+
return seen.length > 0 && seen.every((value) => typeof value === "number" && Number.isInteger(value) && value >= 1900 && value <= 2100);
|
|
67
|
+
}
|
|
58
68
|
function cellText(value, lang) {
|
|
59
69
|
if (value === null || value === void 0) return "";
|
|
60
70
|
if (value instanceof Date) {
|
|
@@ -120,7 +130,10 @@ const PRINTABLE_SUBSTITUTES = [
|
|
|
120
130
|
[/…/g, "..."],
|
|
121
131
|
[/≥/g, ">="],
|
|
122
132
|
[/≤/g, "<="],
|
|
123
|
-
|
|
133
|
+
// Espaces fines et insecables, dont celle que `toLocaleString('fr-FR')`
|
|
134
|
+
// pose entre les milliers (U+202F) : hors table WinAnsi, elle tombait et
|
|
135
|
+
// « 1 203 981 » sortait « 1203981 ».
|
|
136
|
+
[/[\u00a0\u2007\u2009\u202f]/g, " "]
|
|
124
137
|
];
|
|
125
138
|
function printable(text) {
|
|
126
139
|
const substituted = PRINTABLE_SUBSTITUTES.reduce(
|
|
@@ -140,10 +153,17 @@ function cardsOf(payload, config, lang) {
|
|
|
140
153
|
const head = title ?? columns[0];
|
|
141
154
|
if (!head) return [];
|
|
142
155
|
const fields = columns.filter((column) => column !== head && column !== subtitle);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
156
|
+
const rows = rowsOf(sheet, payload.cardLimit ?? CARD_COUNT_MAX);
|
|
157
|
+
const plain = new Set(
|
|
158
|
+
fields.filter((column) => isYearColumn(rows.map((row) => column.value(row)))).map((column) => column.column)
|
|
159
|
+
);
|
|
160
|
+
return rows.map((row) => ({
|
|
161
|
+
title: readableText(head.value(row), lang) || "\u2014",
|
|
162
|
+
subtitle: subtitle ? readableText(subtitle.value(row), lang) : void 0,
|
|
163
|
+
fields: fields.map((column) => ({
|
|
164
|
+
label: column.column,
|
|
165
|
+
value: plain.has(column.column) ? cellText(column.value(row), lang) : readableText(column.value(row), lang)
|
|
166
|
+
})).filter((field) => field.value !== "")
|
|
147
167
|
}));
|
|
148
168
|
}
|
|
149
169
|
export function useRmExport() {
|
|
@@ -345,107 +365,197 @@ export function useRmExport() {
|
|
|
345
365
|
const pageWidth = doc.internal.pageSize.getWidth();
|
|
346
366
|
const margin = 14;
|
|
347
367
|
const inner = pageWidth - margin * 2;
|
|
368
|
+
const FOOTER = 12;
|
|
369
|
+
const bottom = doc.internal.pageSize.getHeight() - margin - FOOTER;
|
|
370
|
+
const CHART_MAX_HEIGHT = landscape ? 88 : 104;
|
|
348
371
|
let y = margin;
|
|
349
372
|
const violet = [107, 63, 255];
|
|
350
373
|
const ink = [26, 26, 46];
|
|
351
374
|
const grey = [108, 108, 138];
|
|
375
|
+
const line = [228, 228, 240];
|
|
376
|
+
const tint = [245, 243, 255];
|
|
377
|
+
const surface = [250, 250, 253];
|
|
378
|
+
function sectionTitle(text) {
|
|
379
|
+
doc.setFillColor(...violet);
|
|
380
|
+
doc.rect(margin, y - 2.6, 2, 2.6, "F");
|
|
381
|
+
doc.setTextColor(...ink);
|
|
382
|
+
doc.setFont("helvetica", "bold");
|
|
383
|
+
doc.setFontSize(8);
|
|
384
|
+
doc.setCharSpace(0.5);
|
|
385
|
+
doc.text(printable(text).toUpperCase(), margin + 4, y);
|
|
386
|
+
doc.setCharSpace(0);
|
|
387
|
+
y += 5;
|
|
388
|
+
}
|
|
352
389
|
if (config.options.includes("logo")) {
|
|
390
|
+
const bandHeight = 30;
|
|
353
391
|
doc.setFillColor(...violet);
|
|
354
|
-
doc.rect(0, 0, pageWidth,
|
|
392
|
+
doc.rect(0, 0, pageWidth, bandHeight, "F");
|
|
355
393
|
doc.setTextColor(255, 255, 255);
|
|
356
394
|
doc.setFont("helvetica", "bold");
|
|
357
|
-
doc.setFontSize(
|
|
358
|
-
doc.text(printable(BRAND_TITLE), margin,
|
|
395
|
+
doc.setFontSize(17);
|
|
396
|
+
doc.text(printable(BRAND_TITLE), margin, 15);
|
|
359
397
|
doc.setFont("helvetica", "normal");
|
|
360
|
-
doc.setFontSize(11);
|
|
361
|
-
doc.text(printable(BRAND_LINE), margin, 28);
|
|
362
398
|
doc.setFontSize(9);
|
|
363
|
-
doc.text(
|
|
399
|
+
doc.text(printable(BRAND_LINE), margin, 21.5);
|
|
400
|
+
doc.text(longDate(lang), pageWidth - margin, 21.5, { align: "right" });
|
|
364
401
|
if (payload.logoDataUrl) {
|
|
365
|
-
const logoHeight =
|
|
366
|
-
|
|
402
|
+
const logoHeight = 7;
|
|
403
|
+
let logoWidth = logoHeight * 3;
|
|
404
|
+
try {
|
|
405
|
+
const meta = doc.getImageProperties?.(payload.logoDataUrl);
|
|
406
|
+
if (meta?.width && meta?.height) logoWidth = meta.width / meta.height * logoHeight;
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
409
|
+
doc.addImage(payload.logoDataUrl, "PNG", pageWidth - margin - logoWidth, 8, logoWidth, logoHeight);
|
|
367
410
|
}
|
|
368
|
-
y =
|
|
411
|
+
y = bandHeight + 12;
|
|
369
412
|
}
|
|
370
413
|
doc.setTextColor(...ink);
|
|
371
414
|
doc.setFont("helvetica", "bold");
|
|
372
415
|
doc.setFontSize(16);
|
|
373
|
-
doc.
|
|
374
|
-
|
|
416
|
+
const titleLines = doc.splitTextToSize(printable(payload.title), inner);
|
|
417
|
+
doc.text(titleLines, margin, y);
|
|
418
|
+
y += titleLines.length * 7 + 1;
|
|
375
419
|
if (payload.subtitle) {
|
|
376
420
|
doc.setFont("helvetica", "normal");
|
|
377
421
|
doc.setFontSize(10);
|
|
378
422
|
doc.setTextColor(...grey);
|
|
379
423
|
const lines = doc.splitTextToSize(printable(payload.subtitle), inner);
|
|
380
424
|
doc.text(lines, margin, y);
|
|
381
|
-
y += lines.length * 5
|
|
425
|
+
y += lines.length * 5;
|
|
382
426
|
}
|
|
427
|
+
doc.setDrawColor(...violet);
|
|
428
|
+
doc.setLineWidth(0.6);
|
|
429
|
+
doc.line(margin, y + 1, margin + 24, y + 1);
|
|
430
|
+
doc.setLineWidth(0.2);
|
|
431
|
+
y += 8;
|
|
383
432
|
if (payload.kpi?.length && config.sections.includes("kpi")) {
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
|
|
433
|
+
const shown = payload.kpi.slice(0, 8);
|
|
434
|
+
const perRow = Math.min(shown.length, 4);
|
|
435
|
+
const GAP = 3;
|
|
436
|
+
const boxWidth = (inner - GAP * (perRow - 1)) / perRow;
|
|
437
|
+
const boxHeight = 18;
|
|
438
|
+
const ACCENT = 1.4;
|
|
439
|
+
shown.forEach((kpi, index) => {
|
|
387
440
|
const column = index % perRow;
|
|
388
441
|
const row = Math.floor(index / perRow);
|
|
389
|
-
const x = margin + column * boxWidth;
|
|
390
|
-
const top = y + row *
|
|
391
|
-
doc.setFillColor(
|
|
392
|
-
doc.
|
|
393
|
-
doc.
|
|
442
|
+
const x = margin + column * (boxWidth + GAP);
|
|
443
|
+
const top = y + row * (boxHeight + GAP);
|
|
444
|
+
doc.setFillColor(255, 255, 255);
|
|
445
|
+
doc.setDrawColor(226, 226, 238);
|
|
446
|
+
doc.setLineWidth(0.2);
|
|
447
|
+
doc.roundedRect(x, top, boxWidth, boxHeight, 1.5, 1.5, "FD");
|
|
448
|
+
doc.setFillColor(...violet);
|
|
449
|
+
doc.rect(x, top + 1.5, ACCENT, boxHeight - 3, "F");
|
|
450
|
+
const textX = x + ACCENT + 4;
|
|
451
|
+
const textWidth = boxWidth - ACCENT - 7;
|
|
452
|
+
doc.setTextColor(...grey);
|
|
453
|
+
doc.setFont("helvetica", "bold");
|
|
454
|
+
doc.setFontSize(6.5);
|
|
455
|
+
doc.text(
|
|
456
|
+
doc.splitTextToSize(printable(kpi.label).toUpperCase(), textWidth)[0] ?? "",
|
|
457
|
+
textX,
|
|
458
|
+
top + 6.5
|
|
459
|
+
);
|
|
460
|
+
doc.setTextColor(...ink);
|
|
394
461
|
doc.setFont("helvetica", "bold");
|
|
395
462
|
doc.setFontSize(14);
|
|
396
|
-
|
|
397
|
-
doc.
|
|
398
|
-
doc.
|
|
399
|
-
doc.
|
|
400
|
-
doc.text(doc.splitTextToSize(printable(kpi.label), boxWidth - 6)[0] ?? "", x + boxWidth / 2, top + 13, { align: "center" });
|
|
463
|
+
const value = printable(String(kpi.value));
|
|
464
|
+
if (doc.getTextWidth(value) > textWidth) doc.setFontSize(11);
|
|
465
|
+
if (doc.getTextWidth(value) > textWidth) doc.setFontSize(9);
|
|
466
|
+
doc.text(value, textX, top + 13.5);
|
|
401
467
|
});
|
|
402
|
-
y += Math.ceil(
|
|
468
|
+
y += Math.ceil(shown.length / perRow) * (boxHeight + GAP) + 5;
|
|
403
469
|
}
|
|
404
470
|
if (payload.filters?.length && config.sections.includes("filters")) {
|
|
405
|
-
|
|
406
|
-
doc.setFont("helvetica", "bold");
|
|
407
|
-
doc.setFontSize(10);
|
|
408
|
-
doc.text(lang === "en" ? "Reading this view" : "Lecture de la vue", margin, y);
|
|
409
|
-
y += 5;
|
|
410
|
-
doc.setFont("helvetica", "normal");
|
|
411
|
-
doc.setFontSize(9);
|
|
412
|
-
doc.setTextColor(...grey);
|
|
471
|
+
sectionTitle(lang === "en" ? "Reading this view" : "Lecture de la vue");
|
|
413
472
|
const applied = payload.filters.map((filter) => `${filter.label} = ${filter.value}`).join(" ; ");
|
|
414
473
|
const legend = lang === "en" ? `This report reproduces the page as it was displayed, with the following filters: ${applied}.` : `Ce rapport reprend la page telle qu'elle \xE9tait affich\xE9e, avec les filtres suivants : ${applied}.`;
|
|
415
|
-
|
|
416
|
-
doc.
|
|
417
|
-
|
|
474
|
+
doc.setFont("helvetica", "normal");
|
|
475
|
+
doc.setFontSize(8.5);
|
|
476
|
+
const lines = doc.splitTextToSize(printable(legend), inner - 12);
|
|
477
|
+
const boxHeight = lines.length * 4.4 + 7;
|
|
478
|
+
doc.setFillColor(...tint);
|
|
479
|
+
doc.roundedRect(margin, y - 1, inner, boxHeight, 1.5, 1.5, "F");
|
|
480
|
+
doc.setFillColor(...violet);
|
|
481
|
+
doc.rect(margin, y - 1, 1.2, boxHeight, "F");
|
|
482
|
+
doc.setTextColor(...grey);
|
|
483
|
+
doc.text(lines, margin + 6, y + 4);
|
|
484
|
+
y += boxHeight + 5;
|
|
485
|
+
}
|
|
486
|
+
const sources = sourcesOf(config, payload);
|
|
487
|
+
if (sources.length) {
|
|
488
|
+
sectionTitle("Sources");
|
|
489
|
+
doc.setFont("helvetica", "normal");
|
|
490
|
+
doc.setFontSize(8.5);
|
|
491
|
+
sources.forEach((source) => {
|
|
492
|
+
doc.setFillColor(...violet);
|
|
493
|
+
doc.circle(margin + 1, y - 1, 0.7, "F");
|
|
494
|
+
doc.setTextColor(...grey);
|
|
495
|
+
const lines = doc.splitTextToSize(printable(sourceLabel(source, lang)), inner - 5);
|
|
496
|
+
doc.text(lines, margin + 4, y);
|
|
497
|
+
y += lines.length * 4.4;
|
|
498
|
+
});
|
|
499
|
+
y += 5;
|
|
418
500
|
}
|
|
419
501
|
const charts = config.sections.includes("charts") ? payload.charts ?? [] : [];
|
|
502
|
+
if (charts.length) sectionTitle(lang === "en" ? "Charts" : "Graphiques");
|
|
420
503
|
for (const chart of charts) {
|
|
421
504
|
const svg = svgOf(chart.el);
|
|
422
505
|
if (!svg) continue;
|
|
423
506
|
const part = serialize(svg);
|
|
424
507
|
const canvas = await raster(part.markup, part.width, part.height, Math.min(2, 1400 / part.width));
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (
|
|
508
|
+
let drawWidth = inner;
|
|
509
|
+
let drawHeight = part.height / part.width * drawWidth;
|
|
510
|
+
if (drawHeight > CHART_MAX_HEIGHT) {
|
|
511
|
+
drawHeight = CHART_MAX_HEIGHT;
|
|
512
|
+
drawWidth = part.width / part.height * drawHeight;
|
|
513
|
+
}
|
|
514
|
+
const titleHeight = chart.title ? 7 : 0;
|
|
515
|
+
const cardHeight = titleHeight + drawHeight + 8;
|
|
516
|
+
if (y + cardHeight > bottom) {
|
|
428
517
|
doc.addPage();
|
|
429
518
|
y = margin;
|
|
430
519
|
}
|
|
520
|
+
doc.setFillColor(255, 255, 255);
|
|
521
|
+
doc.setDrawColor(...line);
|
|
522
|
+
doc.setLineWidth(0.2);
|
|
523
|
+
doc.roundedRect(margin, y, inner, cardHeight, 2, 2, "FD");
|
|
524
|
+
let chartY = y + 4;
|
|
431
525
|
if (chart.title) {
|
|
432
526
|
doc.setTextColor(...ink);
|
|
433
527
|
doc.setFont("helvetica", "bold");
|
|
434
528
|
doc.setFontSize(10);
|
|
435
|
-
doc.text(printable(chart.title), margin,
|
|
436
|
-
|
|
529
|
+
doc.text(printable(chart.title), margin + 5, chartY + 2);
|
|
530
|
+
chartY += titleHeight;
|
|
437
531
|
}
|
|
438
|
-
doc.addImage(
|
|
439
|
-
|
|
532
|
+
doc.addImage(
|
|
533
|
+
canvas.toDataURL("image/png"),
|
|
534
|
+
"PNG",
|
|
535
|
+
margin + (inner - drawWidth) / 2,
|
|
536
|
+
chartY,
|
|
537
|
+
drawWidth,
|
|
538
|
+
drawHeight,
|
|
539
|
+
void 0,
|
|
540
|
+
"FAST"
|
|
541
|
+
);
|
|
542
|
+
y += cardHeight + 6;
|
|
440
543
|
}
|
|
441
544
|
let rowCount = 0;
|
|
442
545
|
if (config.sections.includes("cards")) {
|
|
443
546
|
const cards = cardsOf(payload, config, lang);
|
|
547
|
+
if (cards.length) {
|
|
548
|
+
if (y + 12 > bottom) {
|
|
549
|
+
doc.addPage();
|
|
550
|
+
y = margin;
|
|
551
|
+
}
|
|
552
|
+
sectionTitle(lang === "en" ? "Records" : "Fiches");
|
|
553
|
+
}
|
|
444
554
|
const pageHeight = doc.internal.pageSize.getHeight();
|
|
445
555
|
const PAD = 6;
|
|
446
556
|
const COUNT_WIDTH = 32;
|
|
447
557
|
const COUNT_GAP = 6;
|
|
448
|
-
const LABEL_WIDTH =
|
|
558
|
+
const LABEL_WIDTH = 34;
|
|
449
559
|
const firstCardPage = doc.getNumberOfPages();
|
|
450
560
|
let drawn = 0;
|
|
451
561
|
for (const card of cards) {
|
|
@@ -454,7 +564,7 @@ export function useRmExport() {
|
|
|
454
564
|
const bodyWidth = inner - PAD * 2 - (hasCount ? COUNT_WIDTH + COUNT_GAP : 0);
|
|
455
565
|
doc.setFont("helvetica", "bold");
|
|
456
566
|
doc.setFontSize(12);
|
|
457
|
-
const
|
|
567
|
+
const titleLines2 = doc.splitTextToSize(printable(card.title), inner - PAD * 2);
|
|
458
568
|
doc.setFont("helvetica", "normal");
|
|
459
569
|
doc.setFontSize(9);
|
|
460
570
|
const subtitleLines = card.subtitle ? doc.splitTextToSize(printable(card.subtitle), inner - PAD * 2) : [];
|
|
@@ -479,9 +589,9 @@ export function useRmExport() {
|
|
|
479
589
|
doc.setFont(typo.mono ? "courier" : "helvetica", typo.bold ? "bold" : "normal");
|
|
480
590
|
doc.setFontSize(typo.size);
|
|
481
591
|
const width = doc.getTextWidth(text) + (typo.pill ? 3 : 0);
|
|
482
|
-
const
|
|
483
|
-
const used =
|
|
484
|
-
if (
|
|
592
|
+
const line2 = inline[inline.length - 1];
|
|
593
|
+
const used = line2 ? line2.reduce((total2, item) => total2 + item.width + 3, 0) : 0;
|
|
594
|
+
if (line2 && used + width <= bodyWidth) line2.push({ text, pill: !!typo.pill, width });
|
|
485
595
|
else inline.push([{ text, pill: !!typo.pill, width }]);
|
|
486
596
|
taken += 1;
|
|
487
597
|
}
|
|
@@ -503,33 +613,32 @@ export function useRmExport() {
|
|
|
503
613
|
const label = doc.splitTextToSize(printable(field.label).toUpperCase(), LABEL_WIDTH);
|
|
504
614
|
doc.setFont("helvetica", "normal");
|
|
505
615
|
doc.setFontSize(9);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
value: doc.splitTextToSize(printable(field.value), bodyWidth - LABEL_WIDTH - 4)
|
|
509
|
-
};
|
|
616
|
+
const value = doc.splitTextToSize(printable(field.value), bodyWidth - LABEL_WIDTH - 6);
|
|
617
|
+
return { label, value, right: value.length === 1 };
|
|
510
618
|
});
|
|
511
|
-
const headHeight =
|
|
512
|
-
const bodyHeight = inline.length * 4.8 + stacked.reduce((total2, block) => total2 + block.lines.length * block.typo.lh + 1, 0) + fields.reduce((total2, field) => total2 + Math.max(field.label.length, field.value.length) * 4.4, 0);
|
|
619
|
+
const headHeight = titleLines2.length * 5.2 + (subtitleLines.length ? subtitleLines.length * 4.2 + 1 : 0) + (card.badges?.length ? 6 : 0);
|
|
620
|
+
const bodyHeight = inline.length * 4.8 + stacked.reduce((total2, block) => total2 + block.lines.length * block.typo.lh + 1, 0) + fields.reduce((total2, field) => total2 + Math.max(field.label.length, field.value.length) * 4.4 + 1.4, 0);
|
|
513
621
|
const countHeight = hasCount ? 9 + (card.count?.label ? 4 : 0) + (card.count?.extra ? 4.5 : 0) : 0;
|
|
514
622
|
const height = PAD + headHeight + 3 + Math.max(bodyHeight, countHeight) + PAD;
|
|
515
|
-
if (y + height >
|
|
623
|
+
if (y + height > bottom) {
|
|
516
624
|
if (doc.getNumberOfPages() - firstCardPage + 1 >= CARD_PAGES_MAX) break;
|
|
517
625
|
doc.addPage();
|
|
518
626
|
y = margin;
|
|
519
627
|
}
|
|
520
|
-
doc.setDrawColor(
|
|
628
|
+
doc.setDrawColor(...line);
|
|
521
629
|
doc.setFillColor(255, 255, 255);
|
|
522
|
-
doc.
|
|
630
|
+
doc.setLineWidth(0.2);
|
|
631
|
+
doc.roundedRect(margin, y, inner, height, 2.5, 2.5, "FD");
|
|
523
632
|
let cursor = y + PAD + 3;
|
|
524
633
|
doc.setTextColor(...ink);
|
|
525
634
|
doc.setFont("helvetica", "bold");
|
|
526
635
|
doc.setFontSize(12);
|
|
527
|
-
doc.text(
|
|
528
|
-
cursor +=
|
|
636
|
+
doc.text(titleLines2, margin + PAD, cursor);
|
|
637
|
+
cursor += titleLines2.length * 5.2;
|
|
529
638
|
if (subtitleLines.length) {
|
|
530
|
-
doc.setTextColor(...
|
|
531
|
-
doc.setFont("helvetica", "
|
|
532
|
-
doc.setFontSize(
|
|
639
|
+
doc.setTextColor(...violet);
|
|
640
|
+
doc.setFont("helvetica", "bold");
|
|
641
|
+
doc.setFontSize(8.5);
|
|
533
642
|
doc.text(subtitleLines, margin + PAD, cursor + 1);
|
|
534
643
|
cursor += subtitleLines.length * 4.2 + 1;
|
|
535
644
|
}
|
|
@@ -582,9 +691,9 @@ export function useRmExport() {
|
|
|
582
691
|
);
|
|
583
692
|
}
|
|
584
693
|
cursor = bodyTop;
|
|
585
|
-
inline.forEach((
|
|
694
|
+
inline.forEach((line2) => {
|
|
586
695
|
let itemX = bodyX;
|
|
587
|
-
|
|
696
|
+
line2.forEach((item) => {
|
|
588
697
|
if (item.pill) {
|
|
589
698
|
doc.setFillColor(244, 244, 248);
|
|
590
699
|
doc.roundedRect(itemX, cursor - 2.6, item.width, 4.6, 1, 1, "F");
|
|
@@ -609,7 +718,12 @@ export function useRmExport() {
|
|
|
609
718
|
doc.text(block.lines, bodyX, cursor + 1);
|
|
610
719
|
cursor += block.lines.length * block.typo.lh + 1;
|
|
611
720
|
});
|
|
612
|
-
|
|
721
|
+
const rowRight = margin + inner - PAD;
|
|
722
|
+
fields.forEach((field, index) => {
|
|
723
|
+
if (index) {
|
|
724
|
+
doc.setDrawColor(240, 240, 246);
|
|
725
|
+
doc.line(bodyX, cursor - 2.6, rowRight, cursor - 2.6);
|
|
726
|
+
}
|
|
613
727
|
doc.setTextColor(...grey);
|
|
614
728
|
doc.setFont("helvetica", "bold");
|
|
615
729
|
doc.setFontSize(6.5);
|
|
@@ -617,8 +731,9 @@ export function useRmExport() {
|
|
|
617
731
|
doc.setTextColor(...ink);
|
|
618
732
|
doc.setFont("helvetica", "normal");
|
|
619
733
|
doc.setFontSize(9);
|
|
620
|
-
doc.text(field.value,
|
|
621
|
-
|
|
734
|
+
if (field.right) doc.text(field.value[0], rowRight, cursor + 1, { align: "right" });
|
|
735
|
+
else doc.text(field.value, bodyX + LABEL_WIDTH + 6, cursor + 1);
|
|
736
|
+
cursor += Math.max(field.label.length, field.value.length) * 4.4 + 1.4;
|
|
622
737
|
});
|
|
623
738
|
y += height + 4;
|
|
624
739
|
drawn += 1;
|
|
@@ -648,10 +763,8 @@ export function useRmExport() {
|
|
|
648
763
|
if (!config.sections.includes("cards")) rowCount += rows.length;
|
|
649
764
|
doc.addPage();
|
|
650
765
|
const wide = doc.internal.pageSize.getWidth() - margin * 2;
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
doc.setFontSize(11);
|
|
654
|
-
doc.text(printable(sheet.name), margin, margin + 2);
|
|
766
|
+
y = margin + 3;
|
|
767
|
+
sectionTitle(sheet.name);
|
|
655
768
|
if (all.length > columns.length) {
|
|
656
769
|
doc.setFont("helvetica", "italic");
|
|
657
770
|
doc.setFontSize(8);
|
|
@@ -661,38 +774,86 @@ export function useRmExport() {
|
|
|
661
774
|
lang === "en" ? `${columns.length} of ${all.length} columns. The Excel export carries them all.` : `${columns.length} colonnes sur ${all.length}. L'export Excel les porte toutes.`
|
|
662
775
|
),
|
|
663
776
|
margin,
|
|
664
|
-
|
|
777
|
+
y + 1
|
|
665
778
|
);
|
|
779
|
+
y += 5;
|
|
666
780
|
}
|
|
781
|
+
const numeric = columns.map((column) => {
|
|
782
|
+
const seen = rows.map((row) => column.value(row)).filter((value) => value !== null && value !== void 0 && value !== "");
|
|
783
|
+
return seen.length > 0 && seen.every((value) => typeof value === "number" && Number.isFinite(value));
|
|
784
|
+
});
|
|
785
|
+
const years = columns.map((column) => isYearColumn(rows.map((row) => column.value(row))));
|
|
667
786
|
autoTable(doc, {
|
|
668
|
-
startY:
|
|
787
|
+
startY: y + 2,
|
|
669
788
|
head: [columns.map((column) => printable(column.column))],
|
|
670
|
-
body: rows.map(
|
|
789
|
+
body: rows.map(
|
|
790
|
+
(row) => columns.map(
|
|
791
|
+
(column, index) => printable(
|
|
792
|
+
years[index] ? cellText(column.value(row), lang) : readableText(column.value(row), lang)
|
|
793
|
+
)
|
|
794
|
+
)
|
|
795
|
+
),
|
|
671
796
|
// Passe huit colonnes, la police et les marges se resserrent : c'est
|
|
672
797
|
// ce qui fait tenir un suivi mensuel complet sans casser les mots.
|
|
673
798
|
styles: {
|
|
674
|
-
fontSize: columns.length > 8 ?
|
|
675
|
-
cellPadding: columns.length > 8 ? 1 : 1.
|
|
799
|
+
fontSize: columns.length > 8 ? 6 : 7.5,
|
|
800
|
+
cellPadding: columns.length > 8 ? 1.2 : 1.8,
|
|
676
801
|
overflow: "linebreak",
|
|
677
802
|
textColor: ink,
|
|
803
|
+
lineColor: [235, 235, 243],
|
|
804
|
+
lineWidth: 0.1,
|
|
678
805
|
minCellWidth: wide / columns.length / 2
|
|
679
806
|
},
|
|
680
|
-
headStyles: {
|
|
681
|
-
|
|
682
|
-
|
|
807
|
+
headStyles: {
|
|
808
|
+
fillColor: violet,
|
|
809
|
+
textColor: [255, 255, 255],
|
|
810
|
+
fontStyle: "bold",
|
|
811
|
+
valign: "middle",
|
|
812
|
+
fontSize: columns.length > 8 ? 6 : 7
|
|
813
|
+
},
|
|
814
|
+
// Une colonne de nombres se taille sur son contenu : rognee par une
|
|
815
|
+
// colonne de libelles, « 1 203 981 » se coupait en deux lignes, le
|
|
816
|
+
// separateur de milliers servant de point de rupture.
|
|
817
|
+
columnStyles: Object.fromEntries(
|
|
818
|
+
numeric.map((isNumber, index) => [
|
|
819
|
+
index,
|
|
820
|
+
isNumber ? { halign: "right", cellWidth: "wrap" } : { halign: "left" }
|
|
821
|
+
])
|
|
822
|
+
),
|
|
823
|
+
alternateRowStyles: { fillColor: surface },
|
|
824
|
+
// L'entete suit l'alignement de sa colonne : « # Patients » ferre a
|
|
825
|
+
// gauche au-dessus d'une colonne de chiffres ferres a droite, les
|
|
826
|
+
// deux ne se lisaient pas comme la meme colonne.
|
|
827
|
+
didParseCell: (data) => {
|
|
828
|
+
if (data.section === "head" && numeric[data.column.index]) {
|
|
829
|
+
data.cell.styles.halign = "right";
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
// Le pied de page est reserve : sans cette marge, la derniere ligne
|
|
833
|
+
// d'un tableau long passait dessous.
|
|
834
|
+
margin: { left: margin, right: margin, top: margin, bottom: margin + FOOTER },
|
|
683
835
|
tableWidth: wide
|
|
684
836
|
});
|
|
685
837
|
y = doc.lastAutoTable?.finalY ?? margin;
|
|
686
838
|
});
|
|
687
839
|
}
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
doc.
|
|
840
|
+
const pages = doc.getNumberOfPages();
|
|
841
|
+
for (let page = 1; page <= pages; page += 1) {
|
|
842
|
+
doc.setPage(page);
|
|
843
|
+
const width = doc.internal.pageSize.getWidth();
|
|
844
|
+
const foot = doc.internal.pageSize.getHeight() - margin + 2;
|
|
845
|
+
doc.setDrawColor(232, 232, 240);
|
|
846
|
+
doc.setLineWidth(0.2);
|
|
847
|
+
doc.line(margin, foot - 4, width - margin, foot - 4);
|
|
691
848
|
doc.setFont("helvetica", "normal");
|
|
692
|
-
doc.setFontSize(
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
|
|
849
|
+
doc.setFontSize(7.5);
|
|
850
|
+
doc.setTextColor(...grey);
|
|
851
|
+
const trail = doc.splitTextToSize(
|
|
852
|
+
printable(`${BRAND_TITLE} - ${payload.title}`),
|
|
853
|
+
width - margin * 2 - 20
|
|
854
|
+
)[0] ?? "";
|
|
855
|
+
doc.text(trail, margin, foot);
|
|
856
|
+
doc.text(`${page} / ${pages}`, width - margin, foot, { align: "right" });
|
|
696
857
|
}
|
|
697
858
|
const fileName = `${config.fileName}.pdf`;
|
|
698
859
|
const blob = doc.output("blob");
|