valtech-components 4.0.167 → 4.0.169
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/components/molecules/ticket-card/ticket-card.component.mjs +4 -1
- package/esm2022/lib/components/molecules/ticket-card/types.mjs +1 -1
- package/esm2022/lib/components/organisms/html-viewer-modal/html-viewer-modal.component.mjs +9 -4
- package/esm2022/lib/services/ticket-card-image/ticket-card-image.service.mjs +117 -41
- package/esm2022/lib/services/ticket-card-image/types.mjs +1 -1
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +126 -43
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/cta-card/cta-card.component.d.ts +1 -1
- package/lib/components/molecules/media-object/media-object.component.d.ts +1 -1
- package/lib/components/molecules/ticket-card/types.d.ts +7 -1
- package/lib/components/organisms/article/article.component.d.ts +2 -2
- package/lib/components/organisms/auth-cta/auth-cta.component.d.ts +1 -1
- package/lib/components/organisms/html-viewer-modal/html-viewer-modal.component.d.ts +3 -0
- package/lib/services/ticket-card-image/ticket-card-image.service.d.ts +3 -0
- package/lib/services/ticket-card-image/types.d.ts +6 -0
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
|
|
|
56
56
|
* Current version of valtech-components.
|
|
57
57
|
* This is automatically updated during the publish process.
|
|
58
58
|
*/
|
|
59
|
-
const VERSION = '4.0.
|
|
59
|
+
const VERSION = '4.0.169';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -35283,6 +35283,10 @@ class HtmlViewerModalComponent {
|
|
|
35283
35283
|
constructor() {
|
|
35284
35284
|
this.html = '';
|
|
35285
35285
|
this.title = 'documento';
|
|
35286
|
+
this.sanitizer = inject(DomSanitizer);
|
|
35287
|
+
}
|
|
35288
|
+
get safeHtml() {
|
|
35289
|
+
return this.sanitizer.bypassSecurityTrustHtml(this.html);
|
|
35286
35290
|
}
|
|
35287
35291
|
dismiss() {
|
|
35288
35292
|
this._modalRef?.dismiss();
|
|
@@ -35323,7 +35327,7 @@ class HtmlViewerModalComponent {
|
|
|
35323
35327
|
</ion-header>
|
|
35324
35328
|
|
|
35325
35329
|
<ion-content>
|
|
35326
|
-
<iframe [srcdoc]="
|
|
35330
|
+
<iframe [srcdoc]="safeHtml" style="width: 100%; height: 100%; border: none;" title="Visor de documento"></iframe>
|
|
35327
35331
|
</ion-content>
|
|
35328
35332
|
`, isInline: true, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-bottom: 0;--padding-top: 0}iframe{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
|
|
35329
35333
|
}
|
|
@@ -35349,7 +35353,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
35349
35353
|
</ion-header>
|
|
35350
35354
|
|
|
35351
35355
|
<ion-content>
|
|
35352
|
-
<iframe [srcdoc]="
|
|
35356
|
+
<iframe [srcdoc]="safeHtml" style="width: 100%; height: 100%; border: none;" title="Visor de documento"></iframe>
|
|
35353
35357
|
</ion-content>
|
|
35354
35358
|
`, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-bottom: 0;--padding-top: 0}iframe{display:block}\n"] }]
|
|
35355
35359
|
}], propDecorators: { html: [{
|
|
@@ -61516,14 +61520,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
61516
61520
|
// Use LocaleService for language management instead
|
|
61517
61521
|
|
|
61518
61522
|
const CANVAS_WIDTH = 560;
|
|
61519
|
-
const HEADER_H =
|
|
61520
|
-
const QR_ZONE_H =
|
|
61521
|
-
const
|
|
61522
|
-
const
|
|
61523
|
-
const
|
|
61523
|
+
const HEADER_H = 210;
|
|
61524
|
+
const QR_ZONE_H = 340;
|
|
61525
|
+
const PERF_H = 28;
|
|
61526
|
+
const FOOTER_H = 220;
|
|
61527
|
+
const CANVAS_HEIGHT = HEADER_H + QR_ZONE_H + PERF_H + FOOTER_H;
|
|
61528
|
+
const QR_SIZE = 260;
|
|
61524
61529
|
const DEFAULT_BRAND_COLOR = '#9b1fde';
|
|
61525
61530
|
const DEFAULT_BRAND_COLOR_TO = '#c93075';
|
|
61526
|
-
const FONT_STACK = 'Khula, Arial, sans-serif';
|
|
61531
|
+
const FONT_STACK = '"Plus Jakarta Sans", Khula, Arial, sans-serif';
|
|
61527
61532
|
class TicketCardImageService {
|
|
61528
61533
|
async generate(config) {
|
|
61529
61534
|
const width = config.width ?? CANVAS_WIDTH;
|
|
@@ -61541,6 +61546,7 @@ class TicketCardImageService {
|
|
|
61541
61546
|
}
|
|
61542
61547
|
await this.drawHeader(ctx, config, width);
|
|
61543
61548
|
await this.drawQrZone(ctx, config, width);
|
|
61549
|
+
this.drawPerforated(ctx, config, width);
|
|
61544
61550
|
this.drawFooter(ctx, config, width);
|
|
61545
61551
|
if (canvas instanceof OffscreenCanvas) {
|
|
61546
61552
|
return canvas.convertToBlob({ type: 'image/png' });
|
|
@@ -61594,31 +61600,41 @@ class TicketCardImageService {
|
|
|
61594
61600
|
grad.addColorStop(1, colorTo);
|
|
61595
61601
|
ctx.fillStyle = grad;
|
|
61596
61602
|
ctx.fillRect(0, 0, width, HEADER_H);
|
|
61597
|
-
const logoH =
|
|
61598
|
-
const
|
|
61599
|
-
let
|
|
61603
|
+
const logoH = 48;
|
|
61604
|
+
const logoPadTop = 28;
|
|
61605
|
+
let cursorY = logoPadTop;
|
|
61600
61606
|
if (config.logoUrl) {
|
|
61601
61607
|
try {
|
|
61602
61608
|
const logoImg = await this.loadImage(config.logoUrl);
|
|
61603
61609
|
const ratio = logoImg.width / logoImg.height;
|
|
61604
61610
|
const logoW = logoH * ratio;
|
|
61605
|
-
const logoX =
|
|
61606
|
-
|
|
61607
|
-
|
|
61608
|
-
if (config.brandName) {
|
|
61609
|
-
textY = logoY + logoH + 14;
|
|
61610
|
-
}
|
|
61611
|
+
const logoX = (width - logoW) / 2;
|
|
61612
|
+
ctx.drawImage(logoImg, logoX, cursorY, logoW, logoH);
|
|
61613
|
+
cursorY += logoH + 14;
|
|
61611
61614
|
}
|
|
61612
61615
|
catch {
|
|
61613
|
-
// logo failed —
|
|
61616
|
+
// logo failed — continue without it
|
|
61614
61617
|
}
|
|
61615
61618
|
}
|
|
61616
61619
|
if (config.brandName) {
|
|
61617
61620
|
ctx.fillStyle = '#ffffff';
|
|
61618
|
-
ctx.font = `bold 1.
|
|
61621
|
+
ctx.font = `bold 1.625rem ${FONT_STACK}`;
|
|
61619
61622
|
ctx.textAlign = 'center';
|
|
61620
|
-
ctx.textBaseline = '
|
|
61621
|
-
ctx.fillText(config.brandName, width / 2,
|
|
61623
|
+
ctx.textBaseline = 'top';
|
|
61624
|
+
ctx.fillText(config.brandName, width / 2, cursorY, width - 48);
|
|
61625
|
+
cursorY += 32;
|
|
61626
|
+
}
|
|
61627
|
+
const metaParts = [];
|
|
61628
|
+
if (config.eventDate)
|
|
61629
|
+
metaParts.push(config.eventDate);
|
|
61630
|
+
if (config.eventLocation)
|
|
61631
|
+
metaParts.push(config.eventLocation);
|
|
61632
|
+
if (metaParts.length > 0) {
|
|
61633
|
+
ctx.fillStyle = 'rgba(255,255,255,0.78)';
|
|
61634
|
+
ctx.font = `0.875rem ${FONT_STACK}`;
|
|
61635
|
+
ctx.textAlign = 'center';
|
|
61636
|
+
ctx.textBaseline = 'top';
|
|
61637
|
+
ctx.fillText(metaParts.join(' · '), width / 2, cursorY, width - 48);
|
|
61622
61638
|
}
|
|
61623
61639
|
}
|
|
61624
61640
|
async drawQrZone(ctx, config, width) {
|
|
@@ -61626,7 +61642,7 @@ class TicketCardImageService {
|
|
|
61626
61642
|
ctx.fillStyle = '#ffffff';
|
|
61627
61643
|
ctx.fillRect(0, zoneTop, width, QR_ZONE_H);
|
|
61628
61644
|
const qrX = (width - QR_SIZE) / 2;
|
|
61629
|
-
const qrY = zoneTop + (QR_ZONE_H - QR_SIZE -
|
|
61645
|
+
const qrY = zoneTop + (QR_ZONE_H - QR_SIZE - 40) / 2;
|
|
61630
61646
|
try {
|
|
61631
61647
|
const qrImg = await this.loadImage(config.qrDataUrl);
|
|
61632
61648
|
ctx.drawImage(qrImg, qrX, qrY, QR_SIZE, QR_SIZE);
|
|
@@ -61635,42 +61651,106 @@ class TicketCardImageService {
|
|
|
61635
61651
|
ctx.strokeStyle = '#e0e0e0';
|
|
61636
61652
|
ctx.strokeRect(qrX, qrY, QR_SIZE, QR_SIZE);
|
|
61637
61653
|
}
|
|
61638
|
-
|
|
61639
|
-
ctx.
|
|
61654
|
+
const hint = config.scanHint ?? 'Muestra este QR en la puerta para canjear tu entrada.';
|
|
61655
|
+
ctx.fillStyle = '#6b6675';
|
|
61656
|
+
ctx.font = `0.875rem ${FONT_STACK}`;
|
|
61640
61657
|
ctx.textAlign = 'center';
|
|
61641
61658
|
ctx.textBaseline = 'top';
|
|
61642
|
-
|
|
61659
|
+
const hintY = qrY + QR_SIZE + 14;
|
|
61660
|
+
this.fillWrappedText(ctx, hint, width / 2, hintY, width - 80, 22);
|
|
61661
|
+
}
|
|
61662
|
+
drawPerforated(ctx, config, width) {
|
|
61663
|
+
const y = HEADER_H + QR_ZONE_H;
|
|
61664
|
+
const notchR = PERF_H / 2;
|
|
61665
|
+
const colorFrom = config.brandColor ?? DEFAULT_BRAND_COLOR;
|
|
61666
|
+
ctx.fillStyle = '#ffffff';
|
|
61667
|
+
ctx.fillRect(0, y, width, PERF_H);
|
|
61668
|
+
// Left notch (semicircle cut from edge)
|
|
61669
|
+
ctx.fillStyle = '#f8f8f8';
|
|
61670
|
+
ctx.beginPath();
|
|
61671
|
+
ctx.arc(-1, y + notchR, notchR + 1, -Math.PI / 2, Math.PI / 2);
|
|
61672
|
+
ctx.fill();
|
|
61673
|
+
// Right notch
|
|
61674
|
+
ctx.beginPath();
|
|
61675
|
+
ctx.arc(width + 1, y + notchR, notchR + 1, Math.PI / 2, -Math.PI / 2);
|
|
61676
|
+
ctx.fill();
|
|
61677
|
+
// Dashed line in middle
|
|
61678
|
+
ctx.setLineDash([8, 6]);
|
|
61679
|
+
ctx.strokeStyle = colorFrom + '55';
|
|
61680
|
+
ctx.lineWidth = 1.5;
|
|
61681
|
+
ctx.beginPath();
|
|
61682
|
+
ctx.moveTo(notchR * 2 + 8, y + notchR);
|
|
61683
|
+
ctx.lineTo(width - notchR * 2 - 8, y + notchR);
|
|
61684
|
+
ctx.stroke();
|
|
61685
|
+
ctx.setLineDash([]);
|
|
61643
61686
|
}
|
|
61644
61687
|
drawFooter(ctx, config, width) {
|
|
61645
|
-
const footerTop = HEADER_H + QR_ZONE_H;
|
|
61688
|
+
const footerTop = HEADER_H + QR_ZONE_H + PERF_H;
|
|
61689
|
+
const colorFrom = config.brandColor ?? DEFAULT_BRAND_COLOR;
|
|
61646
61690
|
ctx.fillStyle = '#ffffff';
|
|
61647
61691
|
ctx.fillRect(0, footerTop, width, FOOTER_H);
|
|
61648
|
-
|
|
61649
|
-
ctx.fillStyle = colorFrom;
|
|
61650
|
-
ctx.fillRect(0, footerTop, width, 3);
|
|
61651
|
-
let contentY = footerTop + 40;
|
|
61692
|
+
let contentY = footerTop + 32;
|
|
61652
61693
|
if (config.folio !== undefined && config.folio !== null && config.folio !== '') {
|
|
61653
|
-
const label = config.folioLabel ?? '
|
|
61654
|
-
|
|
61655
|
-
ctx.fillStyle =
|
|
61656
|
-
ctx.font = `
|
|
61694
|
+
const label = (config.folioLabel ?? 'Cartón').toUpperCase();
|
|
61695
|
+
// Label small — brand color, letter-spaced
|
|
61696
|
+
ctx.fillStyle = colorFrom;
|
|
61697
|
+
ctx.font = `600 0.8125rem ${FONT_STACK}`;
|
|
61657
61698
|
ctx.textAlign = 'center';
|
|
61658
61699
|
ctx.textBaseline = 'top';
|
|
61659
|
-
|
|
61660
|
-
contentY +=
|
|
61700
|
+
this.fillLetterSpaced(ctx, label, width / 2, contentY, 3);
|
|
61701
|
+
contentY += 24;
|
|
61702
|
+
// Folio number — large, bold, brand color
|
|
61703
|
+
ctx.fillStyle = colorFrom;
|
|
61704
|
+
ctx.font = `800 3.5rem ${FONT_STACK}`;
|
|
61705
|
+
ctx.textAlign = 'center';
|
|
61706
|
+
ctx.textBaseline = 'top';
|
|
61707
|
+
ctx.fillText(`#${config.folio}`, width / 2, contentY, width - 40);
|
|
61708
|
+
contentY += 68;
|
|
61661
61709
|
}
|
|
61662
61710
|
if (config.buyerName) {
|
|
61663
|
-
ctx.fillStyle = '#
|
|
61664
|
-
ctx.font = `
|
|
61711
|
+
ctx.fillStyle = '#6b6675';
|
|
61712
|
+
ctx.font = `0.9375rem ${FONT_STACK}`;
|
|
61665
61713
|
ctx.textAlign = 'center';
|
|
61666
61714
|
ctx.textBaseline = 'top';
|
|
61667
61715
|
ctx.fillText(config.buyerName, width / 2, contentY, width - 40);
|
|
61668
61716
|
}
|
|
61669
|
-
|
|
61670
|
-
ctx.
|
|
61671
|
-
ctx.
|
|
61717
|
+
// Copyright
|
|
61718
|
+
ctx.fillStyle = '#b0a8bc';
|
|
61719
|
+
ctx.font = `0.6875rem ${FONT_STACK}`;
|
|
61720
|
+
ctx.textAlign = 'center';
|
|
61672
61721
|
ctx.textBaseline = 'bottom';
|
|
61673
|
-
ctx.fillText('Valtech', width
|
|
61722
|
+
ctx.fillText('© 2026 Valtech. Todos los derechos reservados.', width / 2, footerTop + FOOTER_H - 14);
|
|
61723
|
+
}
|
|
61724
|
+
fillWrappedText(ctx, text, x, y, maxWidth, lineHeight) {
|
|
61725
|
+
const words = text.split(' ');
|
|
61726
|
+
let line = '';
|
|
61727
|
+
let curY = y;
|
|
61728
|
+
for (const word of words) {
|
|
61729
|
+
const test = line ? `${line} ${word}` : word;
|
|
61730
|
+
if (ctx.measureText(test).width > maxWidth && line) {
|
|
61731
|
+
ctx.fillText(line, x, curY);
|
|
61732
|
+
line = word;
|
|
61733
|
+
curY += lineHeight;
|
|
61734
|
+
}
|
|
61735
|
+
else {
|
|
61736
|
+
line = test;
|
|
61737
|
+
}
|
|
61738
|
+
}
|
|
61739
|
+
if (line)
|
|
61740
|
+
ctx.fillText(line, x, curY);
|
|
61741
|
+
}
|
|
61742
|
+
fillLetterSpaced(ctx, text, cx, y, spacing) {
|
|
61743
|
+
// Measure total width with spacing and center
|
|
61744
|
+
const chars = text.split('');
|
|
61745
|
+
const charWidths = chars.map(c => ctx.measureText(c).width);
|
|
61746
|
+
const totalW = charWidths.reduce((s, w) => s + w, 0) + spacing * (chars.length - 1);
|
|
61747
|
+
let x = cx - totalW / 2;
|
|
61748
|
+
ctx.textAlign = 'left';
|
|
61749
|
+
for (let i = 0; i < chars.length; i++) {
|
|
61750
|
+
ctx.fillText(chars[i], x, y);
|
|
61751
|
+
x += charWidths[i] + spacing;
|
|
61752
|
+
}
|
|
61753
|
+
ctx.textAlign = 'center';
|
|
61674
61754
|
}
|
|
61675
61755
|
loadImage(src) {
|
|
61676
61756
|
return new Promise((resolve, reject) => {
|
|
@@ -70432,6 +70512,9 @@ class TicketCardComponent {
|
|
|
70432
70512
|
folio: p.folio,
|
|
70433
70513
|
folioLabel: p.shareFolioLabel,
|
|
70434
70514
|
buyerName: p.buyerName,
|
|
70515
|
+
eventDate: p.shareEventDate,
|
|
70516
|
+
eventLocation: p.shareEventLocation,
|
|
70517
|
+
scanHint: p.shareScanHint,
|
|
70435
70518
|
};
|
|
70436
70519
|
}
|
|
70437
70520
|
async copyToClipboard(config, p) {
|