@tickean/checkout-elements 0.2.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.
package/dist/index.js ADDED
@@ -0,0 +1,770 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TickeanBuyerVerification: () => TickeanBuyerVerification,
24
+ TickeanCheckout: () => TickeanCheckout,
25
+ TickeanDiscount: () => TickeanDiscount,
26
+ TickeanOrderSummary: () => TickeanOrderSummary,
27
+ TickeanPayment: () => TickeanPayment,
28
+ TickeanTicketSelector: () => TickeanTicketSelector,
29
+ applyAppearance: () => applyAppearance,
30
+ attachController: () => attachController,
31
+ defineTickeanElements: () => defineTickeanElements,
32
+ detachController: () => detachController,
33
+ getController: () => getController,
34
+ registerController: () => registerController,
35
+ unregisterController: () => unregisterController
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/context.ts
40
+ var CONTROLLER_ATTR = "controller-id";
41
+ var registry = /* @__PURE__ */ new Map();
42
+ var autoId = 0;
43
+ function registerController(controller, id) {
44
+ const key = id || `tickean_${++autoId}`;
45
+ registry.set(key, controller);
46
+ return key;
47
+ }
48
+ function unregisterController(id) {
49
+ registry.delete(id);
50
+ }
51
+ function getController(id) {
52
+ if (!id) return null;
53
+ return registry.get(id) || null;
54
+ }
55
+ function controllerAttr() {
56
+ return CONTROLLER_ATTR;
57
+ }
58
+ function emitTickeanEvent(el, name, detail) {
59
+ el.dispatchEvent(
60
+ new CustomEvent(name, {
61
+ detail,
62
+ bubbles: true,
63
+ composed: true
64
+ })
65
+ );
66
+ }
67
+ function formatMoney(amount, currency = "ARS", locale = "es-AR") {
68
+ try {
69
+ return new Intl.NumberFormat(locale, {
70
+ style: "currency",
71
+ currency,
72
+ maximumFractionDigits: 0
73
+ }).format(amount);
74
+ } catch {
75
+ return `$${amount}`;
76
+ }
77
+ }
78
+
79
+ // src/appearance.ts
80
+ var themeVars = {
81
+ default: {
82
+ "--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
83
+ "--tickean-bg": "#ffffff",
84
+ "--tickean-fg": "#111827",
85
+ "--tickean-muted": "#6b7280",
86
+ "--tickean-border": "#e5e7eb",
87
+ "--tickean-primary": "#111827",
88
+ "--tickean-primary-fg": "#ffffff",
89
+ "--tickean-danger": "#b91c1c",
90
+ "--tickean-radius": "10px",
91
+ "--tickean-space": "12px",
92
+ "--tickean-input-bg": "#ffffff"
93
+ },
94
+ flat: {
95
+ "--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
96
+ "--tickean-bg": "#f8fafc",
97
+ "--tickean-fg": "#0f172a",
98
+ "--tickean-muted": "#64748b",
99
+ "--tickean-border": "#cbd5e1",
100
+ "--tickean-primary": "#0f172a",
101
+ "--tickean-primary-fg": "#ffffff",
102
+ "--tickean-danger": "#dc2626",
103
+ "--tickean-radius": "0px",
104
+ "--tickean-space": "12px",
105
+ "--tickean-input-bg": "#ffffff"
106
+ },
107
+ night: {
108
+ "--tickean-font-family": "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
109
+ "--tickean-bg": "#0b1220",
110
+ "--tickean-fg": "#e5e7eb",
111
+ "--tickean-muted": "#9ca3af",
112
+ "--tickean-border": "#1f2937",
113
+ "--tickean-primary": "#e5e7eb",
114
+ "--tickean-primary-fg": "#0b1220",
115
+ "--tickean-danger": "#f87171",
116
+ "--tickean-radius": "10px",
117
+ "--tickean-space": "12px",
118
+ "--tickean-input-bg": "#111827"
119
+ }
120
+ };
121
+ var baseStyles = `
122
+ :host {
123
+ display: block;
124
+ color: var(--tickean-fg, #111827);
125
+ font-family: var(--tickean-font-family, ui-sans-serif, system-ui, sans-serif);
126
+ box-sizing: border-box;
127
+ }
128
+ *, *::before, *::after { box-sizing: border-box; }
129
+ .wrap {
130
+ background: var(--tickean-bg, #fff);
131
+ border: 1px solid var(--tickean-border, #e5e7eb);
132
+ border-radius: var(--tickean-radius, 10px);
133
+ padding: calc(var(--tickean-space, 12px) * 1.25);
134
+ }
135
+ .muted { color: var(--tickean-muted, #6b7280); font-size: 0.9rem; }
136
+ .danger { color: var(--tickean-danger, #b91c1c); }
137
+ .row { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
138
+ .stack { display: grid; gap: var(--tickean-space, 12px); }
139
+ .ticket {
140
+ border: 1px solid var(--tickean-border, #e5e7eb);
141
+ border-radius: var(--tickean-radius, 10px);
142
+ padding: var(--tickean-space, 12px);
143
+ }
144
+ button, input {
145
+ font: inherit;
146
+ }
147
+ button {
148
+ border: 1px solid var(--tickean-border, #e5e7eb);
149
+ background: var(--tickean-primary, #111827);
150
+ color: var(--tickean-primary-fg, #fff);
151
+ border-radius: calc(var(--tickean-radius, 10px) * 0.7);
152
+ padding: 8px 12px;
153
+ cursor: pointer;
154
+ }
155
+ button.secondary {
156
+ background: transparent;
157
+ color: var(--tickean-fg, #111827);
158
+ }
159
+ button:disabled { opacity: 0.55; cursor: not-allowed; }
160
+ input {
161
+ width: 100%;
162
+ border: 1px solid var(--tickean-border, #e5e7eb);
163
+ background: var(--tickean-input-bg, #fff);
164
+ color: var(--tickean-fg, #111827);
165
+ border-radius: calc(var(--tickean-radius, 10px) * 0.7);
166
+ padding: 8px 10px;
167
+ }
168
+ label { display: grid; gap: 4px; font-size: 0.85rem; }
169
+ .qty button { min-width: 36px; }
170
+ .total[aria-live] { font-weight: 700; font-size: 1.1rem; }
171
+ .provider-slot {
172
+ min-height: 48px;
173
+ border: 1px dashed var(--tickean-border, #e5e7eb);
174
+ border-radius: var(--tickean-radius, 10px);
175
+ padding: var(--tickean-space, 12px);
176
+ color: var(--tickean-muted, #6b7280);
177
+ }
178
+ `;
179
+ function applyAppearance(host, appearance) {
180
+ let parsed = {};
181
+ if (typeof appearance === "string") {
182
+ try {
183
+ parsed = JSON.parse(appearance);
184
+ } catch {
185
+ parsed = { theme: appearance };
186
+ }
187
+ } else if (appearance) {
188
+ parsed = appearance;
189
+ }
190
+ const theme = parsed.theme || "default";
191
+ if (theme !== "none") {
192
+ const vars = themeVars[theme] || themeVars.default;
193
+ for (const [key, value] of Object.entries(vars)) {
194
+ host.style.setProperty(key, value);
195
+ }
196
+ }
197
+ if (parsed.variables) {
198
+ for (const [key, value] of Object.entries(parsed.variables)) {
199
+ const prop = key.startsWith("--") ? key : `--tickean-${key}`;
200
+ host.style.setProperty(prop, value);
201
+ }
202
+ }
203
+ }
204
+
205
+ // src/i18n.ts
206
+ var catalogs = {
207
+ "es-AR": {
208
+ loading: "Cargando checkout\u2026",
209
+ tickets: "Entradas",
210
+ discount: "C\xF3digo de descuento",
211
+ apply: "Aplicar",
212
+ phone: "Tel\xE9fono",
213
+ name: "Nombre",
214
+ email: "Email",
215
+ otpCode: "C\xF3digo OTP",
216
+ sendOtp: "Enviar OTP",
217
+ verifyOtp: "Verificar",
218
+ total: "Total",
219
+ pay: "Confirmar compra",
220
+ transferInstructions: "Datos para transferir",
221
+ alias: "Alias",
222
+ cvu: "CVU",
223
+ amount: "Monto",
224
+ redirectPay: "Continuar al pago",
225
+ providerPlaceholder: "Complet\xE1 el pago con tu proveedor",
226
+ completed: "\xA1Compra completada!",
227
+ error: "Ocurri\xF3 un error",
228
+ emptyCart: "Eleg\xED al menos una entrada",
229
+ quantity: "Cantidad"
230
+ },
231
+ "es-CL": {
232
+ loading: "Cargando checkout\u2026",
233
+ tickets: "Entradas",
234
+ discount: "C\xF3digo de descuento",
235
+ apply: "Aplicar",
236
+ phone: "Tel\xE9fono",
237
+ name: "Nombre",
238
+ email: "Email",
239
+ otpCode: "C\xF3digo OTP",
240
+ sendOtp: "Enviar OTP",
241
+ verifyOtp: "Verificar",
242
+ total: "Total",
243
+ pay: "Confirmar compra",
244
+ transferInstructions: "Datos para transferir",
245
+ alias: "Alias",
246
+ cvu: "CVU",
247
+ amount: "Monto",
248
+ redirectPay: "Continuar al pago",
249
+ providerPlaceholder: "Completa el pago con tu proveedor",
250
+ completed: "\xA1Compra completada!",
251
+ error: "Ocurri\xF3 un error",
252
+ emptyCart: "Elige al menos una entrada",
253
+ quantity: "Cantidad"
254
+ },
255
+ en: {
256
+ loading: "Loading checkout\u2026",
257
+ tickets: "Tickets",
258
+ discount: "Discount code",
259
+ apply: "Apply",
260
+ phone: "Phone",
261
+ name: "Name",
262
+ email: "Email",
263
+ otpCode: "OTP code",
264
+ sendOtp: "Send OTP",
265
+ verifyOtp: "Verify",
266
+ total: "Total",
267
+ pay: "Confirm purchase",
268
+ transferInstructions: "Transfer details",
269
+ alias: "Alias",
270
+ cvu: "CVU",
271
+ amount: "Amount",
272
+ redirectPay: "Continue to payment",
273
+ providerPlaceholder: "Complete payment with your provider",
274
+ completed: "Purchase completed!",
275
+ error: "Something went wrong",
276
+ emptyCart: "Select at least one ticket",
277
+ quantity: "Quantity"
278
+ }
279
+ };
280
+ function t(locale, key) {
281
+ const resolved = locale && catalogs[locale] ? locale : "es-AR";
282
+ return catalogs[resolved][key];
283
+ }
284
+ function resolveLocale(locale) {
285
+ if (locale && catalogs[locale]) return locale;
286
+ return "es-AR";
287
+ }
288
+
289
+ // src/base.ts
290
+ var NativeHTMLElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
291
+ attachShadow() {
292
+ return { innerHTML: "" };
293
+ }
294
+ };
295
+ var TickeanElementBase = class extends NativeHTMLElement {
296
+ constructor() {
297
+ super();
298
+ this.unsubscribe = null;
299
+ this.state = null;
300
+ this.lastPhase = null;
301
+ this.lastErrorCode = null;
302
+ this.root = this.attachShadow({ mode: "open" });
303
+ }
304
+ static get observedAttributes() {
305
+ return ["appearance", "locale", controllerAttr()];
306
+ }
307
+ connectedCallback() {
308
+ this.applyTheme();
309
+ this.bindController();
310
+ this.render();
311
+ }
312
+ disconnectedCallback() {
313
+ this.unsubscribe?.();
314
+ this.unsubscribe = null;
315
+ }
316
+ attributeChangedCallback(_name, _oldValue, _newValue) {
317
+ this.applyTheme();
318
+ this.bindController();
319
+ this.render();
320
+ }
321
+ get elementLocale() {
322
+ return resolveLocale(this.getAttribute("locale"));
323
+ }
324
+ get controller() {
325
+ return getController(this.getAttribute(controllerAttr()));
326
+ }
327
+ applyTheme() {
328
+ applyAppearance(this, this.getAttribute("appearance") || void 0);
329
+ }
330
+ bindController() {
331
+ this.unsubscribe?.();
332
+ this.unsubscribe = null;
333
+ const controller = this.controller;
334
+ if (!controller) return;
335
+ const onChange = (snapshot) => {
336
+ this.state = snapshot;
337
+ this.render();
338
+ emitTickeanEvent(this, "change", { state: snapshot });
339
+ if (snapshot.error && snapshot.error.code !== this.lastErrorCode) {
340
+ this.lastErrorCode = snapshot.error.code;
341
+ emitTickeanEvent(this, "error", { error: snapshot.error });
342
+ }
343
+ if (snapshot.phase === "completed" && this.lastPhase !== "completed") {
344
+ emitTickeanEvent(this, "complete", { state: snapshot });
345
+ }
346
+ if (this.lastPhase === "initializing" && snapshot.phase !== "initializing" && !snapshot.loading) {
347
+ emitTickeanEvent(this, "ready", { state: snapshot });
348
+ }
349
+ this.lastPhase = snapshot.phase;
350
+ };
351
+ this.state = controller.getSnapshot();
352
+ this.unsubscribe = controller.subscribe(onChange);
353
+ void controller.ready.then(() => {
354
+ const snap = controller.getSnapshot();
355
+ onChange(snap);
356
+ });
357
+ }
358
+ styles() {
359
+ return baseStyles;
360
+ }
361
+ render() {
362
+ this.root.innerHTML = `<style>${this.styles()}</style>${this.renderBody()}`;
363
+ this.afterRender();
364
+ }
365
+ afterRender() {
366
+ }
367
+ money(amount) {
368
+ const currency = this.state?.event?.shows?.[0]?.showOptions?.[0]?.currency || "ARS";
369
+ return formatMoney(amount, currency, this.elementLocale);
370
+ }
371
+ };
372
+
373
+ // src/elements/ticket-selector.ts
374
+ var TickeanTicketSelector = class extends TickeanElementBase {
375
+ renderBody() {
376
+ const state = this.state;
377
+ if (!state || state.loading) {
378
+ return `<div class="wrap muted">${t(this.elementLocale, "loading")}</div>`;
379
+ }
380
+ const options = (state.event?.shows || []).flatMap(
381
+ (show) => (show.showOptions || []).map((opt) => ({ ...opt, showTitle: show.title }))
382
+ );
383
+ return `
384
+ <div class="wrap stack" part="ticket-selector">
385
+ <strong>${t(this.elementLocale, "tickets")}</strong>
386
+ ${options.map((opt) => {
387
+ const qty = state.cart.find((c) => c.showOptionId === opt.id)?.amount || 0;
388
+ return `
389
+ <div class="ticket" data-option-id="${opt.id}">
390
+ <div class="row">
391
+ <div>
392
+ <div><strong>${escapeHtml(opt.name || opt.id)}</strong></div>
393
+ <div class="muted">${escapeHtml(opt.showTitle || "")}</div>
394
+ </div>
395
+ <div>${this.money(Number(opt.price || 0))}</div>
396
+ </div>
397
+ <div class="row qty" style="margin-top:10px" role="group" aria-label="${t(this.elementLocale, "quantity")} ${escapeHtml(opt.name || "")}">
398
+ <button type="button" class="secondary" data-action="dec" aria-label="-">\u2212</button>
399
+ <span aria-live="polite">${qty}</span>
400
+ <button type="button" class="secondary" data-action="inc" aria-label="+">+</button>
401
+ </div>
402
+ </div>
403
+ `;
404
+ }).join("")}
405
+ </div>
406
+ `;
407
+ }
408
+ afterRender() {
409
+ this.root.querySelectorAll("[data-option-id]").forEach((el) => {
410
+ const optionId = el.getAttribute("data-option-id");
411
+ const qty = this.state?.cart.find((c) => c.showOptionId === optionId)?.amount || 0;
412
+ el.querySelector('[data-action="dec"]')?.addEventListener("click", () => {
413
+ this.controller?.setCartItem(optionId, Math.max(0, qty - 1));
414
+ });
415
+ el.querySelector('[data-action="inc"]')?.addEventListener("click", () => {
416
+ this.controller?.setCartItem(optionId, qty + 1);
417
+ });
418
+ });
419
+ }
420
+ };
421
+ function escapeHtml(value) {
422
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
423
+ }
424
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-ticket-selector")) {
425
+ customElements.define("tickean-ticket-selector", TickeanTicketSelector);
426
+ }
427
+
428
+ // src/elements/discount.ts
429
+ var TickeanDiscount = class extends TickeanElementBase {
430
+ renderBody() {
431
+ const code = this.state?.discountCode || "";
432
+ return `
433
+ <div class="wrap stack" part="discount">
434
+ <label>
435
+ ${t(this.elementLocale, "discount")}
436
+ <input id="code" value="${escapeAttr(code)}" autocomplete="off" />
437
+ </label>
438
+ <button type="button" id="apply">${t(this.elementLocale, "apply")}</button>
439
+ ${this.state?.error ? `<div class="danger" role="alert">${escapeHtml2(this.state.error.message)}</div>` : ""}
440
+ </div>
441
+ `;
442
+ }
443
+ afterRender() {
444
+ const input = this.root.querySelector("#code");
445
+ this.root.querySelector("#apply")?.addEventListener("click", async () => {
446
+ const value = input?.value?.trim() || "";
447
+ try {
448
+ await this.controller?.applyDiscountCode(value);
449
+ } catch {
450
+ }
451
+ });
452
+ }
453
+ };
454
+ function escapeAttr(value) {
455
+ return value.replace(/"/g, "&quot;");
456
+ }
457
+ function escapeHtml2(value) {
458
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
459
+ }
460
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-discount")) {
461
+ customElements.define("tickean-discount", TickeanDiscount);
462
+ }
463
+
464
+ // src/elements/buyer-verification.ts
465
+ var TickeanBuyerVerification = class extends TickeanElementBase {
466
+ constructor() {
467
+ super(...arguments);
468
+ this.phone = "+5491112345678";
469
+ this.name = "";
470
+ this.email = "";
471
+ this.code = "";
472
+ }
473
+ renderBody() {
474
+ const verified = this.state?.buyerVerified;
475
+ return `
476
+ <div class="wrap stack" part="buyer-verification">
477
+ <label>
478
+ ${t(this.elementLocale, "phone")}
479
+ <input id="phone" type="tel" value="${escapeAttr2(this.phone)}" autocomplete="tel" />
480
+ </label>
481
+ <label>
482
+ ${t(this.elementLocale, "name")}
483
+ <input id="name" value="${escapeAttr2(this.name)}" autocomplete="name" />
484
+ </label>
485
+ <label>
486
+ ${t(this.elementLocale, "email")}
487
+ <input id="email" type="email" value="${escapeAttr2(this.email)}" autocomplete="email" />
488
+ </label>
489
+ <button type="button" class="secondary" id="send">${t(this.elementLocale, "sendOtp")}</button>
490
+ <label>
491
+ ${t(this.elementLocale, "otpCode")}
492
+ <input id="otp" inputmode="numeric" value="${escapeAttr2(this.code)}" autocomplete="one-time-code" />
493
+ </label>
494
+ <button type="button" id="verify">${t(this.elementLocale, "verifyOtp")}</button>
495
+ ${verified ? `<div class="muted" role="status">${escapeHtml3(this.state?.buyer?.name || this.state?.buyer?.phone || "OK")}</div>` : ""}
496
+ </div>
497
+ `;
498
+ }
499
+ afterRender() {
500
+ const phone = this.root.querySelector("#phone");
501
+ const name = this.root.querySelector("#name");
502
+ const email = this.root.querySelector("#email");
503
+ const otp = this.root.querySelector("#otp");
504
+ const sync = () => {
505
+ this.phone = phone?.value || "";
506
+ this.name = name?.value || "";
507
+ this.email = email?.value || "";
508
+ this.code = otp?.value || "";
509
+ };
510
+ this.root.querySelector("#send")?.addEventListener("click", async () => {
511
+ sync();
512
+ await this.controller?.sendOtp(this.phone);
513
+ });
514
+ this.root.querySelector("#verify")?.addEventListener("click", async () => {
515
+ sync();
516
+ await this.controller?.verifyOtp({
517
+ phone: this.phone,
518
+ code: this.code,
519
+ name: this.name || void 0,
520
+ email: this.email || void 0
521
+ });
522
+ });
523
+ }
524
+ };
525
+ function escapeAttr2(value) {
526
+ return value.replace(/"/g, "&quot;");
527
+ }
528
+ function escapeHtml3(value) {
529
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
530
+ }
531
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-buyer-verification")) {
532
+ if (typeof customElements !== "undefined") customElements.define("tickean-buyer-verification", TickeanBuyerVerification);
533
+ }
534
+
535
+ // src/elements/payment.ts
536
+ var TickeanPayment = class extends TickeanElementBase {
537
+ static get observedAttributes() {
538
+ return [...TickeanElementBase.observedAttributes, "payment-method", "currency"];
539
+ }
540
+ renderBody() {
541
+ const state = this.state;
542
+ const nextAction = state?.nextAction || { type: "none" };
543
+ const paymentMethod = this.getAttribute("payment-method") || state?.event?.availablePaymentMethods?.[0] || "TRANSFER";
544
+ const currency = this.getAttribute("currency") || "ARS";
545
+ if (state?.phase === "completed") {
546
+ return `<div class="wrap" part="payment"><div role="status">${t(this.elementLocale, "completed")}</div></div>`;
547
+ }
548
+ return `
549
+ <div class="wrap stack" part="payment">
550
+ ${renderNextAction(nextAction, this.elementLocale, (n) => this.money(n))}
551
+ ${!state?.purchase ? `<button type="button" id="pay" ${!state?.cart.length || !state?.buyerVerified ? "disabled" : ""}>${t(this.elementLocale, "pay")}</button>` : ""}
552
+ <div data-payment-method="${escapeAttr3(paymentMethod)}" data-currency="${escapeAttr3(currency)}" hidden></div>
553
+ </div>
554
+ `;
555
+ }
556
+ afterRender() {
557
+ const paymentMethod = this.getAttribute("payment-method") || this.state?.event?.availablePaymentMethods?.[0] || "TRANSFER";
558
+ const currency = this.getAttribute("currency") || "ARS";
559
+ this.root.querySelector("#pay")?.addEventListener("click", async () => {
560
+ await this.controller?.purchaseAndPay({
561
+ paymentMethod,
562
+ currency
563
+ });
564
+ });
565
+ this.root.querySelector("#redirect")?.addEventListener("click", () => {
566
+ const url = this.state?.nextAction?.url;
567
+ if (url) window.location.assign(url);
568
+ });
569
+ }
570
+ };
571
+ function renderNextAction(nextAction, locale, money) {
572
+ switch (nextAction.type) {
573
+ case "display_instructions": {
574
+ const instructions = nextAction.paymentInstructions || {};
575
+ return `
576
+ <div>
577
+ <strong>${t(locale, "transferInstructions")}</strong>
578
+ <div class="stack" style="margin-top:8px">
579
+ ${instructions.alias ? `<div><span class="muted">${t(locale, "alias")}</span>: <code>${escapeHtml4(String(instructions.alias))}</code></div>` : ""}
580
+ ${instructions.cvu ? `<div><span class="muted">${t(locale, "cvu")}</span>: <code>${escapeHtml4(String(instructions.cvu))}</code></div>` : ""}
581
+ ${instructions.amount != null ? `<div><span class="muted">${t(locale, "amount")}</span>: ${money(Number(instructions.amount))}</div>` : ""}
582
+ </div>
583
+ </div>
584
+ `;
585
+ }
586
+ case "redirect":
587
+ return `<button type="button" id="redirect">${t(locale, "redirectPay")}</button>`;
588
+ case "stripe_elements":
589
+ return `<div class="provider-slot" data-provider="stripe" part="stripe-mount">${t(locale, "providerPlaceholder")} (Stripe)</div>`;
590
+ case "airwallex_dropin":
591
+ return `<div class="provider-slot" data-provider="airwallex" part="airwallex-mount">${t(locale, "providerPlaceholder")} (Airwallex)</div>`;
592
+ case "dlocal_fields":
593
+ return `<div class="provider-slot" data-provider="dlocal" part="dlocal-mount">${t(locale, "providerPlaceholder")} (dLocal)</div>`;
594
+ case "fintoc_widget":
595
+ return `<div class="provider-slot" data-provider="fintoc" part="fintoc-mount">${t(locale, "providerPlaceholder")} (Fintoc)</div>`;
596
+ default:
597
+ return "";
598
+ }
599
+ }
600
+ function escapeAttr3(value) {
601
+ return value.replace(/"/g, "&quot;");
602
+ }
603
+ function escapeHtml4(value) {
604
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
605
+ }
606
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-payment")) {
607
+ customElements.define("tickean-payment", TickeanPayment);
608
+ }
609
+
610
+ // src/elements/order-summary.ts
611
+ var TickeanOrderSummary = class extends TickeanElementBase {
612
+ renderBody() {
613
+ const total = this.state?.quote?.totalPrice || 0;
614
+ const items = this.state?.cart || [];
615
+ return `
616
+ <div class="wrap stack" part="order-summary">
617
+ <div class="row">
618
+ <span>${t(this.elementLocale, "total")}</span>
619
+ <span class="total" aria-live="polite">${this.money(total)}</span>
620
+ </div>
621
+ ${items.length === 0 ? `<div class="muted">${t(this.elementLocale, "emptyCart")}</div>` : `<div class="muted">${items.length} item(s)</div>`}
622
+ ${this.state?.isQuoting ? `<div class="muted">${t(this.elementLocale, "loading")}</div>` : ""}
623
+ </div>
624
+ `;
625
+ }
626
+ };
627
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-order-summary")) {
628
+ if (typeof customElements !== "undefined") customElements.define("tickean-order-summary", TickeanOrderSummary);
629
+ }
630
+
631
+ // src/elements/checkout.ts
632
+ var import_checkout_js = require("@tickean/checkout-js");
633
+ var TickeanCheckout = class extends TickeanElementBase {
634
+ constructor() {
635
+ super(...arguments);
636
+ this.ownedControllerId = null;
637
+ this.ownedController = null;
638
+ }
639
+ static get observedAttributes() {
640
+ return [
641
+ ...TickeanElementBase.observedAttributes,
642
+ "publishable-key",
643
+ "event-slug",
644
+ "api-base-url",
645
+ "return-url",
646
+ "demo",
647
+ "payment-method",
648
+ "currency"
649
+ ];
650
+ }
651
+ connectedCallback() {
652
+ this.ensureController();
653
+ super.connectedCallback();
654
+ }
655
+ disconnectedCallback() {
656
+ super.disconnectedCallback();
657
+ if (this.ownedControllerId) {
658
+ this.ownedController?.dispose();
659
+ unregisterController(this.ownedControllerId);
660
+ this.ownedControllerId = null;
661
+ this.ownedController = null;
662
+ }
663
+ }
664
+ attributeChangedCallback(name, oldValue, newValue) {
665
+ if (["publishable-key", "event-slug", "api-base-url", "return-url", "demo"].includes(
666
+ name
667
+ ) && oldValue !== newValue) {
668
+ if (this.ownedControllerId) {
669
+ this.ownedController?.dispose();
670
+ unregisterController(this.ownedControllerId);
671
+ this.ownedControllerId = null;
672
+ this.ownedController = null;
673
+ }
674
+ this.ensureController();
675
+ }
676
+ super.attributeChangedCallback(name, oldValue, newValue);
677
+ }
678
+ ensureController() {
679
+ if (this.getAttribute(controllerAttr())) return;
680
+ const eventSlug = this.getAttribute("event-slug");
681
+ const publishableKey = this.getAttribute("publishable-key") || "";
682
+ if (!eventSlug) return;
683
+ const options = {
684
+ publishableKey,
685
+ eventSlug,
686
+ apiBaseUrl: this.getAttribute("api-base-url") || void 0,
687
+ returnUrl: this.getAttribute("return-url") || void 0,
688
+ demo: this.hasAttribute("demo"),
689
+ persistence: false
690
+ };
691
+ this.ownedController = (0, import_checkout_js.createCheckoutController)(options);
692
+ this.ownedControllerId = registerController(this.ownedController);
693
+ this.setAttribute(controllerAttr(), this.ownedControllerId);
694
+ }
695
+ renderBody() {
696
+ const id = this.getAttribute(controllerAttr()) || "";
697
+ const locale = this.getAttribute("locale") || "es-AR";
698
+ const appearance = this.getAttribute("appearance") || "default";
699
+ const paymentMethod = this.getAttribute("payment-method") || "TRANSFER";
700
+ const currency = this.getAttribute("currency") || "ARS";
701
+ const state = this.state;
702
+ if (!state || state.loading) {
703
+ return `<div class="wrap muted">${t(this.elementLocale, "loading")}</div>`;
704
+ }
705
+ return `
706
+ <div class="stack" part="checkout">
707
+ <h2 style="margin:0">${escapeHtml5(state.event?.title || "")}</h2>
708
+ <p class="muted" style="margin:0">${escapeHtml5(state.event?.description || "")}</p>
709
+ <tickean-ticket-selector controller-id="${escapeAttr4(id)}" locale="${escapeAttr4(locale)}" appearance="${escapeAttr4(appearance)}"></tickean-ticket-selector>
710
+ <tickean-discount controller-id="${escapeAttr4(id)}" locale="${escapeAttr4(locale)}" appearance="${escapeAttr4(appearance)}"></tickean-discount>
711
+ <tickean-buyer-verification controller-id="${escapeAttr4(id)}" locale="${escapeAttr4(locale)}" appearance="${escapeAttr4(appearance)}"></tickean-buyer-verification>
712
+ <tickean-order-summary controller-id="${escapeAttr4(id)}" locale="${escapeAttr4(locale)}" appearance="${escapeAttr4(appearance)}"></tickean-order-summary>
713
+ <tickean-payment controller-id="${escapeAttr4(id)}" locale="${escapeAttr4(locale)}" appearance="${escapeAttr4(appearance)}" payment-method="${escapeAttr4(paymentMethod)}" currency="${escapeAttr4(currency)}"></tickean-payment>
714
+ ${state.error ? `<div class="danger" role="alert">${escapeHtml5(state.error.message)}</div>` : ""}
715
+ </div>
716
+ `;
717
+ }
718
+ };
719
+ function escapeAttr4(value) {
720
+ return value.replace(/"/g, "&quot;");
721
+ }
722
+ function escapeHtml5(value) {
723
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
724
+ }
725
+ if (typeof customElements !== "undefined" && !customElements.get("tickean-checkout")) {
726
+ customElements.define("tickean-checkout", TickeanCheckout);
727
+ }
728
+
729
+ // src/index.ts
730
+ var ELEMENT_TAGS = [
731
+ "tickean-checkout",
732
+ "tickean-ticket-selector",
733
+ "tickean-discount",
734
+ "tickean-buyer-verification",
735
+ "tickean-payment",
736
+ "tickean-order-summary"
737
+ ];
738
+ function defineTickeanElements() {
739
+ if (typeof customElements === "undefined") return;
740
+ for (const tag of ELEMENT_TAGS) {
741
+ if (!customElements.get(tag)) {
742
+ void tag;
743
+ }
744
+ }
745
+ }
746
+ function attachController(el, controller, id) {
747
+ const controllerId = registerController(controller, id);
748
+ el.setAttribute("controller-id", controllerId);
749
+ return controllerId;
750
+ }
751
+ function detachController(id) {
752
+ unregisterController(id);
753
+ }
754
+ defineTickeanElements();
755
+ // Annotate the CommonJS export names for ESM import in node:
756
+ 0 && (module.exports = {
757
+ TickeanBuyerVerification,
758
+ TickeanCheckout,
759
+ TickeanDiscount,
760
+ TickeanOrderSummary,
761
+ TickeanPayment,
762
+ TickeanTicketSelector,
763
+ applyAppearance,
764
+ attachController,
765
+ defineTickeanElements,
766
+ detachController,
767
+ getController,
768
+ registerController,
769
+ unregisterController
770
+ });