@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
@@ -16,6 +16,43 @@
16
16
  width: 100%;
17
17
  }
18
18
 
19
+ /* Sale states
20
+ *
21
+ * One state, one @ticketlayer/theme token family, and the hover derived from
22
+ * the same token rather than written down a second time, so a themed palette
23
+ * carries every state with it (TKT-203). These used to be literal hex in the
24
+ * component: red for sold out, blue for the waitlist, amber for coming soon,
25
+ * and a themed channel painted the available state and none of the others.
26
+ *
27
+ * The class comes from the core (`derived.stateClass`), which names the state
28
+ * and nothing more: a core may hold no value and has no shape for saying WHICH
29
+ * family a status paints with, so that decision is made here, exactly as
30
+ * tl-event-card's status badge makes it.
31
+ */
32
+ .state-available {
33
+ --button-color: var(--tl-primary);
34
+ --button-hover-color: color-mix(in srgb, var(--tl-primary) 90%, black);
35
+ --button-foreground: var(--tl-primary-foreground);
36
+ }
37
+
38
+ .state-sold-out {
39
+ --button-color: var(--tl-error);
40
+ --button-hover-color: color-mix(in srgb, var(--tl-error) 90%, black);
41
+ --button-foreground: var(--tl-error-foreground);
42
+ }
43
+
44
+ .state-waitlist {
45
+ --button-color: var(--tl-info);
46
+ --button-hover-color: color-mix(in srgb, var(--tl-info) 90%, black);
47
+ --button-foreground: var(--tl-info-foreground);
48
+ }
49
+
50
+ .state-coming-soon {
51
+ --button-color: var(--tl-warning);
52
+ --button-hover-color: color-mix(in srgb, var(--tl-warning) 90%, black);
53
+ --button-foreground: var(--tl-warning-foreground);
54
+ }
55
+
19
56
  .buy-button {
20
57
  display: inline-flex;
21
58
  align-items: center;
@@ -5,11 +5,20 @@
5
5
  *
6
6
  * @element tl-buy-tickets-button
7
7
  *
8
- * Supports multiple states:
9
- * - available: Default "Buy Tickets" with primary colour
10
- * - soldOut: "Sold Out" with red colour
11
- * - waitlist: "Join Waitlist" with blue colour
12
- * - comingSoon: "On Sale Soon" with yellow colour
8
+ * The web harness for `buyTicketsButtonCore`. What a press does, which states
9
+ * are not pressable, the label each state defaults to and how the price reads
10
+ * are all in `@ticketlayer/elements-core` (AGENTS.md, TKT-11); what is left
11
+ * here is the Stencil surface, the icons, the hover, and which key counts as a
12
+ * press.
13
+ *
14
+ * Each state is painted from a @ticketlayer/theme token family rather than
15
+ * from a literal, so a channel theme reaches every one of them. The core says
16
+ * which state the button is in and the stylesheet says which family that
17
+ * state paints with, because a core cannot map a status onto one:
18
+ * - available: "Buy Tickets", the primary family
19
+ * - soldOut: "Sold Out", the error family
20
+ * - waitlist: "Join Waitlist", the info family
21
+ * - comingSoon: "On Sale Soon", the warning family
13
22
  *
14
23
  * @example
15
24
  * <tl-buy-tickets-button event-id="evt_xxx"></tl-buy-tickets-button>
@@ -17,37 +26,8 @@
17
26
  * <tl-buy-tickets-button event-id="evt_xxx" price="2500" currency="GBP"></tl-buy-tickets-button>
18
27
  */
19
28
  import { h, Host } from "@stencil/core";
20
- import { getSDK, hasSDK } from "../../utils/sdk-connector";
21
- // State configurations
22
- const STATE_CONFIGS = {
23
- available: {
24
- defaultLabel: 'Buy Tickets',
25
- color: 'var(--tl-primary)',
26
- // The hover shade used to be a second colour smuggled into the token's
27
- // fallback slot, which meant it stopped following the channel's brand the
28
- // moment a theme was applied. Derive it instead.
29
- hoverColor: 'color-mix(in srgb, var(--tl-primary) 90%, black)',
30
- foreground: 'var(--tl-primary-foreground)',
31
- },
32
- soldOut: {
33
- defaultLabel: 'Sold Out',
34
- color: '#dc2626',
35
- hoverColor: '#b91c1c',
36
- foreground: '#ffffff',
37
- },
38
- waitlist: {
39
- defaultLabel: 'Join Waitlist',
40
- color: '#2563eb',
41
- hoverColor: '#1d4ed8',
42
- foreground: '#ffffff',
43
- },
44
- comingSoon: {
45
- defaultLabel: 'On Sale Soon',
46
- color: '#d97706',
47
- hoverColor: '#b45309',
48
- foreground: '#ffffff',
49
- },
50
- };
29
+ import { buyTicketsButtonCore, createElement, } from "@ticketlayer/elements-core";
30
+ import { getSDK, whenSDK } from "../../utils/sdk-connector";
51
31
  export class TlBuyTicketsButton {
52
32
  constructor() {
53
33
  /** Button state - determines colour, label, and icon */
@@ -67,65 +47,74 @@ export class TlBuyTicketsButton {
67
47
  // -------------------------------------------------------------------------
68
48
  // State
69
49
  // -------------------------------------------------------------------------
50
+ /**
51
+ * Whether the pointer is over the button. A pointer is not a fact every
52
+ * runtime has, so this stays here for the same reason the key handling
53
+ * does: the core knows nothing of mice.
54
+ */
70
55
  this.isHovered = false;
71
56
  // -------------------------------------------------------------------------
72
- // Methods
57
+ // The core
73
58
  // -------------------------------------------------------------------------
59
+ this.element = createElement(buyTicketsButtonCore, {
60
+ client: () => getSDK(),
61
+ clientReady: () => whenSDK(),
62
+ emit: (event, detail) => this.forward(event, detail),
63
+ });
64
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
65
+ this.view = this.element.view;
74
66
  this.handleClick = () => {
75
- // Check if button should be interactive
76
- if (this.disabled || this.state === 'soldOut' || this.state === 'comingSoon') {
77
- return;
78
- }
79
- // Emit click event
80
- this.tlClick.emit({ eventId: this.eventId, occurrenceId: this.occurrenceId });
81
- // Open modal via SDK if available
82
- if (hasSDK()) {
83
- try {
84
- const sdk = getSDK();
85
- sdk.event(this.eventId).openModal({
86
- occurrenceId: this.occurrenceId,
87
- });
88
- }
89
- catch (err) {
90
- console.error('[tl-buy-tickets-button] Failed to open modal:', err);
91
- }
92
- }
67
+ this.element.actions.activate();
93
68
  };
94
69
  }
95
- formatPrice(priceInMinorUnits) {
96
- const amount = priceInMinorUnits / 100;
97
- const hasDecimals = amount % 1 !== 0;
98
- return new Intl.NumberFormat('en-GB', {
99
- style: 'currency',
70
+ syncProps() {
71
+ this.element.setProps({
72
+ eventId: this.eventId,
73
+ occurrenceId: this.occurrenceId,
74
+ state: this.state,
75
+ label: this.label,
76
+ variant: this.variant,
77
+ size: this.size,
78
+ fullWidth: this.fullWidth,
79
+ price: this.price,
100
80
  currency: this.currency,
101
- minimumFractionDigits: hasDecimals ? 2 : 0,
102
- maximumFractionDigits: hasDecimals ? 2 : 0,
103
- }).format(amount);
81
+ pricePrefix: this.pricePrefix,
82
+ disabled: this.disabled,
83
+ });
104
84
  }
105
- getStateConfig() {
106
- return STATE_CONFIGS[this.state];
85
+ connectedCallback() {
86
+ this.syncProps();
87
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
88
+ this.view = view;
89
+ }));
90
+ this.view = this.element.view;
91
+ // This core has nothing to do on mount, and is mounted anyway: a harness
92
+ // calls mount() unconditionally and pairs it with its teardown (TKT-197).
93
+ this.teardown = this.element.mount();
107
94
  }
108
- isInteractive() {
109
- return !this.disabled && this.state !== 'soldOut' && this.state !== 'comingSoon';
95
+ disconnectedCallback() {
96
+ this.teardown?.();
97
+ this.teardown = undefined;
98
+ this.unsubscribe?.();
99
+ this.unsubscribe = undefined;
100
+ }
101
+ forward(event, detail) {
102
+ if (event === 'tlClick')
103
+ this.tlClick.emit(detail);
110
104
  }
111
105
  // -------------------------------------------------------------------------
112
106
  // Render
113
107
  // -------------------------------------------------------------------------
114
108
  render() {
115
- const config = this.getStateConfig();
116
- const buttonLabel = this.label || config.defaultLabel;
117
- const isInteractive = this.isInteractive();
118
- return (h(Host, { key: '86ee9a81d82c10398dab4e22b3bbae15abb8f449', class: { 'full-width': this.fullWidth } }, h("button", { key: 'b581e199ced7ffd14669ebe0cc727edc2f8c8914', type: "button", class: {
109
+ const { interactive, buttonLabel, stateClass, priceLabel } = this.view.derived;
110
+ return (h(Host, { key: '6d5dddb3fc486c5a877a3718fecbee80a648b274', class: { 'full-width': this.fullWidth } }, h("button", { key: '88f7f0688fb42e97eb2ce352f316b2a085c3bab5', type: "button", class: {
119
111
  'buy-button': true,
112
+ [stateClass]: true,
120
113
  [`variant-${this.variant}`]: true,
121
114
  [`size-${this.size}`]: true,
122
- 'disabled': !isInteractive,
123
- 'hovered': this.isHovered && isInteractive,
124
- }, style: {
125
- '--button-color': config.color,
126
- '--button-hover-color': config.hoverColor,
127
- '--button-foreground': config.foreground,
128
- }, disabled: !isInteractive, onClick: this.handleClick, onMouseEnter: () => (this.isHovered = true), onMouseLeave: () => (this.isHovered = false) }, h("span", { key: 'f776bab4a999ed36b25400424d639b4c9261c02a', class: "icon" }, this.state === 'soldOut' && this.renderSoldOutIcon(), this.state === 'waitlist' && this.renderWaitlistIcon(), this.state === 'comingSoon' && this.renderComingSoonIcon(), this.state === 'available' && this.renderTicketIcon()), h("span", { key: 'e98ecbe6debc5c2c18ce1d304b60bf1074b003b3', class: "label" }, buttonLabel), this.price !== undefined && (h("span", { key: 'e7e261d09a541b126b02b88d00992f3a6483a9b8', class: "price" }, h("span", { key: '5eab43a47c2dd4c1339fe84b583902b1e38a0b2a', class: "price-prefix" }, this.pricePrefix), h("span", { key: 'f03b514ff2bc3c9a1b2e922d93a92accdedd0fad', class: "price-value" }, this.formatPrice(this.price)))))));
115
+ 'disabled': !interactive,
116
+ 'hovered': this.isHovered && interactive,
117
+ }, disabled: !interactive, onClick: this.handleClick, onMouseEnter: () => (this.isHovered = true), onMouseLeave: () => (this.isHovered = false) }, h("span", { key: 'e88c2f05c7bc782d08d78fc8ab53b213ab3c833f', class: "icon" }, this.state === 'soldOut' && this.renderSoldOutIcon(), this.state === 'waitlist' && this.renderWaitlistIcon(), this.state === 'comingSoon' && this.renderComingSoonIcon(), this.state === 'available' && this.renderTicketIcon()), h("span", { key: '7290a58779e884886f26e64c405fd319811613a1', class: "label" }, buttonLabel), priceLabel !== null && (h("span", { key: 'bdf3bcdbfa29d4799f7a64bc0072a4c06c09c393', class: "price" }, h("span", { key: '32b075fc6793ef21e9a2cd188058992f95c10680', class: "price-prefix" }, this.pricePrefix), h("span", { key: 'ff3805b660b2af72a94fabc315507ce063ff02d2', class: "price-value" }, priceLabel))))));
129
118
  }
130
119
  renderTicketIcon() {
131
120
  return (h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" }), h("path", { d: "M13 5v2" }), h("path", { d: "M13 17v2" }), h("path", { d: "M13 11v2" })));
@@ -391,7 +380,8 @@ export class TlBuyTicketsButton {
391
380
  }
392
381
  static get states() {
393
382
  return {
394
- "isHovered": {}
383
+ "isHovered": {},
384
+ "view": {}
395
385
  };
396
386
  }
397
387
  static get events() {
@@ -412,4 +402,40 @@ export class TlBuyTicketsButton {
412
402
  }
413
403
  }];
414
404
  }
405
+ static get watchers() {
406
+ return [{
407
+ "propName": "eventId",
408
+ "methodName": "syncProps"
409
+ }, {
410
+ "propName": "occurrenceId",
411
+ "methodName": "syncProps"
412
+ }, {
413
+ "propName": "state",
414
+ "methodName": "syncProps"
415
+ }, {
416
+ "propName": "label",
417
+ "methodName": "syncProps"
418
+ }, {
419
+ "propName": "variant",
420
+ "methodName": "syncProps"
421
+ }, {
422
+ "propName": "size",
423
+ "methodName": "syncProps"
424
+ }, {
425
+ "propName": "fullWidth",
426
+ "methodName": "syncProps"
427
+ }, {
428
+ "propName": "price",
429
+ "methodName": "syncProps"
430
+ }, {
431
+ "propName": "currency",
432
+ "methodName": "syncProps"
433
+ }, {
434
+ "propName": "pricePrefix",
435
+ "methodName": "syncProps"
436
+ }, {
437
+ "propName": "disabled",
438
+ "methodName": "syncProps"
439
+ }];
440
+ }
415
441
  }
@@ -1,11 +1,18 @@
1
1
  import { h, Host } from "@stencil/core";
2
- import { getSDK, hasSDK } from "../../utils/sdk-connector";
2
+ import { buyTicketsWrapperCore, createElement, } from "@ticketlayer/elements-core";
3
+ import { getSDK, whenSDK } from "../../utils/sdk-connector";
3
4
  /**
4
5
  * @component tl-buy-tickets-wrapper
5
6
  *
6
7
  * Wraps any content and opens the Ticketlayer modal when clicked.
7
8
  * Uses the SDK's modal manager which handles the iframe overlay.
8
9
  *
10
+ * The web harness for `buyTicketsWrapperCore`. What a press does, what it
11
+ * announces and what happens when no client has been published are all in
12
+ * `@ticketlayer/elements-core` (AGENTS.md, TKT-11); what is left here is the
13
+ * Stencil surface, the accessibility attributes and the one genuinely web
14
+ * question the core refuses to answer - which key counts as a press.
15
+ *
9
16
  * @slot - Content to display (typically a button)
10
17
  *
11
18
  * @example
@@ -24,41 +31,61 @@ export class TlBuyTicketsWrapper {
24
31
  * Whether the wrapper is disabled
25
32
  */
26
33
  this.disabled = false;
34
+ this.element = createElement(buyTicketsWrapperCore, {
35
+ client: () => getSDK(),
36
+ clientReady: () => whenSDK(),
37
+ emit: (event, detail) => this.forward(event, detail),
38
+ });
39
+ /** The core's whole view. Assigning it is what makes Stencil re-render. */
40
+ this.view = this.element.view;
27
41
  this.handleClick = () => {
28
- if (this.disabled)
29
- return;
30
- if (!hasSDK()) {
31
- console.error('[tl-buy-tickets-wrapper] SDK not initialized. Call setSDK(ticketlayer) first.');
32
- this.tlError.emit({ message: 'SDK not initialized' });
33
- return;
34
- }
35
- const sdk = getSDK();
36
- // Open the modal via SDK
37
- sdk.event(this.eventId).openModal({
38
- occurrenceId: this.occurrenceId,
39
- onComplete: (order) => {
40
- this.tlComplete.emit(order);
41
- },
42
- onError: (err) => {
43
- this.tlError.emit({ message: err.message });
44
- },
45
- onClose: () => {
46
- this.tlClose.emit();
47
- },
48
- });
42
+ this.element.actions.activate();
49
43
  };
44
+ /** Enter and Space are a press on the web. The core knows nothing of keys. */
50
45
  this.handleKeyDown = (e) => {
51
46
  if (e.key === 'Enter' || e.key === ' ') {
52
47
  e.preventDefault();
53
- this.handleClick();
48
+ this.element.actions.activate();
54
49
  }
55
50
  };
56
51
  }
52
+ syncProps() {
53
+ this.element.setProps({
54
+ eventId: this.eventId,
55
+ occurrenceId: this.occurrenceId,
56
+ disabled: this.disabled,
57
+ });
58
+ }
59
+ connectedCallback() {
60
+ this.syncProps();
61
+ this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
62
+ this.view = view;
63
+ }));
64
+ this.view = this.element.view;
65
+ // This core has nothing to do on mount, and is mounted anyway: a harness
66
+ // calls mount() unconditionally and pairs it with its teardown (TKT-197).
67
+ this.teardown = this.element.mount();
68
+ }
69
+ disconnectedCallback() {
70
+ this.teardown?.();
71
+ this.teardown = undefined;
72
+ this.unsubscribe?.();
73
+ this.unsubscribe = undefined;
74
+ }
75
+ forward(event, detail) {
76
+ if (event === 'tlComplete')
77
+ this.tlComplete.emit(detail);
78
+ else if (event === 'tlError')
79
+ this.tlError.emit(detail);
80
+ else if (event === 'tlClose')
81
+ this.tlClose.emit();
82
+ }
57
83
  render() {
58
- return (h(Host, { key: 'd6ef81ac9bc1a1194eda1603cbb2a75b355a212e', class: {
84
+ const { interactive } = this.view.derived;
85
+ return (h(Host, { key: '14e91259c1401322f969d43bb16b6d3f20327c65', class: {
59
86
  'tl-buy-tickets-wrapper': true,
60
- 'tl-buy-tickets-wrapper--disabled': this.disabled,
61
- }, onClick: this.handleClick, onKeyDown: this.handleKeyDown, role: "button", tabindex: this.disabled ? -1 : 0, "aria-disabled": this.disabled ? 'true' : null }, h("slot", { key: 'faef5aaad20deaf5e442d688c69105e45f9ee26f' })));
87
+ 'tl-buy-tickets-wrapper--disabled': !interactive,
88
+ }, onClick: this.handleClick, onKeyDown: this.handleKeyDown, role: "button", tabindex: interactive ? 0 : -1, "aria-disabled": interactive ? null : 'true' }, h("slot", { key: '3fb33786e09f5ecdf20ff0ea8e7336ccfc43b216' })));
62
89
  }
