@apolopay-sdk/ui 1.0.0

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.
@@ -0,0 +1,668 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { LitElement, html, css } from 'lit';
8
+ import { customElement, property, query, state } from 'lit/decorators.js';
9
+ import { I18n, ModalStep } from '@apolopay-sdk/core';
10
+ import { modalBaseStyles } from '../styles/modal-base';
11
+ import { sharedStyles } from '../styles/shared-styles';
12
+ import { textFieldBaseStyles } from '../styles/text-field-base';
13
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
14
+ import { logoApolo } from '../assets/logo_apolo';
15
+ import { qrBaseStyles } from '../styles/qr-base';
16
+ import { handleImageError } from '../utils/image_error';
17
+ import { spinnerStyles } from '../styles/spinner-styles';
18
+ import './payment-timer.js';
19
+ let PaymentModal = class PaymentModal extends LitElement {
20
+ constructor() {
21
+ super(...arguments);
22
+ // --- Props Received from Parent ---
23
+ this.isOpen = false;
24
+ this.barrierDismissible = false;
25
+ this.lang = 'es';
26
+ this.productTitle = '';
27
+ this.currentStep = ModalStep.SELECT_ASSET;
28
+ this.status = 'idle';
29
+ this.error = null;
30
+ this.isLoadingData = true; // For initial asset/network load
31
+ this.assets = [];
32
+ this.selectedAsset = null;
33
+ this.selectedNetwork = null;
34
+ this.qrCodeUrl = null;
35
+ this.paymentAddress = null;
36
+ this.amount = 0;
37
+ this.email = '';
38
+ this.qrCodeExpiresAt = null;
39
+ this.paymentUrl = null;
40
+ this.isAddressCopied = false;
41
+ // Handle clicks potentially on the backdrop
42
+ this.handleBackdropClick = (event) => {
43
+ if (event.target !== this.dialogElement)
44
+ return;
45
+ // Prevent closing if already animating closed
46
+ if (this.dialogElement.classList.contains('closing'))
47
+ return;
48
+ if (!this.barrierDismissible)
49
+ return;
50
+ const rect = this.dialogElement.getBoundingClientRect();
51
+ const clickedOutside = (event.clientY < rect.top || event.clientY > rect.bottom ||
52
+ event.clientX < rect.left || event.clientX > rect.right);
53
+ if (clickedOutside) {
54
+ this.requestClose();
55
+ }
56
+ };
57
+ }
58
+ disconnectedCallback() {
59
+ super.disconnectedCallback();
60
+ // 🛡️ SEGURIDAD CRÍTICA:
61
+ // Si el componente se desmonta del DOM mientras el diálogo está abierto,
62
+ // forzamos el cierre nativo inmediatamente para eliminar el backdrop.
63
+ const dialog = this.dialogElement;
64
+ if (dialog && dialog.open) {
65
+ dialog.close();
66
+ }
67
+ }
68
+ // --- Lifecycle: Manage Dialog State ---
69
+ async updated(changedProperties) {
70
+ super.updated(changedProperties);
71
+ // Wait for Lit's rendering cycle to complete
72
+ await this.updateComplete;
73
+ // Timer is now managed by <payment-timer> component
74
+ if (changedProperties.has('isOpen')) {
75
+ const dialog = this.dialogElement;
76
+ if (!dialog)
77
+ return; // Guard clause
78
+ if (this.isOpen) {
79
+ // --- Opening ---
80
+ dialog.classList.remove('closing'); // Remove closing class if present
81
+ if (!dialog.open) {
82
+ dialog.showModal(); // Use showModal() for true modal behavior
83
+ }
84
+ dialog.addEventListener('click', this.handleBackdropClick); // Add backdrop listener
85
+ }
86
+ else {
87
+ // --- LÓGICA DE CIERRE MEJORADA ---
88
+ dialog.removeEventListener('click', this.handleBackdropClick);
89
+ // Si ya está cerrado, no hacemos nada
90
+ if (!dialog.open)
91
+ return;
92
+ dialog.classList.add('closing');
93
+ const onAnimationEnd = (e) => {
94
+ // 🛡️ FILTRO: Asegurarse de que el evento viene del dialog y no de un hijo (spinner, etc)
95
+ if (e.target === dialog) {
96
+ this.closeDialogFinal(dialog, onAnimationEnd);
97
+ }
98
+ };
99
+ dialog.addEventListener('animationend', onAnimationEnd);
100
+ // 🛡️ TIMEOUT DE SEGURIDAD REDUCIDO:
101
+ // Si la animación falla o el navegador se congela, forzamos cierre en 200ms
102
+ setTimeout(() => {
103
+ if (dialog.open)
104
+ this.closeDialogFinal(dialog, onAnimationEnd);
105
+ }, 200);
106
+ }
107
+ }
108
+ }
109
+ // Helper actualizado
110
+ closeDialogFinal(dialog, listener) {
111
+ dialog.removeEventListener('animationend', listener);
112
+ dialog.classList.remove('closing');
113
+ // Verificamos de nuevo si sigue abierto antes de cerrar
114
+ if (dialog.open)
115
+ dialog.close();
116
+ }
117
+ // --- Event Dispatchers (Emit events to parent) ---
118
+ // Request to close the modal (triggered by X, backdrop, Escape)
119
+ requestClose() {
120
+ this.dispatchEvent(new CustomEvent('closeRequest'));
121
+ }
122
+ // Handle the native 'close' event (fired by Escape key)
123
+ handleDialogNativeClose(event) {
124
+ event.preventDefault(); // Prevent the default immediate close
125
+ this.requestClose(); // Trigger our animated close flow
126
+ }
127
+ handleTimerExpired() {
128
+ this.status = 'error';
129
+ this.error = {
130
+ code: 'PAYMENT_TIMEOUT',
131
+ message: I18n.t.errors.timeout
132
+ };
133
+ this.changeStep(ModalStep.RESULT);
134
+ this.dispatchEvent(new CustomEvent('expired', { detail: { error: this.error } }));
135
+ }
136
+ // Emit event when a asset is selected
137
+ selectAsset(assetId) {
138
+ this.dispatchEvent(new CustomEvent('assetSelect', { detail: { assetId } }));
139
+ }
140
+ // Emit event when a network is selected
141
+ selectNetwork(networkId) {
142
+ this.dispatchEvent(new CustomEvent('networkSelect', { detail: { networkId } }));
143
+ }
144
+ // Emit event to request changing step (for "Back" buttons)
145
+ changeStep(step, e) {
146
+ e?.stopPropagation(); // Prevent event bubbling if from a button click
147
+ this.dispatchEvent(new CustomEvent('changeStep', { detail: step }));
148
+ }
149
+ copyAddress(event) {
150
+ if (!this.paymentAddress)
151
+ return;
152
+ event.stopPropagation();
153
+ navigator.clipboard.writeText(this.paymentAddress);
154
+ this.isAddressCopied = true;
155
+ setTimeout(() => this.isAddressCopied = false, 2000);
156
+ }
157
+ handlePayFromDevice() {
158
+ if (this.paymentUrl) {
159
+ window.open(this.paymentUrl, '_blank');
160
+ }
161
+ }
162
+ get currentAsset() {
163
+ return this.assets.find(asset => asset.id === this.selectedAsset);
164
+ }
165
+ get currentNetwork() {
166
+ return this.currentAsset?.networks.find(network => network.id === this.selectedNetwork);
167
+ }
168
+ getFormattedTimeWindow() {
169
+ if (!this.qrCodeExpiresAt || isNaN(this.qrCodeExpiresAt))
170
+ return '30 min';
171
+ const endTime = this.qrCodeExpiresAt;
172
+ const now = Date.now();
173
+ const diffMs = endTime - now;
174
+ if (diffMs <= 0)
175
+ return '0 min';
176
+ // Convertimos ms a minutos y redondeamos hacia arriba
177
+ const minutes = Math.ceil(diffMs / (1000 * 60));
178
+ return `${minutes} min`;
179
+ }
180
+ // --- RENDERIZADO DEL QR (Lógica bifurcada) ---
181
+ renderQRStep(t) {
182
+ const timeWindow = this.getFormattedTimeWindow();
183
+ const warningTokenHTML = I18n.interpolate(t.modal.warnings.onlyToken, {
184
+ symbol: this.currentAsset?.symbol || ''
185
+ });
186
+ const warningTimerHTML = I18n.interpolate(t.modal.warnings.timer, {
187
+ time: timeWindow
188
+ });
189
+ const network = this.currentNetwork;
190
+ // 1. Caso Apolo Pay
191
+ if (network?.network === 'apolopay') {
192
+ return html `
193
+ <payment-timer class="timer" .expiresAt=${this.qrCodeExpiresAt} @expired=${this.handleTimerExpired}></payment-timer>
194
+
195
+ <div class="qr-frame">
196
+ <div class="qr-wrapper">
197
+ <img src="${this.qrCodeUrl}" class="qr-code-img" alt="QR Apolo Pay" @error=${handleImageError} />
198
+
199
+ <img src="${logoApolo}" class="qr-overlay-icon" alt="Network Icon" style="padding: 4px;" />
200
+ </div>
201
+
202
+ <span class="qr-badge">${this.amount} ${this.currentAsset?.symbol}</span>
203
+ </div>
204
+
205
+ <div class="warning-text">
206
+ <ul>
207
+ <li>${unsafeHTML(t.modal.warnings.networkMatch)}</li>
208
+ <li>${unsafeHTML(t.modal.warnings.noNFT)}</li>
209
+ <li>${unsafeHTML(warningTokenHTML)}</li>
210
+ </ul>
211
+ <p>${unsafeHTML(warningTimerHTML)}</p>
212
+ </div>
213
+
214
+ <button class="btn-dark">${unsafeHTML(t.modal.actions.scanApp)}</button>
215
+ ${this.paymentUrl ? html `
216
+ <button class="btn-primary" style="width: 100%; margin-top: 0.5rem;" @click=${this.handlePayFromDevice}>
217
+ ${t.modal.actions.payFromDevice}
218
+ </button>
219
+ ` : ''}
220
+ `;
221
+ }
222
+ // 2. Caso Red Externa
223
+ return html `
224
+ <payment-timer class="timer" .expiresAt=${this.qrCodeExpiresAt} @expired=${this.handleTimerExpired}></payment-timer>
225
+
226
+ <div class="qr-frame">
227
+ <div class="qr-wrapper">
228
+ <img src="${this.qrCodeUrl}" class="qr-code-img" alt="QR Wallet" @error=${handleImageError} />
229
+
230
+ ${network
231
+ ? html `<img src="${network.image}" class="qr-overlay-icon" alt="Network Icon" @error=${handleImageError} />`
232
+ : ''}
233
+ </div>
234
+ <span class="qr-badge">${this.amount} ${this.currentAsset?.symbol}</span>
235
+ </div>
236
+
237
+ <div class="text-field">
238
+ <label class="text-field-label">${t.modal.labels.network}</label>
239
+ <input class="text-field-input" readonly value="${this.currentNetwork?.name}" />
240
+ </div>
241
+
242
+ <div class="text-field">
243
+ <label class="text-field-label">${t.modal.labels.address}</label>
244
+ <input class="text-field-input" readonly value="${this.paymentAddress}" @click=${this.copyAddress} />
245
+ ${this.paymentAddress ? html `
246
+ <button class="btn-secondary" @click=${this.copyAddress}>${this.isAddressCopied ? t.modal.actions.copied : t.modal.actions.copy}</button>
247
+ ` : ''}
248
+ </div>
249
+
250
+ <div class="warning-text">
251
+ <ul>
252
+ <li>${unsafeHTML(t.modal.warnings.networkMatch)}</li>
253
+ <li>${unsafeHTML(t.modal.warnings.noNFT)}</li>
254
+ <li>${unsafeHTML(warningTokenHTML)}</li>
255
+ </ul>
256
+ <p>${unsafeHTML(warningTimerHTML)}</p>
257
+ </div>
258
+ ${this.paymentUrl ? html `
259
+ <button class="btn-primary" style="width: 100%; margin-top: 1rem;" @click=${this.handlePayFromDevice}>
260
+ ${t.modal.actions.payFromDevice}
261
+ </button>
262
+ ` : ''}
263
+ `;
264
+ }
265
+ // --- Render Method ---
266
+ render() {
267
+ const t = I18n.t;
268
+ let content;
269
+ // Header simple con navegación
270
+ const header = html `
271
+ <div class="modal-header">
272
+ ${this.currentStep > ModalStep.SELECT_ASSET && this.currentStep < ModalStep.RESULT
273
+ ? html `<button class="back-button" @click=${() => this.changeStep(this.currentStep - 1)} >&larr;</button>`
274
+ : ''}
275
+ <button class="close-button" @click=${this.requestClose}>&times;</button>
276
+ </div>
277
+ `;
278
+ // Selección de Asset
279
+ if (this.currentStep === ModalStep.SELECT_ASSET) {
280
+ content = html `
281
+ <h2>${unsafeHTML(t.modal.titles.selectAsset)}</h2>
282
+ <p class="subtitle">${t.modal.subtitles.selectAsset}</p>
283
+
284
+ <div class="selection-list">
285
+ ${this.assets.map(asset => html `
286
+ <div class="selection-card" @click=${() => this.selectAsset(asset.id)}>
287
+ <img src="${asset.image}" class="coin-icon" @error=${handleImageError} />
288
+ <div class="card-text">
289
+ <span class="card-title">${asset.symbol}</span>
290
+ <span class="card-sub">${asset.name}</span>
291
+ </div>
292
+ </div>
293
+ `)}
294
+ </div>
295
+ <p class="warning-text" style="font-size: 0.9rem; text-align: center; margin-top: 1.5rem">
296
+ ${t.modal.warnings.selectNetworkLater}
297
+ </p>
298
+ `;
299
+ }
300
+ // Selección de Red
301
+ else if (this.currentStep === ModalStep.SELECT_NETWORK) {
302
+ content = html `
303
+ <h2>${unsafeHTML(t.modal.titles.selectNetwork)}</h2>
304
+ <p class="subtitle">${t.modal.subtitles.selectNetwork}</p>
305
+
306
+ <div class="selection-list">
307
+ ${this.currentAsset?.networks.map((network) => html `
308
+ <div class="selection-card" @click=${() => this.selectNetwork(network.id)}>
309
+ <img src="${network.network === 'apolopay' ? logoApolo : network.image}" class="coin-icon" @error=${handleImageError} />
310
+ <div class="card-text">
311
+ <span class="card-title">${network.name}</span>
312
+ </div>
313
+ </div>
314
+ `)}
315
+ </div>
316
+ `;
317
+ }
318
+ // QR
319
+ else if (this.currentStep === ModalStep.SHOW_QR) {
320
+ content = html `
321
+ <h2>${unsafeHTML(I18n.interpolate(t.modal.titles.scanQr, { symbol: this.currentAsset?.symbol || '' }))}</h2>
322
+ ${this.productTitle ? html `<p class="subtitle">${this.productTitle}</p>` : ''}
323
+ ${this.renderQRStep(t)}
324
+ `;
325
+ }
326
+ // Resultado
327
+ else if (this.currentStep === ModalStep.RESULT) {
328
+ // Display final success or error message
329
+ if (this.status === 'success') {
330
+ content = html `
331
+ <div class="result-container">
332
+ <div class="success-icon">
333
+ <svg viewBox="0 0 52 52">
334
+ <circle class="checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
335
+ <path class="checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
336
+ </svg>
337
+ </div>
338
+
339
+ <h2 class="result-title">${unsafeHTML(t.modal.titles.success)}</h2>
340
+
341
+ <p class="result-desc">
342
+ ${t.modal.success.message} ${this.email ? html `<span class="highlight">${this.email}</span>` : ''} ${t.modal.success.message2}
343
+ </p>
344
+
345
+ <div class="purchase-details">
346
+ <h3 class="details-title">${t.modal.success.details}</h3>
347
+
348
+ ${this.productTitle ? html `
349
+ <div class="text-field">
350
+ <label class="text-field-label">${t.modal.labels.product}</label>
351
+ <input class="text-field-input" readonly value=${this.productTitle} />
352
+ </div>
353
+ ` : ''}
354
+
355
+ <div class="text-field">
356
+ <label class="text-field-label">${t.modal.labels.amount}</label>
357
+ <input class="text-field-input" readonly value="${this.amount} ${this.currentAsset?.symbol || ''}" />
358
+ </div>
359
+ </div>
360
+ </div>
361
+ `;
362
+ }
363
+ else if (this.status === 'error') {
364
+ content = html `
365
+ <div class="result-container">
366
+ <div class="error-icon">❌</div>
367
+ <h2 class="result-title">${t.modal.titles.error}</h2>
368
+ <p class="result-desc">${this.error?.message || t.errors.generic}</p>
369
+ <button class="btn-primary" @click=${this.requestClose}>${t.modal.actions.close}</button>
370
+ </div>
371
+ `;
372
+ }
373
+ else {
374
+ content = html `
375
+ <div class="result-container">
376
+ <div class="error-icon">⏳</div>
377
+ <h2 class="result-title">${t.modal.titles.idle}</h2>
378
+ <p class="result-desc">${t.modal.subtitles.idle}</p>
379
+ <button class="btn-primary" @click=${this.requestClose}>${t.modal.actions.close}</button>
380
+ </div>
381
+ `;
382
+ }
383
+ }
384
+ const showOverlay = this.isLoadingData ||
385
+ (this.currentStep === ModalStep.SHOW_QR && !this.qrCodeUrl);
386
+ return html `
387
+ <dialog @close=${this.handleDialogNativeClose}>
388
+ ${showOverlay
389
+ ? html `
390
+ <div class="spinner-overlay">
391
+ <div class="spinner"></div>
392
+ </div>`
393
+ : ''}
394
+ ${header}
395
+ <div class="modal-body">
396
+ ${content}
397
+ </div>
398
+ </dialog>
399
+ `;
400
+ } // End render
401
+ }; // End class
402
+ // --- Styles ---
403
+ PaymentModal.styles = [
404
+ sharedStyles,
405
+ modalBaseStyles,
406
+ textFieldBaseStyles,
407
+ qrBaseStyles,
408
+ spinnerStyles,
409
+ css `
410
+ /* --- HEADER --- */
411
+ .modal-header {
412
+ position: relative; /* Para posicionar el botón de cerrar */
413
+ padding: 1.5rem 1.5rem 0.5rem;
414
+ display: flex;
415
+ justify-content: center; /* Título centrado si lo hubiera */
416
+ align-items: center;
417
+ }
418
+
419
+ .close-button, .back-button {
420
+ position: absolute;
421
+ top: 1.5rem;
422
+ background: none;
423
+ border: none;
424
+ cursor: pointer;
425
+ color: #9ca3af;
426
+ transition: color 0.2s;
427
+ padding: 5px;
428
+ }
429
+ .close-button { right: 1.5rem; font-size: 1.5rem; }
430
+ .back-button { left: 1.5rem; font-size: 1.2rem; }
431
+ .close-button:hover, .back-button:hover { color: #374151; }
432
+
433
+ /* --- BODY --- */
434
+ .modal-body {
435
+ padding: 1rem 2rem 2.5rem; /* Padding generoso abajo */
436
+ text-align: center;
437
+ }
438
+
439
+ /* Títulos */
440
+ h2 {
441
+ font-size: 1.25rem;
442
+ font-weight: 700;
443
+ margin: 0 0 0.5rem;
444
+ }
445
+ .highlight { color: var(--apolo-accent); } /* Naranja de tus imágenes */
446
+
447
+ p.subtitle {
448
+ font-size: 0.9rem;
449
+ color: #6b7280;
450
+ margin: 0 0 1rem;
451
+ line-height: 1.4;
452
+ }
453
+
454
+ /* --- LISTAS DE SELECCIÓN (Botones grandes blancos) --- */
455
+ .selection-list {
456
+ display: flex;
457
+ flex-direction: column;
458
+ gap: 1rem;
459
+ }
460
+
461
+ .selection-card {
462
+ display: flex;
463
+ align-items: center;
464
+ background: white;
465
+ border: 1px solid #f3f4f6; /* Borde muy sutil */
466
+ border-radius: 16px;
467
+ padding: 1rem;
468
+ cursor: pointer;
469
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
470
+ transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
471
+ }
472
+
473
+ .selection-card:hover {
474
+ transform: translateY(-2px);
475
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08);
476
+ border-color: var(--apolo-accent); /* Hover naranja */
477
+ }
478
+
479
+ .coin-icon {
480
+ width: 40px;
481
+ height: 40px;
482
+ margin-right: 1rem;
483
+ object-fit: cover;
484
+ }
485
+
486
+ .card-text {
487
+ text-align: left;
488
+ display: flex;
489
+ flex-direction: column;
490
+ }
491
+ .card-title { font-weight: 600; font-size: 1rem; color: var(--apolo-text); }
492
+ .card-sub { font-size: 0.8rem; color: var(--apolo-text-muted); text-transform: uppercase;}
493
+
494
+ /* --- QR SCREENS --- */
495
+ .timer {
496
+ color: var(--apolo-accent);
497
+ font-weight: 600;
498
+ font-size: 0.9rem;
499
+ margin-bottom: 1rem;
500
+ display: block;
501
+ }
502
+
503
+ /* Botón Naranja Grande */
504
+ .btn-primary {
505
+ background-color: var(--apolo-accent); /* Naranja Apolo */
506
+ color: white;
507
+ padding: 0.5rem 1.5rem;
508
+ border-radius: var(--apolo-radius-lg); /* Pill shape */
509
+ border: none;
510
+ font-weight: 400;
511
+ font-size: .9rem;
512
+ cursor: pointer;
513
+ box-shadow: 0 4px 10px rgba(234, 88, 12, 0.3);
514
+ transition: transform 0.1s, box-shadow 0.1s;
515
+ }
516
+ .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 15px rgba(234, 88, 12, 0.4); }
517
+
518
+ /* Botón Azul Oscuro (Apolo Pay QR) */
519
+ .btn-dark {
520
+ background-color: var(--apolo-primary-darkest);
521
+ color: white;
522
+ width: 100%;
523
+ padding: 1rem;
524
+ border-radius: var(--apolo-radius);
525
+ border: none;
526
+ font-weight: 600;
527
+ cursor: pointer;
528
+ margin-block: 0.25rem 1.25rem;
529
+ }
530
+
531
+ .warning-text {
532
+ font-size: 0.75rem;
533
+ text-align: left;
534
+ margin-top: 1.5rem;
535
+ line-height: 1.5;
536
+ }
537
+ .warning-text strong { color: var(--apolo-accent); }
538
+
539
+ .warning-text ul {
540
+ padding-left: 1.5rem;
541
+ }
542
+
543
+
544
+ /* --- PANTALLA DE RESULTADO --- */
545
+ .result-container {
546
+ text-align: center;
547
+ animation: fadeIn 0.5s ease-out;
548
+ }
549
+
550
+ /* Animación simple de entrada */
551
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
552
+
553
+ .success-icon {
554
+ width: 80px;
555
+ height: 80px;
556
+ margin: 0 auto 1.5rem;
557
+ }
558
+
559
+ /* Animación del Check SVG */
560
+ .checkmark-circle {
561
+ stroke-dasharray: 166;
562
+ stroke-dashoffset: 166;
563
+ stroke-width: 2;
564
+ stroke: #22c55e; /* Verde éxito */
565
+ fill: none;
566
+ animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
567
+ }
568
+ .checkmark-check {
569
+ transform-origin: 50% 50%;
570
+ stroke-dasharray: 48;
571
+ stroke-dashoffset: 48;
572
+ stroke: #22c55e;
573
+ stroke-width: 4;
574
+ animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.6s forwards;
575
+ }
576
+ @keyframes stroke { 100% { stroke-dashoffset: 0; } }
577
+
578
+ .result-title {
579
+ font-size: 1.5rem;
580
+ margin-bottom: 1rem;
581
+ }
582
+
583
+ .result-desc {
584
+ font-size: 0.95rem;
585
+ margin-bottom: 1.5rem;
586
+ line-height: 1.5;
587
+ }
588
+
589
+ .purchase-details {
590
+ text-align: left;
591
+ margin-bottom: 1.5rem;
592
+ }
593
+
594
+ .details-title {
595
+ font-size: 1rem;
596
+ font-weight: 700;
597
+ text-decoration: underline;
598
+ text-decoration-color: var(--apolo-text);
599
+ text-underline-offset: 4px;
600
+ text-align: center;
601
+ margin-bottom: 1.5rem;
602
+ }
603
+
604
+ /* Estilo Error */
605
+ .error-icon { font-size: 4rem; margin-bottom: 1rem; }
606
+ `
607
+ ];
608
+ __decorate([
609
+ property({ type: Boolean })
610
+ ], PaymentModal.prototype, "isOpen", void 0);
611
+ __decorate([
612
+ property({ type: Boolean })
613
+ ], PaymentModal.prototype, "barrierDismissible", void 0);
614
+ __decorate([
615
+ property({ type: String })
616
+ ], PaymentModal.prototype, "lang", void 0);
617
+ __decorate([
618
+ property({ type: String })
619
+ ], PaymentModal.prototype, "productTitle", void 0);
620
+ __decorate([
621
+ property({ type: Number })
622
+ ], PaymentModal.prototype, "currentStep", void 0);
623
+ __decorate([
624
+ property({ type: String })
625
+ ], PaymentModal.prototype, "status", void 0);
626
+ __decorate([
627
+ property({ type: Object })
628
+ ], PaymentModal.prototype, "error", void 0);
629
+ __decorate([
630
+ property({ type: Boolean })
631
+ ], PaymentModal.prototype, "isLoadingData", void 0);
632
+ __decorate([
633
+ property({ type: Array })
634
+ ], PaymentModal.prototype, "assets", void 0);
635
+ __decorate([
636
+ property({ type: String })
637
+ ], PaymentModal.prototype, "selectedAsset", void 0);
638
+ __decorate([
639
+ property({ type: String })
640
+ ], PaymentModal.prototype, "selectedNetwork", void 0);
641
+ __decorate([
642
+ property({ type: String })
643
+ ], PaymentModal.prototype, "qrCodeUrl", void 0);
644
+ __decorate([
645
+ property({ type: String })
646
+ ], PaymentModal.prototype, "paymentAddress", void 0);
647
+ __decorate([
648
+ property({ type: Number })
649
+ ], PaymentModal.prototype, "amount", void 0);
650
+ __decorate([
651
+ property({ type: String })
652
+ ], PaymentModal.prototype, "email", void 0);
653
+ __decorate([
654
+ property({ type: Number })
655
+ ], PaymentModal.prototype, "qrCodeExpiresAt", void 0);
656
+ __decorate([
657
+ property({ type: String })
658
+ ], PaymentModal.prototype, "paymentUrl", void 0);
659
+ __decorate([
660
+ state()
661
+ ], PaymentModal.prototype, "isAddressCopied", void 0);
662
+ __decorate([
663
+ query('dialog')
664
+ ], PaymentModal.prototype, "dialogElement", void 0);
665
+ PaymentModal = __decorate([
666
+ customElement('payment-modal')
667
+ ], PaymentModal);
668
+ export { PaymentModal };
@@ -0,0 +1,18 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class PaymentTimer extends LitElement {
3
+ expiresAt: number;
4
+ private timerString;
5
+ private _interval;
6
+ connectedCallback(): void;
7
+ disconnectedCallback(): void;
8
+ updated(changedProperties: Map<string | number | symbol, unknown>): void;
9
+ private startTimer;
10
+ private stopTimer;
11
+ static styles: import("lit").CSSResult;
12
+ render(): import("lit").TemplateResult<1>;
13
+ }
14
+ declare global {
15
+ interface HTMLElementTagNameMap {
16
+ 'payment-timer': PaymentTimer;
17
+ }
18
+ }