@wodira/browser 0.1.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,3309 @@
1
+ // ../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/development/css-tag.js
2
+ var NODE_MODE = false;
3
+ var global = globalThis;
4
+ var supportsAdoptingStyleSheets = global.ShadowRoot && (global.ShadyCSS === undefined || global.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype;
5
+ var constructionToken = Symbol();
6
+ var cssTagCache = new WeakMap;
7
+
8
+ class CSSResult {
9
+ constructor(cssText, strings, safeToken) {
10
+ this["_$cssResult$"] = true;
11
+ if (safeToken !== constructionToken) {
12
+ throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
13
+ }
14
+ this.cssText = cssText;
15
+ this._strings = strings;
16
+ }
17
+ get styleSheet() {
18
+ let styleSheet = this._styleSheet;
19
+ const strings = this._strings;
20
+ if (supportsAdoptingStyleSheets && styleSheet === undefined) {
21
+ const cacheable = strings !== undefined && strings.length === 1;
22
+ if (cacheable) {
23
+ styleSheet = cssTagCache.get(strings);
24
+ }
25
+ if (styleSheet === undefined) {
26
+ (this._styleSheet = styleSheet = new CSSStyleSheet).replaceSync(this.cssText);
27
+ if (cacheable) {
28
+ cssTagCache.set(strings, styleSheet);
29
+ }
30
+ }
31
+ }
32
+ return styleSheet;
33
+ }
34
+ toString() {
35
+ return this.cssText;
36
+ }
37
+ }
38
+ var textFromCSSResult = (value) => {
39
+ if (value["_$cssResult$"] === true) {
40
+ return value.cssText;
41
+ } else if (typeof value === "number") {
42
+ return value;
43
+ } else {
44
+ throw new Error(`Value passed to 'css' function must be a 'css' function result: ` + `${value}. Use 'unsafeCSS' to pass non-literal values, but take care ` + `to ensure page security.`);
45
+ }
46
+ };
47
+ var unsafeCSS = (value) => new CSSResult(typeof value === "string" ? value : String(value), undefined, constructionToken);
48
+ var css = (strings, ...values) => {
49
+ const cssText = strings.length === 1 ? strings[0] : values.reduce((acc, v, idx) => acc + textFromCSSResult(v) + strings[idx + 1], strings[0]);
50
+ return new CSSResult(cssText, strings, constructionToken);
51
+ };
52
+ var adoptStyles = (renderRoot, styles) => {
53
+ if (supportsAdoptingStyleSheets) {
54
+ renderRoot.adoptedStyleSheets = styles.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet);
55
+ } else {
56
+ for (const s of styles) {
57
+ const style = document.createElement("style");
58
+ const nonce = global["litNonce"];
59
+ if (nonce !== undefined) {
60
+ style.setAttribute("nonce", nonce);
61
+ }
62
+ style.textContent = s.cssText;
63
+ renderRoot.appendChild(style);
64
+ }
65
+ }
66
+ };
67
+ var cssResultFromStyleSheet = (sheet) => {
68
+ let cssText = "";
69
+ for (const rule of sheet.cssRules) {
70
+ cssText += rule.cssText;
71
+ }
72
+ return unsafeCSS(cssText);
73
+ };
74
+ var getCompatibleStyle = supportsAdoptingStyleSheets || NODE_MODE && global.CSSStyleSheet === undefined ? (s) => s : (s) => s instanceof CSSStyleSheet ? cssResultFromStyleSheet(s) : s;
75
+
76
+ // ../../node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/development/reactive-element.js
77
+ var { is, defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols, getPrototypeOf } = Object;
78
+ var NODE_MODE2 = false;
79
+ var global2 = globalThis;
80
+ if (NODE_MODE2) {
81
+ global2.customElements ??= customElements;
82
+ }
83
+ var DEV_MODE = true;
84
+ var issueWarning;
85
+ var trustedTypes = global2.trustedTypes;
86
+ var emptyStringForBooleanAttribute = trustedTypes ? trustedTypes.emptyScript : "";
87
+ var polyfillSupport = DEV_MODE ? global2.reactiveElementPolyfillSupportDevMode : global2.reactiveElementPolyfillSupport;
88
+ if (DEV_MODE) {
89
+ global2.litIssuedWarnings ??= new Set;
90
+ issueWarning = (code, warning) => {
91
+ warning += ` See https://lit.dev/msg/${code} for more information.`;
92
+ if (!global2.litIssuedWarnings.has(warning) && !global2.litIssuedWarnings.has(code)) {
93
+ console.warn(warning);
94
+ global2.litIssuedWarnings.add(warning);
95
+ }
96
+ };
97
+ queueMicrotask(() => {
98
+ issueWarning("dev-mode", `Lit is in dev mode. Not recommended for production!`);
99
+ if (global2.ShadyDOM?.inUse && polyfillSupport === undefined) {
100
+ issueWarning("polyfill-support-missing", `Shadow DOM is being polyfilled via \`ShadyDOM\` but ` + `the \`polyfill-support\` module has not been loaded.`);
101
+ }
102
+ });
103
+ }
104
+ var debugLogEvent = DEV_MODE ? (event) => {
105
+ const shouldEmit = global2.emitLitDebugLogEvents;
106
+ if (!shouldEmit) {
107
+ return;
108
+ }
109
+ global2.dispatchEvent(new CustomEvent("lit-debug", {
110
+ detail: event
111
+ }));
112
+ } : undefined;
113
+ var JSCompiler_renameProperty = (prop, _obj) => prop;
114
+ var defaultConverter = {
115
+ toAttribute(value, type) {
116
+ switch (type) {
117
+ case Boolean:
118
+ value = value ? emptyStringForBooleanAttribute : null;
119
+ break;
120
+ case Object:
121
+ case Array:
122
+ value = value == null ? value : JSON.stringify(value);
123
+ break;
124
+ }
125
+ return value;
126
+ },
127
+ fromAttribute(value, type) {
128
+ let fromValue = value;
129
+ switch (type) {
130
+ case Boolean:
131
+ fromValue = value !== null;
132
+ break;
133
+ case Number:
134
+ fromValue = value === null ? null : Number(value);
135
+ break;
136
+ case Object:
137
+ case Array:
138
+ try {
139
+ fromValue = JSON.parse(value);
140
+ } catch (e) {
141
+ fromValue = null;
142
+ }
143
+ break;
144
+ }
145
+ return fromValue;
146
+ }
147
+ };
148
+ var notEqual = (value, old) => !is(value, old);
149
+ var defaultPropertyDeclaration = {
150
+ attribute: true,
151
+ type: String,
152
+ converter: defaultConverter,
153
+ reflect: false,
154
+ useDefault: false,
155
+ hasChanged: notEqual
156
+ };
157
+ Symbol.metadata ??= Symbol("metadata");
158
+ global2.litPropertyMetadata ??= new WeakMap;
159
+
160
+ class ReactiveElement extends HTMLElement {
161
+ static addInitializer(initializer) {
162
+ this.__prepare();
163
+ (this._initializers ??= []).push(initializer);
164
+ }
165
+ static get observedAttributes() {
166
+ this.finalize();
167
+ return this.__attributeToPropertyMap && [...this.__attributeToPropertyMap.keys()];
168
+ }
169
+ static createProperty(name, options = defaultPropertyDeclaration) {
170
+ if (options.state) {
171
+ options.attribute = false;
172
+ }
173
+ this.__prepare();
174
+ if (this.prototype.hasOwnProperty(name)) {
175
+ options = Object.create(options);
176
+ options.wrapped = true;
177
+ }
178
+ this.elementProperties.set(name, options);
179
+ if (!options.noAccessor) {
180
+ const key = DEV_MODE ? Symbol.for(`${String(name)} (@property() cache)`) : Symbol();
181
+ const descriptor = this.getPropertyDescriptor(name, key, options);
182
+ if (descriptor !== undefined) {
183
+ defineProperty(this.prototype, name, descriptor);
184
+ }
185
+ }
186
+ }
187
+ static getPropertyDescriptor(name, key, options) {
188
+ const { get, set } = getOwnPropertyDescriptor(this.prototype, name) ?? {
189
+ get() {
190
+ return this[key];
191
+ },
192
+ set(v) {
193
+ this[key] = v;
194
+ }
195
+ };
196
+ if (DEV_MODE && get == null) {
197
+ if ("value" in (getOwnPropertyDescriptor(this.prototype, name) ?? {})) {
198
+ throw new Error(`Field ${JSON.stringify(String(name))} on ` + `${this.name} was declared as a reactive property ` + `but it's actually declared as a value on the prototype. ` + `Usually this is due to using @property or @state on a method.`);
199
+ }
200
+ issueWarning("reactive-property-without-getter", `Field ${JSON.stringify(String(name))} on ` + `${this.name} was declared as a reactive property ` + `but it does not have a getter. This will be an error in a ` + `future version of Lit.`);
201
+ }
202
+ return {
203
+ get,
204
+ set(value) {
205
+ const oldValue = get?.call(this);
206
+ set?.call(this, value);
207
+ this.requestUpdate(name, oldValue, options);
208
+ },
209
+ configurable: true,
210
+ enumerable: true
211
+ };
212
+ }
213
+ static getPropertyOptions(name) {
214
+ return this.elementProperties.get(name) ?? defaultPropertyDeclaration;
215
+ }
216
+ static __prepare() {
217
+ if (this.hasOwnProperty(JSCompiler_renameProperty("elementProperties", this))) {
218
+ return;
219
+ }
220
+ const superCtor = getPrototypeOf(this);
221
+ superCtor.finalize();
222
+ if (superCtor._initializers !== undefined) {
223
+ this._initializers = [...superCtor._initializers];
224
+ }
225
+ this.elementProperties = new Map(superCtor.elementProperties);
226
+ }
227
+ static finalize() {
228
+ if (this.hasOwnProperty(JSCompiler_renameProperty("finalized", this))) {
229
+ return;
230
+ }
231
+ this.finalized = true;
232
+ this.__prepare();
233
+ if (this.hasOwnProperty(JSCompiler_renameProperty("properties", this))) {
234
+ const props = this.properties;
235
+ const propKeys = [
236
+ ...getOwnPropertyNames(props),
237
+ ...getOwnPropertySymbols(props)
238
+ ];
239
+ for (const p of propKeys) {
240
+ this.createProperty(p, props[p]);
241
+ }
242
+ }
243
+ const metadata = this[Symbol.metadata];
244
+ if (metadata !== null) {
245
+ const properties = litPropertyMetadata.get(metadata);
246
+ if (properties !== undefined) {
247
+ for (const [p, options] of properties) {
248
+ this.elementProperties.set(p, options);
249
+ }
250
+ }
251
+ }
252
+ this.__attributeToPropertyMap = new Map;
253
+ for (const [p, options] of this.elementProperties) {
254
+ const attr = this.__attributeNameForProperty(p, options);
255
+ if (attr !== undefined) {
256
+ this.__attributeToPropertyMap.set(attr, p);
257
+ }
258
+ }
259
+ this.elementStyles = this.finalizeStyles(this.styles);
260
+ if (DEV_MODE) {
261
+ if (this.hasOwnProperty("createProperty")) {
262
+ issueWarning("no-override-create-property", "Overriding ReactiveElement.createProperty() is deprecated. " + "The override will not be called with standard decorators");
263
+ }
264
+ if (this.hasOwnProperty("getPropertyDescriptor")) {
265
+ issueWarning("no-override-get-property-descriptor", "Overriding ReactiveElement.getPropertyDescriptor() is deprecated. " + "The override will not be called with standard decorators");
266
+ }
267
+ }
268
+ }
269
+ static finalizeStyles(styles) {
270
+ const elementStyles = [];
271
+ if (Array.isArray(styles)) {
272
+ const set = new Set(styles.flat(Infinity).reverse());
273
+ for (const s of set) {
274
+ elementStyles.unshift(getCompatibleStyle(s));
275
+ }
276
+ } else if (styles !== undefined) {
277
+ elementStyles.push(getCompatibleStyle(styles));
278
+ }
279
+ return elementStyles;
280
+ }
281
+ static __attributeNameForProperty(name, options) {
282
+ const attribute = options.attribute;
283
+ return attribute === false ? undefined : typeof attribute === "string" ? attribute : typeof name === "string" ? name.toLowerCase() : undefined;
284
+ }
285
+ constructor() {
286
+ super();
287
+ this.__instanceProperties = undefined;
288
+ this.isUpdatePending = false;
289
+ this.hasUpdated = false;
290
+ this.__reflectingProperty = null;
291
+ this.__initialize();
292
+ }
293
+ __initialize() {
294
+ this.__updatePromise = new Promise((res) => this.enableUpdating = res);
295
+ this._$changedProperties = new Map;
296
+ this.__saveInstanceProperties();
297
+ this.requestUpdate();
298
+ this.constructor._initializers?.forEach((i) => i(this));
299
+ }
300
+ addController(controller) {
301
+ (this.__controllers ??= new Set).add(controller);
302
+ if (this.renderRoot !== undefined && this.isConnected) {
303
+ controller.hostConnected?.();
304
+ }
305
+ }
306
+ removeController(controller) {
307
+ this.__controllers?.delete(controller);
308
+ }
309
+ __saveInstanceProperties() {
310
+ const instanceProperties = new Map;
311
+ const elementProperties = this.constructor.elementProperties;
312
+ for (const p of elementProperties.keys()) {
313
+ if (this.hasOwnProperty(p)) {
314
+ instanceProperties.set(p, this[p]);
315
+ delete this[p];
316
+ }
317
+ }
318
+ if (instanceProperties.size > 0) {
319
+ this.__instanceProperties = instanceProperties;
320
+ }
321
+ }
322
+ createRenderRoot() {
323
+ const renderRoot = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
324
+ adoptStyles(renderRoot, this.constructor.elementStyles);
325
+ return renderRoot;
326
+ }
327
+ connectedCallback() {
328
+ this.renderRoot ??= this.createRenderRoot();
329
+ this.enableUpdating(true);
330
+ this.__controllers?.forEach((c) => c.hostConnected?.());
331
+ }
332
+ enableUpdating(_requestedUpdate) {}
333
+ disconnectedCallback() {
334
+ this.__controllers?.forEach((c) => c.hostDisconnected?.());
335
+ }
336
+ attributeChangedCallback(name, _old, value) {
337
+ this._$attributeToProperty(name, value);
338
+ }
339
+ __propertyToAttribute(name, value) {
340
+ const elemProperties = this.constructor.elementProperties;
341
+ const options = elemProperties.get(name);
342
+ const attr = this.constructor.__attributeNameForProperty(name, options);
343
+ if (attr !== undefined && options.reflect === true) {
344
+ const converter = options.converter?.toAttribute !== undefined ? options.converter : defaultConverter;
345
+ const attrValue = converter.toAttribute(value, options.type);
346
+ if (DEV_MODE && this.constructor.enabledWarnings.includes("migration") && attrValue === undefined) {
347
+ issueWarning("undefined-attribute-value", `The attribute value for the ${name} property is ` + `undefined on element ${this.localName}. The attribute will be ` + `removed, but in the previous version of \`ReactiveElement\`, ` + `the attribute would not have changed.`);
348
+ }
349
+ this.__reflectingProperty = name;
350
+ if (attrValue == null) {
351
+ this.removeAttribute(attr);
352
+ } else {
353
+ this.setAttribute(attr, attrValue);
354
+ }
355
+ this.__reflectingProperty = null;
356
+ }
357
+ }
358
+ _$attributeToProperty(name, value) {
359
+ const ctor = this.constructor;
360
+ const propName = ctor.__attributeToPropertyMap.get(name);
361
+ if (propName !== undefined && this.__reflectingProperty !== propName) {
362
+ const options = ctor.getPropertyOptions(propName);
363
+ const converter = typeof options.converter === "function" ? { fromAttribute: options.converter } : options.converter?.fromAttribute !== undefined ? options.converter : defaultConverter;
364
+ this.__reflectingProperty = propName;
365
+ const convertedValue = converter.fromAttribute(value, options.type);
366
+ this[propName] = convertedValue ?? this.__defaultValues?.get(propName) ?? convertedValue;
367
+ this.__reflectingProperty = null;
368
+ }
369
+ }
370
+ requestUpdate(name, oldValue, options, useNewValue = false, newValue) {
371
+ if (name !== undefined) {
372
+ if (DEV_MODE && name instanceof Event) {
373
+ issueWarning(``, `The requestUpdate() method was called with an Event as the property name. This is probably a mistake caused by binding this.requestUpdate as an event listener. Instead bind a function that will call it with no arguments: () => this.requestUpdate()`);
374
+ }
375
+ const ctor = this.constructor;
376
+ if (useNewValue === false) {
377
+ newValue = this[name];
378
+ }
379
+ options ??= ctor.getPropertyOptions(name);
380
+ const changed = (options.hasChanged ?? notEqual)(newValue, oldValue) || options.useDefault && options.reflect && newValue === this.__defaultValues?.get(name) && !this.hasAttribute(ctor.__attributeNameForProperty(name, options));
381
+ if (changed) {
382
+ this._$changeProperty(name, oldValue, options);
383
+ } else {
384
+ return;
385
+ }
386
+ }
387
+ if (this.isUpdatePending === false) {
388
+ this.__updatePromise = this.__enqueueUpdate();
389
+ }
390
+ }
391
+ _$changeProperty(name, oldValue, { useDefault, reflect, wrapped }, initializeValue) {
392
+ if (useDefault && !(this.__defaultValues ??= new Map).has(name)) {
393
+ this.__defaultValues.set(name, initializeValue ?? oldValue ?? this[name]);
394
+ if (wrapped !== true || initializeValue !== undefined) {
395
+ return;
396
+ }
397
+ }
398
+ if (!this._$changedProperties.has(name)) {
399
+ if (!this.hasUpdated && !useDefault) {
400
+ oldValue = undefined;
401
+ }
402
+ this._$changedProperties.set(name, oldValue);
403
+ }
404
+ if (reflect === true && this.__reflectingProperty !== name) {
405
+ (this.__reflectingProperties ??= new Set).add(name);
406
+ }
407
+ }
408
+ async __enqueueUpdate() {
409
+ this.isUpdatePending = true;
410
+ try {
411
+ await this.__updatePromise;
412
+ } catch (e) {
413
+ Promise.reject(e);
414
+ }
415
+ const result = this.scheduleUpdate();
416
+ if (result != null) {
417
+ await result;
418
+ }
419
+ return !this.isUpdatePending;
420
+ }
421
+ scheduleUpdate() {
422
+ const result = this.performUpdate();
423
+ if (DEV_MODE && this.constructor.enabledWarnings.includes("async-perform-update") && typeof result?.then === "function") {
424
+ issueWarning("async-perform-update", `Element ${this.localName} returned a Promise from performUpdate(). ` + `This behavior is deprecated and will be removed in a future ` + `version of ReactiveElement.`);
425
+ }
426
+ return result;
427
+ }
428
+ performUpdate() {
429
+ if (!this.isUpdatePending) {
430
+ return;
431
+ }
432
+ debugLogEvent?.({ kind: "update" });
433
+ if (!this.hasUpdated) {
434
+ this.renderRoot ??= this.createRenderRoot();
435
+ if (DEV_MODE) {
436
+ const ctor = this.constructor;
437
+ const shadowedProperties = [...ctor.elementProperties.keys()].filter((p) => this.hasOwnProperty(p) && (p in getPrototypeOf(this)));
438
+ if (shadowedProperties.length) {
439
+ throw new Error(`The following properties on element ${this.localName} will not ` + `trigger updates as expected because they are set using class ` + `fields: ${shadowedProperties.join(", ")}. ` + `Native class fields and some compiled output will overwrite ` + `accessors used for detecting changes. See ` + `https://lit.dev/msg/class-field-shadowing ` + `for more information.`);
440
+ }
441
+ }
442
+ if (this.__instanceProperties) {
443
+ for (const [p, value] of this.__instanceProperties) {
444
+ this[p] = value;
445
+ }
446
+ this.__instanceProperties = undefined;
447
+ }
448
+ const elementProperties = this.constructor.elementProperties;
449
+ if (elementProperties.size > 0) {
450
+ for (const [p, options] of elementProperties) {
451
+ const { wrapped } = options;
452
+ const value = this[p];
453
+ if (wrapped === true && !this._$changedProperties.has(p) && value !== undefined) {
454
+ this._$changeProperty(p, undefined, options, value);
455
+ }
456
+ }
457
+ }
458
+ }
459
+ let shouldUpdate = false;
460
+ const changedProperties = this._$changedProperties;
461
+ try {
462
+ shouldUpdate = this.shouldUpdate(changedProperties);
463
+ if (shouldUpdate) {
464
+ this.willUpdate(changedProperties);
465
+ this.__controllers?.forEach((c) => c.hostUpdate?.());
466
+ this.update(changedProperties);
467
+ } else {
468
+ this.__markUpdated();
469
+ }
470
+ } catch (e) {
471
+ shouldUpdate = false;
472
+ this.__markUpdated();
473
+ throw e;
474
+ }
475
+ if (shouldUpdate) {
476
+ this._$didUpdate(changedProperties);
477
+ }
478
+ }
479
+ willUpdate(_changedProperties) {}
480
+ _$didUpdate(changedProperties) {
481
+ this.__controllers?.forEach((c) => c.hostUpdated?.());
482
+ if (!this.hasUpdated) {
483
+ this.hasUpdated = true;
484
+ this.firstUpdated(changedProperties);
485
+ }
486
+ this.updated(changedProperties);
487
+ if (DEV_MODE && this.isUpdatePending && this.constructor.enabledWarnings.includes("change-in-update")) {
488
+ issueWarning("change-in-update", `Element ${this.localName} scheduled an update ` + `(generally because a property was set) ` + `after an update completed, causing a new update to be scheduled. ` + `This is inefficient and should be avoided unless the next update ` + `can only be scheduled as a side effect of the previous update.`);
489
+ }
490
+ }
491
+ __markUpdated() {
492
+ this._$changedProperties = new Map;
493
+ this.isUpdatePending = false;
494
+ }
495
+ get updateComplete() {
496
+ return this.getUpdateComplete();
497
+ }
498
+ getUpdateComplete() {
499
+ return this.__updatePromise;
500
+ }
501
+ shouldUpdate(_changedProperties) {
502
+ return true;
503
+ }
504
+ update(_changedProperties) {
505
+ this.__reflectingProperties &&= this.__reflectingProperties.forEach((p) => this.__propertyToAttribute(p, this[p]));
506
+ this.__markUpdated();
507
+ }
508
+ updated(_changedProperties) {}
509
+ firstUpdated(_changedProperties) {}
510
+ }
511
+ ReactiveElement.elementStyles = [];
512
+ ReactiveElement.shadowRootOptions = { mode: "open" };
513
+ ReactiveElement[JSCompiler_renameProperty("elementProperties", ReactiveElement)] = new Map;
514
+ ReactiveElement[JSCompiler_renameProperty("finalized", ReactiveElement)] = new Map;
515
+ polyfillSupport?.({ ReactiveElement });
516
+ if (DEV_MODE) {
517
+ ReactiveElement.enabledWarnings = [
518
+ "change-in-update",
519
+ "async-perform-update"
520
+ ];
521
+ const ensureOwnWarnings = function(ctor) {
522
+ if (!ctor.hasOwnProperty(JSCompiler_renameProperty("enabledWarnings", ctor))) {
523
+ ctor.enabledWarnings = ctor.enabledWarnings.slice();
524
+ }
525
+ };
526
+ ReactiveElement.enableWarning = function(warning) {
527
+ ensureOwnWarnings(this);
528
+ if (!this.enabledWarnings.includes(warning)) {
529
+ this.enabledWarnings.push(warning);
530
+ }
531
+ };
532
+ ReactiveElement.disableWarning = function(warning) {
533
+ ensureOwnWarnings(this);
534
+ const i = this.enabledWarnings.indexOf(warning);
535
+ if (i >= 0) {
536
+ this.enabledWarnings.splice(i, 1);
537
+ }
538
+ };
539
+ }
540
+ (global2.reactiveElementVersions ??= []).push("2.1.2");
541
+ if (DEV_MODE && global2.reactiveElementVersions.length > 1) {
542
+ queueMicrotask(() => {
543
+ issueWarning("multiple-versions", `Multiple versions of Lit loaded. Loading multiple versions ` + `is not recommended.`);
544
+ });
545
+ }
546
+
547
+ // ../../node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/development/lit-html.js
548
+ var DEV_MODE2 = true;
549
+ var ENABLE_EXTRA_SECURITY_HOOKS = true;
550
+ var ENABLE_SHADYDOM_NOPATCH = true;
551
+ var NODE_MODE3 = false;
552
+ var global3 = globalThis;
553
+ var debugLogEvent2 = DEV_MODE2 ? (event) => {
554
+ const shouldEmit = global3.emitLitDebugLogEvents;
555
+ if (!shouldEmit) {
556
+ return;
557
+ }
558
+ global3.dispatchEvent(new CustomEvent("lit-debug", {
559
+ detail: event
560
+ }));
561
+ } : undefined;
562
+ var debugLogRenderId = 0;
563
+ var issueWarning2;
564
+ if (DEV_MODE2) {
565
+ global3.litIssuedWarnings ??= new Set;
566
+ issueWarning2 = (code, warning) => {
567
+ warning += code ? ` See https://lit.dev/msg/${code} for more information.` : "";
568
+ if (!global3.litIssuedWarnings.has(warning) && !global3.litIssuedWarnings.has(code)) {
569
+ console.warn(warning);
570
+ global3.litIssuedWarnings.add(warning);
571
+ }
572
+ };
573
+ queueMicrotask(() => {
574
+ issueWarning2("dev-mode", `Lit is in dev mode. Not recommended for production!`);
575
+ });
576
+ }
577
+ var wrap = ENABLE_SHADYDOM_NOPATCH && global3.ShadyDOM?.inUse && global3.ShadyDOM?.noPatch === true ? global3.ShadyDOM.wrap : (node) => node;
578
+ var trustedTypes2 = global3.trustedTypes;
579
+ var policy = trustedTypes2 ? trustedTypes2.createPolicy("lit-html", {
580
+ createHTML: (s) => s
581
+ }) : undefined;
582
+ var identityFunction = (value) => value;
583
+ var noopSanitizer = (_node, _name, _type) => identityFunction;
584
+ var setSanitizer = (newSanitizer) => {
585
+ if (!ENABLE_EXTRA_SECURITY_HOOKS) {
586
+ return;
587
+ }
588
+ if (sanitizerFactoryInternal !== noopSanitizer) {
589
+ throw new Error(`Attempted to overwrite existing lit-html security policy.` + ` setSanitizeDOMValueFactory should be called at most once.`);
590
+ }
591
+ sanitizerFactoryInternal = newSanitizer;
592
+ };
593
+ var _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => {
594
+ sanitizerFactoryInternal = noopSanitizer;
595
+ };
596
+ var createSanitizer = (node, name, type) => {
597
+ return sanitizerFactoryInternal(node, name, type);
598
+ };
599
+ var boundAttributeSuffix = "$lit$";
600
+ var marker = `lit$${Math.random().toFixed(9).slice(2)}$`;
601
+ var markerMatch = "?" + marker;
602
+ var nodeMarker = `<${markerMatch}>`;
603
+ var d = NODE_MODE3 && global3.document === undefined ? {
604
+ createTreeWalker() {
605
+ return {};
606
+ }
607
+ } : document;
608
+ var createMarker = () => d.createComment("");
609
+ var isPrimitive = (value) => value === null || typeof value != "object" && typeof value != "function";
610
+ var isArray = Array.isArray;
611
+ var isIterable = (value) => isArray(value) || typeof value?.[Symbol.iterator] === "function";
612
+ var SPACE_CHAR = `[
613
+ \f\r]`;
614
+ var ATTR_VALUE_CHAR = `[^
615
+ \f\r"'\`<>=]`;
616
+ var NAME_CHAR = `[^\\s"'>=/]`;
617
+ var textEndRegex = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g;
618
+ var COMMENT_START = 1;
619
+ var TAG_NAME = 2;
620
+ var DYNAMIC_TAG_NAME = 3;
621
+ var commentEndRegex = /-->/g;
622
+ var comment2EndRegex = />/g;
623
+ var tagEndRegex = new RegExp(`>|${SPACE_CHAR}(?:(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))|$)`, "g");
624
+ var ENTIRE_MATCH = 0;
625
+ var ATTRIBUTE_NAME = 1;
626
+ var SPACES_AND_EQUALS = 2;
627
+ var QUOTE_CHAR = 3;
628
+ var singleQuoteAttrEndRegex = /'/g;
629
+ var doubleQuoteAttrEndRegex = /"/g;
630
+ var rawTextElement = /^(?:script|style|textarea|title)$/i;
631
+ var HTML_RESULT = 1;
632
+ var SVG_RESULT = 2;
633
+ var MATHML_RESULT = 3;
634
+ var ATTRIBUTE_PART = 1;
635
+ var CHILD_PART = 2;
636
+ var PROPERTY_PART = 3;
637
+ var BOOLEAN_ATTRIBUTE_PART = 4;
638
+ var EVENT_PART = 5;
639
+ var ELEMENT_PART = 6;
640
+ var COMMENT_PART = 7;
641
+ var tag = (type) => (strings, ...values) => {
642
+ if (DEV_MODE2 && strings.some((s) => s === undefined)) {
643
+ console.warn(`Some template strings are undefined.
644
+ ` + "This is probably caused by illegal octal escape sequences.");
645
+ }
646
+ if (DEV_MODE2) {
647
+ if (values.some((val) => val?.["_$litStatic$"])) {
648
+ issueWarning2("", `Static values 'literal' or 'unsafeStatic' cannot be used as values to non-static templates.
649
+ ` + `Please use the static 'html' tag function. See https://lit.dev/docs/templates/expressions/#static-expressions`);
650
+ }
651
+ }
652
+ return {
653
+ ["_$litType$"]: type,
654
+ strings,
655
+ values
656
+ };
657
+ };
658
+ var html = tag(HTML_RESULT);
659
+ var svg = tag(SVG_RESULT);
660
+ var mathml = tag(MATHML_RESULT);
661
+ var noChange = Symbol.for("lit-noChange");
662
+ var nothing = Symbol.for("lit-nothing");
663
+ var templateCache = new WeakMap;
664
+ var walker = d.createTreeWalker(d, 129);
665
+ var sanitizerFactoryInternal = noopSanitizer;
666
+ function trustFromTemplateString(tsa, stringFromTSA) {
667
+ if (!isArray(tsa) || !tsa.hasOwnProperty("raw")) {
668
+ let message = "invalid template strings array";
669
+ if (DEV_MODE2) {
670
+ message = `
671
+ Internal Error: expected template strings to be an array
672
+ with a 'raw' field. Faking a template strings array by
673
+ calling html or svg like an ordinary function is effectively
674
+ the same as calling unsafeHtml and can lead to major security
675
+ issues, e.g. opening your code up to XSS attacks.
676
+ If you're using the html or svg tagged template functions normally
677
+ and still seeing this error, please file a bug at
678
+ https://github.com/lit/lit/issues/new?template=bug_report.md
679
+ and include information about your build tooling, if any.
680
+ `.trim().replace(/\n */g, `
681
+ `);
682
+ }
683
+ throw new Error(message);
684
+ }
685
+ return policy !== undefined ? policy.createHTML(stringFromTSA) : stringFromTSA;
686
+ }
687
+ var getTemplateHtml = (strings, type) => {
688
+ const l = strings.length - 1;
689
+ const attrNames = [];
690
+ let html2 = type === SVG_RESULT ? "<svg>" : type === MATHML_RESULT ? "<math>" : "";
691
+ let rawTextEndRegex;
692
+ let regex = textEndRegex;
693
+ for (let i = 0;i < l; i++) {
694
+ const s = strings[i];
695
+ let attrNameEndIndex = -1;
696
+ let attrName;
697
+ let lastIndex = 0;
698
+ let match;
699
+ while (lastIndex < s.length) {
700
+ regex.lastIndex = lastIndex;
701
+ match = regex.exec(s);
702
+ if (match === null) {
703
+ break;
704
+ }
705
+ lastIndex = regex.lastIndex;
706
+ if (regex === textEndRegex) {
707
+ if (match[COMMENT_START] === "!--") {
708
+ regex = commentEndRegex;
709
+ } else if (match[COMMENT_START] !== undefined) {
710
+ regex = comment2EndRegex;
711
+ } else if (match[TAG_NAME] !== undefined) {
712
+ if (rawTextElement.test(match[TAG_NAME])) {
713
+ rawTextEndRegex = new RegExp(`</${match[TAG_NAME]}`, "g");
714
+ }
715
+ regex = tagEndRegex;
716
+ } else if (match[DYNAMIC_TAG_NAME] !== undefined) {
717
+ if (DEV_MODE2) {
718
+ throw new Error("Bindings in tag names are not supported. Please use static templates instead. " + "See https://lit.dev/docs/templates/expressions/#static-expressions");
719
+ }
720
+ regex = tagEndRegex;
721
+ }
722
+ } else if (regex === tagEndRegex) {
723
+ if (match[ENTIRE_MATCH] === ">") {
724
+ regex = rawTextEndRegex ?? textEndRegex;
725
+ attrNameEndIndex = -1;
726
+ } else if (match[ATTRIBUTE_NAME] === undefined) {
727
+ attrNameEndIndex = -2;
728
+ } else {
729
+ attrNameEndIndex = regex.lastIndex - match[SPACES_AND_EQUALS].length;
730
+ attrName = match[ATTRIBUTE_NAME];
731
+ regex = match[QUOTE_CHAR] === undefined ? tagEndRegex : match[QUOTE_CHAR] === '"' ? doubleQuoteAttrEndRegex : singleQuoteAttrEndRegex;
732
+ }
733
+ } else if (regex === doubleQuoteAttrEndRegex || regex === singleQuoteAttrEndRegex) {
734
+ regex = tagEndRegex;
735
+ } else if (regex === commentEndRegex || regex === comment2EndRegex) {
736
+ regex = textEndRegex;
737
+ } else {
738
+ regex = tagEndRegex;
739
+ rawTextEndRegex = undefined;
740
+ }
741
+ }
742
+ if (DEV_MODE2) {
743
+ console.assert(attrNameEndIndex === -1 || regex === tagEndRegex || regex === singleQuoteAttrEndRegex || regex === doubleQuoteAttrEndRegex, "unexpected parse state B");
744
+ }
745
+ const end = regex === tagEndRegex && strings[i + 1].startsWith("/>") ? " " : "";
746
+ html2 += regex === textEndRegex ? s + nodeMarker : attrNameEndIndex >= 0 ? (attrNames.push(attrName), s.slice(0, attrNameEndIndex) + boundAttributeSuffix + s.slice(attrNameEndIndex)) + marker + end : s + marker + (attrNameEndIndex === -2 ? i : end);
747
+ }
748
+ const htmlResult = html2 + (strings[l] || "<?>") + (type === SVG_RESULT ? "</svg>" : type === MATHML_RESULT ? "</math>" : "");
749
+ return [trustFromTemplateString(strings, htmlResult), attrNames];
750
+ };
751
+
752
+ class Template {
753
+ constructor({ strings, ["_$litType$"]: type }, options) {
754
+ this.parts = [];
755
+ let node;
756
+ let nodeIndex = 0;
757
+ let attrNameIndex = 0;
758
+ const partCount = strings.length - 1;
759
+ const parts = this.parts;
760
+ const [html2, attrNames] = getTemplateHtml(strings, type);
761
+ this.el = Template.createElement(html2, options);
762
+ walker.currentNode = this.el.content;
763
+ if (type === SVG_RESULT || type === MATHML_RESULT) {
764
+ const wrapper = this.el.content.firstChild;
765
+ wrapper.replaceWith(...wrapper.childNodes);
766
+ }
767
+ while ((node = walker.nextNode()) !== null && parts.length < partCount) {
768
+ if (node.nodeType === 1) {
769
+ if (DEV_MODE2) {
770
+ const tag2 = node.localName;
771
+ if (/^(?:textarea|template)$/i.test(tag2) && node.innerHTML.includes(marker)) {
772
+ const m = `Expressions are not supported inside \`${tag2}\` ` + `elements. See https://lit.dev/msg/expression-in-${tag2} for more ` + `information.`;
773
+ if (tag2 === "template") {
774
+ throw new Error(m);
775
+ } else
776
+ issueWarning2("", m);
777
+ }
778
+ }
779
+ if (node.hasAttributes()) {
780
+ for (const name of node.getAttributeNames()) {
781
+ if (name.endsWith(boundAttributeSuffix)) {
782
+ const realName = attrNames[attrNameIndex++];
783
+ const value = node.getAttribute(name);
784
+ const statics = value.split(marker);
785
+ const m = /([.?@])?(.*)/.exec(realName);
786
+ parts.push({
787
+ type: ATTRIBUTE_PART,
788
+ index: nodeIndex,
789
+ name: m[2],
790
+ strings: statics,
791
+ ctor: m[1] === "." ? PropertyPart : m[1] === "?" ? BooleanAttributePart : m[1] === "@" ? EventPart : AttributePart
792
+ });
793
+ node.removeAttribute(name);
794
+ } else if (name.startsWith(marker)) {
795
+ parts.push({
796
+ type: ELEMENT_PART,
797
+ index: nodeIndex
798
+ });
799
+ node.removeAttribute(name);
800
+ }
801
+ }
802
+ }
803
+ if (rawTextElement.test(node.tagName)) {
804
+ const strings2 = node.textContent.split(marker);
805
+ const lastIndex = strings2.length - 1;
806
+ if (lastIndex > 0) {
807
+ node.textContent = trustedTypes2 ? trustedTypes2.emptyScript : "";
808
+ for (let i = 0;i < lastIndex; i++) {
809
+ node.append(strings2[i], createMarker());
810
+ walker.nextNode();
811
+ parts.push({ type: CHILD_PART, index: ++nodeIndex });
812
+ }
813
+ node.append(strings2[lastIndex], createMarker());
814
+ }
815
+ }
816
+ } else if (node.nodeType === 8) {
817
+ const data = node.data;
818
+ if (data === markerMatch) {
819
+ parts.push({ type: CHILD_PART, index: nodeIndex });
820
+ } else {
821
+ let i = -1;
822
+ while ((i = node.data.indexOf(marker, i + 1)) !== -1) {
823
+ parts.push({ type: COMMENT_PART, index: nodeIndex });
824
+ i += marker.length - 1;
825
+ }
826
+ }
827
+ }
828
+ nodeIndex++;
829
+ }
830
+ if (DEV_MODE2) {
831
+ if (attrNames.length !== attrNameIndex) {
832
+ throw new Error(`Detected duplicate attribute bindings. This occurs if your template ` + `has duplicate attributes on an element tag. For example ` + `"<input ?disabled=\${true} ?disabled=\${false}>" contains a ` + `duplicate "disabled" attribute. The error was detected in ` + `the following template:
833
+ ` + "`" + strings.join("${...}") + "`");
834
+ }
835
+ }
836
+ debugLogEvent2 && debugLogEvent2({
837
+ kind: "template prep",
838
+ template: this,
839
+ clonableTemplate: this.el,
840
+ parts: this.parts,
841
+ strings
842
+ });
843
+ }
844
+ static createElement(html2, _options) {
845
+ const el = d.createElement("template");
846
+ el.innerHTML = html2;
847
+ return el;
848
+ }
849
+ }
850
+ function resolveDirective(part, value, parent = part, attributeIndex) {
851
+ if (value === noChange) {
852
+ return value;
853
+ }
854
+ let currentDirective = attributeIndex !== undefined ? parent.__directives?.[attributeIndex] : parent.__directive;
855
+ const nextDirectiveConstructor = isPrimitive(value) ? undefined : value["_$litDirective$"];
856
+ if (currentDirective?.constructor !== nextDirectiveConstructor) {
857
+ currentDirective?.["_$notifyDirectiveConnectionChanged"]?.(false);
858
+ if (nextDirectiveConstructor === undefined) {
859
+ currentDirective = undefined;
860
+ } else {
861
+ currentDirective = new nextDirectiveConstructor(part);
862
+ currentDirective._$initialize(part, parent, attributeIndex);
863
+ }
864
+ if (attributeIndex !== undefined) {
865
+ (parent.__directives ??= [])[attributeIndex] = currentDirective;
866
+ } else {
867
+ parent.__directive = currentDirective;
868
+ }
869
+ }
870
+ if (currentDirective !== undefined) {
871
+ value = resolveDirective(part, currentDirective._$resolve(part, value.values), currentDirective, attributeIndex);
872
+ }
873
+ return value;
874
+ }
875
+
876
+ class TemplateInstance {
877
+ constructor(template, parent) {
878
+ this._$parts = [];
879
+ this._$disconnectableChildren = undefined;
880
+ this._$template = template;
881
+ this._$parent = parent;
882
+ }
883
+ get parentNode() {
884
+ return this._$parent.parentNode;
885
+ }
886
+ get _$isConnected() {
887
+ return this._$parent._$isConnected;
888
+ }
889
+ _clone(options) {
890
+ const { el: { content }, parts } = this._$template;
891
+ const fragment = (options?.creationScope ?? d).importNode(content, true);
892
+ walker.currentNode = fragment;
893
+ let node = walker.nextNode();
894
+ let nodeIndex = 0;
895
+ let partIndex = 0;
896
+ let templatePart = parts[0];
897
+ while (templatePart !== undefined) {
898
+ if (nodeIndex === templatePart.index) {
899
+ let part;
900
+ if (templatePart.type === CHILD_PART) {
901
+ part = new ChildPart(node, node.nextSibling, this, options);
902
+ } else if (templatePart.type === ATTRIBUTE_PART) {
903
+ part = new templatePart.ctor(node, templatePart.name, templatePart.strings, this, options);
904
+ } else if (templatePart.type === ELEMENT_PART) {
905
+ part = new ElementPart(node, this, options);
906
+ }
907
+ this._$parts.push(part);
908
+ templatePart = parts[++partIndex];
909
+ }
910
+ if (nodeIndex !== templatePart?.index) {
911
+ node = walker.nextNode();
912
+ nodeIndex++;
913
+ }
914
+ }
915
+ walker.currentNode = d;
916
+ return fragment;
917
+ }
918
+ _update(values) {
919
+ let i = 0;
920
+ for (const part of this._$parts) {
921
+ if (part !== undefined) {
922
+ debugLogEvent2 && debugLogEvent2({
923
+ kind: "set part",
924
+ part,
925
+ value: values[i],
926
+ valueIndex: i,
927
+ values,
928
+ templateInstance: this
929
+ });
930
+ if (part.strings !== undefined) {
931
+ part._$setValue(values, part, i);
932
+ i += part.strings.length - 2;
933
+ } else {
934
+ part._$setValue(values[i]);
935
+ }
936
+ }
937
+ i++;
938
+ }
939
+ }
940
+ }
941
+
942
+ class ChildPart {
943
+ get _$isConnected() {
944
+ return this._$parent?._$isConnected ?? this.__isConnected;
945
+ }
946
+ constructor(startNode, endNode, parent, options) {
947
+ this.type = CHILD_PART;
948
+ this._$committedValue = nothing;
949
+ this._$disconnectableChildren = undefined;
950
+ this._$startNode = startNode;
951
+ this._$endNode = endNode;
952
+ this._$parent = parent;
953
+ this.options = options;
954
+ this.__isConnected = options?.isConnected ?? true;
955
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
956
+ this._textSanitizer = undefined;
957
+ }
958
+ }
959
+ get parentNode() {
960
+ let parentNode = wrap(this._$startNode).parentNode;
961
+ const parent = this._$parent;
962
+ if (parent !== undefined && parentNode?.nodeType === 11) {
963
+ parentNode = parent.parentNode;
964
+ }
965
+ return parentNode;
966
+ }
967
+ get startNode() {
968
+ return this._$startNode;
969
+ }
970
+ get endNode() {
971
+ return this._$endNode;
972
+ }
973
+ _$setValue(value, directiveParent = this) {
974
+ if (DEV_MODE2 && this.parentNode === null) {
975
+ throw new Error(`This \`ChildPart\` has no \`parentNode\` and therefore cannot accept a value. This likely means the element containing the part was manipulated in an unsupported way outside of Lit's control such that the part's marker nodes were ejected from DOM. For example, setting the element's \`innerHTML\` or \`textContent\` can do this.`);
976
+ }
977
+ value = resolveDirective(this, value, directiveParent);
978
+ if (isPrimitive(value)) {
979
+ if (value === nothing || value == null || value === "") {
980
+ if (this._$committedValue !== nothing) {
981
+ debugLogEvent2 && debugLogEvent2({
982
+ kind: "commit nothing to child",
983
+ start: this._$startNode,
984
+ end: this._$endNode,
985
+ parent: this._$parent,
986
+ options: this.options
987
+ });
988
+ this._$clear();
989
+ }
990
+ this._$committedValue = nothing;
991
+ } else if (value !== this._$committedValue && value !== noChange) {
992
+ this._commitText(value);
993
+ }
994
+ } else if (value["_$litType$"] !== undefined) {
995
+ this._commitTemplateResult(value);
996
+ } else if (value.nodeType !== undefined) {
997
+ if (DEV_MODE2 && this.options?.host === value) {
998
+ this._commitText(`[probable mistake: rendered a template's host in itself ` + `(commonly caused by writing \${this} in a template]`);
999
+ console.warn(`Attempted to render the template host`, value, `inside itself. This is almost always a mistake, and in dev mode `, `we render some warning text. In production however, we'll `, `render it, which will usually result in an error, and sometimes `, `in the element disappearing from the DOM.`);
1000
+ return;
1001
+ }
1002
+ this._commitNode(value);
1003
+ } else if (isIterable(value)) {
1004
+ this._commitIterable(value);
1005
+ } else {
1006
+ this._commitText(value);
1007
+ }
1008
+ }
1009
+ _insert(node) {
1010
+ return wrap(wrap(this._$startNode).parentNode).insertBefore(node, this._$endNode);
1011
+ }
1012
+ _commitNode(value) {
1013
+ if (this._$committedValue !== value) {
1014
+ this._$clear();
1015
+ if (ENABLE_EXTRA_SECURITY_HOOKS && sanitizerFactoryInternal !== noopSanitizer) {
1016
+ const parentNodeName = this._$startNode.parentNode?.nodeName;
1017
+ if (parentNodeName === "STYLE" || parentNodeName === "SCRIPT") {
1018
+ let message = "Forbidden";
1019
+ if (DEV_MODE2) {
1020
+ if (parentNodeName === "STYLE") {
1021
+ message = `Lit does not support binding inside style nodes. ` + `This is a security risk, as style injection attacks can ` + `exfiltrate data and spoof UIs. ` + `Consider instead using css\`...\` literals ` + `to compose styles, and do dynamic styling with ` + `css custom properties, ::parts, <slot>s, ` + `and by mutating the DOM rather than stylesheets.`;
1022
+ } else {
1023
+ message = `Lit does not support binding inside script nodes. ` + `This is a security risk, as it could allow arbitrary ` + `code execution.`;
1024
+ }
1025
+ }
1026
+ throw new Error(message);
1027
+ }
1028
+ }
1029
+ debugLogEvent2 && debugLogEvent2({
1030
+ kind: "commit node",
1031
+ start: this._$startNode,
1032
+ parent: this._$parent,
1033
+ value,
1034
+ options: this.options
1035
+ });
1036
+ this._$committedValue = this._insert(value);
1037
+ }
1038
+ }
1039
+ _commitText(value) {
1040
+ if (this._$committedValue !== nothing && isPrimitive(this._$committedValue)) {
1041
+ const node = wrap(this._$startNode).nextSibling;
1042
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1043
+ if (this._textSanitizer === undefined) {
1044
+ this._textSanitizer = createSanitizer(node, "data", "property");
1045
+ }
1046
+ value = this._textSanitizer(value);
1047
+ }
1048
+ debugLogEvent2 && debugLogEvent2({
1049
+ kind: "commit text",
1050
+ node,
1051
+ value,
1052
+ options: this.options
1053
+ });
1054
+ node.data = value;
1055
+ } else {
1056
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1057
+ const textNode = d.createTextNode("");
1058
+ this._commitNode(textNode);
1059
+ if (this._textSanitizer === undefined) {
1060
+ this._textSanitizer = createSanitizer(textNode, "data", "property");
1061
+ }
1062
+ value = this._textSanitizer(value);
1063
+ debugLogEvent2 && debugLogEvent2({
1064
+ kind: "commit text",
1065
+ node: textNode,
1066
+ value,
1067
+ options: this.options
1068
+ });
1069
+ textNode.data = value;
1070
+ } else {
1071
+ this._commitNode(d.createTextNode(value));
1072
+ debugLogEvent2 && debugLogEvent2({
1073
+ kind: "commit text",
1074
+ node: wrap(this._$startNode).nextSibling,
1075
+ value,
1076
+ options: this.options
1077
+ });
1078
+ }
1079
+ }
1080
+ this._$committedValue = value;
1081
+ }
1082
+ _commitTemplateResult(result) {
1083
+ const { values, ["_$litType$"]: type } = result;
1084
+ const template = typeof type === "number" ? this._$getTemplate(result) : (type.el === undefined && (type.el = Template.createElement(trustFromTemplateString(type.h, type.h[0]), this.options)), type);
1085
+ if (this._$committedValue?._$template === template) {
1086
+ debugLogEvent2 && debugLogEvent2({
1087
+ kind: "template updating",
1088
+ template,
1089
+ instance: this._$committedValue,
1090
+ parts: this._$committedValue._$parts,
1091
+ options: this.options,
1092
+ values
1093
+ });
1094
+ this._$committedValue._update(values);
1095
+ } else {
1096
+ const instance = new TemplateInstance(template, this);
1097
+ const fragment = instance._clone(this.options);
1098
+ debugLogEvent2 && debugLogEvent2({
1099
+ kind: "template instantiated",
1100
+ template,
1101
+ instance,
1102
+ parts: instance._$parts,
1103
+ options: this.options,
1104
+ fragment,
1105
+ values
1106
+ });
1107
+ instance._update(values);
1108
+ debugLogEvent2 && debugLogEvent2({
1109
+ kind: "template instantiated and updated",
1110
+ template,
1111
+ instance,
1112
+ parts: instance._$parts,
1113
+ options: this.options,
1114
+ fragment,
1115
+ values
1116
+ });
1117
+ this._commitNode(fragment);
1118
+ this._$committedValue = instance;
1119
+ }
1120
+ }
1121
+ _$getTemplate(result) {
1122
+ let template = templateCache.get(result.strings);
1123
+ if (template === undefined) {
1124
+ templateCache.set(result.strings, template = new Template(result));
1125
+ }
1126
+ return template;
1127
+ }
1128
+ _commitIterable(value) {
1129
+ if (!isArray(this._$committedValue)) {
1130
+ this._$committedValue = [];
1131
+ this._$clear();
1132
+ }
1133
+ const itemParts = this._$committedValue;
1134
+ let partIndex = 0;
1135
+ let itemPart;
1136
+ for (const item of value) {
1137
+ if (partIndex === itemParts.length) {
1138
+ itemParts.push(itemPart = new ChildPart(this._insert(createMarker()), this._insert(createMarker()), this, this.options));
1139
+ } else {
1140
+ itemPart = itemParts[partIndex];
1141
+ }
1142
+ itemPart._$setValue(item);
1143
+ partIndex++;
1144
+ }
1145
+ if (partIndex < itemParts.length) {
1146
+ this._$clear(itemPart && wrap(itemPart._$endNode).nextSibling, partIndex);
1147
+ itemParts.length = partIndex;
1148
+ }
1149
+ }
1150
+ _$clear(start = wrap(this._$startNode).nextSibling, from) {
1151
+ this._$notifyConnectionChanged?.(false, true, from);
1152
+ while (start !== this._$endNode) {
1153
+ const n = wrap(start).nextSibling;
1154
+ wrap(start).remove();
1155
+ start = n;
1156
+ }
1157
+ }
1158
+ setConnected(isConnected) {
1159
+ if (this._$parent === undefined) {
1160
+ this.__isConnected = isConnected;
1161
+ this._$notifyConnectionChanged?.(isConnected);
1162
+ } else if (DEV_MODE2) {
1163
+ throw new Error("part.setConnected() may only be called on a " + "RootPart returned from render().");
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ class AttributePart {
1169
+ get tagName() {
1170
+ return this.element.tagName;
1171
+ }
1172
+ get _$isConnected() {
1173
+ return this._$parent._$isConnected;
1174
+ }
1175
+ constructor(element, name, strings, parent, options) {
1176
+ this.type = ATTRIBUTE_PART;
1177
+ this._$committedValue = nothing;
1178
+ this._$disconnectableChildren = undefined;
1179
+ this.element = element;
1180
+ this.name = name;
1181
+ this._$parent = parent;
1182
+ this.options = options;
1183
+ if (strings.length > 2 || strings[0] !== "" || strings[1] !== "") {
1184
+ this._$committedValue = new Array(strings.length - 1).fill(new String);
1185
+ this.strings = strings;
1186
+ } else {
1187
+ this._$committedValue = nothing;
1188
+ }
1189
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1190
+ this._sanitizer = undefined;
1191
+ }
1192
+ }
1193
+ _$setValue(value, directiveParent = this, valueIndex, noCommit) {
1194
+ const strings = this.strings;
1195
+ let change = false;
1196
+ if (strings === undefined) {
1197
+ value = resolveDirective(this, value, directiveParent, 0);
1198
+ change = !isPrimitive(value) || value !== this._$committedValue && value !== noChange;
1199
+ if (change) {
1200
+ this._$committedValue = value;
1201
+ }
1202
+ } else {
1203
+ const values = value;
1204
+ value = strings[0];
1205
+ let i, v;
1206
+ for (i = 0;i < strings.length - 1; i++) {
1207
+ v = resolveDirective(this, values[valueIndex + i], directiveParent, i);
1208
+ if (v === noChange) {
1209
+ v = this._$committedValue[i];
1210
+ }
1211
+ change ||= !isPrimitive(v) || v !== this._$committedValue[i];
1212
+ if (v === nothing) {
1213
+ value = nothing;
1214
+ } else if (value !== nothing) {
1215
+ value += (v ?? "") + strings[i + 1];
1216
+ }
1217
+ this._$committedValue[i] = v;
1218
+ }
1219
+ }
1220
+ if (change && !noCommit) {
1221
+ this._commitValue(value);
1222
+ }
1223
+ }
1224
+ _commitValue(value) {
1225
+ if (value === nothing) {
1226
+ wrap(this.element).removeAttribute(this.name);
1227
+ } else {
1228
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1229
+ if (this._sanitizer === undefined) {
1230
+ this._sanitizer = sanitizerFactoryInternal(this.element, this.name, "attribute");
1231
+ }
1232
+ value = this._sanitizer(value ?? "");
1233
+ }
1234
+ debugLogEvent2 && debugLogEvent2({
1235
+ kind: "commit attribute",
1236
+ element: this.element,
1237
+ name: this.name,
1238
+ value,
1239
+ options: this.options
1240
+ });
1241
+ wrap(this.element).setAttribute(this.name, value ?? "");
1242
+ }
1243
+ }
1244
+ }
1245
+
1246
+ class PropertyPart extends AttributePart {
1247
+ constructor() {
1248
+ super(...arguments);
1249
+ this.type = PROPERTY_PART;
1250
+ }
1251
+ _commitValue(value) {
1252
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1253
+ if (this._sanitizer === undefined) {
1254
+ this._sanitizer = sanitizerFactoryInternal(this.element, this.name, "property");
1255
+ }
1256
+ value = this._sanitizer(value);
1257
+ }
1258
+ debugLogEvent2 && debugLogEvent2({
1259
+ kind: "commit property",
1260
+ element: this.element,
1261
+ name: this.name,
1262
+ value,
1263
+ options: this.options
1264
+ });
1265
+ this.element[this.name] = value === nothing ? undefined : value;
1266
+ }
1267
+ }
1268
+
1269
+ class BooleanAttributePart extends AttributePart {
1270
+ constructor() {
1271
+ super(...arguments);
1272
+ this.type = BOOLEAN_ATTRIBUTE_PART;
1273
+ }
1274
+ _commitValue(value) {
1275
+ debugLogEvent2 && debugLogEvent2({
1276
+ kind: "commit boolean attribute",
1277
+ element: this.element,
1278
+ name: this.name,
1279
+ value: !!(value && value !== nothing),
1280
+ options: this.options
1281
+ });
1282
+ wrap(this.element).toggleAttribute(this.name, !!value && value !== nothing);
1283
+ }
1284
+ }
1285
+
1286
+ class EventPart extends AttributePart {
1287
+ constructor(element, name, strings, parent, options) {
1288
+ super(element, name, strings, parent, options);
1289
+ this.type = EVENT_PART;
1290
+ if (DEV_MODE2 && this.strings !== undefined) {
1291
+ throw new Error(`A \`<${element.localName}>\` has a \`@${name}=...\` listener with ` + "invalid content. Event listeners in templates must have exactly " + "one expression and no surrounding text.");
1292
+ }
1293
+ }
1294
+ _$setValue(newListener, directiveParent = this) {
1295
+ newListener = resolveDirective(this, newListener, directiveParent, 0) ?? nothing;
1296
+ if (newListener === noChange) {
1297
+ return;
1298
+ }
1299
+ const oldListener = this._$committedValue;
1300
+ const shouldRemoveListener = newListener === nothing && oldListener !== nothing || newListener.capture !== oldListener.capture || newListener.once !== oldListener.once || newListener.passive !== oldListener.passive;
1301
+ const shouldAddListener = newListener !== nothing && (oldListener === nothing || shouldRemoveListener);
1302
+ debugLogEvent2 && debugLogEvent2({
1303
+ kind: "commit event listener",
1304
+ element: this.element,
1305
+ name: this.name,
1306
+ value: newListener,
1307
+ options: this.options,
1308
+ removeListener: shouldRemoveListener,
1309
+ addListener: shouldAddListener,
1310
+ oldListener
1311
+ });
1312
+ if (shouldRemoveListener) {
1313
+ this.element.removeEventListener(this.name, this, oldListener);
1314
+ }
1315
+ if (shouldAddListener) {
1316
+ this.element.addEventListener(this.name, this, newListener);
1317
+ }
1318
+ this._$committedValue = newListener;
1319
+ }
1320
+ handleEvent(event) {
1321
+ if (typeof this._$committedValue === "function") {
1322
+ this._$committedValue.call(this.options?.host ?? this.element, event);
1323
+ } else {
1324
+ this._$committedValue.handleEvent(event);
1325
+ }
1326
+ }
1327
+ }
1328
+
1329
+ class ElementPart {
1330
+ constructor(element, parent, options) {
1331
+ this.element = element;
1332
+ this.type = ELEMENT_PART;
1333
+ this._$disconnectableChildren = undefined;
1334
+ this._$parent = parent;
1335
+ this.options = options;
1336
+ }
1337
+ get _$isConnected() {
1338
+ return this._$parent._$isConnected;
1339
+ }
1340
+ _$setValue(value) {
1341
+ debugLogEvent2 && debugLogEvent2({
1342
+ kind: "commit to element binding",
1343
+ element: this.element,
1344
+ value,
1345
+ options: this.options
1346
+ });
1347
+ resolveDirective(this, value);
1348
+ }
1349
+ }
1350
+ var polyfillSupport2 = DEV_MODE2 ? global3.litHtmlPolyfillSupportDevMode : global3.litHtmlPolyfillSupport;
1351
+ polyfillSupport2?.(Template, ChildPart);
1352
+ (global3.litHtmlVersions ??= []).push("3.3.3");
1353
+ if (DEV_MODE2 && global3.litHtmlVersions.length > 1) {
1354
+ queueMicrotask(() => {
1355
+ issueWarning2("multiple-versions", `Multiple versions of Lit loaded. ` + `Loading multiple versions is not recommended.`);
1356
+ });
1357
+ }
1358
+ var render = (value, container, options) => {
1359
+ if (DEV_MODE2 && container == null) {
1360
+ throw new TypeError(`The container to render into may not be ${container}`);
1361
+ }
1362
+ const renderId = DEV_MODE2 ? debugLogRenderId++ : 0;
1363
+ const partOwnerNode = options?.renderBefore ?? container;
1364
+ let part = partOwnerNode["_$litPart$"];
1365
+ debugLogEvent2 && debugLogEvent2({
1366
+ kind: "begin render",
1367
+ id: renderId,
1368
+ value,
1369
+ container,
1370
+ options,
1371
+ part
1372
+ });
1373
+ if (part === undefined) {
1374
+ const endNode = options?.renderBefore ?? null;
1375
+ partOwnerNode["_$litPart$"] = part = new ChildPart(container.insertBefore(createMarker(), endNode), endNode, undefined, options ?? {});
1376
+ }
1377
+ part._$setValue(value);
1378
+ debugLogEvent2 && debugLogEvent2({
1379
+ kind: "end render",
1380
+ id: renderId,
1381
+ value,
1382
+ container,
1383
+ options,
1384
+ part
1385
+ });
1386
+ return part;
1387
+ };
1388
+ if (ENABLE_EXTRA_SECURITY_HOOKS) {
1389
+ render.setSanitizer = setSanitizer;
1390
+ render.createSanitizer = createSanitizer;
1391
+ if (DEV_MODE2) {
1392
+ render._testOnlyClearSanitizerFactoryDoNotCallOrElse = _testOnlyClearSanitizerFactoryDoNotCallOrElse;
1393
+ }
1394
+ }
1395
+
1396
+ // ../../node_modules/.pnpm/lit-element@4.2.2/node_modules/lit-element/development/lit-element.js
1397
+ var JSCompiler_renameProperty2 = (prop, _obj) => prop;
1398
+ var DEV_MODE3 = true;
1399
+ var global4 = globalThis;
1400
+ var issueWarning3;
1401
+ if (DEV_MODE3) {
1402
+ global4.litIssuedWarnings ??= new Set;
1403
+ issueWarning3 = (code, warning) => {
1404
+ warning += ` See https://lit.dev/msg/${code} for more information.`;
1405
+ if (!global4.litIssuedWarnings.has(warning) && !global4.litIssuedWarnings.has(code)) {
1406
+ console.warn(warning);
1407
+ global4.litIssuedWarnings.add(warning);
1408
+ }
1409
+ };
1410
+ }
1411
+
1412
+ class LitElement extends ReactiveElement {
1413
+ constructor() {
1414
+ super(...arguments);
1415
+ this.renderOptions = { host: this };
1416
+ this.__childPart = undefined;
1417
+ }
1418
+ createRenderRoot() {
1419
+ const renderRoot = super.createRenderRoot();
1420
+ this.renderOptions.renderBefore ??= renderRoot.firstChild;
1421
+ return renderRoot;
1422
+ }
1423
+ update(changedProperties) {
1424
+ const value = this.render();
1425
+ if (!this.hasUpdated) {
1426
+ this.renderOptions.isConnected = this.isConnected;
1427
+ }
1428
+ super.update(changedProperties);
1429
+ this.__childPart = render(value, this.renderRoot, this.renderOptions);
1430
+ }
1431
+ connectedCallback() {
1432
+ super.connectedCallback();
1433
+ this.__childPart?.setConnected(true);
1434
+ }
1435
+ disconnectedCallback() {
1436
+ super.disconnectedCallback();
1437
+ this.__childPart?.setConnected(false);
1438
+ }
1439
+ render() {
1440
+ return noChange;
1441
+ }
1442
+ }
1443
+ LitElement["_$litElement$"] = true;
1444
+ LitElement[JSCompiler_renameProperty2("finalized", LitElement)] = true;
1445
+ global4.litElementHydrateSupport?.({ LitElement });
1446
+ var polyfillSupport3 = DEV_MODE3 ? global4.litElementPolyfillSupportDevMode : global4.litElementPolyfillSupport;
1447
+ polyfillSupport3?.({ LitElement });
1448
+ (global4.litElementVersions ??= []).push("4.2.2");
1449
+ if (DEV_MODE3 && global4.litElementVersions.length > 1) {
1450
+ queueMicrotask(() => {
1451
+ issueWarning3("multiple-versions", `Multiple versions of Lit loaded. Loading multiple versions ` + `is not recommended.`);
1452
+ });
1453
+ }
1454
+ // src/index.ts
1455
+ class WodiraBrowserApiError extends Error {
1456
+ status;
1457
+ statusText;
1458
+ payload;
1459
+ constructor(params) {
1460
+ super(params.message);
1461
+ this.name = "WodiraBrowserApiError";
1462
+ this.status = params.status;
1463
+ this.statusText = params.statusText;
1464
+ this.payload = params.payload;
1465
+ }
1466
+ }
1467
+ var DEFAULT_BASE_URL = "https://api.wodira.app";
1468
+ var BROWSER_API_PREFIX = "/external/v1/browser";
1469
+
1470
+ class WodiraBrowserClient {
1471
+ publishableKey;
1472
+ baseUrl;
1473
+ fetcher;
1474
+ headers;
1475
+ constructor(options) {
1476
+ if (!options.publishableKey?.trim()) {
1477
+ throw new Error("WodiraBrowserClient requires a publishableKey.");
1478
+ }
1479
+ if (!options.publishableKey.startsWith("wpk_live_")) {
1480
+ throw new Error("Wodira browser publishable keys must start with wpk_live_.");
1481
+ }
1482
+ this.publishableKey = options.publishableKey.trim();
1483
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
1484
+ this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
1485
+ this.headers = options.headers;
1486
+ }
1487
+ searchEvents(input = {}, signal) {
1488
+ return this.request("/events/search", {
1489
+ method: "POST",
1490
+ body: input,
1491
+ signal
1492
+ });
1493
+ }
1494
+ getEvent(eventId, signal) {
1495
+ return this.request(`/events/${encodeURIComponent(eventId)}`, { signal });
1496
+ }
1497
+ getPricingCards(eventId, signal) {
1498
+ return this.request(`/events/${encodeURIComponent(eventId)}/pricing-cards`, { signal });
1499
+ }
1500
+ getRegistrationSummary(input, signal) {
1501
+ return this.request("/checkout/registration/summary", {
1502
+ method: "POST",
1503
+ body: input,
1504
+ signal
1505
+ });
1506
+ }
1507
+ createRegistrationCheckoutSession(input, signal) {
1508
+ return this.request("/checkout/registration/session", {
1509
+ method: "POST",
1510
+ body: serializeRegistrationInput(input),
1511
+ signal
1512
+ });
1513
+ }
1514
+ async request(path, options = {}) {
1515
+ const headers = new Headers(this.headers);
1516
+ headers.set("accept", "application/json");
1517
+ headers.set("x-publishable-key", this.publishableKey);
1518
+ const init = { method: options.method ?? "GET", headers };
1519
+ if (options.signal)
1520
+ init.signal = options.signal;
1521
+ if (options.body !== undefined) {
1522
+ headers.set("content-type", "application/json");
1523
+ init.body = JSON.stringify(options.body);
1524
+ }
1525
+ const response = await this.fetcher(`${this.baseUrl}${BROWSER_API_PREFIX}${path}`, init);
1526
+ if (!response.ok) {
1527
+ const payload = await readResponsePayload(response);
1528
+ throw new WodiraBrowserApiError({
1529
+ status: response.status,
1530
+ statusText: response.statusText,
1531
+ message: extractErrorMessage(payload, response),
1532
+ payload
1533
+ });
1534
+ }
1535
+ const text = await response.text();
1536
+ return text ? JSON.parse(text) : undefined;
1537
+ }
1538
+ }
1539
+ function createWodiraBrowserClient(options) {
1540
+ return new WodiraBrowserClient(options);
1541
+ }
1542
+ async function readResponsePayload(response) {
1543
+ const text = await response.text();
1544
+ if (!text)
1545
+ return null;
1546
+ try {
1547
+ return JSON.parse(text);
1548
+ } catch {
1549
+ return text;
1550
+ }
1551
+ }
1552
+ function extractErrorMessage(payload, response) {
1553
+ if (typeof payload === "string")
1554
+ return payload;
1555
+ const message = payload?.message;
1556
+ if (typeof message === "string")
1557
+ return message;
1558
+ if (Array.isArray(message))
1559
+ return message.filter((item) => typeof item === "string").join(", ");
1560
+ if (typeof payload?.error === "string")
1561
+ return payload.error;
1562
+ return `WODira browser API request failed with ${response.status} ${response.statusText}`;
1563
+ }
1564
+ function serializeRegistrationInput(input) {
1565
+ return {
1566
+ ...input,
1567
+ athletes: input.athletes.map(serializeAthleteInput),
1568
+ ...input.guestRegistrations ? {
1569
+ guestRegistrations: input.guestRegistrations.map((guest) => ({
1570
+ ...guest,
1571
+ athletes: guest.athletes.map(serializeAthleteInput)
1572
+ }))
1573
+ } : {}
1574
+ };
1575
+ }
1576
+ function serializeAthleteInput(athlete) {
1577
+ return {
1578
+ ...athlete,
1579
+ birthDate: athlete.birthDate instanceof Date ? athlete.birthDate.toISOString().slice(0, 10) : athlete.birthDate
1580
+ };
1581
+ }
1582
+
1583
+ // src/ui/events.ts
1584
+ function emitWodiraEvent(target, name, detail) {
1585
+ target.dispatchEvent(new CustomEvent(name, {
1586
+ detail,
1587
+ bubbles: true,
1588
+ composed: true
1589
+ }));
1590
+ }
1591
+ function getErrorMessage(error) {
1592
+ if (error instanceof Error)
1593
+ return error.message;
1594
+ if (typeof error === "string")
1595
+ return error;
1596
+ return "Ha ocurrido un error inesperado.";
1597
+ }
1598
+
1599
+ // src/ui/validators.ts
1600
+ var VALID_GENDERS = new Set(["FEMALE", "MALE"]);
1601
+ function isBlank(value) {
1602
+ return value === undefined || value === null || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.length === 0;
1603
+ }
1604
+ function isEmail(value) {
1605
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
1606
+ }
1607
+ function isHttpUrl(value) {
1608
+ try {
1609
+ const url = new URL(value.trim());
1610
+ return url.protocol === "http:" || url.protocol === "https:";
1611
+ } catch {
1612
+ return false;
1613
+ }
1614
+ }
1615
+ function validateRequiredText(value) {
1616
+ return isBlank(value) ? invalid("required") : valid();
1617
+ }
1618
+ function validateEmail(value, required) {
1619
+ if (isBlank(value))
1620
+ return required ? invalid("required") : valid();
1621
+ return isEmail(value) ? valid() : invalid("email");
1622
+ }
1623
+ function validateHttpUrl(value, required) {
1624
+ if (isBlank(value))
1625
+ return required ? invalid("required") : valid();
1626
+ return isHttpUrl(value) ? valid() : invalid("url");
1627
+ }
1628
+ function validateDate(value, required) {
1629
+ if (isBlank(value))
1630
+ return required ? invalid("required") : valid();
1631
+ const parsed = new Date(value);
1632
+ return Number.isNaN(parsed.getTime()) ? invalid("date") : valid();
1633
+ }
1634
+ function validateNumber(value, required) {
1635
+ if (isBlank(value))
1636
+ return required ? invalid("required") : valid();
1637
+ const parsed = typeof value === "number" ? value : Number(String(value).trim());
1638
+ return Number.isFinite(parsed) ? valid() : invalid("number");
1639
+ }
1640
+ function validateSelect(value, options, required) {
1641
+ if (isBlank(value))
1642
+ return required ? invalid("required") : valid();
1643
+ const normalized = String(value);
1644
+ return options.includes(normalized) ? valid() : invalid("selection");
1645
+ }
1646
+ function validateCheckbox(value, required) {
1647
+ return required && value !== true ? invalid("required") : valid();
1648
+ }
1649
+ function validateGender(value) {
1650
+ return VALID_GENDERS.has(value) ? valid() : invalid("selection");
1651
+ }
1652
+ function validateCustomFieldValue(value, type, options, required) {
1653
+ if (type === "CHECKBOX")
1654
+ return validateCheckbox(value, required);
1655
+ if (type === "SELECT")
1656
+ return validateSelect(value, options, required);
1657
+ if (type === "DATE")
1658
+ return validateDate(String(value ?? ""), required);
1659
+ if (type === "NUMBER")
1660
+ return validateNumber(value, required);
1661
+ return required ? validateRequiredText(value) : valid();
1662
+ }
1663
+ function normalizeLimit(value, fallback, max) {
1664
+ if (!Number.isFinite(value))
1665
+ return fallback;
1666
+ return Math.min(max, Math.max(1, Math.trunc(value)));
1667
+ }
1668
+ function valid() {
1669
+ return { valid: true };
1670
+ }
1671
+ function invalid(reason) {
1672
+ return { valid: false, reason };
1673
+ }
1674
+
1675
+ // src/ui/i18n.ts
1676
+ var es = {
1677
+ availableSlots: "plazas disponibles",
1678
+ buyerEmail: "Email comprador",
1679
+ buyerName: "Nombre comprador",
1680
+ categorySelected: "Categoría seleccionada",
1681
+ checkoutCreating: "Creando checkout…",
1682
+ checkoutSubmit: "Continuar al pago",
1683
+ club: "Club",
1684
+ customFieldsTitle: "Información adicional",
1685
+ documentId: "Documento",
1686
+ emptyCategories: "No hay categorías disponibles.",
1687
+ emptyEvent: "Selecciona un evento.",
1688
+ emptyEvents: "No hay eventos publicados.",
1689
+ eventUnavailable: "Evento no disponible.",
1690
+ female: "Femenino",
1691
+ guestAthlete: "Atleta invitado",
1692
+ guestCategory: "Categoría invitado",
1693
+ guestEmail: "Email invitado",
1694
+ guestName: "Nombre invitado",
1695
+ guestRegistrations: "Inscripciones de invitados",
1696
+ guestRegistrationsHelp: "Añade otra inscripción al mismo pago.",
1697
+ addGuestRegistration: "Agregar invitado",
1698
+ remove: "Eliminar",
1699
+ gender: "Género",
1700
+ invalidDate: "Fecha inválida",
1701
+ invalidEmail: "Email inválido",
1702
+ invalidNumber: "Número inválido",
1703
+ invalidSelection: "Selecciona una opción válida",
1704
+ invalidUrl: "URL inválida",
1705
+ loadingEvent: "Cargando evento…",
1706
+ loadingEvents: "Cargando eventos…",
1707
+ loadingPricing: "Cargando tarifas…",
1708
+ loadingSummary: "Calculando resumen…",
1709
+ male: "Masculino",
1710
+ noPublishableKey: "Configura publishable-key.",
1711
+ promocode: "Promocode",
1712
+ registerData: "Datos de inscripción",
1713
+ requiredField: "Este campo es obligatorio",
1714
+ requiredWaiver: "Debes aceptar este documento",
1715
+ select: "Seleccionar",
1716
+ selectCategory: "Selecciona una categoría.",
1717
+ selectEvent: "Ver inscripciones",
1718
+ selectOption: "Seleccionar",
1719
+ summary: "Resumen",
1720
+ teamName: "Nombre del equipo",
1721
+ teamSize: "Equipo de",
1722
+ terms: "Acepto los términos y condiciones de la inscripción.",
1723
+ total: "Total",
1724
+ waiversTitle: "Documentos legales"
1725
+ };
1726
+ var en = {
1727
+ availableSlots: "spots available",
1728
+ buyerEmail: "Buyer email",
1729
+ buyerName: "Buyer name",
1730
+ categorySelected: "Category selected",
1731
+ checkoutCreating: "Creating checkout…",
1732
+ checkoutSubmit: "Continue to payment",
1733
+ club: "Club",
1734
+ customFieldsTitle: "Additional information",
1735
+ documentId: "Document ID",
1736
+ emptyCategories: "No categories available.",
1737
+ emptyEvent: "Select an event.",
1738
+ emptyEvents: "No published events.",
1739
+ eventUnavailable: "Event unavailable.",
1740
+ female: "Female",
1741
+ guestAthlete: "Guest athlete",
1742
+ guestCategory: "Guest category",
1743
+ guestEmail: "Guest email",
1744
+ guestName: "Guest name",
1745
+ guestRegistrations: "Guest registrations",
1746
+ guestRegistrationsHelp: "Add another registration to the same payment.",
1747
+ addGuestRegistration: "Add guest",
1748
+ remove: "Remove",
1749
+ gender: "Gender",
1750
+ invalidDate: "Invalid date",
1751
+ invalidEmail: "Invalid email",
1752
+ invalidNumber: "Invalid number",
1753
+ invalidSelection: "Select a valid option",
1754
+ invalidUrl: "Invalid URL",
1755
+ loadingEvent: "Loading event…",
1756
+ loadingEvents: "Loading events…",
1757
+ loadingPricing: "Loading pricing…",
1758
+ loadingSummary: "Calculating summary…",
1759
+ male: "Male",
1760
+ noPublishableKey: "Configure publishable-key.",
1761
+ promocode: "Promo code",
1762
+ registerData: "Registration details",
1763
+ requiredField: "This field is required",
1764
+ requiredWaiver: "You must accept this waiver",
1765
+ select: "Select",
1766
+ selectCategory: "Select a category.",
1767
+ selectEvent: "View registrations",
1768
+ selectOption: "Select",
1769
+ summary: "Summary",
1770
+ teamName: "Team name",
1771
+ teamSize: "Team of",
1772
+ terms: "I accept the registration terms and conditions.",
1773
+ total: "Total",
1774
+ waiversTitle: "Legal documents"
1775
+ };
1776
+ function t(locale, key) {
1777
+ return locale?.toLowerCase().startsWith("en") ? en[key] : es[key];
1778
+ }
1779
+
1780
+ // src/ui/styles.ts
1781
+ var wodiraUiStyles = css`
1782
+ :host {
1783
+ --wodira-color-primary: #111827;
1784
+ --wodira-color-primary-contrast: #ffffff;
1785
+ --wodira-color-accent: #2563eb;
1786
+ --wodira-color-accent-contrast: #ffffff;
1787
+ --wodira-color-surface: #ffffff;
1788
+ --wodira-color-muted: #f3f4f6;
1789
+ --wodira-color-border: #d1d5db;
1790
+ --wodira-color-text: #111827;
1791
+ --wodira-color-text-muted: #6b7280;
1792
+ --wodira-color-danger: #dc2626;
1793
+ --wodira-radius: 1rem;
1794
+ --wodira-radius-sm: 0.625rem;
1795
+ --wodira-space: 1rem;
1796
+ --wodira-font-family:
1797
+ ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1798
+ --wodira-shadow: 0 20px 45px rgb(15 23 42 / 0.08);
1799
+ box-sizing: border-box;
1800
+ color: var(--wodira-color-text);
1801
+ display: block;
1802
+ font-family: var(--wodira-font-family);
1803
+ }
1804
+
1805
+ *,
1806
+ *::before,
1807
+ *::after {
1808
+ box-sizing: inherit;
1809
+ }
1810
+
1811
+ [hidden] {
1812
+ display: none !important;
1813
+ }
1814
+
1815
+ [part~='stack'] {
1816
+ display: grid;
1817
+ gap: var(--wodira-space);
1818
+ }
1819
+
1820
+ [part~='grid'] {
1821
+ display: grid;
1822
+ gap: var(--wodira-space);
1823
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1824
+ }
1825
+
1826
+ [part~='card'] {
1827
+ background: var(--wodira-color-surface);
1828
+ border: 1px solid var(--wodira-color-border);
1829
+ border-radius: var(--wodira-radius);
1830
+ box-shadow: var(--wodira-shadow);
1831
+ padding: calc(var(--wodira-space) * 1.25);
1832
+ }
1833
+
1834
+ [part~='button'] {
1835
+ align-items: center;
1836
+ background: var(--wodira-color-primary);
1837
+ border: 0;
1838
+ border-radius: var(--wodira-radius-sm);
1839
+ color: var(--wodira-color-primary-contrast);
1840
+ cursor: pointer;
1841
+ display: inline-flex;
1842
+ font: inherit;
1843
+ font-weight: 700;
1844
+ justify-content: center;
1845
+ min-height: 2.75rem;
1846
+ padding: 0.75rem 1rem;
1847
+ transition:
1848
+ opacity 0.2s ease,
1849
+ transform 0.2s ease;
1850
+ }
1851
+
1852
+ [part~='button']:hover:not(:disabled) {
1853
+ transform: translateY(-1px);
1854
+ }
1855
+
1856
+ [part~='button']:focus-visible,
1857
+ [part~='input']:focus-visible,
1858
+ [part~='select']:focus-visible,
1859
+ [part~='textarea']:focus-visible,
1860
+ [part~='checkbox']:focus-visible {
1861
+ outline: 3px solid color-mix(in srgb, var(--wodira-color-accent) 35%, transparent);
1862
+ outline-offset: 2px;
1863
+ }
1864
+
1865
+ [part~='button']:disabled {
1866
+ cursor: not-allowed;
1867
+ opacity: 0.6;
1868
+ }
1869
+
1870
+ [part~='button'][part~='secondary'] {
1871
+ background: var(--wodira-color-muted);
1872
+ color: var(--wodira-color-text);
1873
+ }
1874
+
1875
+ [part~='input'],
1876
+ [part~='select'],
1877
+ [part~='textarea'] {
1878
+ background: var(--wodira-color-surface);
1879
+ border: 1px solid var(--wodira-color-border);
1880
+ border-radius: var(--wodira-radius-sm);
1881
+ color: var(--wodira-color-text);
1882
+ font: inherit;
1883
+ min-height: 2.75rem;
1884
+ padding: 0.7rem 0.8rem;
1885
+ width: 100%;
1886
+ }
1887
+
1888
+ [part~='input'][aria-invalid='true'],
1889
+ [part~='select'][aria-invalid='true'],
1890
+ [part~='textarea'][aria-invalid='true'] {
1891
+ border-color: var(--wodira-color-danger);
1892
+ }
1893
+
1894
+ [part~='textarea'] {
1895
+ min-height: 5.5rem;
1896
+ resize: vertical;
1897
+ }
1898
+
1899
+ [part~='label'] {
1900
+ display: grid;
1901
+ gap: 0.35rem;
1902
+ font-size: 0.92rem;
1903
+ font-weight: 700;
1904
+ }
1905
+
1906
+ [part~='checkbox'] {
1907
+ margin-inline-end: 0.5rem;
1908
+ }
1909
+
1910
+ [part~='hint'],
1911
+ [part~='meta'],
1912
+ [part~='field-error'] {
1913
+ color: var(--wodira-color-text-muted);
1914
+ font-size: 0.875rem;
1915
+ }
1916
+
1917
+ [part~='field-error'] {
1918
+ color: var(--wodira-color-danger);
1919
+ }
1920
+
1921
+ [part~='error'] {
1922
+ background: color-mix(in srgb, var(--wodira-color-danger) 10%, transparent);
1923
+ border: 1px solid color-mix(in srgb, var(--wodira-color-danger) 35%, transparent);
1924
+ border-radius: var(--wodira-radius-sm);
1925
+ color: var(--wodira-color-danger);
1926
+ padding: 0.75rem 0.9rem;
1927
+ }
1928
+
1929
+ [part~='loader'],
1930
+ [part~='empty'] {
1931
+ color: var(--wodira-color-text-muted);
1932
+ padding: var(--wodira-space);
1933
+ text-align: center;
1934
+ }
1935
+
1936
+ [part~='title'] {
1937
+ font-size: clamp(1.15rem, 2vw, 1.5rem);
1938
+ font-weight: 800;
1939
+ line-height: 1.15;
1940
+ margin: 0;
1941
+ }
1942
+
1943
+ [part~='price'] {
1944
+ color: var(--wodira-color-accent);
1945
+ font-size: 1.35rem;
1946
+ font-weight: 850;
1947
+ }
1948
+
1949
+ [part~='image'] {
1950
+ aspect-ratio: 16 / 9;
1951
+ border-radius: calc(var(--wodira-radius) * 0.75);
1952
+ display: block;
1953
+ margin-block-end: var(--wodira-space);
1954
+ object-fit: cover;
1955
+ width: 100%;
1956
+ }
1957
+
1958
+ [part~='summary-row'] {
1959
+ display: flex;
1960
+ justify-content: space-between;
1961
+ gap: var(--wodira-space);
1962
+ }
1963
+ `;
1964
+
1965
+ // src/ui/components.ts
1966
+ var DEFAULT_LIMIT = 12;
1967
+ var DEFAULT_LOCALE = "es-ES";
1968
+ var DEFAULT_CURRENCY = "EUR";
1969
+ function createClient(options) {
1970
+ const clientOptions = { publishableKey: options.publishableKey };
1971
+ if (options.baseUrl)
1972
+ clientOptions.baseUrl = options.baseUrl;
1973
+ return createWodiraBrowserClient(clientOptions);
1974
+ }
1975
+ function resolveLocale(locale) {
1976
+ const explicitLocale = normalizeLocale(locale);
1977
+ if (explicitLocale)
1978
+ return explicitLocale;
1979
+ const documentLocale = normalizeLocale(globalThis.document?.documentElement?.lang);
1980
+ if (documentLocale)
1981
+ return documentLocale;
1982
+ const navigatorLocales = globalThis.navigator?.languages;
1983
+ const navigatorLocale = normalizeLocale(navigatorLocales && navigatorLocales.length > 0 ? navigatorLocales[0] : globalThis.navigator?.language);
1984
+ return navigatorLocale ?? DEFAULT_LOCALE;
1985
+ }
1986
+ function normalizeLocale(locale) {
1987
+ const normalized = locale?.trim();
1988
+ if (!normalized)
1989
+ return;
1990
+ try {
1991
+ return Intl.getCanonicalLocales(normalized)[0] ?? normalized;
1992
+ } catch {
1993
+ return normalized;
1994
+ }
1995
+ }
1996
+ function formatDate(date, locale) {
1997
+ if (!date)
1998
+ return "";
1999
+ const parsed = new Date(date);
2000
+ if (Number.isNaN(parsed.getTime()))
2001
+ return date;
2002
+ return new Intl.DateTimeFormat(resolveLocale(locale), { dateStyle: "medium" }).format(parsed);
2003
+ }
2004
+ function formatNumber(value, locale, options = {}) {
2005
+ if (value === undefined || value === null || !Number.isFinite(value))
2006
+ return "—";
2007
+ return new Intl.NumberFormat(resolveLocale(locale), {
2008
+ maximumFractionDigits: Number.isInteger(value) ? 0 : 2,
2009
+ ...options
2010
+ }).format(value);
2011
+ }
2012
+ function formatMoney(value, currency = DEFAULT_CURRENCY, locale, formatOptions = {}) {
2013
+ if (value === undefined)
2014
+ return "—";
2015
+ const resolvedLocale = resolveLocale(locale);
2016
+ if (value === null || formatOptions.zeroAsFree === true && value === 0) {
2017
+ return resolvedLocale.toLowerCase().startsWith("en") ? "Free" : "Gratis";
2018
+ }
2019
+ if (!Number.isFinite(value))
2020
+ return "—";
2021
+ const normalizedCurrency = normalizeCurrency(currency);
2022
+ const absoluteValue = Math.abs(value);
2023
+ const fractionDigits = getCurrencyFractionDigits(absoluteValue, normalizedCurrency, resolvedLocale);
2024
+ const options = {
2025
+ style: "currency",
2026
+ currency: normalizedCurrency,
2027
+ currencyDisplay: "narrowSymbol",
2028
+ minimumFractionDigits: Number.isInteger(absoluteValue) ? 0 : fractionDigits.minimum,
2029
+ maximumFractionDigits: Number.isInteger(absoluteValue) ? 0 : fractionDigits.maximum
2030
+ };
2031
+ try {
2032
+ return new Intl.NumberFormat(resolvedLocale, options).format(value);
2033
+ } catch {
2034
+ return `${formatPlainNumber(value, resolvedLocale, options.maximumFractionDigits ?? 2)} ${normalizedCurrency}`;
2035
+ }
2036
+ }
2037
+ function normalizeCurrency(currency) {
2038
+ const normalized = (currency ?? DEFAULT_CURRENCY).trim().toUpperCase();
2039
+ return /^[A-Z]{3}$/.test(normalized) ? normalized : DEFAULT_CURRENCY;
2040
+ }
2041
+ function getCurrencyFractionDigits(value, currency, locale) {
2042
+ if (Number.isInteger(value))
2043
+ return { minimum: 0, maximum: 0 };
2044
+ try {
2045
+ const resolved = new Intl.NumberFormat(locale, {
2046
+ style: "currency",
2047
+ currency
2048
+ }).resolvedOptions();
2049
+ const minimumFractionDigits = resolved.minimumFractionDigits ?? 2;
2050
+ const maximumFractionDigits = resolved.maximumFractionDigits ?? minimumFractionDigits;
2051
+ return {
2052
+ minimum: minimumFractionDigits,
2053
+ maximum: Math.max(maximumFractionDigits, 2)
2054
+ };
2055
+ } catch {
2056
+ return { minimum: 2, maximum: 2 };
2057
+ }
2058
+ }
2059
+ function formatPlainNumber(value, locale, maximumFractionDigits) {
2060
+ return new Intl.NumberFormat(locale, {
2061
+ minimumFractionDigits: Number.isInteger(value) ? 0 : Math.min(2, maximumFractionDigits),
2062
+ maximumFractionDigits
2063
+ }).format(value);
2064
+ }
2065
+ function isCheckboxField(field) {
2066
+ return field.type === "CHECKBOX";
2067
+ }
2068
+ function emptyAthlete() {
2069
+ return {
2070
+ fullname: "",
2071
+ email: "",
2072
+ phone: "",
2073
+ idNumber: "",
2074
+ gender: "FEMALE",
2075
+ birthDate: ""
2076
+ };
2077
+ }
2078
+ function createIdempotencyKey() {
2079
+ return globalThis.crypto?.randomUUID?.() ?? `checkout_${Date.now()}_${Math.random().toString(36).slice(2)}`;
2080
+ }
2081
+ function isFormFieldTargetAthlete(field) {
2082
+ return field.target === "ATHLETE";
2083
+ }
2084
+ function fieldErrorId(fieldName) {
2085
+ return `wodira-error-${fieldName.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
2086
+ }
2087
+ function validationMessage(locale, result) {
2088
+ if (result.valid)
2089
+ return "";
2090
+ if (result.reason === "email")
2091
+ return t(locale, "invalidEmail");
2092
+ if (result.reason === "url")
2093
+ return t(locale, "invalidUrl");
2094
+ if (result.reason === "date")
2095
+ return t(locale, "invalidDate");
2096
+ if (result.reason === "number")
2097
+ return t(locale, "invalidNumber");
2098
+ if (result.reason === "selection")
2099
+ return t(locale, "invalidSelection");
2100
+ return t(locale, "requiredField");
2101
+ }
2102
+ function setValidationError(errors, locale, key, result) {
2103
+ if (!result.valid)
2104
+ errors[key] = validationMessage(locale, result);
2105
+ }
2106
+ function hasText(value) {
2107
+ return typeof value === "string" && value.trim().length > 0;
2108
+ }
2109
+
2110
+ class WodiraEventCard extends LitElement {
2111
+ static styles = [wodiraUiStyles];
2112
+ static properties = {
2113
+ event: { attribute: false },
2114
+ locale: { type: String }
2115
+ };
2116
+ constructor() {
2117
+ super();
2118
+ this.locale = "";
2119
+ }
2120
+ render() {
2121
+ if (!this.event)
2122
+ return html`<article part="card empty">${t(this.locale, "eventUnavailable")}</article>`;
2123
+ const event = this.event;
2124
+ return html`
2125
+ <article part="card event-card">
2126
+ ${event.banner ? html`<img part="image" src=${event.banner} alt=${event.name} loading="lazy" />` : nothing}
2127
+ <div part="stack">
2128
+ <h3 part="title">${event.name}</h3>
2129
+ <p part="meta">
2130
+ ${formatDate(event.startDate, this.locale)}${event.locationAddress ? ` · ${event.locationAddress}` : ""}
2131
+ </p>
2132
+ ${event.descriptionText ? html`<p part="description">${event.descriptionText}</p>` : nothing}
2133
+ <button part="button" type="button" @click=${this.selectEvent}>
2134
+ ${t(this.locale, "selectEvent")}
2135
+ </button>
2136
+ </div>
2137
+ </article>
2138
+ `;
2139
+ }
2140
+ selectEvent() {
2141
+ if (!this.event)
2142
+ return;
2143
+ emitWodiraEvent(this, "wodira:event-select", { event: this.event });
2144
+ }
2145
+ }
2146
+
2147
+ class WodiraEventList extends LitElement {
2148
+ static styles = [wodiraUiStyles];
2149
+ static properties = {
2150
+ publishableKey: { type: String, attribute: "publishable-key" },
2151
+ baseUrl: { type: String, attribute: "base-url" },
2152
+ search: { type: String },
2153
+ limit: { type: Number },
2154
+ locale: { type: String },
2155
+ loading: { type: Boolean, state: true },
2156
+ error: { type: String, state: true },
2157
+ eventItems: { state: true }
2158
+ };
2159
+ didInitialLoad = false;
2160
+ constructor() {
2161
+ super();
2162
+ this.publishableKey = "";
2163
+ this.search = "";
2164
+ this.limit = DEFAULT_LIMIT;
2165
+ this.locale = "";
2166
+ this.loading = false;
2167
+ this.error = "";
2168
+ this.eventItems = [];
2169
+ }
2170
+ firstUpdated() {
2171
+ this.didInitialLoad = true;
2172
+ this.loadEvents();
2173
+ }
2174
+ updated(changed) {
2175
+ if (this.didInitialLoad && (changed.has("publishableKey") || changed.has("baseUrl") || changed.has("search") || changed.has("limit"))) {
2176
+ this.loadEvents();
2177
+ }
2178
+ }
2179
+ render() {
2180
+ if (!this.publishableKey)
2181
+ return html`<div part="error" role="alert">${t(this.locale, "noPublishableKey")}</div>`;
2182
+ if (this.loading)
2183
+ return html`<div part="loader" role="status" aria-live="polite">
2184
+ ${t(this.locale, "loadingEvents")}
2185
+ </div>`;
2186
+ if (this.error)
2187
+ return html`<div part="error" role="alert">${this.error}</div>`;
2188
+ if (this.eventItems.length === 0)
2189
+ return html`<div part="empty">${t(this.locale, "emptyEvents")}</div>`;
2190
+ return html`
2191
+ <section part="stack event-list" aria-busy=${this.loading ? "true" : "false"}>
2192
+ ${this.eventItems.map((event) => html`<wodira-event-card .event=${event} locale=${this.locale}></wodira-event-card>`)}
2193
+ </section>
2194
+ `;
2195
+ }
2196
+ validateClientConfig() {
2197
+ if (this.baseUrl && !validateHttpUrl(this.baseUrl, false).valid) {
2198
+ throw new Error(t(this.locale, "invalidUrl"));
2199
+ }
2200
+ }
2201
+ async loadEvents() {
2202
+ if (!this.isConnected)
2203
+ return;
2204
+ if (!hasText(this.publishableKey)) {
2205
+ this.eventItems = [];
2206
+ return;
2207
+ }
2208
+ this.loading = true;
2209
+ this.error = "";
2210
+ try {
2211
+ this.validateClientConfig();
2212
+ const client = createClient({
2213
+ publishableKey: this.publishableKey.trim(),
2214
+ baseUrl: this.baseUrl
2215
+ });
2216
+ const limit = normalizeLimit(this.limit, DEFAULT_LIMIT, 100);
2217
+ const searchInput = this.search.trim() ? { search: this.search.trim(), limit } : { limit };
2218
+ const response = await client.searchEvents(searchInput);
2219
+ this.eventItems = response.data;
2220
+ } catch (error) {
2221
+ this.error = getErrorMessage(error);
2222
+ emitWodiraEvent(this, "wodira:error", { error, message: this.error });
2223
+ } finally {
2224
+ this.loading = false;
2225
+ }
2226
+ }
2227
+ }
2228
+
2229
+ class WodiraPricingCards extends LitElement {
2230
+ static styles = [wodiraUiStyles];
2231
+ static properties = {
2232
+ publishableKey: { type: String, attribute: "publishable-key" },
2233
+ baseUrl: { type: String, attribute: "base-url" },
2234
+ eventId: { type: String, attribute: "event-id" },
2235
+ selectedCategoryId: { type: String, attribute: "selected-category-id" },
2236
+ locale: { type: String },
2237
+ pricing: { state: true },
2238
+ loading: { type: Boolean, state: true },
2239
+ error: { type: String, state: true }
2240
+ };
2241
+ didInitialLoad = false;
2242
+ constructor() {
2243
+ super();
2244
+ this.publishableKey = "";
2245
+ this.eventId = "";
2246
+ this.selectedCategoryId = "";
2247
+ this.locale = "";
2248
+ this.loading = false;
2249
+ this.error = "";
2250
+ }
2251
+ firstUpdated() {
2252
+ this.didInitialLoad = true;
2253
+ this.loadPricing();
2254
+ }
2255
+ updated(changed) {
2256
+ if (this.didInitialLoad && (changed.has("publishableKey") || changed.has("baseUrl") || changed.has("eventId"))) {
2257
+ this.loadPricing();
2258
+ }
2259
+ }
2260
+ render() {
2261
+ if (!this.eventId)
2262
+ return html`<div part="empty">${t(this.locale, "emptyEvent")}</div>`;
2263
+ if (!this.publishableKey)
2264
+ return html`<div part="error" role="alert">${t(this.locale, "noPublishableKey")}</div>`;
2265
+ if (this.loading)
2266
+ return html`<div part="loader" role="status" aria-live="polite">
2267
+ ${t(this.locale, "loadingPricing")}
2268
+ </div>`;
2269
+ if (this.error)
2270
+ return html`<div part="error" role="alert">${this.error}</div>`;
2271
+ if (!this.pricing?.cards.length)
2272
+ return html`<div part="empty">${t(this.locale, "emptyCategories")}</div>`;
2273
+ return html`
2274
+ <section part="stack pricing-cards">
2275
+ ${this.pricing.cards.map((card) => this.renderCard(card, this.pricing?.currency ?? "EUR"))}
2276
+ </section>
2277
+ `;
2278
+ }
2279
+ renderCard(card, currency) {
2280
+ const selected = this.selectedCategoryId === card.id;
2281
+ const remaining = card.quota === null || card.quota === undefined ? null : Math.max(0, card.quota - card.registered);
2282
+ return html`
2283
+ <article part="card pricing-card ${selected ? "selected" : ""}">
2284
+ <div part="stack">
2285
+ <h3 part="title">${card.name}</h3>
2286
+ ${card.description ? html`<p part="description">${card.description}</p>` : nothing}
2287
+ <div part="price">
2288
+ ${formatMoney(card.activePrice ?? card.price, currency, this.locale, {
2289
+ zeroAsFree: true
2290
+ })}
2291
+ </div>
2292
+ <p part="meta">
2293
+ ${t(this.locale, "teamSize")}
2294
+ ${formatNumber(card.teamSize, this.locale)}${remaining === null ? "" : ` · ${formatNumber(remaining, this.locale)} ${t(this.locale, "availableSlots")}`}
2295
+ </p>
2296
+ <button
2297
+ part="button"
2298
+ type="button"
2299
+ aria-pressed=${selected ? "true" : "false"}
2300
+ @click=${() => this.selectCategory(card)}
2301
+ >
2302
+ ${selected ? t(this.locale, "categorySelected") : t(this.locale, "select")}
2303
+ </button>
2304
+ </div>
2305
+ </article>
2306
+ `;
2307
+ }
2308
+ validateClientConfig() {
2309
+ if (this.baseUrl && !validateHttpUrl(this.baseUrl, false).valid) {
2310
+ throw new Error(t(this.locale, "invalidUrl"));
2311
+ }
2312
+ }
2313
+ async loadPricing() {
2314
+ if (!this.isConnected)
2315
+ return;
2316
+ if (!hasText(this.publishableKey) || !hasText(this.eventId))
2317
+ return;
2318
+ this.loading = true;
2319
+ this.error = "";
2320
+ try {
2321
+ this.validateClientConfig();
2322
+ const client = createClient({
2323
+ publishableKey: this.publishableKey.trim(),
2324
+ baseUrl: this.baseUrl
2325
+ });
2326
+ this.pricing = await client.getPricingCards(this.eventId.trim());
2327
+ } catch (error) {
2328
+ this.error = getErrorMessage(error);
2329
+ emitWodiraEvent(this, "wodira:error", { error, message: this.error });
2330
+ } finally {
2331
+ this.loading = false;
2332
+ }
2333
+ }
2334
+ selectCategory(category) {
2335
+ this.selectedCategoryId = category.id;
2336
+ emitWodiraEvent(this, "wodira:category-select", { category, eventId: this.eventId });
2337
+ }
2338
+ }
2339
+
2340
+ class WodiraRegistrationForm extends LitElement {
2341
+ static styles = [wodiraUiStyles];
2342
+ static properties = {
2343
+ publishableKey: { type: String, attribute: "publishable-key" },
2344
+ baseUrl: { type: String, attribute: "base-url" },
2345
+ eventId: { type: String, attribute: "event-id" },
2346
+ categoryId: { type: String, attribute: "category-id" },
2347
+ successUrl: { type: String, attribute: "success-url" },
2348
+ cancelUrl: { type: String, attribute: "cancel-url" },
2349
+ locale: { type: String },
2350
+ autoRedirect: { type: Boolean, attribute: "auto-redirect" },
2351
+ event: { attribute: false },
2352
+ loading: { type: Boolean, state: true },
2353
+ error: { type: String, state: true },
2354
+ teamName: { state: true },
2355
+ club: { state: true },
2356
+ purchaserName: { state: true },
2357
+ purchaserEmail: { state: true },
2358
+ promocode: { state: true },
2359
+ termsAccepted: { type: Boolean, state: true },
2360
+ athletes: { state: true },
2361
+ customFieldValues: { state: true },
2362
+ waiversAccepted: { state: true },
2363
+ selectedSupplementIds: { state: true },
2364
+ guestRegistrations: { state: true },
2365
+ summary: { state: true },
2366
+ summaryLoading: { type: Boolean, state: true },
2367
+ fieldErrors: { state: true }
2368
+ };
2369
+ summaryRequestId = 0;
2370
+ summaryTimer;
2371
+ constructor() {
2372
+ super();
2373
+ this.publishableKey = "";
2374
+ this.eventId = "";
2375
+ this.categoryId = "";
2376
+ this.successUrl = "";
2377
+ this.cancelUrl = "";
2378
+ this.locale = "";
2379
+ this.autoRedirect = true;
2380
+ this.loading = false;
2381
+ this.error = "";
2382
+ this.teamName = "";
2383
+ this.club = "";
2384
+ this.purchaserName = "";
2385
+ this.purchaserEmail = "";
2386
+ this.promocode = "";
2387
+ this.termsAccepted = false;
2388
+ this.athletes = [emptyAthlete()];
2389
+ this.customFieldValues = {};
2390
+ this.waiversAccepted = {};
2391
+ this.selectedSupplementIds = [];
2392
+ this.guestRegistrations = [];
2393
+ this.summary = undefined;
2394
+ this.summaryLoading = false;
2395
+ this.fieldErrors = {};
2396
+ }
2397
+ updated(changed) {
2398
+ if (changed.has("categoryId") || changed.has("event")) {
2399
+ this.syncAthleteCount();
2400
+ this.syncRequiredSupplements();
2401
+ this.syncGuestRegistrations();
2402
+ }
2403
+ if (changed.has("categoryId") || changed.has("purchaserEmail") || changed.has("promocode") || changed.has("selectedSupplementIds") || changed.has("guestRegistrations")) {
2404
+ this.scheduleSummaryLoad();
2405
+ }
2406
+ }
2407
+ render() {
2408
+ const category = this.selectedCategory;
2409
+ if (!this.eventId || !this.categoryId)
2410
+ return html`<div part="empty">${t(this.locale, "selectCategory")}</div>`;
2411
+ if (!this.publishableKey)
2412
+ return html`<div part="error" role="alert">${t(this.locale, "noPublishableKey")}</div>`;
2413
+ return html`
2414
+ <form part="card registration-form" @submit=${this.submit} novalidate>
2415
+ <div part="stack">
2416
+ <h3 part="title">
2417
+ ${t(this.locale, "registerData")}${category ? ` · ${category.name}` : ""}
2418
+ </h3>
2419
+ ${this.error ? html`<div part="error" role="alert">${this.error}</div>` : nothing}
2420
+ <div part="grid purchaser-grid">
2421
+ ${this.renderInput(t(this.locale, "buyerName"), "purchaserName", this.purchaserName, "text", true)}
2422
+ ${this.renderInput(t(this.locale, "buyerEmail"), "purchaserEmail", this.purchaserEmail, "email", true)}
2423
+ ${this.renderInput(t(this.locale, "teamName"), "teamName", this.teamName, "text", true)}
2424
+ ${this.renderInput(t(this.locale, "club"), "club", this.club, "text", false)}
2425
+ ${this.renderInput(t(this.locale, "promocode"), "promocode", this.promocode, "text", false)}
2426
+ </div>
2427
+ ${this.athletes.map((athlete, index) => this.renderAthlete(athlete, index))}
2428
+ ${this.renderCustomFields()} ${this.renderSupplements()} ${this.renderGuestRegistrations()}
2429
+ ${this.renderWaivers()}
2430
+ <label part="label checkbox terms">
2431
+ <span>
2432
+ <input
2433
+ part="checkbox"
2434
+ type="checkbox"
2435
+ .checked=${this.termsAccepted}
2436
+ aria-invalid=${this.fieldErrors.termsAccepted ? "true" : "false"}
2437
+ @change=${(event) => this.setTermsAccepted(event.target.checked)}
2438
+ />
2439
+ ${t(this.locale, "terms")}
2440
+ </span>
2441
+ ${this.renderFieldError("termsAccepted")}
2442
+ </label>
2443
+ ${this.renderSummary()}
2444
+ <button part="button submit" type="submit" ?disabled=${this.loading}>
2445
+ ${this.loading ? t(this.locale, "checkoutCreating") : t(this.locale, "checkoutSubmit")}
2446
+ </button>
2447
+ </div>
2448
+ </form>
2449
+ `;
2450
+ }
2451
+ get selectedCategory() {
2452
+ return this.event?.categories.find((category) => category.id === this.categoryId);
2453
+ }
2454
+ get activeSupplements() {
2455
+ return (this.event?.supplements ?? []).filter((supplement) => supplement.isActive !== false);
2456
+ }
2457
+ renderInput(label, key, value, type = "text", required = false) {
2458
+ const id = fieldErrorId(key);
2459
+ return html`
2460
+ <label part="label field">
2461
+ <span>${label}${required ? " *" : ""}</span>
2462
+ <input
2463
+ part="input"
2464
+ type=${type}
2465
+ .value=${value}
2466
+ aria-invalid=${this.fieldErrors[key] ? "true" : "false"}
2467
+ aria-describedby=${this.fieldErrors[key] ? id : nothing}
2468
+ @input=${(event) => this.updateTextField(key, event.target.value)}
2469
+ />
2470
+ ${this.renderFieldError(key)}
2471
+ </label>
2472
+ `;
2473
+ }
2474
+ renderAthlete(athlete, index) {
2475
+ return html`
2476
+ <fieldset part="card athlete">
2477
+ <legend part="title">Atleta ${index + 1}</legend>
2478
+ <div part="grid athlete-grid">
2479
+ ${this.renderAthleteInput(index, "fullname", "Nombre completo", "text", athlete.fullname)}
2480
+ ${this.renderAthleteInput(index, "email", "Email", "email", athlete.email)}
2481
+ ${this.renderAthleteInput(index, "phone", "Teléfono", "tel", athlete.phone)}
2482
+ ${this.renderAthleteInput(index, "idNumber", t(this.locale, "documentId"), "text", athlete.idNumber)}
2483
+ ${this.renderAthleteInput(index, "birthDate", "Fecha nacimiento", "date", athlete.birthDate)}
2484
+ <label part="label field">
2485
+ <span>${t(this.locale, "gender")} *</span>
2486
+ <select
2487
+ part="select"
2488
+ .value=${athlete.gender}
2489
+ aria-invalid=${this.fieldErrors[`athletes.${index}.gender`] ? "true" : "false"}
2490
+ aria-describedby=${this.fieldErrors[`athletes.${index}.gender`] ? fieldErrorId(`athletes.${index}.gender`) : nothing}
2491
+ @change=${(event) => this.updateAthlete(index, "gender", event.target.value)}
2492
+ >
2493
+ <option value="FEMALE">${t(this.locale, "female")}</option>
2494
+ <option value="MALE">${t(this.locale, "male")}</option>
2495
+ </select>
2496
+ ${this.renderFieldError(`athletes.${index}.gender`)}
2497
+ </label>
2498
+ </div>
2499
+ </fieldset>
2500
+ `;
2501
+ }
2502
+ renderAthleteInput(index, key, label, type, value) {
2503
+ const errorKey = `athletes.${index}.${key}`;
2504
+ const id = fieldErrorId(errorKey);
2505
+ return html`
2506
+ <label part="label field">
2507
+ <span>${label} *</span>
2508
+ <input
2509
+ part="input"
2510
+ type=${type}
2511
+ .value=${value}
2512
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2513
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2514
+ @input=${(event) => this.updateAthlete(index, key, event.target.value)}
2515
+ />
2516
+ ${this.renderFieldError(errorKey)}
2517
+ </label>
2518
+ `;
2519
+ }
2520
+ renderCustomFields() {
2521
+ const fields = this.event?.eventFormFields.filter(isFormFieldTargetAthlete) ?? [];
2522
+ if (!fields.length)
2523
+ return nothing;
2524
+ return html`
2525
+ <fieldset part="card custom-fields">
2526
+ <legend part="title">${t(this.locale, "customFieldsTitle")}</legend>
2527
+ <div part="grid custom-fields-grid">
2528
+ ${fields.map((field) => this.renderCustomField(field))}
2529
+ </div>
2530
+ </fieldset>
2531
+ `;
2532
+ }
2533
+ renderCustomField(field) {
2534
+ const errorKey = `customFields.${field.id}`;
2535
+ const id = fieldErrorId(errorKey);
2536
+ if (field.type === "SELECT") {
2537
+ return html`
2538
+ <label part="label field custom-field">
2539
+ <span>${field.label}${field.isRequired ? " *" : ""}</span>
2540
+ <select
2541
+ part="select"
2542
+ .value=${String(this.customFieldValues[field.id] ?? "")}
2543
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2544
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2545
+ @change=${(event) => this.updateCustomField(field.id, event.target.value)}
2546
+ >
2547
+ <option value="">${t(this.locale, "selectOption")}</option>
2548
+ ${field.options.map((option) => html`<option value=${option}>${option}</option>`)}
2549
+ </select>
2550
+ ${this.renderFieldError(errorKey)}
2551
+ </label>
2552
+ `;
2553
+ }
2554
+ if (isCheckboxField(field)) {
2555
+ return html`
2556
+ <label part="label checkbox custom-field">
2557
+ <span>
2558
+ <input
2559
+ part="checkbox"
2560
+ type="checkbox"
2561
+ .checked=${this.customFieldValues[field.id] === true}
2562
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2563
+ @change=${(event) => this.updateCustomField(field.id, event.target.checked)}
2564
+ />
2565
+ ${field.label}${field.isRequired ? " *" : ""}
2566
+ </span>
2567
+ ${this.renderFieldError(errorKey)}
2568
+ </label>
2569
+ `;
2570
+ }
2571
+ if (field.type === "TEXTAREA") {
2572
+ return html`
2573
+ <label part="label field custom-field">
2574
+ <span>${field.label}${field.isRequired ? " *" : ""}</span>
2575
+ <textarea
2576
+ part="textarea"
2577
+ .value=${String(this.customFieldValues[field.id] ?? "")}
2578
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2579
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2580
+ @input=${(event) => this.updateCustomField(field.id, event.target.value)}
2581
+ ></textarea>
2582
+ ${this.renderFieldError(errorKey)}
2583
+ </label>
2584
+ `;
2585
+ }
2586
+ const inputType = field.type === "DATE" ? "date" : field.type === "NUMBER" ? "number" : "text";
2587
+ return html`
2588
+ <label part="label field custom-field">
2589
+ <span>${field.label}${field.isRequired ? " *" : ""}</span>
2590
+ <input
2591
+ part="input"
2592
+ type=${inputType}
2593
+ .value=${String(this.customFieldValues[field.id] ?? "")}
2594
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2595
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2596
+ @input=${(event) => this.updateCustomField(field.id, event.target.value)}
2597
+ />
2598
+ ${this.renderFieldError(errorKey)}
2599
+ </label>
2600
+ `;
2601
+ }
2602
+ renderSupplements() {
2603
+ const supplements = this.activeSupplements;
2604
+ if (!supplements.length)
2605
+ return nothing;
2606
+ const currency = this.event?.currency ?? "EUR";
2607
+ return html`
2608
+ <fieldset part="card supplements">
2609
+ <legend part="title">Extras</legend>
2610
+ <div part="stack">
2611
+ ${supplements.map((supplement) => html`
2612
+ <label part="label checkbox supplement">
2613
+ <span>
2614
+ <input
2615
+ part="checkbox"
2616
+ type="checkbox"
2617
+ .checked=${this.selectedSupplementIds.includes(supplement.id)}
2618
+ ?disabled=${supplement.isRequired}
2619
+ @change=${(event) => this.updateSupplement(supplement.id, event.target.checked)}
2620
+ />
2621
+ ${supplement.name}${supplement.isRequired ? " *" : ""} ·
2622
+ ${formatMoney(supplement.price, currency, this.locale, { zeroAsFree: true })}
2623
+ </span>
2624
+ ${supplement.description ? html`<small part="hint">${supplement.description}</small>` : nothing}
2625
+ </label>
2626
+ `)}
2627
+ </div>
2628
+ </fieldset>
2629
+ `;
2630
+ }
2631
+ renderGuestRegistrations() {
2632
+ const categories = this.event?.categories.filter((category) => !category.isPrivate) ?? [];
2633
+ return html`
2634
+ <fieldset part="card guest-registrations">
2635
+ <legend part="title">${t(this.locale, "guestRegistrations")}</legend>
2636
+ <p part="hint">${t(this.locale, "guestRegistrationsHelp")}</p>
2637
+ <div part="stack">
2638
+ ${this.guestRegistrations.map((guest, index) => this.renderGuestRegistration(guest, index))}
2639
+ <button part="button secondary add-guest" type="button" @click=${this.addGuestRegistration}>
2640
+ ${t(this.locale, "addGuestRegistration")}
2641
+ </button>
2642
+ </div>
2643
+ ${categories.length === 0 ? html`<small part="hint">${t(this.locale, "emptyCategories")}</small>` : nothing}
2644
+ </fieldset>
2645
+ `;
2646
+ }
2647
+ renderGuestRegistration(guest, index) {
2648
+ const categories = this.event?.categories.filter((category) => !category.isPrivate) ?? [];
2649
+ const guestCategory = this.getCategory(guest.categoryId);
2650
+ return html`
2651
+ <fieldset part="card guest-registration">
2652
+ <legend part="title">${t(this.locale, "guestRegistrations")} #${index + 1}</legend>
2653
+ <div part="stack">
2654
+ <div part="grid guest-grid">
2655
+ ${this.renderGuestInput(index, "guestName", t(this.locale, "guestName"), guest.guestName, "text", true)}
2656
+ ${this.renderGuestInput(index, "guestEmail", t(this.locale, "guestEmail"), guest.guestEmail, "email", true)}
2657
+ <label part="label field">
2658
+ <span>${t(this.locale, "guestCategory")} *</span>
2659
+ <select
2660
+ part="select"
2661
+ .value=${guest.categoryId}
2662
+ aria-invalid=${this.fieldErrors[`guestRegistrations.${index}.categoryId`] ? "true" : "false"}
2663
+ @change=${(event) => this.updateGuestCategory(index, event.target.value)}
2664
+ >
2665
+ <option value="">${t(this.locale, "selectOption")}</option>
2666
+ ${categories.map((category) => html`<option value=${category.id}>${category.name}</option>`)}
2667
+ </select>
2668
+ ${this.renderFieldError(`guestRegistrations.${index}.categoryId`)}
2669
+ </label>
2670
+ ${this.renderGuestInput(index, "teamName", t(this.locale, "teamName"), guest.teamName, "text", true)}
2671
+ ${this.renderGuestInput(index, "club", t(this.locale, "club"), guest.club, "text", false)}
2672
+ </div>
2673
+ ${guest.athletes.map((athlete, athleteIndex) => this.renderGuestAthlete(index, athlete, athleteIndex))}
2674
+ ${this.renderGuestSupplements(guest, index)}
2675
+ <button part="button secondary remove-guest" type="button" @click=${() => this.removeGuestRegistration(index)}>
2676
+ ${t(this.locale, "remove")}
2677
+ </button>
2678
+ ${guestCategory ? html`<small part="hint">${t(this.locale, "teamSize")} ${formatNumber(guestCategory.teamSize, this.locale)}</small>` : nothing}
2679
+ </div>
2680
+ </fieldset>
2681
+ `;
2682
+ }
2683
+ renderGuestInput(index, key, label, value, type, required) {
2684
+ const errorKey = `guestRegistrations.${index}.${key}`;
2685
+ const id = fieldErrorId(errorKey);
2686
+ return html`
2687
+ <label part="label field">
2688
+ <span>${label}${required ? " *" : ""}</span>
2689
+ <input
2690
+ part="input"
2691
+ type=${type}
2692
+ .value=${value}
2693
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2694
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2695
+ @input=${(event) => this.updateGuestField(index, key, event.target.value)}
2696
+ />
2697
+ ${this.renderFieldError(errorKey)}
2698
+ </label>
2699
+ `;
2700
+ }
2701
+ renderGuestAthlete(guestIndex, athlete, athleteIndex) {
2702
+ return html`
2703
+ <fieldset part="card guest-athlete">
2704
+ <legend part="title">${t(this.locale, "guestAthlete")} ${athleteIndex + 1}</legend>
2705
+ <div part="grid athlete-grid">
2706
+ ${this.renderGuestAthleteInput(guestIndex, athleteIndex, "fullname", "Nombre completo", "text", athlete.fullname)}
2707
+ ${this.renderGuestAthleteInput(guestIndex, athleteIndex, "email", "Email", "email", athlete.email)}
2708
+ ${this.renderGuestAthleteInput(guestIndex, athleteIndex, "phone", "Teléfono", "tel", athlete.phone)}
2709
+ ${this.renderGuestAthleteInput(guestIndex, athleteIndex, "idNumber", t(this.locale, "documentId"), "text", athlete.idNumber)}
2710
+ ${this.renderGuestAthleteInput(guestIndex, athleteIndex, "birthDate", "Fecha nacimiento", "date", athlete.birthDate)}
2711
+ <label part="label field">
2712
+ <span>${t(this.locale, "gender")} *</span>
2713
+ <select
2714
+ part="select"
2715
+ .value=${athlete.gender}
2716
+ aria-invalid=${this.fieldErrors[`guestRegistrations.${guestIndex}.athletes.${athleteIndex}.gender`] ? "true" : "false"}
2717
+ @change=${(event) => this.updateGuestAthlete(guestIndex, athleteIndex, "gender", event.target.value)}
2718
+ >
2719
+ <option value="FEMALE">${t(this.locale, "female")}</option>
2720
+ <option value="MALE">${t(this.locale, "male")}</option>
2721
+ </select>
2722
+ ${this.renderFieldError(`guestRegistrations.${guestIndex}.athletes.${athleteIndex}.gender`)}
2723
+ </label>
2724
+ </div>
2725
+ </fieldset>
2726
+ `;
2727
+ }
2728
+ renderGuestAthleteInput(guestIndex, athleteIndex, key, label, type, value) {
2729
+ const errorKey = `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.${key}`;
2730
+ const id = fieldErrorId(errorKey);
2731
+ return html`
2732
+ <label part="label field">
2733
+ <span>${label} *</span>
2734
+ <input
2735
+ part="input"
2736
+ type=${type}
2737
+ .value=${value}
2738
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2739
+ aria-describedby=${this.fieldErrors[errorKey] ? id : nothing}
2740
+ @input=${(event) => this.updateGuestAthlete(guestIndex, athleteIndex, key, event.target.value)}
2741
+ />
2742
+ ${this.renderFieldError(errorKey)}
2743
+ </label>
2744
+ `;
2745
+ }
2746
+ renderGuestSupplements(guest, guestIndex) {
2747
+ const supplements = this.activeSupplements;
2748
+ if (!supplements.length)
2749
+ return nothing;
2750
+ const currency = this.event?.currency ?? "EUR";
2751
+ return html`
2752
+ <fieldset part="card guest-supplements">
2753
+ <legend part="title">Extras</legend>
2754
+ <div part="stack">
2755
+ ${supplements.map((supplement) => html`
2756
+ <label part="label checkbox supplement">
2757
+ <span>
2758
+ <input
2759
+ part="checkbox"
2760
+ type="checkbox"
2761
+ .checked=${guest.selectedSupplementIds.includes(supplement.id)}
2762
+ ?disabled=${supplement.isRequired}
2763
+ @change=${(event) => this.updateGuestSupplement(guestIndex, supplement.id, event.target.checked)}
2764
+ />
2765
+ ${supplement.name}${supplement.isRequired ? " *" : ""} ·
2766
+ ${formatMoney(supplement.price, currency, this.locale, { zeroAsFree: true })}
2767
+ </span>
2768
+ </label>
2769
+ `)}
2770
+ </div>
2771
+ </fieldset>
2772
+ `;
2773
+ }
2774
+ renderWaivers() {
2775
+ const waivers = this.event?.waivers ?? [];
2776
+ if (!waivers.length)
2777
+ return nothing;
2778
+ return html`
2779
+ <fieldset part="card waivers">
2780
+ <legend part="title">${t(this.locale, "waiversTitle")}</legend>
2781
+ <div part="stack">${waivers.map((waiver) => this.renderWaiver(waiver))}</div>
2782
+ </fieldset>
2783
+ `;
2784
+ }
2785
+ renderWaiver(waiver) {
2786
+ const errorKey = `waivers.${waiver.id}`;
2787
+ return html`
2788
+ <label part="label checkbox waiver">
2789
+ <span>
2790
+ <input
2791
+ part="checkbox"
2792
+ type="checkbox"
2793
+ .checked=${this.waiversAccepted[waiver.id] === true}
2794
+ aria-invalid=${this.fieldErrors[errorKey] ? "true" : "false"}
2795
+ @change=${(event) => this.updateWaiver(waiver.id, event.target.checked)}
2796
+ />
2797
+ ${waiver.name}${waiver.isRequired ? " *" : ""}
2798
+ </span>
2799
+ ${waiver.bodyText ? html`<small part="hint">${waiver.bodyText}</small>` : nothing}
2800
+ ${this.renderFieldError(errorKey)}
2801
+ </label>
2802
+ `;
2803
+ }
2804
+ renderSummary() {
2805
+ if (!this.summary && !this.summaryLoading)
2806
+ return nothing;
2807
+ const currency = this.summary?.currency ?? this.event?.currency ?? "EUR";
2808
+ return html`
2809
+ <aside part="card summary" aria-live="polite">
2810
+ <h4 part="title">${t(this.locale, "summary")}</h4>
2811
+ ${this.summaryLoading ? html`<p part="loader">${t(this.locale, "loadingSummary")}</p>` : html`
2812
+ <div part="summary-row">
2813
+ <span>Subtotal</span
2814
+ ><strong>${formatMoney(this.summary?.subtotal, currency, this.locale)}</strong>
2815
+ </div>
2816
+ <div part="summary-row">
2817
+ <span>Extras</span
2818
+ ><strong
2819
+ >${formatMoney(this.summary?.supplementsTotal, currency, this.locale)}</strong
2820
+ >
2821
+ </div>
2822
+ ${this.summary?.guestRegistrations?.map((guest) => html`<div part="summary-row guest-summary-row">
2823
+ <span>${guest.categoryName}</span
2824
+ ><strong>${formatMoney(guest.subtotal, currency, this.locale)}</strong>
2825
+ </div>`)}
2826
+ ${this.summary?.couponDiscount ? html`<div part="summary-row">
2827
+ <span>Descuento</span
2828
+ ><strong
2829
+ >${formatMoney(-this.summary.couponDiscount, currency, this.locale)}</strong
2830
+ >
2831
+ </div>` : nothing}
2832
+ <div part="summary-row">
2833
+ <span>${t(this.locale, "total")}</span
2834
+ ><strong>${formatMoney(this.summary?.total, currency, this.locale)}</strong>
2835
+ </div>
2836
+ `}
2837
+ </aside>
2838
+ `;
2839
+ }
2840
+ renderFieldError(key) {
2841
+ const error = this.fieldErrors[key];
2842
+ if (!error)
2843
+ return nothing;
2844
+ return html`<small id=${fieldErrorId(key)} part="field-error" role="alert">${error}</small>`;
2845
+ }
2846
+ syncAthleteCount() {
2847
+ const teamSize = this.selectedCategory?.teamSize ?? 1;
2848
+ if (this.athletes.length === teamSize)
2849
+ return;
2850
+ this.athletes = Array.from({ length: teamSize }, (_, index) => this.athletes[index] ?? emptyAthlete());
2851
+ }
2852
+ syncRequiredSupplements() {
2853
+ const requiredIds = this.getRequiredSupplementIds();
2854
+ const next = [...new Set([...requiredIds, ...this.selectedSupplementIds])];
2855
+ if (next.length !== this.selectedSupplementIds.length)
2856
+ this.selectedSupplementIds = next;
2857
+ }
2858
+ syncGuestRegistrations() {
2859
+ const next = this.guestRegistrations.map((guest) => this.normalizeGuestRegistration(guest));
2860
+ if (JSON.stringify(next) !== JSON.stringify(this.guestRegistrations)) {
2861
+ this.guestRegistrations = next;
2862
+ }
2863
+ }
2864
+ getRequiredSupplementIds() {
2865
+ return this.activeSupplements.filter((supplement) => supplement.isRequired).map((supplement) => supplement.id);
2866
+ }
2867
+ getCategory(categoryId) {
2868
+ return this.event?.categories.find((category) => category.id === categoryId);
2869
+ }
2870
+ createGuestRegistration() {
2871
+ const categoryId = this.categoryId || this.event?.categories.find((category) => !category.isPrivate)?.id || "";
2872
+ return this.normalizeGuestRegistration({
2873
+ guestName: "",
2874
+ guestEmail: "",
2875
+ categoryId,
2876
+ teamName: "",
2877
+ club: "",
2878
+ athletes: [emptyAthlete()],
2879
+ selectedSupplementIds: this.getRequiredSupplementIds()
2880
+ });
2881
+ }
2882
+ normalizeGuestRegistration(guest) {
2883
+ const category = this.getCategory(guest.categoryId);
2884
+ const teamSize = category?.teamSize ?? 1;
2885
+ const athletes = Array.from({ length: teamSize }, (_, index) => guest.athletes[index] ?? emptyAthlete());
2886
+ const selectedSupplementIds = [
2887
+ ...new Set([...this.getRequiredSupplementIds(), ...guest.selectedSupplementIds])
2888
+ ];
2889
+ return { ...guest, athletes, selectedSupplementIds };
2890
+ }
2891
+ addGuestRegistration = () => {
2892
+ this.guestRegistrations = [...this.guestRegistrations, this.createGuestRegistration()];
2893
+ };
2894
+ removeGuestRegistration(index) {
2895
+ this.guestRegistrations = this.guestRegistrations.filter((_, candidateIndex) => candidateIndex !== index);
2896
+ }
2897
+ updateGuestField(index, key, value) {
2898
+ this.guestRegistrations = this.guestRegistrations.map((guest, candidateIndex) => candidateIndex === index ? { ...guest, [key]: value } : guest);
2899
+ this.clearFieldError(`guestRegistrations.${index}.${key}`);
2900
+ }
2901
+ updateGuestCategory(index, categoryId) {
2902
+ this.guestRegistrations = this.guestRegistrations.map((guest, candidateIndex) => candidateIndex === index ? this.normalizeGuestRegistration({ ...guest, categoryId }) : guest);
2903
+ this.clearFieldError(`guestRegistrations.${index}.categoryId`);
2904
+ }
2905
+ updateGuestAthlete(guestIndex, athleteIndex, key, value) {
2906
+ this.guestRegistrations = this.guestRegistrations.map((guest, candidateIndex) => {
2907
+ if (candidateIndex !== guestIndex)
2908
+ return guest;
2909
+ return {
2910
+ ...guest,
2911
+ athletes: guest.athletes.map((athlete, currentAthleteIndex) => currentAthleteIndex === athleteIndex ? { ...athlete, [key]: value } : athlete)
2912
+ };
2913
+ });
2914
+ this.clearFieldError(`guestRegistrations.${guestIndex}.athletes.${athleteIndex}.${key}`);
2915
+ }
2916
+ updateGuestSupplement(guestIndex, supplementId, selected) {
2917
+ this.guestRegistrations = this.guestRegistrations.map((guest, candidateIndex) => {
2918
+ if (candidateIndex !== guestIndex)
2919
+ return guest;
2920
+ const selectedSupplementIds = selected ? [...new Set([...guest.selectedSupplementIds, supplementId])] : guest.selectedSupplementIds.filter((id) => id !== supplementId);
2921
+ return this.normalizeGuestRegistration({ ...guest, selectedSupplementIds });
2922
+ });
2923
+ }
2924
+ updateTextField(key, value) {
2925
+ this[key] = value;
2926
+ this.clearFieldError(key);
2927
+ }
2928
+ updateAthlete(index, key, value) {
2929
+ this.athletes = this.athletes.map((athlete, candidateIndex) => candidateIndex === index ? { ...athlete, [key]: value } : athlete);
2930
+ this.clearFieldError(`athletes.${index}.${key}`);
2931
+ }
2932
+ updateCustomField(fieldId, value) {
2933
+ this.customFieldValues = { ...this.customFieldValues, [fieldId]: value };
2934
+ this.clearFieldError(`customFields.${fieldId}`);
2935
+ }
2936
+ updateWaiver(waiverId, accepted) {
2937
+ this.waiversAccepted = { ...this.waiversAccepted, [waiverId]: accepted };
2938
+ this.clearFieldError(`waivers.${waiverId}`);
2939
+ }
2940
+ updateSupplement(supplementId, selected) {
2941
+ this.selectedSupplementIds = selected ? [...new Set([...this.selectedSupplementIds, supplementId])] : this.selectedSupplementIds.filter((id) => id !== supplementId);
2942
+ }
2943
+ setTermsAccepted(accepted) {
2944
+ this.termsAccepted = accepted;
2945
+ this.clearFieldError("termsAccepted");
2946
+ }
2947
+ clearFieldError(key) {
2948
+ if (!this.fieldErrors[key])
2949
+ return;
2950
+ const { [key]: _removed, ...rest } = this.fieldErrors;
2951
+ this.fieldErrors = rest;
2952
+ }
2953
+ validate() {
2954
+ const errors = {};
2955
+ setValidationError(errors, this.locale, "purchaserName", validateRequiredText(this.purchaserName));
2956
+ setValidationError(errors, this.locale, "purchaserEmail", validateEmail(this.purchaserEmail, true));
2957
+ setValidationError(errors, this.locale, "teamName", validateRequiredText(this.teamName));
2958
+ setValidationError(errors, this.locale, "successUrl", validateHttpUrl(this.successUrl, true));
2959
+ setValidationError(errors, this.locale, "cancelUrl", validateHttpUrl(this.cancelUrl, true));
2960
+ if (this.baseUrl) {
2961
+ setValidationError(errors, this.locale, "baseUrl", validateHttpUrl(this.baseUrl, false));
2962
+ }
2963
+ if (!this.selectedCategory) {
2964
+ errors.categoryId = t(this.locale, "invalidSelection");
2965
+ }
2966
+ for (const [index, athlete] of this.athletes.entries()) {
2967
+ setValidationError(errors, this.locale, `athletes.${index}.fullname`, validateRequiredText(athlete.fullname));
2968
+ setValidationError(errors, this.locale, `athletes.${index}.email`, validateEmail(athlete.email, true));
2969
+ setValidationError(errors, this.locale, `athletes.${index}.phone`, validateRequiredText(athlete.phone));
2970
+ setValidationError(errors, this.locale, `athletes.${index}.idNumber`, validateRequiredText(athlete.idNumber));
2971
+ setValidationError(errors, this.locale, `athletes.${index}.birthDate`, validateDate(athlete.birthDate, true));
2972
+ setValidationError(errors, this.locale, `athletes.${index}.gender`, validateGender(athlete.gender));
2973
+ }
2974
+ for (const [guestIndex, guest] of this.guestRegistrations.entries()) {
2975
+ const guestCategory = this.getCategory(guest.categoryId);
2976
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.guestName`, validateRequiredText(guest.guestName));
2977
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.guestEmail`, validateEmail(guest.guestEmail, true));
2978
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.teamName`, validateRequiredText(guest.teamName));
2979
+ if (!guestCategory || guestCategory.isPrivate) {
2980
+ errors[`guestRegistrations.${guestIndex}.categoryId`] = t(this.locale, "invalidSelection");
2981
+ }
2982
+ for (const [athleteIndex, athlete] of guest.athletes.entries()) {
2983
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.fullname`, validateRequiredText(athlete.fullname));
2984
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.email`, validateEmail(athlete.email, true));
2985
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.phone`, validateRequiredText(athlete.phone));
2986
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.idNumber`, validateRequiredText(athlete.idNumber));
2987
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.birthDate`, validateDate(athlete.birthDate, true));
2988
+ setValidationError(errors, this.locale, `guestRegistrations.${guestIndex}.athletes.${athleteIndex}.gender`, validateGender(athlete.gender));
2989
+ }
2990
+ }
2991
+ for (const field of this.event?.eventFormFields.filter(isFormFieldTargetAthlete) ?? []) {
2992
+ setValidationError(errors, this.locale, `customFields.${field.id}`, validateCustomFieldValue(this.customFieldValues[field.id], field.type, field.options, field.isRequired));
2993
+ }
2994
+ for (const waiver of this.event?.waivers ?? []) {
2995
+ if (waiver.isRequired && this.waiversAccepted[waiver.id] !== true) {
2996
+ errors[`waivers.${waiver.id}`] = t(this.locale, "requiredWaiver");
2997
+ }
2998
+ }
2999
+ setValidationError(errors, this.locale, "termsAccepted", validateCheckbox(this.termsAccepted, true));
3000
+ this.fieldErrors = errors;
3001
+ this.error = this.getConfigurationError(errors);
3002
+ return Object.keys(errors).length === 0;
3003
+ }
3004
+ getConfigurationError(errors) {
3005
+ return errors.successUrl ?? errors.cancelUrl ?? errors.baseUrl ?? errors.categoryId ?? "";
3006
+ }
3007
+ scheduleSummaryLoad() {
3008
+ if (this.summaryTimer)
3009
+ clearTimeout(this.summaryTimer);
3010
+ this.summaryTimer = setTimeout(() => void this.loadSummary(), 250);
3011
+ }
3012
+ buildGuestRegistrationsSummaryInput() {
3013
+ return this.guestRegistrations.filter((guest) => hasText(guest.categoryId)).map((guest) => ({
3014
+ categoryId: guest.categoryId.trim(),
3015
+ ...guest.selectedSupplementIds.length > 0 ? { selectedSupplementIds: guest.selectedSupplementIds } : {}
3016
+ }));
3017
+ }
3018
+ buildGuestRegistrationsCheckoutInput() {
3019
+ if (!this.guestRegistrations.length)
3020
+ return;
3021
+ return this.guestRegistrations.map((guest) => ({
3022
+ guestEmail: guest.guestEmail.trim(),
3023
+ guestName: guest.guestName.trim(),
3024
+ categoryId: guest.categoryId.trim(),
3025
+ teamName: guest.teamName.trim(),
3026
+ ...guest.club.trim() ? { club: guest.club.trim() } : {},
3027
+ selectedSupplementIds: guest.selectedSupplementIds,
3028
+ athletes: guest.athletes.map((athlete) => ({
3029
+ ...athlete,
3030
+ fullname: athlete.fullname.trim(),
3031
+ email: athlete.email.trim(),
3032
+ phone: athlete.phone.trim(),
3033
+ idNumber: athlete.idNumber.trim()
3034
+ }))
3035
+ }));
3036
+ }
3037
+ async loadSummary() {
3038
+ if (!hasText(this.publishableKey) || !hasText(this.categoryId) || !validateEmail(this.purchaserEmail, true).valid || this.baseUrl && !validateHttpUrl(this.baseUrl, false).valid) {
3039
+ this.summary = undefined;
3040
+ return;
3041
+ }
3042
+ const requestId = ++this.summaryRequestId;
3043
+ this.summaryLoading = true;
3044
+ try {
3045
+ const client = createClient({
3046
+ publishableKey: this.publishableKey.trim(),
3047
+ baseUrl: this.baseUrl
3048
+ });
3049
+ const input = {
3050
+ categoryId: this.categoryId.trim(),
3051
+ purchaserEmail: this.purchaserEmail.trim(),
3052
+ selectedSupplementIds: this.selectedSupplementIds,
3053
+ ...this.buildGuestRegistrationsSummaryInput().length > 0 ? { guestRegistrations: this.buildGuestRegistrationsSummaryInput() } : {},
3054
+ ...this.promocode.trim() ? { promocode: this.promocode.trim() } : {}
3055
+ };
3056
+ const summary = await client.getRegistrationSummary(input);
3057
+ if (requestId === this.summaryRequestId)
3058
+ this.summary = summary;
3059
+ } catch {
3060
+ if (requestId === this.summaryRequestId)
3061
+ this.summary = undefined;
3062
+ } finally {
3063
+ if (requestId === this.summaryRequestId)
3064
+ this.summaryLoading = false;
3065
+ }
3066
+ }
3067
+ async submit(event) {
3068
+ event.preventDefault();
3069
+ this.error = "";
3070
+ if (!this.validate())
3071
+ return;
3072
+ this.loading = true;
3073
+ try {
3074
+ emitWodiraEvent(this, "wodira:checkout-start", {
3075
+ eventId: this.eventId,
3076
+ categoryId: this.categoryId
3077
+ });
3078
+ const customFields = Object.entries(this.customFieldValues).filter(([, value]) => !isBlank(value)).map(([fieldId, value]) => ({ fieldId, value }));
3079
+ const client = createClient({
3080
+ publishableKey: this.publishableKey.trim(),
3081
+ baseUrl: this.baseUrl
3082
+ });
3083
+ const checkoutInput = {
3084
+ eventId: this.eventId,
3085
+ categoryId: this.categoryId,
3086
+ purchaserEmail: this.purchaserEmail.trim(),
3087
+ purchaserName: this.purchaserName.trim(),
3088
+ teamName: this.teamName.trim(),
3089
+ termsAccepted: this.termsAccepted,
3090
+ waiversAccepted: this.waiversAccepted,
3091
+ selectedSupplementIds: this.selectedSupplementIds,
3092
+ idempotencyKey: createIdempotencyKey(),
3093
+ successUrl: this.successUrl.trim(),
3094
+ cancelUrl: this.cancelUrl.trim(),
3095
+ customFields,
3096
+ athletes: this.athletes.map((athlete) => ({
3097
+ ...athlete,
3098
+ fullname: athlete.fullname.trim(),
3099
+ email: athlete.email.trim(),
3100
+ phone: athlete.phone.trim(),
3101
+ idNumber: athlete.idNumber.trim()
3102
+ }))
3103
+ };
3104
+ const guestRegistrations = this.buildGuestRegistrationsCheckoutInput();
3105
+ if (guestRegistrations)
3106
+ checkoutInput.guestRegistrations = guestRegistrations;
3107
+ if (this.club.trim())
3108
+ checkoutInput.club = this.club.trim();
3109
+ if (this.promocode.trim())
3110
+ checkoutInput.promocode = this.promocode.trim();
3111
+ const session = await client.createRegistrationCheckoutSession(checkoutInput);
3112
+ this.handleCheckoutSession(session);
3113
+ } catch (error) {
3114
+ this.error = getErrorMessage(error);
3115
+ emitWodiraEvent(this, "wodira:error", { error, message: this.error });
3116
+ } finally {
3117
+ this.loading = false;
3118
+ }
3119
+ }
3120
+ handleCheckoutSession(session) {
3121
+ emitWodiraEvent(this, "wodira:checkout-created", { session });
3122
+ if (!session.sessionUrl)
3123
+ return;
3124
+ emitWodiraEvent(this, "wodira:redirect", { sessionUrl: session.sessionUrl, session });
3125
+ if (this.autoRedirect)
3126
+ globalThis.location.assign(session.sessionUrl);
3127
+ }
3128
+ }
3129
+
3130
+ class WodiraCheckoutFlow extends LitElement {
3131
+ static styles = [wodiraUiStyles];
3132
+ static properties = {
3133
+ publishableKey: { type: String, attribute: "publishable-key" },
3134
+ baseUrl: { type: String, attribute: "base-url" },
3135
+ eventId: { type: String, attribute: "event-id" },
3136
+ successUrl: { type: String, attribute: "success-url" },
3137
+ cancelUrl: { type: String, attribute: "cancel-url" },
3138
+ locale: { type: String },
3139
+ autoRedirect: { type: Boolean, attribute: "auto-redirect" },
3140
+ selectedEvent: { state: true },
3141
+ selectedCategoryId: { type: String, state: true },
3142
+ loading: { type: Boolean, state: true },
3143
+ error: { type: String, state: true }
3144
+ };
3145
+ didInitialLoad = false;
3146
+ constructor() {
3147
+ super();
3148
+ this.publishableKey = "";
3149
+ this.eventId = "";
3150
+ this.successUrl = "";
3151
+ this.cancelUrl = "";
3152
+ this.locale = "";
3153
+ this.autoRedirect = true;
3154
+ this.selectedCategoryId = "";
3155
+ this.loading = false;
3156
+ this.error = "";
3157
+ }
3158
+ firstUpdated() {
3159
+ this.didInitialLoad = true;
3160
+ this.loadEvent();
3161
+ }
3162
+ updated(changed) {
3163
+ if (this.didInitialLoad && (changed.has("publishableKey") || changed.has("baseUrl") || changed.has("eventId"))) {
3164
+ this.selectedCategoryId = "";
3165
+ this.loadEvent();
3166
+ }
3167
+ }
3168
+ render() {
3169
+ if (!this.publishableKey)
3170
+ return html`<div part="error" role="alert">${t(this.locale, "noPublishableKey")}</div>`;
3171
+ if (this.error)
3172
+ return html`<div part="error" role="alert">${this.error}</div>`;
3173
+ return html`
3174
+ <section part="stack checkout-flow">
3175
+ ${this.eventId ? this.renderSelectedEventFlow() : this.renderEventPicker()}
3176
+ </section>
3177
+ `;
3178
+ }
3179
+ renderEventPicker() {
3180
+ return html`
3181
+ <wodira-event-list
3182
+ publishable-key=${this.publishableKey}
3183
+ .baseUrl=${this.baseUrl}
3184
+ locale=${this.locale}
3185
+ @wodira:event-select=${this.onEventSelect}
3186
+ ></wodira-event-list>
3187
+ ${this.selectedEvent ? this.renderPricingAndForm(this.selectedEvent) : nothing}
3188
+ `;
3189
+ }
3190
+ renderSelectedEventFlow() {
3191
+ if (this.loading)
3192
+ return html`<div part="loader" role="status" aria-live="polite">
3193
+ ${t(this.locale, "loadingEvent")}
3194
+ </div>`;
3195
+ if (!this.selectedEvent)
3196
+ return html`<div part="empty">${t(this.locale, "eventUnavailable")}</div>`;
3197
+ return this.renderPricingAndForm(this.selectedEvent);
3198
+ }
3199
+ renderPricingAndForm(event) {
3200
+ return html`
3201
+ <div part="card event-summary">
3202
+ <h2 part="title">${event.name}</h2>
3203
+ <p part="meta">
3204
+ ${formatDate(event.startDate, this.locale)}${event.locationAddress ? ` · ${event.locationAddress}` : ""}
3205
+ </p>
3206
+ </div>
3207
+ <wodira-pricing-cards
3208
+ publishable-key=${this.publishableKey}
3209
+ .baseUrl=${this.baseUrl}
3210
+ event-id=${event.id}
3211
+ selected-category-id=${this.selectedCategoryId}
3212
+ locale=${this.locale}
3213
+ @wodira:category-select=${this.onCategorySelect}
3214
+ ></wodira-pricing-cards>
3215
+ ${this.selectedCategoryId ? html`
3216
+ <wodira-registration-form
3217
+ publishable-key=${this.publishableKey}
3218
+ .baseUrl=${this.baseUrl}
3219
+ event-id=${event.id}
3220
+ category-id=${this.selectedCategoryId}
3221
+ success-url=${this.successUrl}
3222
+ cancel-url=${this.cancelUrl}
3223
+ locale=${this.locale}
3224
+ .event=${event}
3225
+ .autoRedirect=${this.autoRedirect}
3226
+ ></wodira-registration-form>
3227
+ ` : nothing}
3228
+ `;
3229
+ }
3230
+ validateClientConfig() {
3231
+ if (this.baseUrl && !validateHttpUrl(this.baseUrl, false).valid) {
3232
+ throw new Error(t(this.locale, "invalidUrl"));
3233
+ }
3234
+ }
3235
+ async loadEvent() {
3236
+ if (!this.isConnected)
3237
+ return;
3238
+ if (!hasText(this.publishableKey) || !hasText(this.eventId))
3239
+ return;
3240
+ this.loading = true;
3241
+ this.error = "";
3242
+ try {
3243
+ this.validateClientConfig();
3244
+ const client = createClient({
3245
+ publishableKey: this.publishableKey.trim(),
3246
+ baseUrl: this.baseUrl
3247
+ });
3248
+ this.selectedEvent = await client.getEvent(this.eventId.trim());
3249
+ } catch (error) {
3250
+ this.error = getErrorMessage(error);
3251
+ emitWodiraEvent(this, "wodira:error", { error, message: this.error });
3252
+ } finally {
3253
+ this.loading = false;
3254
+ }
3255
+ }
3256
+ onEventSelect = (event) => {
3257
+ this.selectedEvent = event.detail.event;
3258
+ this.eventId = event.detail.event.id;
3259
+ this.selectedCategoryId = "";
3260
+ };
3261
+ onCategorySelect = (event) => {
3262
+ this.selectedCategoryId = event.detail.category.id;
3263
+ };
3264
+ }
3265
+
3266
+ // src/ui/index.ts
3267
+ var ELEMENTS = [
3268
+ ["wodira-event-card", WodiraEventCard],
3269
+ ["wodira-event-list", WodiraEventList],
3270
+ ["wodira-pricing-cards", WodiraPricingCards],
3271
+ ["wodira-registration-form", WodiraRegistrationForm],
3272
+ ["wodira-checkout-flow", WodiraCheckoutFlow]
3273
+ ];
3274
+ function defineWodiraElements(_options = {}) {
3275
+ if (!globalThis.customElements) {
3276
+ throw new Error("@wodira/browser/ui requires a browser Custom Elements environment.");
3277
+ }
3278
+ for (const [tagName, element] of ELEMENTS) {
3279
+ if (!globalThis.customElements.get(tagName)) {
3280
+ globalThis.customElements.define(tagName, element);
3281
+ }
3282
+ }
3283
+ }
3284
+ function createWodiraCheckoutFlow(container, options) {
3285
+ defineWodiraElements();
3286
+ const element = document.createElement("wodira-checkout-flow");
3287
+ element.publishableKey = options.publishableKey;
3288
+ if (options.baseUrl)
3289
+ element.baseUrl = options.baseUrl;
3290
+ if (options.eventId)
3291
+ element.eventId = options.eventId;
3292
+ element.successUrl = options.successUrl;
3293
+ element.cancelUrl = options.cancelUrl;
3294
+ if (options.locale)
3295
+ element.locale = options.locale;
3296
+ if (options.autoRedirect !== undefined)
3297
+ element.autoRedirect = options.autoRedirect;
3298
+ container.append(element);
3299
+ return element;
3300
+ }
3301
+ export {
3302
+ defineWodiraElements,
3303
+ createWodiraCheckoutFlow,
3304
+ WodiraRegistrationForm,
3305
+ WodiraPricingCards,
3306
+ WodiraEventList,
3307
+ WodiraEventCard,
3308
+ WodiraCheckoutFlow
3309
+ };