63
90
  static get is() { return "tl-buy-tickets-wrapper"; }
64
91
  static get originalStyleUrls() {
@@ -133,10 +160,15 @@ export class TlBuyTicketsWrapper {
133
160
  }
134
161
  };
135
162
  }
163
+ static get states() {
164
+ return {
165
+ "view": {}
166
+ };
167
+ }
136
168
  static get events() {
137
169
  return [{
138
170
  "method": "tlComplete",
139
- "name": "tl-complete",
171
+ "name": "tlComplete",
140
172
  "bubbles": true,
141
173
  "cancelable": true,
142
174
  "composed": true,
@@ -158,7 +190,7 @@ export class TlBuyTicketsWrapper {
158
190
  }
159
191
  }, {
160
192
  "method": "tlError",
161
- "name": "tl-error",
193
+ "name": "tlError",
162
194
  "bubbles": true,
163
195
  "cancelable": true,
164
196
  "composed": true,
@@ -173,7 +205,7 @@ export class TlBuyTicketsWrapper {
173
205
  }
174
206
  }, {
175
207
  "method": "tlClose",
176
- "name": "tl-close",
208
+ "name": "tlClose",
177
209
  "bubbles": true,
178
210
  "cancelable": true,
179
211
  "composed": true,
@@ -188,5 +220,16 @@ export class TlBuyTicketsWrapper {
188
220
  }
189
221
  }];
190
222
  }
191
- static get elementRef() { return "el"; }
223
+ static get watchers() {
224
+ return [{
225
+ "propName": "eventId",
226
+ "methodName": "syncProps"
227
+ }, {
228
+ "propName": "occurrenceId",
229
+ "methodName": "syncProps"
230
+ }, {
231
+ "propName": "disabled",
232
+ "methodName": "syncProps"
233
+ }];
234
+ }
192
235
  }