@ticketlayer/elements 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/cjs/{index-I9CoYv3k.js → index-DanPUX0s.js} +1 -1
  2. package/dist/cjs/index.cjs.js +2 -1
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
  5. package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
  6. package/dist/cjs/tl-buy-modal.cjs.entry.js +3 -3
  7. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -74
  8. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +61 -30
  9. package/dist/cjs/tl-cart-badge.cjs.entry.js +74 -61
  10. package/dist/cjs/tl-cart-drawer.cjs.entry.js +51 -23
  11. package/dist/cjs/tl-cart.cjs.entry.js +71 -103
  12. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +51 -29
  13. package/dist/cjs/tl-event-card.cjs.entry.js +43 -105
  14. package/dist/cjs/tl-event-list.cjs.entry.js +72 -35
  15. package/dist/cjs/tl-login.cjs.entry.js +74 -141
  16. package/dist/cjs/tl-my-orders.cjs.entry.js +52 -92
  17. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +153 -921
  18. package/dist/cjs/tl-order-confirmation.cjs.entry.js +71 -107
  19. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +48 -74
  20. package/dist/cjs/tl-promo-code.cjs.entry.js +50 -47
  21. package/dist/cjs/tl-seat-map.cjs.entry.js +3 -3
  22. package/dist/cjs/web-query-Ce73jB4k.js +16 -0
  23. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
  24. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +37 -0
  25. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -79
  26. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
  27. package/dist/collection/components/tl-cart/tl-cart.js +82 -107
  28. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
  29. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
  30. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
  31. package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
  32. package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
  33. package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
  34. package/dist/collection/components/tl-login/tl-login.js +83 -147
  35. package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
  36. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
  37. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -115
  38. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
  39. package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
  40. package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
  41. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +34 -20
  42. package/dist/collection/utils/order-summary.js +15 -135
  43. package/dist/collection/utils/testing/deep-text.js +29 -0
  44. package/dist/collection/utils/testing/fixtures.js +290 -0
  45. package/dist/collection/utils/testing/mock-sdk.js +13 -2
  46. package/dist/collection/utils/web-query.js +12 -0
  47. package/dist/collection/utils/web-storage.js +22 -0
  48. package/dist/components/ticket-selector.js +1 -0
  49. package/dist/components/tl-buy-modal.js +1 -1
  50. package/dist/components/tl-buy-tickets-button.js +1 -1
  51. package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
  52. package/dist/components/tl-cart-badge.js +1 -1
  53. package/dist/components/tl-cart-drawer.js +1 -1
  54. package/dist/components/tl-cart2.js +1 -1
  55. package/dist/components/tl-checkout-wrapper2.js +1 -1
  56. package/dist/components/tl-event-card2.js +1 -1
  57. package/dist/components/tl-event-list.js +1 -1
  58. package/dist/components/tl-login2.js +1 -1
  59. package/dist/components/tl-my-orders.js +1 -1
  60. package/dist/components/tl-occurrence-selector2.js +1 -1
  61. package/dist/components/tl-order-confirmation.js +1 -1
  62. package/dist/components/tl-order-tickets2.js +1 -1
  63. package/dist/components/tl-promo-code.js +1 -1
  64. package/dist/components/tl-seat-map.js +1 -1
  65. package/dist/components/tl-ticket-selector2.js +1 -1
  66. package/dist/components/web-query.js +1 -0
  67. package/dist/elements.docs.json +34 -34
  68. package/dist/esm/{index-OoYV1faY.js → index-imfxp765.js} +1 -1
  69. package/dist/esm/index.js +2 -1
  70. package/dist/esm/loader.js +3 -3
  71. package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
  72. package/dist/esm/ticketlayer-elements.js +3 -3
  73. package/dist/esm/tl-buy-modal.entry.js +3 -3
  74. package/dist/esm/tl-buy-tickets-button.entry.js +90 -75
  75. package/dist/esm/tl-buy-tickets-wrapper.entry.js +62 -31
  76. package/dist/esm/tl-cart-badge.entry.js +75 -62
  77. package/dist/esm/tl-cart-drawer.entry.js +52 -24
  78. package/dist/esm/tl-cart.entry.js +72 -104
  79. package/dist/esm/tl-checkout-wrapper.entry.js +52 -30
  80. package/dist/esm/tl-event-card.entry.js +44 -106
  81. package/dist/esm/tl-event-list.entry.js +73 -36
  82. package/dist/esm/tl-login.entry.js +75 -142
  83. package/dist/esm/tl-my-orders.entry.js +53 -93
  84. package/dist/esm/tl-occurrence-selector_2.entry.js +152 -920
  85. package/dist/esm/tl-order-confirmation.entry.js +72 -108
  86. package/dist/esm/tl-order-tickets_2.entry.js +49 -75
  87. package/dist/esm/tl-promo-code.entry.js +51 -48
  88. package/dist/esm/tl-seat-map.entry.js +3 -3
  89. package/dist/esm/web-query-Bdbb2lk5.js +14 -0
  90. package/dist/ticketlayer-elements/index.esm.js +1 -1
  91. package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
  92. package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
  93. package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
  94. package/dist/ticketlayer-elements/{p-ac264adc.entry.js → p-5135630e.entry.js} +1 -1
  95. package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
  96. package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
  97. package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
  98. package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
  99. package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
  100. package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
  101. package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
  102. package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
  103. package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
  104. package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
  105. package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
  106. package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
  107. package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
  108. package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
  109. package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
  110. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
  111. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
  112. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
  113. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
  114. package/dist/types/components/tl-cart/tl-cart.d.ts +40 -42
  115. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
  116. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
  117. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
  118. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
  119. package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
  120. package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
  121. package/dist/types/components/tl-login/tl-login.d.ts +68 -22
  122. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
  123. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
  124. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
  125. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
  126. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
  127. package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
  128. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +22 -4
  129. package/dist/types/components.d.ts +270 -62
  130. package/dist/types/utils/order-summary.d.ts +14 -97
  131. package/dist/types/utils/testing/deep-text.d.ts +13 -0
  132. package/dist/types/utils/testing/fixtures.d.ts +225 -0
  133. package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
  134. package/dist/types/utils/web-query.d.ts +18 -0
  135. package/dist/types/utils/web-storage.d.ts +16 -0
  136. package/elements.catalogue.json +602 -50
  137. package/package.json +8 -6
  138. package/dist/cjs/money-BtmM2gt-.js +0 -118
  139. package/dist/cjs/order-summary-X3TlZxc2.js +0 -150
  140. package/dist/components/money.js +0 -1
  141. package/dist/components/order-summary.js +0 -1
  142. package/dist/esm/money-4ig0-WB6.js +0 -112
  143. package/dist/esm/order-summary-DiDtvfZM.js +0 -136
  144. package/dist/ticketlayer-elements/p-0814420f.entry.js +0 -1
  145. package/dist/ticketlayer-elements/p-0acabc26.entry.js +0 -1
  146. package/dist/ticketlayer-elements/p-109a3feb.entry.js +0 -1
  147. package/dist/ticketlayer-elements/p-1a4ba71c.entry.js +0 -1
  148. package/dist/ticketlayer-elements/p-318965c1.entry.js +0 -1
  149. package/dist/ticketlayer-elements/p-347c7fd1.entry.js +0 -1
  150. package/dist/ticketlayer-elements/p-3649c26d.entry.js +0 -1
  151. package/dist/ticketlayer-elements/p-40260cc9.entry.js +0 -1
  152. package/dist/ticketlayer-elements/p-4ig0-WB6.js +0 -1
  153. package/dist/ticketlayer-elements/p-55fd2f9f.entry.js +0 -1
  154. package/dist/ticketlayer-elements/p-62ee659b.entry.js +0 -1
  155. package/dist/ticketlayer-elements/p-7d4ecc2b.entry.js +0 -1
  156. package/dist/ticketlayer-elements/p-7f0abf95.entry.js +0 -1
  157. package/dist/ticketlayer-elements/p-BXpVGW51.js +0 -1
  158. package/dist/ticketlayer-elements/p-OoYV1faY.js +0 -3
  159. package/dist/ticketlayer-elements/p-a3c1b26c.entry.js +0 -1
  160. package/dist/ticketlayer-elements/p-aeb4c6fb.entry.js +0 -1
  161. package/dist/ticketlayer-elements/p-ba62541f.entry.js +0 -1
