@ticketlayer/elements 0.3.3 → 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 (179) hide show
  1. package/README.md +3 -2
  2. package/dist/cjs/{index-BpfoKd9Q.js → index-DanPUX0s.js} +1 -1
  3. package/dist/cjs/index.cjs.js +2 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
  6. package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
  7. package/dist/cjs/tl-buy-modal.cjs.entry.js +4 -4
  8. package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -71
  9. package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +62 -31
  10. package/dist/cjs/tl-cart-badge.cjs.entry.js +75 -62
  11. package/dist/cjs/tl-cart-drawer.cjs.entry.js +52 -24
  12. package/dist/cjs/tl-cart.cjs.entry.js +72 -98
  13. package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +52 -30
  14. package/dist/cjs/tl-event-card.cjs.entry.js +44 -105
  15. package/dist/cjs/tl-event-list.cjs.entry.js +73 -36
  16. package/dist/cjs/tl-login.cjs.entry.js +75 -141
  17. package/dist/cjs/tl-my-orders.cjs.entry.js +53 -92
  18. package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +205 -641
  19. package/dist/cjs/tl-order-confirmation.cjs.entry.js +72 -105
  20. package/dist/cjs/tl-order-tickets_2.cjs.entry.js +50 -76
  21. package/dist/cjs/tl-promo-code.cjs.entry.js +51 -47
  22. package/dist/cjs/tl-seat-map.cjs.entry.js +4 -4
  23. package/dist/cjs/web-query-Ce73jB4k.js +16 -0
  24. package/dist/collection/components/tl-buy-modal/tl-buy-modal.css +12 -19
  25. package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
  26. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +41 -4
  27. package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -76
  28. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.css +2 -2
  29. package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
  30. package/dist/collection/components/tl-cart/tl-cart.css +52 -51
  31. package/dist/collection/components/tl-cart/tl-cart.js +82 -101
  32. package/dist/collection/components/tl-cart-badge/tl-cart-badge.css +11 -11
  33. package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
  34. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.css +6 -6
  35. package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
  36. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.css +2 -2
  37. package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
  38. package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
  39. package/dist/collection/components/tl-event-card/tl-event-card.css +22 -22
  40. package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
  41. package/dist/collection/components/tl-event-list/tl-event-list.css +17 -17
  42. package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
  43. package/dist/collection/components/tl-login/tl-login.css +26 -26
  44. package/dist/collection/components/tl-login/tl-login.js +83 -147
  45. package/dist/collection/components/tl-my-orders/tl-my-orders.css +40 -40
  46. package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
  47. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.css +54 -65
  48. package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
  49. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.css +38 -38
  50. package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -113
  51. package/dist/collection/components/tl-order-tickets/tl-order-tickets.css +9 -9
  52. package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
  53. package/dist/collection/components/tl-promo-code/tl-promo-code.css +25 -25
  54. package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
  55. package/dist/collection/components/tl-seat-map/tl-seat-map.css +10 -10
  56. package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
  57. package/dist/collection/components/tl-ticket/tl-ticket.css +13 -13
  58. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.css +74 -49
  59. package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +95 -135
  60. package/dist/collection/utils/cart-totals.js +9 -0
  61. package/dist/collection/utils/order-summary.js +15 -99
  62. package/dist/collection/utils/testing/deep-text.js +29 -0
  63. package/dist/collection/utils/testing/fixtures.js +290 -0
  64. package/dist/collection/utils/testing/mock-sdk.js +13 -2
  65. package/dist/collection/utils/web-query.js +12 -0
  66. package/dist/collection/utils/web-storage.js +22 -0
  67. package/dist/components/ticket-selector.js +1 -0
  68. package/dist/components/tl-buy-modal.js +1 -1
  69. package/dist/components/tl-buy-tickets-button.js +1 -1
  70. package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
  71. package/dist/components/tl-cart-badge.js +1 -1
  72. package/dist/components/tl-cart-drawer.js +1 -1
  73. package/dist/components/tl-cart2.js +1 -1
  74. package/dist/components/tl-checkout-wrapper2.js +1 -1
  75. package/dist/components/tl-event-card2.js +1 -1
  76. package/dist/components/tl-event-list.js +1 -1
  77. package/dist/components/tl-login2.js +1 -1
  78. package/dist/components/tl-my-orders.js +1 -1
  79. package/dist/components/tl-occurrence-selector2.js +1 -1
  80. package/dist/components/tl-order-confirmation.js +1 -1
  81. package/dist/components/tl-order-tickets2.js +1 -1
  82. package/dist/components/tl-promo-code.js +1 -1
  83. package/dist/components/tl-seat-map.js +1 -1
  84. package/dist/components/tl-ticket-selector2.js +1 -1
  85. package/dist/components/tl-ticket2.js +1 -1
  86. package/dist/components/web-query.js +1 -0
  87. package/dist/elements.docs.json +36 -36
  88. package/dist/esm/{index-CNX8hZa8.js → index-imfxp765.js} +1 -1
  89. package/dist/esm/index.js +2 -1
  90. package/dist/esm/loader.js +3 -3
  91. package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
  92. package/dist/esm/ticketlayer-elements.js +3 -3
  93. package/dist/esm/tl-buy-modal.entry.js +4 -4
  94. package/dist/esm/tl-buy-tickets-button.entry.js +90 -72
  95. package/dist/esm/tl-buy-tickets-wrapper.entry.js +63 -32
  96. package/dist/esm/tl-cart-badge.entry.js +76 -63
  97. package/dist/esm/tl-cart-drawer.entry.js +53 -25
  98. package/dist/esm/tl-cart.entry.js +73 -99
  99. package/dist/esm/tl-checkout-wrapper.entry.js +53 -31
  100. package/dist/esm/tl-event-card.entry.js +45 -106
  101. package/dist/esm/tl-event-list.entry.js +74 -37
  102. package/dist/esm/tl-login.entry.js +76 -142
  103. package/dist/esm/tl-my-orders.entry.js +54 -93
  104. package/dist/esm/tl-occurrence-selector_2.entry.js +206 -642
  105. package/dist/esm/tl-order-confirmation.entry.js +73 -106
  106. package/dist/esm/tl-order-tickets_2.entry.js +51 -77
  107. package/dist/esm/tl-promo-code.entry.js +52 -48
  108. package/dist/esm/tl-seat-map.entry.js +4 -4
  109. package/dist/esm/web-query-Bdbb2lk5.js +14 -0
  110. package/dist/ticketlayer-elements/index.esm.js +1 -1
  111. package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
  112. package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
  113. package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
  114. package/dist/ticketlayer-elements/p-5135630e.entry.js +1 -0
  115. package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
  116. package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
  117. package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
  118. package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
  119. package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
  120. package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
  121. package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
  122. package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
  123. package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
  124. package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
  125. package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
  126. package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
  127. package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
  128. package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
  129. package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
  130. package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
  131. package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
  132. package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
  133. package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
  134. package/dist/types/components/tl-cart/tl-cart.d.ts +40 -32
  135. package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
  136. package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
  137. package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
  138. package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
  139. package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
  140. package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
  141. package/dist/types/components/tl-login/tl-login.d.ts +68 -22
  142. package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
  143. package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
  144. package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
  145. package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
  146. package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
  147. package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
  148. package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +61 -32
  149. package/dist/types/components.d.ts +302 -62
  150. package/dist/types/utils/cart-totals.d.ts +10 -0
  151. package/dist/types/utils/order-summary.d.ts +16 -65
  152. package/dist/types/utils/testing/deep-text.d.ts +13 -0
  153. package/dist/types/utils/testing/fixtures.d.ts +225 -0
  154. package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
  155. package/dist/types/utils/web-query.d.ts +18 -0
  156. package/dist/types/utils/web-storage.d.ts +16 -0
  157. package/elements.catalogue.json +676 -55
  158. package/package.json +14 -6
  159. package/dist/cjs/order-summary-ZMkJ5bez.js +0 -116
  160. package/dist/components/order-summary.js +0 -1
  161. package/dist/esm/order-summary-BGekdjN9.js +0 -103
  162. package/dist/ticketlayer-elements/p-0b1d16f8.entry.js +0 -1
  163. package/dist/ticketlayer-elements/p-1471f6d8.entry.js +0 -1
  164. package/dist/ticketlayer-elements/p-1c51cbc1.entry.js +0 -1
  165. package/dist/ticketlayer-elements/p-5169f68b.entry.js +0 -1
  166. package/dist/ticketlayer-elements/p-895ba1f3.entry.js +0 -1
  167. package/dist/ticketlayer-elements/p-8d5722e1.entry.js +0 -1
  168. package/dist/ticketlayer-elements/p-917d8e09.entry.js +0 -1
  169. package/dist/ticketlayer-elements/p-9629029a.entry.js +0 -1
  170. package/dist/ticketlayer-elements/p-BGekdjN9.js +0 -1
  171. package/dist/ticketlayer-elements/p-CNX8hZa8.js +0 -3
  172. package/dist/ticketlayer-elements/p-a22714d0.entry.js +0 -1
  173. package/dist/ticketlayer-elements/p-bf73044b.entry.js +0 -1
  174. package/dist/ticketlayer-elements/p-e02079fa.entry.js +0 -1
  175. package/dist/ticketlayer-elements/p-e04ace15.entry.js +0 -1
  176. package/dist/ticketlayer-elements/p-e6fab1d5.entry.js +0 -1
  177. package/dist/ticketlayer-elements/p-f826b753.entry.js +0 -1
  178. package/dist/ticketlayer-elements/p-fa639d88.entry.js +0 -1
  179. package/dist/ticketlayer-elements/p-fbaca948.entry.js +0 -1
