@seatlayer/js 0.24.0 → 0.25.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 +158 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +158 -39
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1462,6 +1462,17 @@ var CSS = `
|
|
|
1462
1462
|
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
1463
1463
|
.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
1464
|
.sl-ba-copy .narrow{display:none}
|
|
1465
|
+
/* "\u2605 Best seats" premium quick-pick \u2014 gold accent echoing the \u2605 Premium pill on
|
|
1466
|
+
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
1467
|
+
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
1468
|
+
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
1469
|
+
color:#c9a24b;background:color-mix(in srgb,#e8c15a 10%,var(--sl-surface));
|
|
1470
|
+
border:1px solid color-mix(in srgb,#e8c15a 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
1471
|
+
.sl-ba-premium .star{font-size:12px;line-height:1;color:#e8c15a}
|
|
1472
|
+
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
1473
|
+
.sl-ba-premium.on{color:#1c1608;background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
|
|
1474
|
+
box-shadow:0 6px 16px color-mix(in srgb,#e8c15a 26%,transparent)}
|
|
1475
|
+
.sl-ba-premium.on .star{color:#5a4410}
|
|
1465
1476
|
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
1466
1477
|
font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
|
|
1467
1478
|
.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 +1570,25 @@ var CSS = `
|
|
|
1559
1570
|
.sl-confirm-view:hover{border-color:var(--sl-muted)}
|
|
1560
1571
|
.sl-confirm-view svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
1561
1572
|
|
|
1573
|
+
/* commercial seat flags \u2014 limited-view caution + premium tag. Amber tone,
|
|
1574
|
+
deliberately distinct from the red taken/held state; shown on the confirm
|
|
1575
|
+
card, echoed as a small \u25D0 marker on cart chips and the hover tip. */
|
|
1576
|
+
.sl-cx{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
|
|
1577
|
+
.sl-cx-warn{display:flex;align-items:flex-start;gap:7px;padding:8px 10px;border-radius:9px;
|
|
1578
|
+
background:color-mix(in srgb,#f4b740 13%,var(--sl-surface));border:1px solid color-mix(in srgb,#f4b740 40%,var(--sl-line));
|
|
1579
|
+
animation:slNoticeIn .28s ease both}
|
|
1580
|
+
.sl-cx-glyph{flex:none;font-size:14px;line-height:1.2;color:#f4b740}
|
|
1581
|
+
.sl-cx-txt{min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
1582
|
+
.sl-cx-txt b{font-size:12px;font-weight:800;color:var(--sl-text)}
|
|
1583
|
+
.sl-cx-note{font-size:11px;line-height:1.4;color:var(--sl-muted)}
|
|
1584
|
+
.sl-cx-premium{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;padding:4px 10px;border-radius:999px;
|
|
1585
|
+
font-size:11px;font-weight:800;letter-spacing:.02em;color:#c9a24b;
|
|
1586
|
+
background:color-mix(in srgb,#e8c15a 13%,var(--sl-surface));border:1px solid color-mix(in srgb,#e8c15a 38%,var(--sl-line))}
|
|
1587
|
+
.sl-cx-star{font-size:12px;line-height:1;color:#e8c15a}
|
|
1588
|
+
.sl-cx-mark{flex:none;font-size:12px;line-height:1;color:#f4b740;cursor:help}
|
|
1589
|
+
.sl-tip-cx{display:flex;align-items:center;gap:6px;padding:5px 10px 7px;font-size:10.5px;font-weight:700;color:#e8b24a}
|
|
1590
|
+
.sl-tip-cx .g{font-size:12px}
|
|
1591
|
+
|
|
1562
1592
|
/* 360\xB0 seat-view modal (fills the widget; drag-to-look-around equirectangular) */
|
|
1563
1593
|
.sl-view{position:absolute;inset:0;z-index:12;display:flex;flex-direction:column;background:var(--sl-bg)}
|
|
1564
1594
|
.sl-view-head{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--sl-line);flex:none}
|
|
@@ -1687,6 +1717,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
1687
1717
|
this.srEl = null;
|
|
1688
1718
|
this.baQty = 2;
|
|
1689
1719
|
this.baCat = "";
|
|
1720
|
+
/** "★ Best seats" premium quick-pick toggle — biases best-available to premium seats. */
|
|
1721
|
+
this.baPremium = false;
|
|
1690
1722
|
this.bestAvailableConfirm = false;
|
|
1691
1723
|
this.releasingHold = false;
|
|
1692
1724
|
/** Event sales window is closed (read-only load state / live close). */
|
|
@@ -1843,6 +1875,50 @@ var SeatPicker = class _SeatPicker {
|
|
|
1843
1875
|
const sight = distance != null ? (0, import_core2.t)("picker.sightline", { m: distance }) : this.tf("picker.sightlineClear", "Clear sightline");
|
|
1844
1876
|
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
1877
|
}
|
|
1878
|
+
/** Minimal HTML/attribute escaper for buyer-authored commercial text (notes). */
|
|
1879
|
+
escCx(value) {
|
|
1880
|
+
return String(value ?? "").replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
1881
|
+
}
|
|
1882
|
+
/** Localized "Restricted view" / "Obstructed view" label for a seat's flags,
|
|
1883
|
+
* or '' when neither is set. Restricted takes precedence when both are on. */
|
|
1884
|
+
limitedViewLabel(c) {
|
|
1885
|
+
if (c?.restrictedView) return this.tf("picker.restrictedView", "Restricted view");
|
|
1886
|
+
if (c?.obstructedView) return this.tf("picker.obstructedView", "Obstructed view");
|
|
1887
|
+
return "";
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Commercial flags block for the confirm/detail surface: a subtle ★ Premium
|
|
1891
|
+
* tag plus an amber ◐ limited-view caution (with the organizer's note when
|
|
1892
|
+
* present). '' when the seat carries no surfaced commercial flag.
|
|
1893
|
+
*/
|
|
1894
|
+
commercialConfirmHtml(c) {
|
|
1895
|
+
if (!c) return "";
|
|
1896
|
+
const rows = [];
|
|
1897
|
+
if (c.premium) {
|
|
1898
|
+
rows.push(
|
|
1899
|
+
`<div class="sl-cx-premium"><span class="sl-cx-star" aria-hidden="true">\u2605</span>${this.tf("picker.premiumSeat", "Premium seat")}</div>`
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
const limited = this.limitedViewLabel(c);
|
|
1903
|
+
if (limited) {
|
|
1904
|
+
rows.push(
|
|
1905
|
+
`<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>`
|
|
1906
|
+
);
|
|
1907
|
+
} else if (c.note) {
|
|
1908
|
+
rows.push(
|
|
1909
|
+
`<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>`
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
return rows.length ? `<div class="sl-cx">${rows.join("")}</div>` : "";
|
|
1913
|
+
}
|
|
1914
|
+
/** Small ◐ limited-view marker for a cart chip; title/aria uses the seat's
|
|
1915
|
+
* note when present, else the generic view label. '' for a clear-view seat. */
|
|
1916
|
+
commercialChipMarker(c) {
|
|
1917
|
+
const limited = this.limitedViewLabel(c);
|
|
1918
|
+
if (!limited) return "";
|
|
1919
|
+
const title = this.escCx(c?.note ? c.note : limited);
|
|
1920
|
+
return `<span class="sl-cx-mark" role="img" aria-label="${title}" title="${title}">\u25D0</span>`;
|
|
1921
|
+
}
|
|
1846
1922
|
/** True when the picker is rendered inside an iframe (snippet embed at /e/:key). */
|
|
1847
1923
|
isFramed() {
|
|
1848
1924
|
return typeof window !== "undefined" && window.parent !== window;
|
|
@@ -2215,45 +2291,71 @@ var SeatPicker = class _SeatPicker {
|
|
|
2215
2291
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
2216
2292
|
this.buildBadge(chartTheme);
|
|
2217
2293
|
const present = /* @__PURE__ */ new Set();
|
|
2294
|
+
let hasLimitedView = false;
|
|
2218
2295
|
if (this.controller.doc) {
|
|
2219
2296
|
for (const seat of (0, import_core2.expandChart)(this.controller.doc)) {
|
|
2220
2297
|
for (const type of seat.accessibility ?? []) present.add(type);
|
|
2221
2298
|
if (seat.accessible && !seat.accessibility?.length) present.add("wheelchair");
|
|
2299
|
+
if (seat.commercial?.restrictedView || seat.commercial?.obstructedView) hasLimitedView = true;
|
|
2222
2300
|
}
|
|
2223
2301
|
}
|
|
2224
|
-
|
|
2302
|
+
const focusSeatsForFilter = () => {
|
|
2303
|
+
if (this.rungsEl && this.controller.getRung() !== "seats") {
|
|
2304
|
+
this.controller.setRung("seats");
|
|
2305
|
+
this.collapseSectionCard();
|
|
2306
|
+
this.syncRung();
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
if (present.size || hasLimitedView) {
|
|
2225
2310
|
const chips = document.createElement("div");
|
|
2226
2311
|
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
2312
|
this.regions["top-left"].appendChild(chips);
|
|
2231
2313
|
this.a11yChipsEl = chips;
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2314
|
+
if (present.size) {
|
|
2315
|
+
const GLYPH = { wheelchair: "\u267F", companion: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}" };
|
|
2316
|
+
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-a11y="1" data-f="${key}">${label}</button>`;
|
|
2317
|
+
chips.insertAdjacentHTML(
|
|
2318
|
+
"beforeend",
|
|
2319
|
+
mk("all", "All seats") + [...present].map((type) => mk(type, `${GLYPH[type] ? GLYPH[type] + " " : ""}${type[0].toUpperCase()}${type.slice(1).replace(/-/g, " ")}`)).join("")
|
|
2320
|
+
);
|
|
2321
|
+
const active = /* @__PURE__ */ new Set();
|
|
2322
|
+
const syncChips = () => {
|
|
2323
|
+
chips.querySelectorAll("button[data-a11y]").forEach((b) => {
|
|
2324
|
+
const f = b.dataset.f;
|
|
2325
|
+
const on = f === "all" ? active.size === 0 : active.has(f);
|
|
2326
|
+
b.classList.toggle("on", on);
|
|
2327
|
+
b.setAttribute("aria-pressed", String(on));
|
|
2328
|
+
});
|
|
2329
|
+
const filter = active.size ? [...active] : null;
|
|
2330
|
+
this.controller.setAccessibilityFilter(filter);
|
|
2331
|
+
if (filter) focusSeatsForFilter();
|
|
2332
|
+
};
|
|
2333
|
+
chips.querySelectorAll("button[data-a11y]").forEach((btn) => {
|
|
2334
|
+
btn.addEventListener("click", () => {
|
|
2335
|
+
const f = btn.dataset.f;
|
|
2336
|
+
if (f === "all") active.clear();
|
|
2337
|
+
else if (active.has(f)) active.delete(f);
|
|
2338
|
+
else active.add(f);
|
|
2339
|
+
syncChips();
|
|
2340
|
+
});
|
|
2239
2341
|
});
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2342
|
+
}
|
|
2343
|
+
if (hasLimitedView) {
|
|
2344
|
+
const limited = document.createElement("button");
|
|
2345
|
+
limited.type = "button";
|
|
2346
|
+
limited.className = "sl-chip-f";
|
|
2347
|
+
limited.setAttribute("aria-pressed", "false");
|
|
2348
|
+
limited.innerHTML = `\u25D0 ${this.tf("picker.hideLimitedView", "Hide limited-view seats")}`;
|
|
2349
|
+
chips.appendChild(limited);
|
|
2350
|
+
let limitedOn = false;
|
|
2351
|
+
limited.addEventListener("click", () => {
|
|
2352
|
+
limitedOn = !limitedOn;
|
|
2353
|
+
limited.classList.toggle("on", limitedOn);
|
|
2354
|
+
limited.setAttribute("aria-pressed", String(limitedOn));
|
|
2355
|
+
this.controller.setCommercialLimitedFilter(limitedOn);
|
|
2356
|
+
if (limitedOn) focusSeatsForFilter();
|
|
2255
2357
|
});
|
|
2256
|
-
}
|
|
2358
|
+
}
|
|
2257
2359
|
}
|
|
2258
2360
|
const cb = document.createElement("button");
|
|
2259
2361
|
cb.type = "button";
|
|
@@ -2976,7 +3078,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2976
3078
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
2977
3079
|
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
3080
|
}).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>`;
|
|
3081
|
+
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
3082
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
2981
3083
|
card.querySelector(".sl-seccard-overview").addEventListener("click", () => this.controller.overview());
|
|
2982
3084
|
(this.regions["top-center"] ?? this.els.map).appendChild(card);
|
|
@@ -3070,7 +3172,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3070
3172
|
el.setAttribute("aria-modal", "true");
|
|
3071
3173
|
el.setAttribute("aria-label", `Confirm seat ${seat.label}`);
|
|
3072
3174
|
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"
|
|
3175
|
+
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
3176
|
this.els.map.appendChild(el);
|
|
3075
3177
|
this.confirmEl = el;
|
|
3076
3178
|
this.reanchorConfirm();
|
|
@@ -3365,14 +3467,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
3365
3467
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
3366
3468
|
if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
3367
3469
|
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>` +
|
|
3470
|
+
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
|
|
3471
|
+
// seats (same present-only philosophy as the a11y filter chips).
|
|
3472
|
+
(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
3473
|
}
|
|
3370
3474
|
const idGrid = (seatId, label) => {
|
|
3371
3475
|
const d = seatId ? this.controller.seatDetails(seatId) : null;
|
|
3372
3476
|
if (!d?.sectionLabel && !d?.rowLabel && !d?.seatNumber) {
|
|
3373
3477
|
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
3478
|
}
|
|
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"
|
|
3479
|
+
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
3480
|
};
|
|
3377
3481
|
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
3482
|
for (const item of heldItems) {
|
|
@@ -3383,7 +3487,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3383
3487
|
const heldSeat = item.objectType !== "ga" ? this.controller.seatByLabel(item.label) : null;
|
|
3384
3488
|
const canView2 = this.seatViewEnabled() && !!heldSeat;
|
|
3385
3489
|
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
|
|
3490
|
+
`<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
3491
|
);
|
|
3388
3492
|
}
|
|
3389
3493
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -3394,7 +3498,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3394
3498
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
3395
3499
|
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
3500
|
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>`
|
|
3501
|
+
`<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
3502
|
);
|
|
3399
3503
|
}
|
|
3400
3504
|
for (const area of gaAreas) {
|
|
@@ -3414,6 +3518,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3414
3518
|
this.els.tray.querySelector("[data-ba-cat]")?.addEventListener("change", (e) => {
|
|
3415
3519
|
this.baCat = e.target.value;
|
|
3416
3520
|
});
|
|
3521
|
+
this.els.tray.querySelector("[data-ba-premium]")?.addEventListener("click", () => {
|
|
3522
|
+
this.baPremium = !this.baPremium;
|
|
3523
|
+
this.syncTray();
|
|
3524
|
+
});
|
|
3417
3525
|
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
3418
3526
|
if (this.pendingSelectionCount() > 0) {
|
|
3419
3527
|
this.bestAvailableConfirm = true;
|
|
@@ -3421,7 +3529,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3421
3529
|
this.els.tray.querySelector("[data-ba-replace]")?.focus();
|
|
3422
3530
|
return;
|
|
3423
3531
|
}
|
|
3424
|
-
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
3532
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium });
|
|
3425
3533
|
});
|
|
3426
3534
|
this.els.tray.querySelector("[data-ba-cancel]")?.addEventListener("click", () => {
|
|
3427
3535
|
this.bestAvailableConfirm = false;
|
|
@@ -3430,7 +3538,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3430
3538
|
});
|
|
3431
3539
|
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
3432
3540
|
this.bestAvailableConfirm = false;
|
|
3433
|
-
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
3541
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium });
|
|
3434
3542
|
});
|
|
3435
3543
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
3436
3544
|
btn.addEventListener("click", () => {
|
|
@@ -3812,6 +3920,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3812
3920
|
* the prefix is exact (won't touch "1040-A" under section "104"); otherwise
|
|
3813
3921
|
* the label is shown verbatim.
|
|
3814
3922
|
*/
|
|
3923
|
+
/** Buyer-facing type word for the row/table key label — the designer's
|
|
3924
|
+
* per-object "Displayed type" override, or the default "Row". */
|
|
3925
|
+
rowTypeWord(details) {
|
|
3926
|
+
const t3 = details?.rowType?.trim();
|
|
3927
|
+
return t3 || "Row";
|
|
3928
|
+
}
|
|
3815
3929
|
rowShort(details) {
|
|
3816
3930
|
const row = details?.rowLabel;
|
|
3817
3931
|
const sec = details?.sectionLabel;
|
|
@@ -3831,10 +3945,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3831
3945
|
const esc2 = (v) => String(v ?? "\u2014").replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
3832
3946
|
const price = this.money(this.paidPrice(details.categoryKey, details.tierId ?? null, details.price));
|
|
3833
3947
|
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"
|
|
3948
|
+
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
3949
|
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>`;
|
|
3950
|
+
const limited = this.limitedViewLabel(details.commercial);
|
|
3951
|
+
const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc2(limited)}</div>` : "";
|
|
3836
3952
|
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;
|
|
3953
|
+
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
3954
|
this.tipEl.style.display = "block";
|
|
3839
3955
|
this.placeTooltip();
|
|
3840
3956
|
}
|
|
@@ -3854,7 +3970,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3854
3970
|
async removeHeldTicket(label) {
|
|
3855
3971
|
return this.removeHeldLabel(label);
|
|
3856
3972
|
}
|
|
3857
|
-
async bestAvailable(qty, categoryKey) {
|
|
3973
|
+
async bestAvailable(qty, categoryKey, opts = {}) {
|
|
3858
3974
|
if (this.salesClosed || this.bestAvailableBusy) return null;
|
|
3859
3975
|
qty = Math.max(1, Math.min(this.maxTickets, Math.floor(qty)));
|
|
3860
3976
|
if (this.confirmSeat) this.cancelConfirm();
|
|
@@ -3866,7 +3982,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3866
3982
|
button.innerHTML = '<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026';
|
|
3867
3983
|
}
|
|
3868
3984
|
try {
|
|
3869
|
-
const h = await this.controller.bestAvailable(qty, categoryKey);
|
|
3985
|
+
const h = await this.controller.bestAvailable(qty, categoryKey, opts);
|
|
3870
3986
|
if (h) {
|
|
3871
3987
|
this.hold = { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items };
|
|
3872
3988
|
this.handedOff = false;
|
|
@@ -3876,6 +3992,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
3876
3992
|
this.flashHeldSeats(this.hold);
|
|
3877
3993
|
this.syncTray();
|
|
3878
3994
|
this.emitHoldChange();
|
|
3995
|
+
if (opts.preferPremium && h.seats.length && !h.seats.every((s) => s.commercial?.premium)) {
|
|
3996
|
+
this.toast((0, import_core2.t)("picker.premiumFallbackNote", { count: qty }), "neutral");
|
|
3997
|
+
}
|
|
3879
3998
|
return this.hold;
|
|
3880
3999
|
}
|
|
3881
4000
|
return null;
|