@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.
- package/dist/cjs/{index-I9CoYv3k.js → index-DanPUX0s.js} +1 -1
- package/dist/cjs/index.cjs.js +2 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/ticket-selector-BYVL13k_.js +3623 -0
- package/dist/cjs/ticketlayer-elements.cjs.js +2 -2
- package/dist/cjs/tl-buy-modal.cjs.entry.js +3 -3
- package/dist/cjs/tl-buy-tickets-button.cjs.entry.js +89 -74
- package/dist/cjs/tl-buy-tickets-wrapper.cjs.entry.js +61 -30
- package/dist/cjs/tl-cart-badge.cjs.entry.js +74 -61
- package/dist/cjs/tl-cart-drawer.cjs.entry.js +51 -23
- package/dist/cjs/tl-cart.cjs.entry.js +71 -103
- package/dist/cjs/tl-checkout-wrapper.cjs.entry.js +51 -29
- package/dist/cjs/tl-event-card.cjs.entry.js +43 -105
- package/dist/cjs/tl-event-list.cjs.entry.js +72 -35
- package/dist/cjs/tl-login.cjs.entry.js +74 -141
- package/dist/cjs/tl-my-orders.cjs.entry.js +52 -92
- package/dist/cjs/tl-occurrence-selector_2.cjs.entry.js +153 -921
- package/dist/cjs/tl-order-confirmation.cjs.entry.js +71 -107
- package/dist/cjs/tl-order-tickets_2.cjs.entry.js +48 -74
- package/dist/cjs/tl-promo-code.cjs.entry.js +50 -47
- package/dist/cjs/tl-seat-map.cjs.entry.js +3 -3
- package/dist/cjs/web-query-Ce73jB4k.js +16 -0
- package/dist/collection/components/tl-buy-modal/tl-buy-modal.js +2 -2
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.css +37 -0
- package/dist/collection/components/tl-buy-tickets-button/tl-buy-tickets-button.js +105 -79
- package/dist/collection/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.js +73 -30
- package/dist/collection/components/tl-cart/tl-cart.js +82 -107
- package/dist/collection/components/tl-cart-badge/tl-cart-badge.js +62 -62
- package/dist/collection/components/tl-cart-drawer/tl-cart-drawer.js +56 -24
- package/dist/collection/components/tl-checkout-wrapper/tl-checkout-wrapper.js +61 -29
- package/dist/collection/components/tl-event-card/event-card-adapter.js +11 -23
- package/dist/collection/components/tl-event-card/tl-event-card.js +51 -84
- package/dist/collection/components/tl-event-list/tl-event-list.js +82 -38
- package/dist/collection/components/tl-login/tl-login.js +83 -147
- package/dist/collection/components/tl-my-orders/tl-my-orders.js +58 -96
- package/dist/collection/components/tl-occurrence-selector/tl-occurrence-selector.js +137 -516
- package/dist/collection/components/tl-order-confirmation/tl-order-confirmation.js +81 -115
- package/dist/collection/components/tl-order-tickets/tl-order-tickets.js +57 -92
- package/dist/collection/components/tl-promo-code/tl-promo-code.js +55 -50
- package/dist/collection/components/tl-seat-map/tl-seat-map.js +2 -2
- package/dist/collection/components/tl-ticket-selector/tl-ticket-selector.js +34 -20
- package/dist/collection/utils/order-summary.js +15 -135
- package/dist/collection/utils/testing/deep-text.js +29 -0
- package/dist/collection/utils/testing/fixtures.js +290 -0
- package/dist/collection/utils/testing/mock-sdk.js +13 -2
- package/dist/collection/utils/web-query.js +12 -0
- package/dist/collection/utils/web-storage.js +22 -0
- package/dist/components/ticket-selector.js +1 -0
- package/dist/components/tl-buy-modal.js +1 -1
- package/dist/components/tl-buy-tickets-button.js +1 -1
- package/dist/components/tl-buy-tickets-wrapper2.js +1 -1
- package/dist/components/tl-cart-badge.js +1 -1
- package/dist/components/tl-cart-drawer.js +1 -1
- package/dist/components/tl-cart2.js +1 -1
- package/dist/components/tl-checkout-wrapper2.js +1 -1
- package/dist/components/tl-event-card2.js +1 -1
- package/dist/components/tl-event-list.js +1 -1
- package/dist/components/tl-login2.js +1 -1
- package/dist/components/tl-my-orders.js +1 -1
- package/dist/components/tl-occurrence-selector2.js +1 -1
- package/dist/components/tl-order-confirmation.js +1 -1
- package/dist/components/tl-order-tickets2.js +1 -1
- package/dist/components/tl-promo-code.js +1 -1
- package/dist/components/tl-seat-map.js +1 -1
- package/dist/components/tl-ticket-selector2.js +1 -1
- package/dist/components/web-query.js +1 -0
- package/dist/elements.docs.json +34 -34
- package/dist/esm/{index-OoYV1faY.js → index-imfxp765.js} +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/ticket-selector-BwChnHVC.js +3606 -0
- package/dist/esm/ticketlayer-elements.js +3 -3
- package/dist/esm/tl-buy-modal.entry.js +3 -3
- package/dist/esm/tl-buy-tickets-button.entry.js +90 -75
- package/dist/esm/tl-buy-tickets-wrapper.entry.js +62 -31
- package/dist/esm/tl-cart-badge.entry.js +75 -62
- package/dist/esm/tl-cart-drawer.entry.js +52 -24
- package/dist/esm/tl-cart.entry.js +72 -104
- package/dist/esm/tl-checkout-wrapper.entry.js +52 -30
- package/dist/esm/tl-event-card.entry.js +44 -106
- package/dist/esm/tl-event-list.entry.js +73 -36
- package/dist/esm/tl-login.entry.js +75 -142
- package/dist/esm/tl-my-orders.entry.js +53 -93
- package/dist/esm/tl-occurrence-selector_2.entry.js +152 -920
- package/dist/esm/tl-order-confirmation.entry.js +72 -108
- package/dist/esm/tl-order-tickets_2.entry.js +49 -75
- package/dist/esm/tl-promo-code.entry.js +51 -48
- package/dist/esm/tl-seat-map.entry.js +3 -3
- package/dist/esm/web-query-Bdbb2lk5.js +14 -0
- package/dist/ticketlayer-elements/index.esm.js +1 -1
- package/dist/ticketlayer-elements/p-45923d2c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-492adc77.entry.js +1 -0
- package/dist/ticketlayer-elements/p-4cfc9e53.entry.js +1 -0
- package/dist/ticketlayer-elements/{p-ac264adc.entry.js → p-5135630e.entry.js} +1 -1
- package/dist/ticketlayer-elements/p-5bbce3b5.entry.js +1 -0
- package/dist/ticketlayer-elements/p-7d547721.entry.js +1 -0
- package/dist/ticketlayer-elements/p-7fe7b6ba.entry.js +1 -0
- package/dist/ticketlayer-elements/p-8122f0c1.entry.js +1 -0
- package/dist/ticketlayer-elements/p-98d40869.entry.js +1 -0
- package/dist/ticketlayer-elements/p-Bdbb2lk5.js +1 -0
- package/dist/ticketlayer-elements/p-BwChnHVC.js +1 -0
- package/dist/ticketlayer-elements/p-abec290a.entry.js +1 -0
- package/dist/ticketlayer-elements/p-ac79a3f6.entry.js +1 -0
- package/dist/ticketlayer-elements/p-d01f4429.entry.js +1 -0
- package/dist/ticketlayer-elements/p-d7ffaf46.entry.js +1 -0
- package/dist/ticketlayer-elements/p-e7170f3c.entry.js +1 -0
- package/dist/ticketlayer-elements/p-e787e862.entry.js +1 -0
- package/dist/ticketlayer-elements/p-f1ff931f.entry.js +1 -0
- package/dist/ticketlayer-elements/p-imfxp765.js +3 -0
- package/dist/ticketlayer-elements/ticketlayer-elements.css +1 -1
- package/dist/ticketlayer-elements/ticketlayer-elements.esm.js +1 -1
- package/dist/types/components/tl-buy-tickets-button/tl-buy-tickets-button.d.ts +40 -8
- package/dist/types/components/tl-buy-tickets-wrapper/tl-buy-tickets-wrapper.d.ts +20 -1
- package/dist/types/components/tl-cart/tl-cart.d.ts +40 -42
- package/dist/types/components/tl-cart-badge/tl-cart-badge.d.ts +22 -7
- package/dist/types/components/tl-cart-drawer/tl-cart-drawer.d.ts +28 -5
- package/dist/types/components/tl-checkout-wrapper/tl-checkout-wrapper.d.ts +18 -1
- package/dist/types/components/tl-event-card/event-card-adapter.d.ts +9 -20
- package/dist/types/components/tl-event-card/tl-event-card.d.ts +25 -28
- package/dist/types/components/tl-event-list/tl-event-list.d.ts +46 -6
- package/dist/types/components/tl-login/tl-login.d.ts +68 -22
- package/dist/types/components/tl-my-orders/tl-my-orders.d.ts +30 -12
- package/dist/types/components/tl-occurrence-selector/tl-occurrence-selector.d.ts +73 -161
- package/dist/types/components/tl-order-confirmation/tl-order-confirmation.d.ts +48 -15
- package/dist/types/components/tl-order-tickets/tl-order-tickets.d.ts +29 -31
- package/dist/types/components/tl-promo-code/tl-promo-code.d.ts +28 -10
- package/dist/types/components/tl-ticket/tl-ticket.d.ts +2 -11
- package/dist/types/components/tl-ticket-selector/tl-ticket-selector.d.ts +22 -4
- package/dist/types/components.d.ts +270 -62
- package/dist/types/utils/order-summary.d.ts +14 -97
- package/dist/types/utils/testing/deep-text.d.ts +13 -0
- package/dist/types/utils/testing/fixtures.d.ts +225 -0
- package/dist/types/utils/testing/mock-sdk.d.ts +14 -1
- package/dist/types/utils/web-query.d.ts +18 -0
- package/dist/types/utils/web-storage.d.ts +16 -0
- package/elements.catalogue.json +602 -50
- package/package.json +8 -6
- package/dist/cjs/money-BtmM2gt-.js +0 -118
- package/dist/cjs/order-summary-X3TlZxc2.js +0 -150
- package/dist/components/money.js +0 -1
- package/dist/components/order-summary.js +0 -1
- package/dist/esm/money-4ig0-WB6.js +0 -112
- package/dist/esm/order-summary-DiDtvfZM.js +0 -136
- package/dist/ticketlayer-elements/p-0814420f.entry.js +0 -1
- package/dist/ticketlayer-elements/p-0acabc26.entry.js +0 -1
- package/dist/ticketlayer-elements/p-109a3feb.entry.js +0 -1
- package/dist/ticketlayer-elements/p-1a4ba71c.entry.js +0 -1
- package/dist/ticketlayer-elements/p-318965c1.entry.js +0 -1
- package/dist/ticketlayer-elements/p-347c7fd1.entry.js +0 -1
- package/dist/ticketlayer-elements/p-3649c26d.entry.js +0 -1
- package/dist/ticketlayer-elements/p-40260cc9.entry.js +0 -1
- package/dist/ticketlayer-elements/p-4ig0-WB6.js +0 -1
- package/dist/ticketlayer-elements/p-55fd2f9f.entry.js +0 -1
- package/dist/ticketlayer-elements/p-62ee659b.entry.js +0 -1
- package/dist/ticketlayer-elements/p-7d4ecc2b.entry.js +0 -1
- package/dist/ticketlayer-elements/p-7f0abf95.entry.js +0 -1
- package/dist/ticketlayer-elements/p-BXpVGW51.js +0 -1
- package/dist/ticketlayer-elements/p-OoYV1faY.js +0 -3
- package/dist/ticketlayer-elements/p-a3c1b26c.entry.js +0 -1
- package/dist/ticketlayer-elements/p-aeb4c6fb.entry.js +0 -1
- package/dist/ticketlayer-elements/p-ba62541f.entry.js +0 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { h, Host } from "@stencil/core";
|
|
2
|
-
import {
|
|
2
|
+
import { checkoutWrapperCore, createElement, } from "@ticketlayer/elements-core";
|
|
3
|
+
import { getSDK, whenSDK } from "../../utils/sdk-connector";
|
|
3
4
|
/**
|
|
4
5
|
* @component tl-checkout-wrapper
|
|
5
6
|
*
|
|
6
7
|
* Wraps any content and opens the Ticketlayer checkout modal when clicked.
|
|
7
8
|
* Uses the SDK's modal manager which handles the iframe overlay.
|
|
8
9
|
*
|
|
10
|
+
* The web harness for `checkoutWrapperCore`: it renders, it forwards, and it
|
|
11
|
+
* decides only which key press counts as activation. Everything else is in
|
|
12
|
+
* `@ticketlayer/elements-core` (AGENTS.md, TKT-11).
|
|
13
|
+
*
|
|
9
14
|
* @slot - Content to display (typically a checkout button)
|
|
10
15
|
*
|
|
11
16
|
* @example
|
|
@@ -19,40 +24,57 @@ export class TlCheckoutWrapper {
|
|
|
19
24
|
* Whether the wrapper is disabled
|
|
20
25
|
*/
|
|
21
26
|
this.disabled = false;
|
|
27
|
+
this.element = createElement(checkoutWrapperCore, {
|
|
28
|
+
client: () => getSDK(),
|
|
29
|
+
clientReady: () => whenSDK(),
|
|
30
|
+
emit: (event, detail) => this.forward(event, detail),
|
|
31
|
+
});
|
|
32
|
+
/** The core's whole view. Assigning it is what makes Stencil re-render. */
|
|
33
|
+
this.view = this.element.view;
|
|
22
34
|
this.handleClick = () => {
|
|
23
|
-
|
|
24
|
-
return;
|
|
25
|
-
if (!hasSDK()) {
|
|
26
|
-
console.error('[tl-checkout-wrapper] SDK not initialized. Call setSDK(ticketlayer) first.');
|
|
27
|
-
this.tlError.emit({ message: 'SDK not initialized' });
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const sdk = getSDK();
|
|
31
|
-
// Open the checkout modal via SDK
|
|
32
|
-
sdk.checkout.openModal({
|
|
33
|
-
onComplete: (order) => {
|
|
34
|
-
this.tlComplete.emit(order);
|
|
35
|
-
},
|
|
36
|
-
onError: (err) => {
|
|
37
|
-
this.tlError.emit({ message: err.message });
|
|
38
|
-
},
|
|
39
|
-
onClose: () => {
|
|
40
|
-
this.tlClose.emit();
|
|
41
|
-
},
|
|
42
|
-
});
|
|
35
|
+
this.element.actions.activate();
|
|
43
36
|
};
|
|
37
|
+
/** Enter and Space are a press on the web. The core knows nothing of keys. */
|
|
44
38
|
this.handleKeyDown = (e) => {
|
|
45
39
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
46
40
|
e.preventDefault();
|
|
47
|
-
this.
|
|
41
|
+
this.element.actions.activate();
|
|
48
42
|
}
|
|
49
43
|
};
|
|
50
44
|
}
|
|
45
|
+
syncProps() {
|
|
46
|
+
this.element.setProps({ disabled: this.disabled });
|
|
47
|
+
}
|
|
48
|
+
connectedCallback() {
|
|
49
|
+
this.syncProps();
|
|
50
|
+
this.unsubscribe ?? (this.unsubscribe = this.element.subscribe((view) => {
|
|
51
|
+
this.view = view;
|
|
52
|
+
}));
|
|
53
|
+
this.view = this.element.view;
|
|
54
|
+
// Nothing to do on mount, and mounted anyway: a harness calls mount()
|
|
55
|
+
// unconditionally and pairs it with its teardown (TKT-197).
|
|
56
|
+
this.teardown = this.element.mount();
|
|
57
|
+
}
|
|
58
|
+
disconnectedCallback() {
|
|
59
|
+
this.teardown?.();
|
|
60
|
+
this.teardown = undefined;
|
|
61
|
+
this.unsubscribe?.();
|
|
62
|
+
this.unsubscribe = undefined;
|
|
63
|
+
}
|
|
64
|
+
forward(event, detail) {
|
|
65
|
+
if (event === 'tlComplete')
|
|
66
|
+
this.tlComplete.emit(detail);
|
|
67
|
+
else if (event === 'tlError')
|
|
68
|
+
this.tlError.emit(detail);
|
|
69
|
+
else if (event === 'tlClose')
|
|
70
|
+
this.tlClose.emit();
|
|
71
|
+
}
|
|
51
72
|
render() {
|
|
52
|
-
|
|
73
|
+
const { interactive } = this.view.derived;
|
|
74
|
+
return (h(Host, { key: 'b261046c2627825258a5905662a79f436d713683', class: {
|
|
53
75
|
'tl-checkout-wrapper': true,
|
|
54
|
-
'tl-checkout-wrapper--disabled':
|
|
55
|
-
}, onClick: this.handleClick, onKeyDown: this.handleKeyDown, role: "button", tabindex:
|
|
76
|
+
'tl-checkout-wrapper--disabled': !interactive,
|
|
77
|
+
}, onClick: this.handleClick, onKeyDown: this.handleKeyDown, role: "button", tabindex: interactive ? 0 : -1, "aria-disabled": interactive ? null : 'true' }, h("slot", { key: '5be08d5007a755e3d88a06ae89c08e082e4ba052' })));
|
|
56
78
|
}
|
|
57
79
|
static get is() { return "tl-checkout-wrapper"; }
|
|
58
80
|
static get originalStyleUrls() {
|
|
@@ -89,10 +111,15 @@ export class TlCheckoutWrapper {
|
|
|
89
111
|
}
|
|
90
112
|
};
|
|
91
113
|
}
|
|
114
|
+
static get states() {
|
|
115
|
+
return {
|
|
116
|
+
"view": {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
92
119
|
static get events() {
|
|
93
120
|
return [{
|
|
94
121
|
"method": "tlComplete",
|
|
95
|
-
"name": "
|
|
122
|
+
"name": "tlComplete",
|
|
96
123
|
"bubbles": true,
|
|
97
124
|
"cancelable": true,
|
|
98
125
|
"composed": true,
|
|
@@ -114,7 +141,7 @@ export class TlCheckoutWrapper {
|
|
|
114
141
|
}
|
|
115
142
|
}, {
|
|
116
143
|
"method": "tlError",
|
|
117
|
-
"name": "
|
|
144
|
+
"name": "tlError",
|
|
118
145
|
"bubbles": true,
|
|
119
146
|
"cancelable": true,
|
|
120
147
|
"composed": true,
|
|
@@ -129,7 +156,7 @@ export class TlCheckoutWrapper {
|
|
|
129
156
|
}
|
|
130
157
|
}, {
|
|
131
158
|
"method": "tlClose",
|
|
132
|
-
"name": "
|
|
159
|
+
"name": "tlClose",
|
|
133
160
|
"bubbles": true,
|
|
134
161
|
"cancelable": true,
|
|
135
162
|
"composed": true,
|
|
@@ -144,5 +171,10 @@ export class TlCheckoutWrapper {
|
|
|
144
171
|
}
|
|
145
172
|
}];
|
|
146
173
|
}
|
|
147
|
-
static get
|
|
174
|
+
static get watchers() {
|
|
175
|
+
return [{
|
|
176
|
+
"propName": "disabled",
|
|
177
|
+
"methodName": "syncProps"
|
|
178
|
+
}];
|
|
179
|
+
}
|
|
148
180
|
}
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
id: detail.id,
|
|
14
|
-
name: detail.name,
|
|
15
|
-
...(detail.shortDescription ? { shortDescription: detail.shortDescription } : {}),
|
|
16
|
-
...(detail.imageUrl ? { imageUrl: detail.imageUrl } : {}),
|
|
17
|
-
...(detail.venueName
|
|
18
|
-
? { venue: { id: detail.venueId ?? '', name: detail.venueName, city: detail.venueCity ?? '' } }
|
|
19
|
-
: {}),
|
|
20
|
-
...(next ? { nextOccurrence: { startsAt: next } } : {}),
|
|
21
|
-
availabilityStatus: soldOut ? 'sold_out' : 'available',
|
|
22
|
-
};
|
|
23
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Adapts the sales event detail (events manager get) to the shape
|
|
3
|
+
* tl-event-card renders.
|
|
4
|
+
*
|
|
5
|
+
* The adapter and its type moved to `@ticketlayer/elements-core` with the core
|
|
6
|
+
* split (TKT-69): picking the next occurrence and deciding whether a card reads
|
|
7
|
+
* sold out is behaviour, and behaviour lives in a core so every harness decides
|
|
8
|
+
* it the same way. This file stays as the import path the component and its
|
|
9
|
+
* spec already use.
|
|
10
|
+
*/
|
|
11
|
+
export { toEventCardEvent } from '@ticketlayer/elements-core';
|
|
@@ -1,102 +1,68 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
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.
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
this.
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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 (
|
|
90
|
-
return h("div", { class: "event-card card-error" },
|
|
58
|
+
if (status === 'error') {
|
|
59
|
+
return h("div", { class: "event-card card-error" }, state.error);
|
|
91
60
|
}
|
|
92
|
-
const event =
|
|
61
|
+
const event = derived.event;
|
|
93
62
|
if (!event) {
|
|
94
63
|
return null;
|
|
95
64
|
}
|
|
96
|
-
|
|
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
|
-
"
|
|
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,5 +1,15 @@
|
|
|
1
1
|
import { h } from "@stencil/core";
|
|
2
|
-
import {
|
|
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.
|
|
14
|
-
|
|
15
|
-
|
|
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.
|
|
37
|
+
this.syncProps();
|
|
38
|
+
this.element.actions.load();
|
|
23
39
|
}
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
syncProps() {
|
|
41
|
+
this.element.setProps({ limit: this.limit, category: this.category, columns: this.columns });
|
|
26
42
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
51
|
-
|
|
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 (
|
|
55
|
-
return (h("div", { class: "error-state" }, h("p", { class: "error-title" },
|
|
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 (
|
|
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" },
|
|
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 },
|
|
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
|
-
"
|
|
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
|
}
|