@@ -1,102 +1,68 @@
1
1
  import { h } from "@stencil/core";
2
- import { whenSDK } from "../../utils/sdk-connector";
3
- import { errorMessage } from "../../utils/order-summary";
4
- import { toEventCardEvent } from "./event-card-adapter";
2
+ import { createElement, eventCardCore, } from "@ticketlayer/elements-core";
3
+ import { getSDK, whenSDK } from "../../utils/sdk-connector";
4
+ /**
5
+ * The web harness for `eventCardCore`.
6
+ *
7
+ * It renders and it forwards: which event is shown, how it is read, the status
8
+ * badge and the date are all in `@ticketlayer/elements-core` (AGENTS.md,
9
+ * TKT-11).
10
+ */
5
11
  export class TlEventCard {
6
12
  constructor() {
7
- this.loadedEvent = null;
8
- this.loading = false;
9
- this.error = null;
13
+ this.element = createElement(eventCardCore, {
14
+ client: () => getSDK(),
15
+ clientReady: () => whenSDK(),
16
+ emit: (event, detail) => this.forward(event, detail),
17
+ });
18
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
19
+ this.view = this.element.view;
10
20
  this.handleClick = () => {
11
- const event = this.current;
12
- if (!event)
13
- return;
14
- console.log('[tl-event-card] Clicked:', event.id);
15
- this.tlClick.emit({ eventId: event.id });
21
+ this.element.actions.click();
16
22
  };
17
23
  }
18
- watchEventId() {
19
- this.loadEvent();
20
- }
21
- async componentWillLoad() {
22
- if (!this.event && this.eventId) {
23
- await this.loadEvent();
24
- }
24
+ syncProps() {
25
+ this.element.setProps({ event: this.event, eventId: this.eventId });
25
26
  }
26
- async loadEvent() {
27
- if (this.event || !this.eventId)
28
- return;
29
- try {
30
- this.loading = true;
31
- this.error = null;
32
- const sdk = await whenSDK();
33
- const detail = (await sdk.getEventsManager().get(this.eventId));
34
- if (!detail)
35
- throw new Error('Event not found');
36
- this.loadedEvent = toEventCardEvent(detail);
37
- }
38
- catch (err) {
39
- console.error('[tl-event-card] Failed to load event:', err);
40
- this.error = errorMessage(err, 'Failed to load event');
41
- this.tlError.emit({ message: this.error });
42
- }
43
- finally {
44
- this.loading = false;
45
- }
46
- }
47
- get current() {
48
- return this.event ?? this.loadedEvent;
27
+ /** A new id is a new subject: tear the old mount down and start again. */
28
+ watchEventId() {
29
+ this.syncProps();
30
+ this.teardown?.();
31
+ this.teardown = this.element.mount();
49
32
  }
50
- formatDate(dateString) {
51
- const date = new Date(dateString);
52
- return date.toLocaleDateString('en-GB', {
53
- weekday: 'short',
54
- day: 'numeric',
55
- month: 'short',
56
- hour: '2-digit',
57
- minute: '2-digit',
58
- });
33
+ connectedCallback() {
34
+ this.syncProps();
35
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
36
+ this.view = view;
37
+ }));
38
+ this.view = this.element.view;
39
+ this.teardown = this.element.mount();
59
40
  }
60
- getStatusBadgeClass() {
61
- const baseClass = 'status-badge';
62
- switch (this.current?.availabilityStatus) {
63
- case 'available':
64
- return `${baseClass} status-available`;
65
- case 'limited':
66
- return `${baseClass} status-limited`;
67
- case 'sold_out':
68
- return `${baseClass} status-sold-out`;
69
- default:
70
- return baseClass;
71
- }
41
+ disconnectedCallback() {
42
+ this.teardown?.();
43
+ this.teardown = undefined;
44
+ this.unsubscribe?.();
45
+ this.unsubscribe = undefined;
72
46
  }
73
- getStatusLabel() {
74
- switch (this.current?.availabilityStatus) {
75
- case 'available':
76
- return 'Available';
77
- case 'limited':
78
- return 'Limited';
79
- case 'sold_out':
80
- return 'Sold Out';
81
- default:
82
- return '';
83
- }
47
+ forward(event, detail) {
48
+ if (event === 'tlClick')
49
+ this.tlClick.emit(detail);
50
+ else if (event === 'tlError')
51
+ this.tlError.emit(detail);
84
52
  }
85
53
  render() {
86
- if (this.loading) {
54
+ const { status, state, derived } = this.view;
55
+ if (status === 'loading') {
87
56
  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" }))));
88
57
  }
89
- if (this.error) {
90
- return h("div", { class: "event-card card-error" }, this.error);
58
+ if (status === 'error') {
59
+ return h("div", { class: "event-card card-error" }, state.error);
91
60
  }
92
- const event = this.current;
61
+ const event = derived.event;
93
62
  if (!event) {
94
63
  return null;
95
64
  }
96
- // The sales surface exposes a single `imageUrl` (card/listing thumbnail);
97
- // `thumbnailUrl` is an accepted alias if a host ever supplies a distinct one.
98
- const image = event.thumbnailUrl || event.imageUrl;
99
- 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)))));
65
+ 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))));
100
66
  }