@@ -1,31 +1,6 @@
1
- import { r as registerInstance, c as createEvent, h } from './index-OoYV1faY.js';
2
- import { w as whenSDK } from './sdk-connector-CqhvfyKV.js';
3
- import { e as errorMessage } from './order-summary-DiDtvfZM.js';
4
- import './money-4ig0-WB6.js';
5
-
6
- /** Adapt an event detail to the card shape (the list adapter lives in the SDK connector). */
7
- function toEventCardEvent(detail) {
8
- const occurrences = detail.occurrences ?? [];
9
- const starts = occurrences
10
- .map((o) => o.startsAt)
11
- .filter((s) => !!s)
12
- .sort();
13
- const now = Date.now();
14
- const next = starts.find((s) => new Date(s).getTime() >= now) ?? starts[0];
15
- const soldOut = detail.status === 'sold_out' ||
16
- (occurrences.length > 0 && occurrences.every((o) => o.status === 'sold_out'));
17
- return {
18
- id: detail.id,
19
- name: detail.name,
20
- ...(detail.shortDescription ? { shortDescription: detail.shortDescription } : {}),
21
- ...(detail.imageUrl ? { imageUrl: detail.imageUrl } : {}),
22
- ...(detail.venueName
23
- ? { venue: { id: detail.venueId ?? '', name: detail.venueName, city: detail.venueCity ?? '' } }
24
- : {}),
25
- ...(next ? { nextOccurrence: { startsAt: next } } : {}),
26
- availabilityStatus: soldOut ? 'sold_out' : 'available',
27
- };
28
- }
1
+ import { r as registerInstance, c as createEvent, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { c as createElement, f as eventCardCore } from './ticket-selector-BwChnHVC.js';
29
4
 
30
5
  const tlEventCardCss = () => `:host{display:block;font-family:var(--tl-font-family)}.event-card{background:var(--tl-secondary);border-radius:var(--tl-radius-lg);overflow:hidden;box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);cursor:pointer;transition:box-shadow 0.2s ease, transform 0.2s ease}.event-card:hover{box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);transform:translateY(-2px)}.image-container{position:relative;aspect-ratio:16 / 9;overflow:hidden;background:var(--tl-muted)}.event-image{width:100%;height:100%;object-fit:cover;transition:transform 0.3s ease}.event-card:hover .event-image{transform:scale(1.05)}.image-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--tl-muted-foreground)}.status-container{position:absolute;top:0.5rem;right:0.5rem}.status-badge{display:inline-block;padding:0.25rem 0.5rem;font-size:0.75rem;font-weight:500;border-radius:var(--tl-radius-sm)}.status-available{background:var(--tl-success);color:var(--tl-success-foreground)}.status-limited{background:var(--tl-warning);color:var(--tl-warning-foreground)}.status-sold-out{background:var(--tl-error);color:var(--tl-error-foreground)}.content{padding:var(--tl-spacing-4)}.category{display:inline-block;font-size:0.75rem;font-weight:500;text-transform:uppercase;letter-spacing:0.05em;color:var(--tl-primary)}.title{margin:0.25rem 0 0;font-size:1.125rem;font-weight:600;color:var(--tl-foreground);line-height:1.3}.event-card:hover .title{color:var(--tl-primary)}.meta{display:flex;align-items:center;gap:0.5rem;margin:0.5rem 0 0;font-size:0.875rem;color:var(--tl-muted-foreground)}.meta:first-of-type{margin-top:0.75rem}.icon{width:1rem;height:1rem;flex-shrink:0}.description{margin:0.75rem 0 0;font-size:0.875rem;color:var(--tl-muted-foreground);line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card-skeleton{cursor:default;pointer-events:none}.card-skeleton .image-container{animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-line{height:0.875rem;margin-top:0.5rem;background:var(--tl-muted);border-radius:var(--tl-radius-sm);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-line.short{width:40%}.skeleton-line.long{width:80%}.card-error{padding:var(--tl-spacing-4);font-size:0.875rem;color:var(--tl-error);cursor:default}.card-error:hover{transform:none}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}`;
31
6
 
@@ -34,101 +9,64 @@ const TlEventCard = class {
34
9
  registerInstance(this, hostRef);
35
10
  this.tlClick = createEvent(this, "tlClick");
36
11
  this.tlError = createEvent(this, "tlError");
37
- this.loadedEvent = null;
38
- this.loading = false;
39
- this.error = null;
12
+ this.element = createElement(eventCardCore, {
13
+ client: () => getSDK(),
14
+ clientReady: () => whenSDK(),
15
+ emit: (event, detail) => this.forward(event, detail),
16
+ });
17
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
18
+ this.view = this.element.view;
40
19
  this.handleClick = () => {
41
- const event = this.current;
42
- if (!event)
43
- return;
44
- console.log('[tl-event-card] Clicked:', event.id);
45
- this.tlClick.emit({ eventId: event.id });
20
+ this.element.actions.click();
46
21
  };
47
22
  }
48
- watchEventId() {
49
- this.loadEvent();
23
+ syncProps() {
24
+ this.element.setProps({ event: this.event, eventId: this.eventId });
50
25
  }
51
- async componentWillLoad() {
52
- if (!this.event && this.eventId) {
53
- await this.loadEvent();
54
- }
55
- }
56
- async loadEvent() {
57
- if (this.event || !this.eventId)
58
- return;
59
- try {
60
- this.loading = true;
61
- this.error = null;
62
- const sdk = await whenSDK();
63
- const detail = (await sdk.getEventsManager().get(this.eventId));
64
- if (!detail)
65
- throw new Error('Event not found');
66
- this.loadedEvent = toEventCardEvent(detail);
67
- }
68
- catch (err) {
69
- console.error('[tl-event-card] Failed to load event:', err);
70
- this.error = errorMessage(err, 'Failed to load event');
71
- this.tlError.emit({ message: this.error });
72
- }
73
- finally {
74
- this.loading = false;
75
- }
76
- }
77
- get current() {
78
- return this.event ?? this.loadedEvent;
26
+ /** A new id is a new subject: tear the old mount down and start again. */
27
+ watchEventId() {
28
+ this.syncProps();
29
+ this.teardown?.();
30
+ this.teardown = this.element.mount();
79
31
  }
80
- formatDate(dateString) {
81
- const date = new Date(dateString);
82
- return date.toLocaleDateString('en-GB', {
83
- weekday: 'short',
84
- day: 'numeric',
85
- month: 'short',
86
- hour: '2-digit',
87
- minute: '2-digit',
88
- });
32
+ connectedCallback() {
33
+ this.syncProps();
34
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
35
+ this.view = view;
36
+ }));
37
+ this.view = this.element.view;
38
+ this.teardown = this.element.mount();
89
39
  }
90
- getStatusBadgeClass() {
91
- const baseClass = 'status-badge';
92
- switch (this.current?.availabilityStatus) {
93
- case 'available':
94
- return `${baseClass} status-available`;
95
- case 'limited':
96
- return `${baseClass} status-limited`;
97
- case 'sold_out':
98
- return `${baseClass} status-sold-out`;
99
- default:
100
- return baseClass;
101
- }
40
+ disconnectedCallback() {
41
+ this.teardown?.();
42
+ this.teardown = undefined;
43
+ this.unsubscribe?.();
44
+ this.unsubscribe = undefined;
102
45
  }
103
- getStatusLabel() {
104
- switch (this.current?.availabilityStatus) {
105
- case 'available':
106
- return 'Available';
107
- case 'limited':
108
- return 'Limited';
109
- case 'sold_out':
110
- return 'Sold Out';
111
- default:
112
- return '';
113
- }
46
+ forward(event, detail) {
47
+ if (event === 'tlClick')
48
+ this.tlClick.emit(detail);
49
+ else if (event === 'tlError')
50
+ this.tlError.emit(detail);
114
51
  }
115
52
  render() {
116
- if (this.loading) {
53
+ const { status, state, derived } = this.view;
54
+ if (status === 'loading') {
117
55
  return (h("div", { class: "event-card card-skeleton", "aria-busy": "true" }, h("div", { class: "image-container" }), h("div", { class: "content" }, h("div", { class: "skeleton-line short" }), h("div", { class: "skeleton-line long" }))));
118
56
  }
119
- if (this.error) {
120
- return h("div", { class: "event-card card-error" }, this.error);
57
+ if (status === 'error') {
58
+ return h("div", { class: "event-card card-error" }, state.error);
121
59
  }
122
- const event = this.current;
60
+ const event = derived.event;
123
61
  if (!event) {
124
62
  return null;
125
63
  }
126
- // The sales surface exposes a single `imageUrl` (card/listing thumbnail);
127
- // `thumbnailUrl` is an accepted alias if a host ever supplies a distinct one.
128
- const image = event.thumbnailUrl || event.imageUrl;
129
- return (h("div", { class: "event-card", onClick: this.handleClick }, h("div", { class: "image-container" }, image ? (h("img", { src: image, alt: event.name, class: "event-image" })) : (h("div", { class: "image-placeholder" }, h("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" })))), h("div", { class: "status-container" }, h("span", { class: this.getStatusBadgeClass() }, this.getStatusLabel()))), h("div", { class: "content" }, event.category && h("span", { class: "category" }, event.category), h("h3", { class: "title" }, event.name), event.nextOccurrence && (h("p", { class: "meta" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" })), this.formatDate(event.nextOccurrence.startsAt))), event.venue && (h("p", { class: "meta" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })), event.venue.name, ", ", event.venue.city)), event.shortDescription && (h("p", { class: "description" }, event.shortDescription)))));
64
+ return (h("div", { class: "event-card", onClick: this.handleClick }, h("div", { class: "image-container" }, derived.image ? (h("img", { src: derived.image, alt: event.name, class: "event-image" })) : (h("div", { class: "image-placeholder" }, h("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" })))), h("div", { class: "status-container" }, h("span", { class: derived.statusBadgeClass }, derived.statusLabel))), h("div", { class: "content" }, event.category && h("span", { class: "category" }, event.category), h("h3", { class: "title" }, event.name), derived.dateLabel && (h("p", { class: "meta" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" })), derived.dateLabel)), derived.venueLabel && (h("p", { class: "meta" }, h("svg", { class: "icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })), derived.venueLabel)), event.shortDescription && h("p", { class: "description" }, event.shortDescription))));
130
65
  }
131
66
  static get watchers() { return {
67
+ "event": [{
68
+ "syncProps": 0
69
+ }],
132
70
  "eventId": [{
133
71
  "watchEventId": 0
134
72
  }]
@@ -1,5 +1,6 @@
1
- import { r as registerInstance, c as createEvent, h } from './index-OoYV1faY.js';
2
- import { w as whenSDK } from './sdk-connector-CqhvfyKV.js';
1
+ import { r as registerInstance, c as createEvent, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { c as createElement, e as eventListCore } from './ticket-selector-BwChnHVC.js';
3
4
 
4
5
  const tlEventListCss = () => `:host{display:block;font-family:var(--tl-font-family)}.event-grid{display:grid;gap:var(--tl-spacing-6)}.cols-1{grid-template-columns:1fr}.cols-2{grid-template-columns:repeat(2, 1fr)}.cols-3{grid-template-columns:repeat(3, 1fr)}.cols-4{grid-template-columns:repeat(4, 1fr)}@media (max-width: 1024px){.cols-4,.cols-3{grid-template-columns:repeat(2, 1fr)}}@media (max-width: 640px){.cols-4,.cols-3,.cols-2{grid-template-columns:1fr}}.skeleton-card{background:var(--tl-secondary);border-radius:var(--tl-radius-lg);overflow:hidden}.skeleton-image{aspect-ratio:16 / 9;background:var(--tl-muted);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-content{padding:var(--tl-spacing-4);display:flex;flex-direction:column;gap:0.5rem}.skeleton-line{height:1rem;background:var(--tl-muted);border-radius:var(--tl-radius-sm);animation:pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite}.skeleton-line.short{width:4rem;height:0.75rem}.skeleton-line.medium{width:75%}.skeleton-line.long{width:50%}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}.error-state{background:rgba(239, 68, 68, 0.1);border:1px solid var(--tl-error);border-radius:var(--tl-radius-lg);padding:var(--tl-spacing-4)}.error-title{margin:0;font-weight:500;color:var(--tl-error)}.error-message{margin:0.25rem 0 0;font-size:0.875rem;color:var(--tl-error)}.empty-state{text-align:center;padding:var(--tl-spacing-8) var(--tl-spacing-4)}.empty-icon{width:4rem;height:4rem;margin:0 auto 1rem;color:var(--tl-muted-foreground)}.empty-title{margin:0;font-size:1.25rem;font-weight:600;color:var(--tl-foreground)}.empty-message{margin:0.5rem 0 0;color:var(--tl-muted-foreground)}`;
5
6
 
@@ -15,54 +16,87 @@ const TlEventList = class {
15
16
  * Columns for grid layout (1, 2, 3, or 4)
16
17
  */
17
18
  this.columns = 3;
18
- this.events = [];
19
- this.loading = true;
20
- this.error = null;
19
+ this.element = createElement(eventListCore, {
20
+ client: () => getSDK(),
21
+ clientReady: () => whenSDK(),
22
+ emit: (event, detail) => this.forward(event, detail),
23
+ });
24
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
25
+ this.view = this.element.view;
26
+ /** A card was clicked. The core has nothing to decide about it; pass it on. */
21
27
  this.handleEventClick = (event) => {
22
- console.log('[tl-event-list] Event clicked:', event.detail.eventId);
23
28
  this.tlEventClick.emit(event.detail);
24
29
  };
25
30
  }
31
+ /** A different question deserves a fresh answer. */
26
32
  watchFiltersChange() {
27
- this.loadEvents();
33
+ this.syncProps();
34
+ this.element.actions.load();
28
35
  }
29
- async componentWillLoad() {
30
- await this.loadEvents();
36
+ syncProps() {
37
+ this.element.setProps({ limit: this.limit, category: this.category, columns: this.columns });
31
38
  }
32
- async loadEvents() {
33
- try {
34
- console.log('[tl-event-list] Loading events via SDK...', { limit: this.limit, category: this.category });
35
- this.loading = true;
36
- this.error = null;
37
- const sdk = await whenSDK();
38
- const eventsManager = sdk.getEventsManager();
39
- const response = await eventsManager.list({
40
- limit: this.limit,
41
- category: this.category,
39
+ connectedCallback() {
40
+ this.syncProps();
41
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
42
+ this.view = view;
43
+ }));
44
+ this.view = this.element.view;
45
+ this.teardown = this.element.mount();
46
+ }
47
+ /**
48
+ * Nothing is painted until the first listing has landed.
49
+ *
50
+ * Kept as found rather than improved, and it is the harness's to keep: the
51
+ * component awaited its read in `componentWillLoad`, and Stencil does not
52
+ * paint a component until that resolves, so a slow channel shows an empty
53
+ * box where the skeleton markup below would fit. Held by the spec.
54
+ *
55
+ * Expressed against the core's own status rather than against a read this
56
+ * harness starts, because the read is the core's (`connectedCallback` has
57
+ * already mounted it). Every later read draws the skeleton, exactly as
58
+ * before.
59
+ */
60
+ componentWillLoad() {
61
+ if (this.view.status !== 'loading')
62
+ return undefined;
63
+ return new Promise((resolve) => {
64
+ const off = this.element.subscribe((view) => {
65
+ if (view.status === 'loading')
66
+ return;
67
+ off();
68
+ resolve();
42
69
  });
43
- console.log('[tl-event-list] Events loaded:', response.items.length);
44
- this.events = response.items;
45
- }
46
- catch (err) {
47
- console.error('[tl-event-list] Failed to load events:', err);
48
- this.error = err instanceof Error ? err.message : 'Failed to load events';
49
- }
50
- finally {
51
- this.loading = false;
52
- }
70
+ });
71
+ }
72
+ /**
73
+ * Off screen: run the mount's teardown, and nothing more. `destroy()` is
74
+ * terminal and Stencil calls this for a move as well as a removal, so
75
+ * destroying here would leave a re-inserted element frozen.
76
+ */
77
+ disconnectedCallback() {
78
+ this.teardown?.();
79
+ this.teardown = undefined;
80
+ this.unsubscribe?.();
81
+ this.unsubscribe = undefined;
82
+ }
83
+ forward(event, detail) {
84
+ if (event === 'tlEventClick')
85
+ this.tlEventClick.emit(detail);
53
86
  }
54
87
  render() {
55
- const gridClass = `event-grid cols-${Math.min(Math.max(this.columns, 1), 4)}`;
56
- if (this.loading) {
88
+ const { status, state, strings, derived } = this.view;
89
+ const gridClass = `event-grid cols-${derived.columns}`;
90
+ if (status === 'loading') {
57
91
  return (h("div", { class: gridClass }, Array.from({ length: 6 }).map((_, i) => (h("div", { class: "skeleton-card", key: i }, h("div", { class: "skeleton-image" }), h("div", { class: "skeleton-content" }, h("div", { class: "skeleton-line short" }), h("div", { class: "skeleton-line medium" }), h("div", { class: "skeleton-line long" })))))));
58
92
  }
59
- if (this.error) {
60
- return (h("div", { class: "error-state" }, h("p", { class: "error-title" }, "Error loading events"), h("p", { class: "error-message" }, this.error)));
93
+ if (status === 'error') {
94
+ return (h("div", { class: "error-state" }, h("p", { class: "error-title" }, strings.errorTitle), h("p", { class: "error-message" }, state.error)));
61
95
  }
62
- if (this.events.length === 0) {
63
- return (h("div", { class: "empty-state" }, h("svg", { class: "empty-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" })), h("p", { class: "empty-title" }, "No events available"), h("p", { class: "empty-message" }, "Check back later for upcoming events.")));
96
+ if (status === 'empty') {
97
+ return (h("div", { class: "empty-state" }, h("svg", { class: "empty-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor" }, h("path", { "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" })), h("p", { class: "empty-title" }, strings.emptyTitle), h("p", { class: "empty-message" }, strings.emptyMessage)));
64
98
  }
65
- return (h("div", { class: gridClass }, this.events.map((event) => (h("tl-event-card", { key: event.id, event: event, onTlClick: this.handleEventClick })))));
99
+ return (h("div", { class: gridClass }, derived.events.map((event) => (h("tl-event-card", { key: event.id, event: event, onTlClick: this.handleEventClick })))));
66
100
  }
67
101
  static get watchers() { return {
68
102
  "limit": [{
@@ -70,6 +104,9 @@ const TlEventList = class {
70
104
  }],
71
105
  "category": [{
72
106
  "watchFiltersChange": 0
107
+ }],
108
+ "columns": [{
109
+ "syncProps": 0
73
110
  }]
74
111
  }; }
75
112
  };
@@ -1,7 +1,7 @@
1
- import { r as registerInstance, c as createEvent, h } from './index-OoYV1faY.js';
2
- import { w as whenSDK } from './sdk-connector-CqhvfyKV.js';
3
- import { e as errorMessage } from './order-summary-DiDtvfZM.js';
4
- import './money-4ig0-WB6.js';
1
+ import { r as registerInstance, c as createEvent, h } from './index-imfxp765.js';
2
+ import { w as whenSDK, g as getSDK } from './sdk-connector-CqhvfyKV.js';
3
+ import { w as webQuery } from './web-query-Bdbb2lk5.js';
4
+ import { c as createElement, l as loginCore } from './ticket-selector-BwChnHVC.js';
5
5
 
6
6
  const tlLoginCss = () => `:host{display:block;font-family:var(--tl-font-family);color:var(--tl-foreground)}.login{display:flex;flex-direction:column;gap:var(--tl-spacing-3);padding:var(--tl-spacing-6);background:var(--tl-background);border:1px solid var(--tl-border);border-radius:var(--tl-radius-lg)}.title{margin:0;font-size:1.25rem;font-weight:600}.lead{margin:0;font-size:0.875rem;color:var(--tl-muted-foreground)}.label{font-size:0.875rem;font-weight:500}.input{padding:var(--tl-spacing-3);font:inherit;font-size:0.9375rem;color:var(--tl-foreground);background:var(--tl-background);border:1px solid var(--tl-border);border-radius:var(--tl-radius-md)}.input:focus{outline:2px solid var(--tl-primary);outline-offset:1px}.code-input{font-family:var(--tl-font-family-mono);font-size:1.25rem;letter-spacing:0.25em;text-align:center}.primary,.secondary{padding:var(--tl-spacing-3) var(--tl-spacing-4);font:inherit;font-size:0.9375rem;font-weight:600;border-radius:var(--tl-radius-md);cursor:pointer}.primary{color:var(--tl-primary-foreground);background:var(--tl-primary);border:none}.primary:hover:not(:disabled){opacity:0.9}.secondary{color:var(--tl-foreground);background:var(--tl-secondary);border:1px solid var(--tl-border)}.primary:disabled,.secondary:disabled,.input:disabled{opacity:0.6;cursor:not-allowed}.link{padding:0;font:inherit;font-size:0.875rem;color:var(--tl-primary);background:none;border:none;cursor:pointer}.link:hover{text-decoration:underline}.actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--tl-spacing-4)}.error{margin:0;font-size:0.8125rem;color:var(--tl-error)}.notice{margin:0;font-size:0.8125rem;color:var(--tl-success)}.signed-in{flex-direction:row;align-items:center;justify-content:space-between;flex-wrap:wrap}.signed-in-text{margin:0;font-size:0.875rem;overflow-wrap:anywhere}`;
7
7
 
@@ -15,168 +15,101 @@ const TlLogin = class {
15
15
  this.heading = 'Sign in';
16
16
  /** Offer a "enter the code" path alongside the emailed link (default true). */
17
17
  this.allowCodeEntry = true;
18
- this.phase = 'email';
19
- this.email = '';
20
- this.code = '';
21
- this.busy = false;
22
- this.error = null;
23
- this.notice = null;
24
- this.customer = null;
25
- this.auth = null;
26
- this.unsubscribes = [];
27
- // A magic-link code is single-use; never exchange the URL code twice.
28
- this.urlCodeUsed = false;
18
+ this.element = createElement(loginCore, {
19
+ client: () => getSDK(),
20
+ clientReady: () => whenSDK(),
21
+ query: webQuery,
22
+ emit: (event, detail) => this.forward(event, detail),
23
+ });
24
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
25
+ this.view = this.element.view;
29
26
  this.handleEmailInput = (e) => {
30
- this.email = e.target.value;
31
- this.error = null;
27
+ this.element.actions.setEmail(e.target.value);
32
28
  };
33
29
  this.handleCodeInput = (e) => {
34
- this.code = e.target.value;
35
- this.error = null;
30
+ this.element.actions.setCode(e.target.value);
36
31
  };
37
32
  this.handleEmailSubmit = (e) => {
38
33
  e.preventDefault();
39
- this.requestLink();
34
+ this.element.actions.requestLink();
40
35
  };
41
36
  this.handleCodeSubmit = (e) => {
42
37
  e.preventDefault();
43
- this.exchange(this.code.trim());
38
+ this.element.actions.submitCode();
44
39
  };
45
40
  }
46
- async componentWillLoad() {
47
- const sdk = await whenSDK().catch(() => null);
48
- if (!sdk) {
49
- this.error = 'Sign-in is not available right now';
50
- return;
51
- }
52
- this.auth = sdk.getAuthManager?.() ?? null;
53
- if (!this.auth) {
54
- this.error = 'Sign-in is not available with this SDK';
55
- return;
56
- }
57
- this.syncFromSDK();
58
- // The session hydrates after the SDK publishes; follow both signals.
59
- this.unsubscribes.push(sdk.on('auth:changed', () => this.syncFromSDK()));
60
- this.unsubscribes.push(sdk.on('session:changed', () => this.syncFromSDK()));
61
- const urlCode = this.codeFromUrl();
62
- if (urlCode && !this.auth.authenticated) {
63
- await this.exchange(urlCode);
64
- }
65
- }
66
- disconnectedCallback() {
67
- this.unsubscribes.forEach((off) => off());
68
- this.unsubscribes = [];
41
+ syncProps() {
42
+ this.element.setProps({ heading: this.heading, allowCodeEntry: this.allowCodeEntry });
69
43
  }
70
- codeFromUrl() {
71
- if (typeof window === 'undefined' || !window.location?.search || this.urlCodeUsed)
72
- return null;
73
- const code = new URLSearchParams(window.location.search).get('code');
74
- if (code)
75
- this.urlCodeUsed = true;
76
- return code;
44
+ connectedCallback() {
45
+ this.syncProps();
46
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
47
+ this.view = view;
48
+ }));
49
+ this.view = this.element.view;
50
+ this.teardown = this.element.mount();
77
51
  }
78
- syncFromSDK() {
79
- if (!this.auth)
80
- return;
81
- if (this.auth.authenticated) {
82
- this.customer = this.auth.customer ?? null;
83
- this.phase = 'signed-in';
84
- }
85
- else if (this.phase === 'signed-in') {
86
- this.customer = null;
87
- this.phase = 'email';
88
- }
89
- }
90
- async requestLink(resend = false) {
91
- const email = this.email.trim();
92
- if (!email) {
93
- this.error = 'Enter your email address';
94
- return;
95
- }
96
- if (!this.auth)
97
- return;
98
- this.busy = true;
99
- this.error = null;
100
- this.notice = null;
101
- try {
102
- // Always reports success; account existence is never revealed.
103
- await this.auth.requestMagicLink({ email });
104
- this.phase = 'sent';
105
- this.notice = resend ? 'Sent again. Check your inbox.' : null;
106
- this.tlMagicLinkSent.emit({ email });
107
- }
108
- catch (err) {
109
- console.error('[tl-login] Magic-link request failed:', err);
110
- this.error = errorMessage(err, 'Could not send your sign-in link');
111
- this.tlError.emit({ message: this.error });
112
- }
113
- finally {
114
- this.busy = false;
115
- }
52
+ /**
53
+ * Nothing is painted until this page's client is up.
54
+ *
55
+ * A rendering decision, and the harness's to make: Stencil waits on this
56
+ * promise before the first render, and without it the form would flash
57
+ * disabled for a microtask on every page. The core is already at work by
58
+ * now - `connectedCallback` mounted it - so this waits on the same
59
+ * `whenSDK()` it is waiting on rather than deciding anything of its own, and
60
+ * a page that never starts Live renders the core's "not available" state
61
+ * instead of hanging.
62
+ */
63
+ componentWillLoad() {
64
+ return whenSDK().catch(() => undefined);
116
65
  }
117
- async exchange(code) {
118
- if (!code) {
119
- this.error = 'Enter the code from your email';
120
- return;
121
- }
122
- if (!this.auth)
123
- return;
124
- const previous = this.phase;
125
- this.busy = true;
126
- this.error = null;
127
- this.phase = 'verifying';
128
- try {
129
- const res = (await this.auth.exchange(code));
130
- this.customer = this.auth.customer ?? res?.customer ?? null;
131
- this.phase = 'signed-in';
132
- this.code = '';
133
- this.tlAuthChanged.emit({ customer: this.customer, authenticated: true });
134
- }
135
- catch (err) {
136
- console.error('[tl-login] Code exchange failed:', err);
137
- this.phase = previous === 'verifying' || previous === 'email' ? 'email' : 'code';
138
- this.error = 'That code did not work. Check it and try again, or request a new link.';
139
- this.tlError.emit({ message: this.error });
140
- }
141
- finally {
142
- this.busy = false;
143
- }
66
+ /**
67
+ * Off screen: run the mount's teardown, which drops both auth
68
+ * subscriptions. Not `destroy()`: Stencil calls this for a DOM move as well
69
+ * as a removal, and a destroyed core never mounts again.
70
+ */
71
+ disconnectedCallback() {
72
+ this.teardown?.();
73
+ this.teardown = undefined;
74
+ this.unsubscribe?.();
75
+ this.unsubscribe = undefined;
144
76
  }
145
- async signOut() {
146
- if (!this.auth)
147
- return;
148
- this.busy = true;
149
- try {
150
- await this.auth.logout();
151
- }
152
- catch (err) {
153
- console.error('[tl-login] Sign out failed:', err);
154
- }
155
- finally {
156
- this.busy = false;
157
- this.customer = null;
158
- this.phase = 'email';
159
- this.tlAuthChanged.emit({ customer: null, authenticated: false });
160
- }
77
+ forward(event, detail) {
78
+ if (event === 'tlAuthChanged')
79
+ this.tlAuthChanged.emit(detail);
80
+ else if (event === 'tlMagicLinkSent')
81
+ this.tlMagicLinkSent.emit(detail);
82
+ else if (event === 'tlError')
83
+ this.tlError.emit(detail);
161
84
  }
162
85
  renderError() {
163
- return this.error ? (h("p", { class: "error", role: "alert" }, this.error)) : null;
86
+ const error = this.view.state.error;
87
+ return error ? (h("p", { class: "error", role: "alert" }, error)) : null;
164
88
  }
165
89
  render() {
166
- if (this.phase === 'signed-in') {
167
- return (h("div", { class: "login signed-in" }, h("p", { class: "signed-in-text" }, "Signed in as ", h("strong", null, this.customer?.email ?? 'your account')), h("button", { type: "button", class: "secondary", disabled: this.busy, onClick: () => this.signOut() }, "Sign out")));
90
+ const { status, state, strings, derived } = this.view;
91
+ if (status === 'signed-in') {
92
+ return (h("div", { class: "login signed-in" }, h("p", { class: "signed-in-text" }, strings.signedInPrefix, h("strong", null, derived.signedInEmail)), h("button", { type: "button", class: "secondary", disabled: state.busy, onClick: () => this.element.actions.signOut() }, strings.signOut)));
168
93
  }
169
- if (this.phase === 'verifying') {
170
- return (h("div", { class: "login" }, h("h2", { class: "title" }, "Signing you in\u2026"), h("p", { class: "lead" }, "Just a moment.")));
94
+ if (status === 'verifying') {
95
+ return (h("div", { class: "login" }, h("h2", { class: "title" }, strings.verifyingTitle), h("p", { class: "lead" }, strings.verifyingLead)));
171
96
  }
172
- if (this.phase === 'sent') {
173
- return (h("div", { class: "login" }, h("h2", { class: "title" }, "Check your inbox"), h("p", { class: "lead" }, "We've sent a sign-in link to ", h("strong", null, this.email), ". Open it on this device to continue."), this.notice && h("p", { class: "notice" }, this.notice), this.renderError(), h("div", { class: "actions" }, h("button", { type: "button", class: "secondary", disabled: this.busy, onClick: () => this.requestLink(true) }, this.busy ? 'Sending…' : 'Resend link'), this.allowCodeEntry && (h("button", { type: "button", class: "link", onClick: () => (this.phase = 'code') }, "Enter the code instead")), h("button", { type: "button", class: "link", onClick: () => (this.phase = 'email') }, "Use a different email"))));
97
+ if (status === 'sent') {
98
+ return (h("div", { class: "login" }, h("h2", { class: "title" }, strings.sentTitle), h("p", { class: "lead" }, strings.sentLeadBefore, h("strong", null, state.email), strings.sentLeadAfter), state.notice && h("p", { class: "notice" }, state.notice), this.renderError(), h("div", { class: "actions" }, h("button", { type: "button", class: "secondary", disabled: state.busy, onClick: () => this.element.actions.requestLink(true) }, derived.resendLabel), derived.showCodeEntry && (h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('code') }, strings.enterCodeInstead)), h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('email') }, strings.differentEmail))));
174
99
  }
175
- if (this.phase === 'code') {
176
- return (h("form", { class: "login", onSubmit: this.handleCodeSubmit }, h("h2", { class: "title" }, "Enter your code"), h("p", { class: "lead" }, "Type the 6-digit code from the email we sent to ", h("strong", null, this.email), "."), h("label", { class: "label", htmlFor: "tl-login-code" }, "Code"), h("input", { id: "tl-login-code", class: "input code-input", type: "text", inputmode: "numeric", autocomplete: "one-time-code", placeholder: "123456", value: this.code, disabled: this.busy, onInput: this.handleCodeInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: this.busy }, this.busy ? 'Checking…' : 'Sign in'), h("div", { class: "actions" }, h("button", { type: "button", class: "link", onClick: () => this.requestLink(true) }, "Resend link"), h("button", { type: "button", class: "link", onClick: () => (this.phase = 'sent') }, "Back"))));
100
+ if (status === 'code') {
101
+ return (h("form", { class: "login", onSubmit: this.handleCodeSubmit }, h("h2", { class: "title" }, strings.codeTitle), h("p", { class: "lead" }, strings.codeLeadBefore, h("strong", null, state.email), strings.codeLeadAfter), h("label", { class: "label", htmlFor: "tl-login-code" }, strings.codeLabel), h("input", { id: "tl-login-code", class: "input code-input", type: "text", inputmode: "numeric", autocomplete: "one-time-code", placeholder: strings.codePlaceholder, value: state.code, disabled: state.busy, onInput: this.handleCodeInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: state.busy }, derived.codeSubmitLabel), h("div", { class: "actions" }, h("button", { type: "button", class: "link", onClick: () => this.element.actions.requestLink(true) }, strings.resendLink), h("button", { type: "button", class: "link", onClick: () => this.element.actions.goTo('sent') }, strings.back))));
177
102
  }
178
- return (h("form", { class: "login", onSubmit: this.handleEmailSubmit }, h("h2", { class: "title" }, this.heading), h("p", { class: "lead" }, "Enter your email and we'll send you a secure sign-in link. No password needed."), h("label", { class: "label", htmlFor: "tl-login-email" }, "Email address"), h("input", { id: "tl-login-email", class: "input", type: "email", autocomplete: "email", placeholder: "you@example.com", required: true, value: this.email, disabled: this.busy || !this.auth, onInput: this.handleEmailInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: this.busy || !this.auth }, this.busy ? 'Sending link…' : 'Email me a sign-in link')));
103
+ return (h("form", { class: "login", onSubmit: this.handleEmailSubmit }, h("h2", { class: "title" }, this.heading), h("p", { class: "lead" }, strings.emailLead), h("label", { class: "label", htmlFor: "tl-login-email" }, strings.emailLabel), h("input", { id: "tl-login-email", class: "input", type: "email", autocomplete: "email", placeholder: strings.emailPlaceholder, required: true, value: state.email, disabled: derived.inputsDisabled, onInput: this.handleEmailInput }), this.renderError(), h("button", { type: "submit", class: "primary", disabled: derived.inputsDisabled }, derived.emailSubmitLabel)));
179
104
  }
105
+ static get watchers() { return {
106
+ "heading": [{
107
+ "syncProps": 0
108
+ }],
109
+ "allowCodeEntry": [{
110
+ "syncProps": 0
111
+ }]
112
+ }; }
180
113
  };
181
114
  TlLogin.style = tlLoginCss();
182
115