@seatlayer/js 0.24.0 → 0.26.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/index.cjs +228 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -4
- package/dist/index.d.ts +66 -4
- package/dist/index.js +228 -47
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -58,7 +58,13 @@ async function request(base, path, init = {}) {
|
|
|
58
58
|
const data = isJson ? await res.json().catch(() => null) : null;
|
|
59
59
|
if (!res.ok) {
|
|
60
60
|
const err = data;
|
|
61
|
-
throw new ApiError(
|
|
61
|
+
throw new ApiError(
|
|
62
|
+
res.status,
|
|
63
|
+
err?.error ?? `request_failed_${res.status}`,
|
|
64
|
+
err?.code ?? err?.error,
|
|
65
|
+
err?.conflicts,
|
|
66
|
+
err?.reason
|
|
67
|
+
);
|
|
62
68
|
}
|
|
63
69
|
return data;
|
|
64
70
|
}
|
|
@@ -131,6 +137,7 @@ var SeatingChart = class {
|
|
|
131
137
|
this.mount = null;
|
|
132
138
|
this.hostEl = null;
|
|
133
139
|
this.rendered = false;
|
|
140
|
+
this.mode_ = null;
|
|
134
141
|
this.tipEl = null;
|
|
135
142
|
this.tipPos = { x: 0, y: 0 };
|
|
136
143
|
this.onTipMove = null;
|
|
@@ -184,6 +191,7 @@ var SeatingChart = class {
|
|
|
184
191
|
this.rendered = false;
|
|
185
192
|
return this;
|
|
186
193
|
}
|
|
194
|
+
this.mode_ = info.mode === "test" ? "test" : "live";
|
|
187
195
|
if (this.opts.seatTooltip !== false) {
|
|
188
196
|
const tip = document.createElement("div");
|
|
189
197
|
tip.setAttribute("role", "tooltip");
|
|
@@ -257,6 +265,19 @@ var SeatingChart = class {
|
|
|
257
265
|
this.tipEl.style.display = "block";
|
|
258
266
|
this.placeTooltip();
|
|
259
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Whether the SERVED event is a live or a test event (`sk_test_` keys create
|
|
270
|
+
* test events, which never book real inventory). `null` before render()
|
|
271
|
+
* resolves — the mode comes from the server with the chart, not from options.
|
|
272
|
+
*
|
|
273
|
+
* The widget already surfaces this visually with the test-mode ribbon; this
|
|
274
|
+
* getter is for hosts that draw their own chrome — notably a native WebView
|
|
275
|
+
* wrapper, which must be able to tell an integrator that the build they are
|
|
276
|
+
* about to ship is pointed at a test event.
|
|
277
|
+
*/
|
|
278
|
+
getMode() {
|
|
279
|
+
return this.mode_;
|
|
280
|
+
}
|
|
260
281
|
/** Current selection with prices resolved from the chart categories. */
|
|
261
282
|
getSelection() {
|
|
262
283
|
return this.controller.getSelection();
|
|
@@ -264,17 +285,49 @@ var SeatingChart = class {
|
|
|
264
285
|
/** Hold the current selection. Resolves the hold, or null on a 409 conflict. */
|
|
265
286
|
async hold(options = {}) {
|
|
266
287
|
try {
|
|
267
|
-
|
|
268
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
288
|
+
return await this.holdOrThrow(options);
|
|
269
289
|
} catch (err) {
|
|
270
290
|
this.opts.onError?.(err);
|
|
271
291
|
return null;
|
|
272
292
|
}
|
|
273
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* @internal Like {@link hold} but RE-THROWS the structured API error (409
|
|
296
|
+
* `reason`/`code` + `conflicts`) instead of swallowing it into `onError` +
|
|
297
|
+
* `null`. The native WebView host adapter needs the throw so it can answer the
|
|
298
|
+
* originating command with a correlated error carrying the SPECIFIC reason
|
|
299
|
+
* (`sold_out` vs `not_enough_together`); the public method above keeps the
|
|
300
|
+
* catch-and-onError contract that direct web consumers rely on. Not a stable
|
|
301
|
+
* part of the embed API.
|
|
302
|
+
*/
|
|
303
|
+
async holdOrThrow(options = {}) {
|
|
304
|
+
const h = await this.controller.hold(void 0, options.ttlMs);
|
|
305
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
306
|
+
}
|
|
274
307
|
/** Restore an active hold by its opaque id without extending its expiry. */
|
|
275
308
|
async resumeHold(holdId) {
|
|
276
309
|
try {
|
|
277
|
-
|
|
310
|
+
return await this.resumeHoldOrThrow(holdId);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
this.opts.onError?.(err);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/** @internal Throwing variant of {@link resumeHold} for the native host adapter. See {@link holdOrThrow}. */
|
|
317
|
+
async resumeHoldOrThrow(holdId) {
|
|
318
|
+
const h = await this.controller.resumeHold(holdId);
|
|
319
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Push the OPEN hold's expiry out ("need more time?"). Resolves the refreshed
|
|
323
|
+
* hold, or `null` when there is nothing held or the server refused (the hold
|
|
324
|
+
* is gone, already expired, or at its renewal cap) — refusal is a normal
|
|
325
|
+
* outcome, not an error, so the host decides the copy. The client-side expiry
|
|
326
|
+
* timer is re-armed to match, so `onHoldExpired` won't fire early.
|
|
327
|
+
*/
|
|
328
|
+
async extendHold(ttlMs) {
|
|
329
|
+
try {
|
|
330
|
+
const h = await this.controller.extendHold(ttlMs);
|
|
278
331
|
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
279
332
|
} catch (err) {
|
|
280
333
|
this.opts.onError?.(err);
|
|
@@ -291,23 +344,31 @@ var SeatingChart = class {
|
|
|
291
344
|
}
|
|
292
345
|
async holdGA(areaId, qty, options = {}) {
|
|
293
346
|
try {
|
|
294
|
-
|
|
295
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
347
|
+
return await this.holdGAOrThrow(areaId, qty, options);
|
|
296
348
|
} catch (err) {
|
|
297
349
|
this.opts.onError?.(err);
|
|
298
350
|
return null;
|
|
299
351
|
}
|
|
300
352
|
}
|
|
353
|
+
/** @internal Throwing variant of {@link holdGA} for the native host adapter. See {@link holdOrThrow}. */
|
|
354
|
+
async holdGAOrThrow(areaId, qty, options = {}) {
|
|
355
|
+
const h = await this.controller.holdGA(areaId, qty, options);
|
|
356
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
357
|
+
}
|
|
301
358
|
/** Ask the server for the `qty` best free seats and hold them atomically. */
|
|
302
359
|
async bestAvailable(qty, categoryKey) {
|
|
303
360
|
try {
|
|
304
|
-
|
|
305
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;
|
|
361
|
+
return await this.bestAvailableOrThrow(qty, categoryKey);
|
|
306
362
|
} catch (err) {
|
|
307
363
|
this.opts.onError?.(err);
|
|
308
364
|
return null;
|
|
309
365
|
}
|
|
310
366
|
}
|
|
367
|
+
/** @internal Throwing variant of {@link bestAvailable} for the native host adapter. See {@link holdOrThrow}. */
|
|
368
|
+
async bestAvailableOrThrow(qty, categoryKey) {
|
|
369
|
+
const h = await this.controller.bestAvailable(qty, categoryKey);
|
|
370
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;
|
|
371
|
+
}
|
|
311
372
|
/**
|
|
312
373
|
* Choose a ticket tier for a selected seat (e.g. Adult → Child). The seat's
|
|
313
374
|
* available `tiers` are on each `SelectedSeat` from `getSelection()` /
|
|
@@ -368,6 +429,7 @@ var SeatingChart = class {
|
|
|
368
429
|
this.hostEl = null;
|
|
369
430
|
this.mount = null;
|
|
370
431
|
this.rendered = false;
|
|
432
|
+
this.mode_ = null;
|
|
371
433
|
}
|
|
372
434
|
};
|
|
373
435
|
|
|
@@ -1462,6 +1524,17 @@ var CSS = `
|
|
|
1462
1524
|
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
1463
1525
|
.sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
1464
1526
|
.sl-ba-copy .narrow{display:none}
|
|
1527
|
+
/* "\u2605 Best seats" premium quick-pick \u2014 gold accent echoing the \u2605 Premium pill on
|
|
1528
|
+
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
1529
|
+
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
1530
|
+
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
1531
|
+
color:#c9a24b;background:color-mix(in srgb,#e8c15a 10%,var(--sl-surface));
|
|
1532
|
+
border:1px solid color-mix(in srgb,#e8c15a 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
1533
|
+
.sl-ba-premium .star{font-size:12px;line-height:1;color:#e8c15a}
|
|
1534
|
+
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
1535
|
+
.sl-ba-premium.on{color:#1c1608;background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
|
|
1536
|
+
box-shadow:0 6px 16px color-mix(in srgb,#e8c15a 26%,transparent)}
|
|
1537
|
+
.sl-ba-premium.on .star{color:#5a4410}
|
|
1465
1538
|
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
1466
1539
|
font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
|
|
1467
1540
|
.sl-ba-qty{display:flex;align-items:center;gap:7px;padding:3px;border:1px solid var(--sl-line);border-radius:9px;background:var(--sl-surface)}
|
|
@@ -1559,6 +1632,25 @@ var CSS = `
|
|
|
1559
1632
|
.sl-confirm-view:hover{border-color:var(--sl-muted)}
|
|
1560
1633
|
.sl-confirm-view svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
1561
1634
|
|
|
1635
|
+
/* commercial seat flags \u2014 limited-view caution + premium tag. Amber tone,
|
|
1636
|
+
deliberately distinct from the red taken/held state; shown on the confirm
|
|
1637
|
+
card, echoed as a small \u25D0 marker on cart chips and the hover tip. */
|
|
1638
|
+
.sl-cx{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
|
|
1639
|
+
.sl-cx-warn{display:flex;align-items:flex-start;gap:7px;padding:8px 10px;border-radius:9px;
|
|
1640
|
+
background:color-mix(in srgb,#f4b740 13%,var(--sl-surface));border:1px solid color-mix(in srgb,#f4b740 40%,var(--sl-line));
|
|
1641
|
+
animation:slNoticeIn .28s ease both}
|
|
1642
|
+
.sl-cx-glyph{flex:none;font-size:14px;line-height:1.2;color:#f4b740}
|
|
1643
|
+
.sl-cx-txt{min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
1644
|
+
.sl-cx-txt b{font-size:12px;font-weight:800;color:var(--sl-text)}
|
|
1645
|
+
.sl-cx-note{font-size:11px;line-height:1.4;color:var(--sl-muted)}
|
|
1646
|
+
.sl-cx-premium{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;padding:4px 10px;border-radius:999px;
|
|
1647
|
+
font-size:11px;font-weight:800;letter-spacing:.02em;color:#c9a24b;
|
|
1648
|
+
background:color-mix(in srgb,#e8c15a 13%,var(--sl-surface));border:1px solid color-mix(in srgb,#e8c15a 38%,var(--sl-line))}
|
|
1649
|
+
.sl-cx-star{font-size:12px;line-height:1;color:#e8c15a}
|
|
1650
|
+
.sl-cx-mark{flex:none;font-size:12px;line-height:1;color:#f4b740;cursor:help}
|
|
1651
|
+
.sl-tip-cx{display:flex;align-items:center;gap:6px;padding:5px 10px 7px;font-size:10.5px;font-weight:700;color:#e8b24a}
|
|
1652
|
+
.sl-tip-cx .g{font-size:12px}
|
|
1653
|
+
|
|
1562
1654
|
/* 360\xB0 seat-view modal (fills the widget; drag-to-look-around equirectangular) */
|
|
1563
1655
|
.sl-view{position:absolute;inset:0;z-index:12;display:flex;flex-direction:column;background:var(--sl-bg)}
|
|
1564
1656
|
.sl-view-head{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--sl-line);flex:none}
|
|
@@ -1687,6 +1779,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
1687
1779
|
this.srEl = null;
|
|
1688
1780
|
this.baQty = 2;
|
|
1689
1781
|
this.baCat = "";
|
|
1782
|
+
/** "★ Best seats" premium quick-pick toggle — biases best-available to premium seats. */
|
|
1783
|
+
this.baPremium = false;
|
|
1690
1784
|
this.bestAvailableConfirm = false;
|
|
1691
1785
|
this.releasingHold = false;
|
|
1692
1786
|
/** Event sales window is closed (read-only load state / live close). */
|
|
@@ -1843,6 +1937,50 @@ var SeatPicker = class _SeatPicker {
|
|
|
1843
1937
|
const sight = distance != null ? (0, import_core2.t)("picker.sightline", { m: distance }) : this.tf("picker.sightlineClear", "Clear sightline");
|
|
1844
1938
|
return `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}"><img class="sl-confirm-thumb" src="${url}" alt="" /><span class="sl-confirm-thumb-badge">\u{1F52D} ${this.tf("picker.viewFromHere", "View from here")}</span></button><div class="sl-confirm-sight"><span aria-hidden="true">\u2713</span>${sight}</div>`;
|
|
1845
1939
|
}
|
|
1940
|
+
/** Minimal HTML/attribute escaper for buyer-authored commercial text (notes). */
|
|
1941
|
+
escCx(value) {
|
|
1942
|
+
return String(value ?? "").replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
1943
|
+
}
|
|
1944
|
+
/** Localized "Restricted view" / "Obstructed view" label for a seat's flags,
|
|
1945
|
+
* or '' when neither is set. Restricted takes precedence when both are on. */
|
|
1946
|
+
limitedViewLabel(c) {
|
|
1947
|
+
if (c?.restrictedView) return this.tf("picker.restrictedView", "Restricted view");
|
|
1948
|
+
if (c?.obstructedView) return this.tf("picker.obstructedView", "Obstructed view");
|
|
1949
|
+
return "";
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Commercial flags block for the confirm/detail surface: a subtle ★ Premium
|
|
1953
|
+
* tag plus an amber ◐ limited-view caution (with the organizer's note when
|
|
1954
|
+
* present). '' when the seat carries no surfaced commercial flag.
|
|
1955
|
+
*/
|
|
1956
|
+
commercialConfirmHtml(c) {
|
|
1957
|
+
if (!c) return "";
|
|
1958
|
+
const rows = [];
|
|
1959
|
+
if (c.premium) {
|
|
1960
|
+
rows.push(
|
|
1961
|
+
`<div class="sl-cx-premium"><span class="sl-cx-star" aria-hidden="true">\u2605</span>${this.tf("picker.premiumSeat", "Premium seat")}</div>`
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
const limited = this.limitedViewLabel(c);
|
|
1965
|
+
if (limited) {
|
|
1966
|
+
rows.push(
|
|
1967
|
+
`<div class="sl-cx-warn"><span class="sl-cx-glyph" aria-hidden="true">\u25D0</span><span class="sl-cx-txt"><b>${limited}</b>${c.note ? `<span class="sl-cx-note">${this.escCx(c.note)}</span>` : ""}</span></div>`
|
|
1968
|
+
);
|
|
1969
|
+
} else if (c.note) {
|
|
1970
|
+
rows.push(
|
|
1971
|
+
`<div class="sl-cx-warn"><span class="sl-cx-glyph" aria-hidden="true">\u2139</span><span class="sl-cx-txt"><span class="sl-cx-note">${this.escCx(c.note)}</span></span></div>`
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
return rows.length ? `<div class="sl-cx">${rows.join("")}</div>` : "";
|
|
1975
|
+
}
|
|
1976
|
+
/** Small ◐ limited-view marker for a cart chip; title/aria uses the seat's
|
|
1977
|
+
* note when present, else the generic view label. '' for a clear-view seat. */
|
|
1978
|
+
commercialChipMarker(c) {
|
|
1979
|
+
const limited = this.limitedViewLabel(c);
|
|
1980
|
+
if (!limited) return "";
|
|
1981
|
+
const title = this.escCx(c?.note ? c.note : limited);
|
|
1982
|
+
return `<span class="sl-cx-mark" role="img" aria-label="${title}" title="${title}">\u25D0</span>`;
|
|
1983
|
+
}
|
|
1846
1984
|
/** True when the picker is rendered inside an iframe (snippet embed at /e/:key). */
|
|
1847
1985
|
isFramed() {
|
|
1848
1986
|
return typeof window !== "undefined" && window.parent !== window;
|
|
@@ -2215,45 +2353,71 @@ var SeatPicker = class _SeatPicker {
|
|
|
2215
2353
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
2216
2354
|
this.buildBadge(chartTheme);
|
|
2217
2355
|
const present = /* @__PURE__ */ new Set();
|
|
2356
|
+
let hasLimitedView = false;
|
|
2218
2357
|
if (this.controller.doc) {
|
|
2219
2358
|
for (const seat of (0, import_core2.expandChart)(this.controller.doc)) {
|
|
2220
2359
|
for (const type of seat.accessibility ?? []) present.add(type);
|
|
2221
2360
|
if (seat.accessible && !seat.accessibility?.length) present.add("wheelchair");
|
|
2361
|
+
if (seat.commercial?.restrictedView || seat.commercial?.obstructedView) hasLimitedView = true;
|
|
2222
2362
|
}
|
|
2223
2363
|
}
|
|
2224
|
-
|
|
2364
|
+
const focusSeatsForFilter = () => {
|
|
2365
|
+
if (this.rungsEl && this.controller.getRung() !== "seats") {
|
|
2366
|
+
this.controller.setRung("seats");
|
|
2367
|
+
this.collapseSectionCard();
|
|
2368
|
+
this.syncRung();
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
if (present.size || hasLimitedView) {
|
|
2225
2372
|
const chips = document.createElement("div");
|
|
2226
2373
|
chips.className = "sl-chips";
|
|
2227
|
-
const GLYPH = { wheelchair: "\u267F", companion: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}" };
|
|
2228
|
-
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-f="${key}">${label}</button>`;
|
|
2229
|
-
chips.innerHTML = mk("all", "All seats") + [...present].map((type) => mk(type, `${GLYPH[type] ? GLYPH[type] + " " : ""}${type[0].toUpperCase()}${type.slice(1).replace(/-/g, " ")}`)).join("");
|
|
2230
2374
|
this.regions["top-left"].appendChild(chips);
|
|
2231
2375
|
this.a11yChipsEl = chips;
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2376
|
+
if (present.size) {
|
|
2377
|
+
const GLYPH = { wheelchair: "\u267F", companion: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}" };
|
|
2378
|
+
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-a11y="1" data-f="${key}">${label}</button>`;
|
|
2379
|
+
chips.insertAdjacentHTML(
|
|
2380
|
+
"beforeend",
|
|
2381
|
+
mk("all", "All seats") + [...present].map((type) => mk(type, `${GLYPH[type] ? GLYPH[type] + " " : ""}${type[0].toUpperCase()}${type.slice(1).replace(/-/g, " ")}`)).join("")
|
|
2382
|
+
);
|
|
2383
|
+
const active = /* @__PURE__ */ new Set();
|
|
2384
|
+
const syncChips = () => {
|
|
2385
|
+
chips.querySelectorAll("button[data-a11y]").forEach((b) => {
|
|
2386
|
+
const f = b.dataset.f;
|
|
2387
|
+
const on = f === "all" ? active.size === 0 : active.has(f);
|
|
2388
|
+
b.classList.toggle("on", on);
|
|
2389
|
+
b.setAttribute("aria-pressed", String(on));
|
|
2390
|
+
});
|
|
2391
|
+
const filter = active.size ? [...active] : null;
|
|
2392
|
+
this.controller.setAccessibilityFilter(filter);
|
|
2393
|
+
if (filter) focusSeatsForFilter();
|
|
2394
|
+
};
|
|
2395
|
+
chips.querySelectorAll("button[data-a11y]").forEach((btn) => {
|
|
2396
|
+
btn.addEventListener("click", () => {
|
|
2397
|
+
const f = btn.dataset.f;
|
|
2398
|
+
if (f === "all") active.clear();
|
|
2399
|
+
else if (active.has(f)) active.delete(f);
|
|
2400
|
+
else active.add(f);
|
|
2401
|
+
syncChips();
|
|
2402
|
+
});
|
|
2239
2403
|
});
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2404
|
+
}
|
|
2405
|
+
if (hasLimitedView) {
|
|
2406
|
+
const limited = document.createElement("button");
|
|
2407
|
+
limited.type = "button";
|
|
2408
|
+
limited.className = "sl-chip-f";
|
|
2409
|
+
limited.setAttribute("aria-pressed", "false");
|
|
2410
|
+
limited.innerHTML = `\u25D0 ${this.tf("picker.hideLimitedView", "Hide limited-view seats")}`;
|
|
2411
|
+
chips.appendChild(limited);
|
|
2412
|
+
let limitedOn = false;
|
|
2413
|
+
limited.addEventListener("click", () => {
|
|
2414
|
+
limitedOn = !limitedOn;
|
|
2415
|
+
limited.classList.toggle("on", limitedOn);
|
|
2416
|
+
limited.setAttribute("aria-pressed", String(limitedOn));
|
|
2417
|
+
this.controller.setCommercialLimitedFilter(limitedOn);
|
|
2418
|
+
if (limitedOn) focusSeatsForFilter();
|
|
2255
2419
|
});
|
|
2256
|
-
}
|
|
2420
|
+
}
|
|
2257
2421
|
}
|
|
2258
2422
|
const cb = document.createElement("button");
|
|
2259
2423
|
cb.type = "button";
|
|
@@ -2976,7 +3140,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2976
3140
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
2977
3141
|
return `<span class="sl-seccard-mix-item${dim ? " sl-dim" : ""}"><span class="sl-seccard-mix-dot" style="background:${c.color}"></span>${c.label} <span class="sl-seccard-mix-price">${this.money(this.paidPrice(c.key, null, c.price))}</span></span>`;
|
|
2978
3142
|
}).join("");
|
|
2979
|
-
card.innerHTML = `<div class="sl-seccard-head"><span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn + `</div><div class="sl-seccard-zone">${summary.zoneLabel ? `${summary.zoneLabel} \xB7 ` : ""}<span class="sl-seccard-left">${leftLabel}</span></div>` + (mix ? `<div class="sl-seccard-mix">${mix}</div>` : "") + `<div class="sl-seccard-foot"><button type="button" class="sl-seccard-overview">\u2190 ${(0, import_core2.t)("picker.overview")}</button><span class="sl-seccard-hint">${(0, import_core2.t)("picker.tapSeatHint")}</span></div>`;
|
|
3143
|
+
card.innerHTML = `<div class="sl-seccard-head"><span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn + `</div><div class="sl-seccard-zone">${summary.zoneLabel ? `${summary.zoneLabel} \xB7 ` : ""}<span class="sl-seccard-left">${leftLabel}</span></div>` + (summary.entrance ? `<div class="sl-seccard-entrance">${(0, import_core2.t)("picker.entrance")} ${String(summary.entrance).replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch])}</div>` : "") + (mix ? `<div class="sl-seccard-mix">${mix}</div>` : "") + `<div class="sl-seccard-foot"><button type="button" class="sl-seccard-overview">\u2190 ${(0, import_core2.t)("picker.overview")}</button><span class="sl-seccard-hint">${(0, import_core2.t)("picker.tapSeatHint")}</span></div>`;
|
|
2980
3144
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
2981
3145
|
card.querySelector(".sl-seccard-overview").addEventListener("click", () => this.controller.overview());
|
|
2982
3146
|
(this.regions["top-center"] ?? this.els.map).appendChild(card);
|
|
@@ -3070,7 +3234,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3070
3234
|
el.setAttribute("aria-modal", "true");
|
|
3071
3235
|
el.setAttribute("aria-label", `Confirm seat ${seat.label}`);
|
|
3072
3236
|
el.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
|
|
3073
|
-
el.innerHTML = `<div class="sl-confirm-grid"><div class="sl-confirm-field"><span class="sl-confirm-key">Section</span><span class="sl-confirm-value">${safe(details?.sectionLabel)}</span></div><div class="sl-confirm-field"><span class="sl-confirm-key"
|
|
3237
|
+
el.innerHTML = `<div class="sl-confirm-grid"><div class="sl-confirm-field"><span class="sl-confirm-key">Section</span><span class="sl-confirm-value">${safe(details?.sectionLabel)}</span></div><div class="sl-confirm-field"><span class="sl-confirm-key">${safe(this.rowTypeWord(details))}</span><span class="sl-confirm-value">${safe(this.rowShort(details))}</span></div><div class="sl-confirm-field"><span class="sl-confirm-key">Seat</span><span class="sl-confirm-value">${safe(details?.seatNumber ?? seat.displayLabel ?? seat.label)}</span></div></div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
|
|
3074
3238
|
this.els.map.appendChild(el);
|
|
3075
3239
|
this.confirmEl = el;
|
|
3076
3240
|
this.reanchorConfirm();
|
|
@@ -3365,14 +3529,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
3365
3529
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
3366
3530
|
if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
3367
3531
|
const cats = this.controller.doc?.categories ?? [];
|
|
3368
|
-
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Replace your current choices?</div><div class="sl-ba-replace"><b>We\u2019ll find ${this.baQty} seats together.</b><span>Your manually selected tickets will be removed only after a new group is secured.</span></div><div class="sl-ba-actions"><button type="button" data-ba-cancel>Keep mine</button><button type="button" class="replace" data-ba-replace>Find new seats</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Find the best seats together</div><div class="sl-ba-copy"><span class="wide">We\u2019ll choose the closest available group for you.</span><span class="narrow">Closest available group, chosen instantly.</span></div>` +
|
|
3532
|
+
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Replace your current choices?</div><div class="sl-ba-replace"><b>We\u2019ll find ${this.baQty} seats together.</b><span>Your manually selected tickets will be removed only after a new group is secured.</span></div><div class="sl-ba-actions"><button type="button" data-ba-cancel>Keep mine</button><button type="button" class="replace" data-ba-replace>Find new seats</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Find the best seats together</div><div class="sl-ba-copy"><span class="wide">We\u2019ll choose the closest available group for you.</span><span class="narrow">Closest available group, chosen instantly.</span></div>` + // Premium quick-pick — present only when the chart actually has premium
|
|
3533
|
+
// seats (same present-only philosophy as the a11y filter chips).
|
|
3534
|
+
(this.controller.hasPremiumSeats() ? `<button type="button" class="sl-ba-premium${this.baPremium ? " on" : ""}" data-ba-premium aria-pressed="${this.baPremium ? "true" : "false"}"><span class="star" aria-hidden="true">\u2605</span>${this.tf("picker.bestSeatsPremium", "Best seats")}</button>` : "") + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
|
|
3369
3535
|
}
|
|
3370
3536
|
const idGrid = (seatId, label) => {
|
|
3371
3537
|
const d = seatId ? this.controller.seatDetails(seatId) : null;
|
|
3372
3538
|
if (!d?.sectionLabel && !d?.rowLabel && !d?.seatNumber) {
|
|
3373
3539
|
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">Seat</span><span class="val">${label}</span></span></div>`;
|
|
3374
3540
|
}
|
|
3375
|
-
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">Section</span><span class="val">${d.sectionLabel ?? "\u2014"}</span></span>` + (d.rowLabel ? `<span class="fld mid"><span class="sl-chip-eb"
|
|
3541
|
+
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">Section</span><span class="val">${d.sectionLabel ?? "\u2014"}</span></span>` + (d.rowLabel ? `<span class="fld mid"><span class="sl-chip-eb">${this.rowTypeWord(d)}</span><span class="val">${this.rowShort(d)}</span></span>` : "") + (d.seatNumber ? `<span class="fld mid"><span class="sl-chip-eb">Seat</span><span class="val">${d.seatNumber}</span></span>` : "") + `</div>`;
|
|
3376
3542
|
};
|
|
3377
3543
|
const iconRail = (rmAria, viewLabel) => `<div class="sl-chip-rail"><button type="button" class="rm" aria-label="${rmAria}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>` + (viewLabel ? `<button type="button" class="view" data-view-label="${viewLabel}" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: viewLabel })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "") + `</div>`;
|
|
3378
3544
|
for (const item of heldItems) {
|
|
@@ -3383,7 +3549,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3383
3549
|
const heldSeat = item.objectType !== "ga" ? this.controller.seatByLabel(item.label) : null;
|
|
3384
3550
|
const canView2 = this.seatViewEnabled() && !!heldSeat;
|
|
3385
3551
|
parts.push(
|
|
3386
|
-
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label) + `<div class="sl-chip-sub"><span class="sl-ticket-state held" aria-label="Held for you" title="Held for you"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span
|
|
3552
|
+
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label) + `<div class="sl-chip-sub"><span class="sl-ticket-state held" aria-label="Held for you" title="Held for you"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>` + this.commercialChipMarker(heldSeat?.commercial) + `<span class="amt">${this.money(this.paidPrice(item.categoryKey, item.tierId, item.unitPrice) * (item.quantity ?? 1))}</span></div></div>` + iconRail(`Remove held ticket ${item.label}`, canView2 ? item.label : null) + `</div>`
|
|
3387
3553
|
);
|
|
3388
3554
|
}
|
|
3389
3555
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -3394,7 +3560,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3394
3560
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
3395
3561
|
const tierSelect = s.tiers && s.tiers.length ? `<select class="tier" data-tier="${s.id}" aria-label="${(0, import_core2.t)("picker.ticketTierFor", { label: s.label })}">` + s.tiers.map((ti) => `<option value="${ti.id}"${ti.id === s.tierId ? " selected" : ""}>${ti.name} \xB7 ${this.money(this.paidPrice(s.categoryKey, ti.id, ti.price))}</option>`).join("") + `</select>` : "";
|
|
3396
3562
|
parts.push(
|
|
3397
|
-
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.label) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>${tierSelect}<span class="amt">${this.money(this.paidPrice(s.categoryKey, s.tierId ?? null, s.price))}</span></div></div>` + iconRail(`Remove ${s.label}`, canView ? s.label : null) + `</div>`
|
|
3563
|
+
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.displayLabel ?? s.label) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>${this.commercialChipMarker(s.commercial)}${tierSelect}<span class="amt">${this.money(this.paidPrice(s.categoryKey, s.tierId ?? null, s.price))}</span></div></div>` + iconRail(`Remove ${s.label}`, canView ? s.label : null) + `</div>`
|
|
3398
3564
|
);
|
|
3399
3565
|
}
|
|
3400
3566
|
for (const area of gaAreas) {
|
|
@@ -3414,6 +3580,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3414
3580
|
this.els.tray.querySelector("[data-ba-cat]")?.addEventListener("change", (e) => {
|
|
3415
3581
|
this.baCat = e.target.value;
|
|
3416
3582
|
});
|
|
3583
|
+
this.els.tray.querySelector("[data-ba-premium]")?.addEventListener("click", () => {
|
|
3584
|
+
this.baPremium = !this.baPremium;
|
|
3585
|
+
this.syncTray();
|
|
3586
|
+
});
|
|
3417
3587
|
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
3418
3588
|
if (this.pendingSelectionCount() > 0) {
|
|
3419
3589
|
this.bestAvailableConfirm = true;
|
|
@@ -3421,7 +3591,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3421
3591
|
this.els.tray.querySelector("[data-ba-replace]")?.focus();
|
|
3422
3592
|
return;
|
|
3423
3593
|
}
|
|
3424
|
-
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
3594
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium });
|
|
3425
3595
|
});
|
|
3426
3596
|
this.els.tray.querySelector("[data-ba-cancel]")?.addEventListener("click", () => {
|
|
3427
3597
|
this.bestAvailableConfirm = false;
|
|
@@ -3430,7 +3600,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3430
3600
|
});
|
|
3431
3601
|
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
3432
3602
|
this.bestAvailableConfirm = false;
|
|
3433
|
-
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
3603
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium });
|
|
3434
3604
|
});
|
|
3435
3605
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
3436
3606
|
btn.addEventListener("click", () => {
|
|
@@ -3812,6 +3982,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3812
3982
|
* the prefix is exact (won't touch "1040-A" under section "104"); otherwise
|
|
3813
3983
|
* the label is shown verbatim.
|
|
3814
3984
|
*/
|
|
3985
|
+
/** Buyer-facing type word for the row/table key label — the designer's
|
|
3986
|
+
* per-object "Displayed type" override, or the default "Row". */
|
|
3987
|
+
rowTypeWord(details) {
|
|
3988
|
+
const t3 = details?.rowType?.trim();
|
|
3989
|
+
return t3 || "Row";
|
|
3990
|
+
}
|
|
3815
3991
|
rowShort(details) {
|
|
3816
3992
|
const row = details?.rowLabel;
|
|
3817
3993
|
const sec = details?.sectionLabel;
|
|
@@ -3831,10 +4007,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3831
4007
|
const esc2 = (v) => String(v ?? "\u2014").replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
3832
4008
|
const price = this.money(this.paidPrice(details.categoryKey, details.tierId ?? null, details.price));
|
|
3833
4009
|
const hasLoc = details.sectionLabel || details.rowLabel || details.seatNumber;
|
|
3834
|
-
const grid = hasLoc ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">Section</span><span class="sl-tip-val">${esc2(details.sectionLabel)}</span></div><div class="sl-tip-field"><span class="sl-tip-key"
|
|
4010
|
+
const grid = hasLoc ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">Section</span><span class="sl-tip-val">${esc2(details.sectionLabel)}</span></div><div class="sl-tip-field"><span class="sl-tip-key">${esc2(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc2(this.rowShort(details))}</span></div><div class="sl-tip-field"><span class="sl-tip-key">Seat</span><span class="sl-tip-val">${esc2(details.seatNumber ?? details.displayLabel ?? details.label)}</span></div></div>` : `<div class="sl-tip-grid one"><div class="sl-tip-field"><span class="sl-tip-key">Seat</span><span class="sl-tip-val">${esc2(details.displayLabel ?? details.label)}</span></div></div>`;
|
|
3835
4011
|
const statusLine = details.status === "free" ? "" : `<div class="sl-tip-status">${details.status === "held" ? (0, import_core2.t)("map.statusHeld") : (0, import_core2.t)("map.statusTaken")}</div>`;
|
|
4012
|
+
const limited = this.limitedViewLabel(details.commercial);
|
|
4013
|
+
const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc2(limited)}</div>` : "";
|
|
3836
4014
|
this.tipEl.style.setProperty("--sl-cat", details.categoryColor);
|
|
3837
|
-
this.tipEl.innerHTML = grid + `<div class="sl-tip-cat"><span class="sl-tip-dot" style="background:${details.categoryColor}"></span><span class="sl-tip-name">${esc2(details.categoryLabel)}</span><span class="sl-tip-amt">${price}</span></div>` + statusLine;
|
|
4015
|
+
this.tipEl.innerHTML = grid + `<div class="sl-tip-cat"><span class="sl-tip-dot" style="background:${details.categoryColor}"></span><span class="sl-tip-name">${esc2(details.categoryLabel)}</span><span class="sl-tip-amt">${price}</span></div>` + cxLine + statusLine;
|
|
3838
4016
|
this.tipEl.style.display = "block";
|
|
3839
4017
|
this.placeTooltip();
|
|
3840
4018
|
}
|
|
@@ -3854,7 +4032,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3854
4032
|
async removeHeldTicket(label) {
|
|
3855
4033
|
return this.removeHeldLabel(label);
|
|
3856
4034
|
}
|
|
3857
|
-
async bestAvailable(qty, categoryKey) {
|
|
4035
|
+
async bestAvailable(qty, categoryKey, opts = {}) {
|
|
3858
4036
|
if (this.salesClosed || this.bestAvailableBusy) return null;
|
|
3859
4037
|
qty = Math.max(1, Math.min(this.maxTickets, Math.floor(qty)));
|
|
3860
4038
|
if (this.confirmSeat) this.cancelConfirm();
|
|
@@ -3866,7 +4044,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3866
4044
|
button.innerHTML = '<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026';
|
|
3867
4045
|
}
|
|
3868
4046
|
try {
|
|
3869
|
-
const h = await this.controller.bestAvailable(qty, categoryKey);
|
|
4047
|
+
const h = await this.controller.bestAvailable(qty, categoryKey, opts);
|
|
3870
4048
|
if (h) {
|
|
3871
4049
|
this.hold = { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items };
|
|
3872
4050
|
this.handedOff = false;
|
|
@@ -3876,6 +4054,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
3876
4054
|
this.flashHeldSeats(this.hold);
|
|
3877
4055
|
this.syncTray();
|
|
3878
4056
|
this.emitHoldChange();
|
|
4057
|
+
if (opts.preferPremium && h.seats.length && !h.seats.every((s) => s.commercial?.premium)) {
|
|
4058
|
+
this.toast((0, import_core2.t)("picker.premiumFallbackNote", { count: qty }), "neutral");
|
|
4059
|
+
}
|
|
3879
4060
|
return this.hold;
|
|
3880
4061
|
}
|
|
3881
4062
|
return null;
|