101
67
  static get is() { return "tl-event-card"; }
102
68
  static get encapsulation() { return "shadow"; }
@@ -158,9 +124,7 @@ export class TlEventCard {
158
124
  }
159
125
  static get states() {
160
126
  return {
161
- "loadedEvent": {},
162
- "loading": {},
163
- "error": {}
127
+ "view": {}
164
128
  };
165
129
  }
166
130
  static get events() {
@@ -198,6 +162,9 @@ export class TlEventCard {
198
162
  }
199
163
  static get watchers() {
200
164
  return [{
165
+ "propName": "event",
166
+ "methodName": "syncProps"
167
+ }, {
201
168
  "propName": "eventId",
202
169
  "methodName": "watchEventId"
203
170
  }];
@@ -1,11 +1,11 @@
1
1
  :host {
2
2
  display: block;
3
- font-family: var(--tl-font-family, system-ui, sans-serif);
3
+ font-family: var(--tl-font-family);
4
4
  }
5
5
 
6
6
  .event-grid {
7
7
  display: grid;
8
- gap: var(--tl-spacing-6, 1.5rem);
8
+ gap: var(--tl-spacing-6);
9
9
  }
10
10
 
11
11
  .cols-1 {
@@ -41,19 +41,19 @@
41
41
 
42
42
  /* Skeleton Loading */
43
43
  .skeleton-card {
44
- background: var(--tl-secondary, #f3f4f6);
45
- border-radius: var(--tl-radius-lg, 0.75rem);
44
+ background: var(--tl-secondary);
45
+ border-radius: var(--tl-radius-lg);
46
46
  overflow: hidden;
47
47
  }
48
48
 
49
49
  .skeleton-image {
50
50
  aspect-ratio: 16 / 9;
51
- background: var(--tl-muted, #f9fafb);
51
+ background: var(--tl-muted);
52
52
  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
53
53
  }
54
54
 
55
55
  .skeleton-content {
56
- padding: var(--tl-spacing-4, 1rem);
56
+ padding: var(--tl-spacing-4);
57
57
  display: flex;
58
58
  flex-direction: column;
59
59
  gap: 0.5rem;
@@ -61,8 +61,8 @@
61
61
 
62
62
  .skeleton-line {
63
63
  height: 1rem;
64
- background: var(--tl-muted, #f9fafb);
65
- border-radius: var(--tl-radius-sm, 0.25rem);
64
+ background: var(--tl-muted);
65
+ border-radius: var(--tl-radius-sm);
66
66
  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
67
67
  }
68
68
 
@@ -91,44 +91,44 @@
91
91
  /* Error State */
92
92
  .error-state {
93
93
  background: rgba(239, 68, 68, 0.1);
94
- border: 1px solid var(--tl-error, #ef4444);
95
- border-radius: var(--tl-radius-lg, 0.75rem);
96
- padding: var(--tl-spacing-4, 1rem);
94
+ border: 1px solid var(--tl-error);
95
+ border-radius: var(--tl-radius-lg);
96
+ padding: var(--tl-spacing-4);
97
97
  }
98
98
 
99
99
  .error-title {
100
100
  margin: 0;
101
101
  font-weight: 500;
102
- color: var(--tl-error, #ef4444);
102
+ color: var(--tl-error);
103
103
  }
104
104
 
105
105
  .error-message {
106
106
  margin: 0.25rem 0 0;
107
107
  font-size: 0.875rem;
108
- color: var(--tl-error, #ef4444);
108
+ color: var(--tl-error);
109
109
  }
110
110
 
111
111
  /* Empty State */
112
112
  .empty-state {
113
113
  text-align: center;
114
- padding: var(--tl-spacing-8, 2rem) var(--tl-spacing-4, 1rem);
114
+ padding: var(--tl-spacing-8) var(--tl-spacing-4);
115
115
  }
116
116
 
117
117
  .empty-icon {
118
118
  width: 4rem;
119
119
  height: 4rem;
120
120
  margin: 0 auto 1rem;
121
- color: var(--tl-muted-foreground, #6b7280);
121
+ color: var(--tl-muted-foreground);
122
122
  }
123
123
 
124
124
  .empty-title {
125
125
  margin: 0;
126
126
  font-size: 1.25rem;
127
127
  font-weight: 600;
128
- color: var(--tl-foreground, #111827);
128
+ color: var(--tl-foreground);
129
129
  }
130
130
 
131
131
  .empty-message {
132
132
  margin: 0.5rem 0 0;
133
- color: var(--tl-muted-foreground, #6b7280);
133
+ color: var(--tl-muted-foreground);
134
134
  }
@@ -1,5 +1,15 @@
1
1
  import { h } from "@stencil/core";
2
- import { whenSDK } from "../../utils/sdk-connector";
2
+ import { createElement, eventListCore, } from "@ticketlayer/elements-core";
3
+ import { getSDK, whenSDK } from "../../utils/sdk-connector";
4
+ /**
5
+ * tl-event-list - what the channel is selling, as a grid of tl-event-card.
6
+ *
7
+ * The web harness for `eventListCore`: it renders and it forwards. What to ask
8
+ * for, what came back and every string are in `@ticketlayer/elements-core`
9
+ * (AGENTS.md, TKT-11).
10
+ *
11
+ * Usage: <tl-event-list limit="12" columns="3"></tl-event-list>
12
+ */
3
13
  export class TlEventList {
4
14
  constructor() {
5
15
  /**
@@ -10,54 +20,87 @@ export class TlEventList {
10
20
  * Columns for grid layout (1, 2, 3, or 4)
11
21
  */
12
22
  this.columns = 3;
13
- this.events = [];
14
- this.loading = true;
15
- this.error = null;
23
+ this.element = createElement(eventListCore, {
24
+ client: () => getSDK(),
25
+ clientReady: () => whenSDK(),
26
+ emit: (event, detail) => this.forward(event, detail),
27
+ });
28
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
29
+ this.view = this.element.view;
30
+ /** A card was clicked. The core has nothing to decide about it; pass it on. */
16
31
  this.handleEventClick = (event) => {
17
- console.log('[tl-event-list] Event clicked:', event.detail.eventId);
18
32
  this.tlEventClick.emit(event.detail);
19
33
  };
20
34
  }
35
+ /** A different question deserves a fresh answer. */
21
36
  watchFiltersChange() {
22
- this.loadEvents();
37
+ this.syncProps();
38
+ this.element.actions.load();
23
39
  }
24
- async componentWillLoad() {
25
- await this.loadEvents();
40
+ syncProps() {
41
+ this.element.setProps({ limit: this.limit, category: this.category, columns: this.columns });
26
42
  }
27
- async loadEvents() {
28
- try {
29
- console.log('[tl-event-list] Loading events via SDK...', { limit: this.limit, category: this.category });
30
- this.loading = true;
31
- this.error = null;
32
- const sdk = await whenSDK();
33
- const eventsManager = sdk.getEventsManager();
34
- const response = await eventsManager.list({
35
- limit: this.limit,
36
- category: this.category,
43
+ connectedCallback() {
44
+ this.syncProps();
45
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
46
+ this.view = view;
47
+ }));
48
+ this.view = this.element.view;
49
+ this.teardown = this.element.mount();
50
+ }
51
+ /**
52
+ * Nothing is painted until the first listing has landed.
53
+ *
54
+ * Kept as found rather than improved, and it is the harness's to keep: the
55
+ * component awaited its read in `componentWillLoad`, and Stencil does not
56
+ * paint a component until that resolves, so a slow channel shows an empty
57
+ * box where the skeleton markup below would fit. Held by the spec.
58
+ *
59
+ * Expressed against the core's own status rather than against a read this
60
+ * harness starts, because the read is the core's (`connectedCallback` has
61
+ * already mounted it). Every later read draws the skeleton, exactly as
62
+ * before.
63
+ */
64
+ componentWillLoad() {
65
+ if (this.view.status !== 'loading')
66
+ return undefined;
67
+ return new Promise((resolve) => {
68
+ const off = this.element.subscribe((view) => {
69
+ if (view.status === 'loading')
70
+ return;
71
+ off();
72
+ resolve();
37
73
  });
38
- console.log('[tl-event-list] Events loaded:', response.items.length);
39
- this.events = response.items;
40
- }
41
- catch (err) {
42
- console.error('[tl-event-list] Failed to load events:', err);
43
- this.error = err instanceof Error ? err.message : 'Failed to load events';
44
- }
45
- finally {
46
- this.loading = false;
47
- }
74
+ });
75
+ }
76
+ /**
77
+ * Off screen: run the mount's teardown, and nothing more. `destroy()` is
78
+ * terminal and Stencil calls this for a move as well as a removal, so
79
+ * destroying here would leave a re-inserted element frozen.
80
+ */
81
+ disconnectedCallback() {
82
+ this.teardown?.();
83
+ this.teardown = undefined;
84
+ this.unsubscribe?.();
85
+ this.unsubscribe = undefined;
86
+ }
87
+ forward(event, detail) {
88
+ if (event === 'tlEventClick')
89
+ this.tlEventClick.emit(detail);
48
90
  }
49
91
  render() {
50
- const gridClass = `event-grid cols-${Math.min(Math.max(this.columns, 1), 4)}`;
51
- if (this.loading) {
92
+ const { status, state, strings, derived } = this.view;
93
+ const gridClass = `event-grid cols-${derived.columns}`;
94
+ if (status === 'loading') {
52
95
  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" })))))));
53
96
  }
54
- if (this.error) {
55
- return (h("div", { class: "error-state" }, h("p", { class: "error-title" }, "Error loading events"), h("p", { class: "error-message" }, this.error)));
97
+ if (status === 'error') {
98
+ return (h("div", { class: "error-state" }, h("p", { class: "error-title" }, strings.errorTitle), h("p", { class: "error-message" }, state.error)));
56
99
  }
57
- if (this.events.length === 0) {
58
- 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.")));
100
+ if (status === 'empty') {
101
+ 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)));
59
102
  }
60
- return (h("div", { class: gridClass }, this.events.map((event) => (h("tl-event-card", { key: event.id, event: event, onTlClick: this.handleEventClick })))));
103
+ return (h("div", { class: gridClass }, derived.events.map((event) => (h("tl-event-card", { key: event.id, event: event, onTlClick: this.handleEventClick })))));
61
104
  }
62
105
  static get is() { return "tl-event-list"; }
63
106
  static get encapsulation() { return "shadow"; }
@@ -136,9 +179,7 @@ export class TlEventList {
136
179
  }
137
180
  static get states() {
138
181
  return {
139
- "events": {},
140
- "loading": {},
141
- "error": {}
182
+ "view": {}
142
183
  };
143
184
  }
144
185
  static get events() {
@@ -166,6 +207,9 @@ export class TlEventList {
166
207
  }, {
167
208
  "propName": "category",
168
209
  "methodName": "watchFiltersChange"
210
+ }, {
211
+ "propName": "columns",
212
+ "methodName": "syncProps"
169
213
  }];
170
214
  }
171
215
  }
@@ -1,17 +1,17 @@
1
1
  :host {
2
2
  display: block;
3
- font-family: var(--tl-font-family, system-ui, sans-serif);
4
- color: var(--tl-foreground, #111827);
3
+ font-family: var(--tl-font-family);
4
+ color: var(--tl-foreground);
5
5
  }
6
6
 
7
7
  .login {
8
8
  display: flex;
9
9
  flex-direction: column;
10
- gap: var(--tl-spacing-3, 0.75rem);
11
- padding: var(--tl-spacing-6, 1.5rem);
12
- background: var(--tl-background, #ffffff);
13
- border: 1px solid var(--tl-border, #e5e7eb);
14
- border-radius: var(--tl-radius-lg, 0.75rem);
10
+ gap: var(--tl-spacing-3);
11
+ padding: var(--tl-spacing-6);
12
+ background: var(--tl-background);
13
+ border: 1px solid var(--tl-border);
14
+ border-radius: var(--tl-radius-lg);
15
15
  }
16
16
 
17
17
  .title {
@@ -23,7 +23,7 @@
23
23
  .lead {
24
24
  margin: 0;
25
25
  font-size: 0.875rem;
26
- color: var(--tl-muted-foreground, #6b7280);
26
+ color: var(--tl-muted-foreground);
27
27
  }
28
28
 
29
29
  .label {
@@ -32,22 +32,22 @@
32
32
  }
33
33
 
34
34
  .input {
35
- padding: var(--tl-spacing-3, 0.75rem);
35
+ padding: var(--tl-spacing-3);
36
36
  font: inherit;
37
37
  font-size: 0.9375rem;
38
- color: var(--tl-foreground, #111827);
39
- background: var(--tl-background, #ffffff);
40
- border: 1px solid var(--tl-border, #e5e7eb);
41
- border-radius: var(--tl-radius-md, 0.5rem);
38
+ color: var(--tl-foreground);
39
+ background: var(--tl-background);
40
+ border: 1px solid var(--tl-border);
41
+ border-radius: var(--tl-radius-md);
42
42
  }
43
43
 
44
44
  .input:focus {
45
- outline: 2px solid var(--tl-primary, #6366f1);
45
+ outline: 2px solid var(--tl-primary);
46
46
  outline-offset: 1px;
47
47
  }
48
48
 
49
49
  .code-input {
50
- font-family: var(--tl-font-family-mono, ui-monospace, monospace);
50
+ font-family: var(--tl-font-family-mono);
51
51
  font-size: 1.25rem;
52
52
  letter-spacing: 0.25em;
53
53
  text-align: center;
@@ -55,17 +55,17 @@
55
55
 
56
56
  .primary,
57
57
  .secondary {
58
- padding: var(--tl-spacing-3, 0.75rem) var(--tl-spacing-4, 1rem);
58
+ padding: var(--tl-spacing-3) var(--tl-spacing-4);
59
59
  font: inherit;
60
60
  font-size: 0.9375rem;
61
61
  font-weight: 600;
62
- border-radius: var(--tl-radius-md, 0.5rem);
62
+ border-radius: var(--tl-radius-md);
63
63
  cursor: pointer;
64
64
  }
65
65
 
66
66
  .primary {
67
- color: var(--tl-primary-foreground, #ffffff);
68
- background: var(--tl-primary, #6366f1);
67
+ color: var(--tl-primary-foreground);
68
+ background: var(--tl-primary);
69
69
  border: none;
70
70
  }
71
71
 
@@ -74,9 +74,9 @@
74
74
  }
75
75
 
76
76
  .secondary {
77
- color: var(--tl-foreground, #111827);
78
- background: var(--tl-secondary, #f3f4f6);
79
- border: 1px solid var(--tl-border, #e5e7eb);
77
+ color: var(--tl-foreground);
78
+ background: var(--tl-secondary);
79
+ border: 1px solid var(--tl-border);
80
80
  }
81
81
 
82
82
  .primary:disabled,
@@ -90,7 +90,7 @@
90
90
  padding: 0;
91
91
  font: inherit;
92
92
  font-size: 0.875rem;
93
- color: var(--tl-primary, #6366f1);
93
+ color: var(--tl-primary);
94
94
  background: none;
95
95
  border: none;
96
96
  cursor: pointer;
@@ -104,19 +104,19 @@
104
104
  display: flex;
105
105
  flex-wrap: wrap;
106
106
  align-items: center;
107
- gap: var(--tl-spacing-4, 1rem);
107
+ gap: var(--tl-spacing-4);
108
108
  }
109
109
 
110
110
  .error {
111
111
  margin: 0;
112
112
  font-size: 0.8125rem;
113
- color: var(--tl-error, #ef4444);
113
+ color: var(--tl-error);
114
114
  }
115
115
 
116
116
  .notice {
117
117
  margin: 0;
118
118
  font-size: 0.8125rem;
119
- color: var(--tl-success, #10b981);
119
+ color: var(--tl-success);
120
120
  }
121
121
 
122
122
  .signed-in {