PageSourceSearch

https://www.eventeny.com/js/Layouts/TicketCardPairingCoordinator.js?v=1790795785

js eventeny.com collected 2026-10-01 10:27:20 UTC 5,074 bytes, 119 lines download raw bytes

1/**
2 * TicketCardPairingCoordinator
3 * -----------------------------
4 * Reusable, page-agnostic helper that keeps <ticket-card-layout ticket-type="pairing">
5 * cards (js/Layouts/TicketCard.js) in sync with the "companion" tickets they require.
6 * The card itself stays a dumb presentational primitive (status/quantity/error-message
7 * are just properties it renders); this class owns the actual pairing business rule so
8 * that rule doesn't have to be re-written by every consumer page that uses the card --
9 * only registered once per page, same as the card itself.
10 *
11 * For each registered pairing ticket, whenever its own quantity or any of its
12 * companions' quantities change, recomputes `status`:
13 *   - own qty > 0, a companion is selected -> "added"  (card shows the +/- stepper)
14 *   - own qty > 0, no companion selected   -> "error"  (card shows the negative
15 *                                              "Choose options" + a fixed message:
16 *                                              "Ticket must be paired with one of the
17 *                                              following tickets first: <names>")
18 *   - own qty == 0, a companion is selected -> "paired" (card shows "Add to cart")
19 *   - own qty == 0, no companion selected    -> "idle"   (card shows "Choose options")
20 *
21 * Auto-listens to each registered card's own `quantity-changed` event (fired by the
22 * card's +/- stepper), so same-page stepper interactions need no extra wiring. For
23 * quantity changes that don't originate from the stepper (e.g. an "add to cart" flow
24 * completing, or removing an item from a cart summary elsewhere on the page), call
25 * notify(ticketId, quantity) manually -- the card's own getQuantity() gives you the
26 * current value to pass along.
27 *
28 * Cost: notify() only touches the pairing tickets that reference the changed id
29 * (typically a handful per page), not every registered ticket.
30 *
31 * @example
32 * const coordinator = new TicketCardPairingCoordinator();
33 * coordinator.registerTicket('10', generalAdmissionCardEl);
34 * coordinator.registerPairing('20', parkingPassCardEl, [
35 *     { id: '10', name: 'General Admission' },
36 * ]);
37 * // after your own add-to-cart flow finishes elsewhere on the page:
38 * coordinator.notify('10', generalAdmissionCardEl.getQuantity());
39 */
40class TicketCardPairingCoordinator {
41    constructor() {
42        this._tickets = new Map();  // id -> { element, quantity }
43        this._pairings = new Map(); // id -> { element, companions: [{id, name}] }
44    }
45
46    /**
47     * Registers any ticket whose quantity a pairing ticket might depend on
48     * (called automatically by registerPairing() for the pairing ticket itself).
49     * @param {string} id
50     * @param {HTMLElement} element - a <ticket-card-layout> instance.
51     */
52    registerTicket(id, element) {
53        if (this._tickets.has(id)) return;
54
55        this._tickets.set(id, { element, quantity: element.getQuantity() });
56
57        element.addEventListener('quantity-changed', (e) => {
58            this.notify(id, e.detail.quantity);
59        });
60    }
61
62    /**
63     * @param {string} id
64     * @param {HTMLElement} element - a <ticket-card-layout ticket-type="pairing"> instance.
65     * @param {{id: string, name: string}[]} companions - The tickets it must be paired with.
66     */
67    registerPairing(id, element, companions) {
68        this.registerTicket(id, element);
69        this._pairings.set(id, { element, companions });
70        this._recompute(id);
71    }
72
73    /**
74     * Call whenever a tracked ticket's quantity changes. The stepper does this
75     * automatically; call it yourself for changes originating elsewhere.
76     * @param {string} ticketId
77     * @param {number} quantity
78     */
79    notify(ticketId, quantity) {
80        const ticket = this._tickets.get(ticketId);
81        if (!ticket) return;
82
83        ticket.quantity = quantity;
84
85        this._pairings.forEach((pairing, pairingId) => {
86            const isSelf = pairingId === ticketId;
87            const isCompanion = pairing.companions.some(c => c.id === ticketId);
88            if (isSelf || isCompanion) {
89                this._recompute(pairingId);
90            }
91        });
92    }
93
94    _recompute(pairingId) {
95        const pairing = this._pairings.get(pairingId);
96        if (!pairing) return;
97
98        const selfQty = this._tickets.get(pairingId).quantity;
99        const companionSelected = pairing.companions.some(c => (this._tickets.get(c.id)?.quantity || 0) > 0);
100
101        let status;
102        if (selfQty > 0) {
103            status = companionSelected ? 'added' : 'error';
104        } else {
105            status = companionSelected ? 'paired' : 'idle';
106        }
107
108        pairing.element.setAttribute('status', status);
109
110        if (status === 'error') {
111            const names = pairing.companions.map(c => c.name).join(', ');
112            pairing.element.setErrorMessage(`Ticket must be paired with one of the following tickets first: ${names}`);
113        }
114    }
115}
116
117if (typeof TicketCardLayout !== 'undefined') {
118    TicketCardLayout.PairingCoordinator = TicketCardPairingCoordinator;
119}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.