@seatlayer/js 0.16.0 → 0.16.1
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 +181 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +181 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -500,7 +500,7 @@ var CSS = `
|
|
|
500
500
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
501
501
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
502
502
|
.sl-map-host{position:absolute;inset:0}
|
|
503
|
-
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow
|
|
503
|
+
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow:hidden}
|
|
504
504
|
|
|
505
505
|
/* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
|
|
506
506
|
container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
|
|
@@ -583,6 +583,7 @@ var CSS = `
|
|
|
583
583
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
584
584
|
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
585
585
|
.sl-prices{padding:5px 14px 10px;border-bottom:1px solid var(--sl-line)}
|
|
586
|
+
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
586
587
|
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px}
|
|
587
588
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
588
589
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
@@ -599,7 +600,8 @@ var CSS = `
|
|
|
599
600
|
/* tray */
|
|
600
601
|
.sl-seats-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
601
602
|
.sl-seat-summary{font-size:10px;letter-spacing:0;text-transform:none;white-space:nowrap}
|
|
602
|
-
.sl-tray{flex:1;padding:10px 14px;display:flex;flex-direction:column;gap:7px;min-height:0
|
|
603
|
+
.sl-tray{flex:1;padding:10px 14px 14px;display:flex;flex-direction:column;gap:7px;min-height:0;overflow-y:auto;
|
|
604
|
+
overscroll-behavior:contain;scrollbar-gutter:stable}
|
|
603
605
|
.sl-tray-hint{font-size:12.5px;color:var(--sl-muted);line-height:1.5}
|
|
604
606
|
.sl-chip{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto 30px;align-items:center;gap:8px;
|
|
605
607
|
min-height:53px;padding:7px 7px 7px 9px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm);
|
|
@@ -634,16 +636,22 @@ var CSS = `
|
|
|
634
636
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
635
637
|
|
|
636
638
|
/* footer */
|
|
637
|
-
.sl-foot{padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none
|
|
638
|
-
|
|
639
|
+
.sl-foot{position:relative;z-index:2;padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none;
|
|
640
|
+
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
641
|
+
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
639
642
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
640
643
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent);
|
|
641
644
|
font-size:11.5px;line-height:1.35;color:var(--sl-muted)}
|
|
642
645
|
.sl-hold-note.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
643
646
|
.sl-hold-note svg{width:16px;height:16px;flex:none;stroke:var(--sl-accent);stroke-width:2.4;fill:none;
|
|
644
647
|
stroke-linecap:round;stroke-linejoin:round}
|
|
645
|
-
.sl-hold-note b{display:block;color:var(--sl-text);font-size:
|
|
646
|
-
.sl-hold-copy{display:block}
|
|
648
|
+
.sl-hold-note b{display:block;color:var(--sl-text);font-size:11.5px;white-space:nowrap}
|
|
649
|
+
.sl-hold-copy{display:block;white-space:nowrap;font-size:10.5px}
|
|
650
|
+
.sl-hold-note>span{flex:1;min-width:0}
|
|
651
|
+
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
652
|
+
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
653
|
+
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
654
|
+
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
647
655
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
648
656
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
649
657
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
@@ -701,6 +709,9 @@ var CSS = `
|
|
|
701
709
|
font-size:12.5px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .22s,transform .22s;white-space:nowrap;
|
|
702
710
|
overflow:hidden;text-overflow:ellipsis}
|
|
703
711
|
.sl-toast.on{opacity:1;transform:translateY(0) scale(1)}
|
|
712
|
+
.sl-toast.has-action{pointer-events:auto;display:flex;align-items:center;gap:12px;padding-right:8px}
|
|
713
|
+
.sl-toast-action{min-height:30px;padding:5px 10px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
714
|
+
font:inherit;font-weight:800}
|
|
704
715
|
.sl-toast[data-tone="error"]{border-color:#ef4444}
|
|
705
716
|
.sl-toast[data-tone="warning"]{border-color:var(--sl-accent)}
|
|
706
717
|
.sl-toast[data-tone="success"]{border-color:#22c55e}
|
|
@@ -804,6 +815,12 @@ var CSS = `
|
|
|
804
815
|
box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
|
|
805
816
|
.sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
|
|
806
817
|
.sl-picker .sl-ba-go:disabled{opacity:.62;cursor:wait}
|
|
818
|
+
.sl-ba-replace{grid-column:1/-1;padding:3px 0 1px}
|
|
819
|
+
.sl-ba-replace b{display:block;font-size:12.5px}
|
|
820
|
+
.sl-ba-replace span{display:block;margin-top:3px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
821
|
+
.sl-ba-actions{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:7px}
|
|
822
|
+
.sl-ba-actions button{min-height:36px;border-radius:9px;border:1px solid var(--sl-line);font-size:11.5px;font-weight:800}
|
|
823
|
+
.sl-ba-actions .replace{border-color:var(--sl-accent);background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
807
824
|
.sl-picker[data-layout="narrow"] .sl-ba{padding:11px}
|
|
808
825
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
809
826
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
@@ -995,6 +1012,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
995
1012
|
this.a11yFilter = "all";
|
|
996
1013
|
this.baQty = 2;
|
|
997
1014
|
this.baCat = "";
|
|
1015
|
+
this.bestAvailableConfirm = false;
|
|
1016
|
+
this.releasingHold = false;
|
|
998
1017
|
// arena / multi-floor / seat-view chrome
|
|
999
1018
|
this.rungsEl = null;
|
|
1000
1019
|
this.floorsEl = null;
|
|
@@ -1042,10 +1061,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
1042
1061
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
1043
1062
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
1044
1063
|
this.api = new PubApi(this.apiBase);
|
|
1064
|
+
this.maxTickets = Math.max(1, Math.floor(options.maxSelection ?? DEFAULT_MAX_SELECTION2));
|
|
1045
1065
|
this.controller = new import_core2.PickerController({
|
|
1046
1066
|
transport: this.api,
|
|
1047
1067
|
eventKey: options.event,
|
|
1048
|
-
maxSelection:
|
|
1068
|
+
maxSelection: this.maxTickets,
|
|
1049
1069
|
currency: options.currency,
|
|
1050
1070
|
flashOnLiveChange: true,
|
|
1051
1071
|
colorblindSafe: options.colorblindSafe,
|
|
@@ -1080,6 +1100,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
1080
1100
|
onDeselect: (seat) => {
|
|
1081
1101
|
if (this.confirmSeat?.id === seat.id) this.dismissConfirm();
|
|
1082
1102
|
},
|
|
1103
|
+
onSelectionLimit: () => {
|
|
1104
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1105
|
+
},
|
|
1083
1106
|
onViewChange: () => {
|
|
1084
1107
|
this.reanchorConfirm();
|
|
1085
1108
|
this.syncRung();
|
|
@@ -1142,6 +1165,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
1142
1165
|
if (picker.confirmSeat) {
|
|
1143
1166
|
e.preventDefault();
|
|
1144
1167
|
picker.cancelConfirm();
|
|
1168
|
+
} else if (picker.bestAvailableConfirm) {
|
|
1169
|
+
e.preventDefault();
|
|
1170
|
+
picker.bestAvailableConfirm = false;
|
|
1171
|
+
picker.syncTray();
|
|
1145
1172
|
} else {
|
|
1146
1173
|
close();
|
|
1147
1174
|
}
|
|
@@ -1165,10 +1192,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
1165
1192
|
this.root = root;
|
|
1166
1193
|
mount.appendChild(root);
|
|
1167
1194
|
root.addEventListener("keydown", (e) => {
|
|
1168
|
-
if (e.key !== "Escape"
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1195
|
+
if (e.key !== "Escape") return;
|
|
1196
|
+
if (this.confirmSeat) {
|
|
1197
|
+
e.preventDefault();
|
|
1198
|
+
e.stopPropagation();
|
|
1199
|
+
this.cancelConfirm();
|
|
1200
|
+
} else if (this.bestAvailableConfirm) {
|
|
1201
|
+
e.preventDefault();
|
|
1202
|
+
e.stopPropagation();
|
|
1203
|
+
this.bestAvailableConfirm = false;
|
|
1204
|
+
this.syncTray();
|
|
1205
|
+
}
|
|
1172
1206
|
});
|
|
1173
1207
|
Object.entries(resolveTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
1174
1208
|
root.innerHTML = `
|
|
@@ -1216,6 +1250,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1216
1250
|
<div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
|
|
1217
1251
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
1218
1252
|
<span><b data-ref="holdTitle">Seats secured</b><span class="sl-hold-copy" data-ref="holdCopy">Checkout timer is running.</span></span>
|
|
1253
|
+
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats">Change</button>
|
|
1219
1254
|
</div>
|
|
1220
1255
|
<div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
|
|
1221
1256
|
<button type="button" class="sl-cta" data-ref="cta" disabled></button>
|
|
@@ -1293,6 +1328,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1293
1328
|
if (this.tipEl && this.tipEl.style.display !== "none") this.placeTooltip();
|
|
1294
1329
|
});
|
|
1295
1330
|
this.els.cta.addEventListener("click", () => void this.handleCta());
|
|
1331
|
+
this.els.holdChange?.addEventListener("click", () => void this.handleChangeSeats());
|
|
1296
1332
|
const canvasHost = document.createElement("div");
|
|
1297
1333
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
1298
1334
|
this.mapHost.appendChild(canvasHost);
|
|
@@ -1499,15 +1535,51 @@ var SeatPicker = class _SeatPicker {
|
|
|
1499
1535
|
const heldItems = this.hold?.items ?? [];
|
|
1500
1536
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
1501
1537
|
const pendingSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1538
|
+
return pendingSeats + this.pendingGACount();
|
|
1539
|
+
}
|
|
1540
|
+
heldGACounts() {
|
|
1502
1541
|
const heldGA = /* @__PURE__ */ new Map();
|
|
1503
|
-
for (const item of
|
|
1542
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1504
1543
|
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1505
1544
|
}
|
|
1506
|
-
|
|
1545
|
+
return heldGA;
|
|
1546
|
+
}
|
|
1547
|
+
pendingGACount() {
|
|
1548
|
+
const heldGA = this.heldGACounts();
|
|
1549
|
+
return [...this.gaQty.entries()].reduce(
|
|
1507
1550
|
(sum, [areaId, qty]) => sum + Math.max(0, qty - (heldGA.get(areaId) ?? 0)),
|
|
1508
1551
|
0
|
|
1509
1552
|
);
|
|
1510
|
-
|
|
1553
|
+
}
|
|
1554
|
+
heldTicketCount() {
|
|
1555
|
+
return (this.hold?.items ?? []).reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
1556
|
+
}
|
|
1557
|
+
totalTicketCount() {
|
|
1558
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1559
|
+
const freshSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1560
|
+
return this.heldTicketCount() + freshSeats + this.pendingGACount();
|
|
1561
|
+
}
|
|
1562
|
+
/** Held tickets and standing quantities consume the same order-wide cap. */
|
|
1563
|
+
updateSelectionCapacity() {
|
|
1564
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1565
|
+
const selectedHeld = this.committedSelection().filter((seat) => heldLabels.has(seat.label)).length;
|
|
1566
|
+
const remaining = Math.max(0, this.maxTickets - this.heldTicketCount() - this.pendingGACount());
|
|
1567
|
+
this.controller.setMaxSelection(selectedHeld + remaining);
|
|
1568
|
+
}
|
|
1569
|
+
canAddTicket() {
|
|
1570
|
+
if (this.totalTicketCount() < this.maxTickets) return true;
|
|
1571
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1572
|
+
return false;
|
|
1573
|
+
}
|
|
1574
|
+
pendingGATotal(gaAreas) {
|
|
1575
|
+
const heldGA = /* @__PURE__ */ new Map();
|
|
1576
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1577
|
+
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1578
|
+
}
|
|
1579
|
+
return gaAreas.reduce(
|
|
1580
|
+
(sum, area) => sum + area.price * Math.max(0, (this.gaQty.get(area.id) ?? 0) - (heldGA.get(area.id) ?? 0)),
|
|
1581
|
+
0
|
|
1582
|
+
);
|
|
1511
1583
|
}
|
|
1512
1584
|
syncCta(count = this.lastTrayCount, pending = this.pendingSelectionCount()) {
|
|
1513
1585
|
const cta = this.els.cta;
|
|
@@ -2234,6 +2306,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2234
2306
|
}
|
|
2235
2307
|
syncTray() {
|
|
2236
2308
|
if (!this.els.tray) return;
|
|
2309
|
+
this.updateSelectionCapacity();
|
|
2237
2310
|
const seats = this.committedSelection();
|
|
2238
2311
|
const gaAreas = this.controller.getGAAreas();
|
|
2239
2312
|
const heldItems = this.hold?.items ?? [];
|
|
@@ -2246,9 +2319,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2246
2319
|
}
|
|
2247
2320
|
if (!this.hold) {
|
|
2248
2321
|
const cats = this.controller.doc?.categories ?? [];
|
|
2249
|
-
parts.push(
|
|
2250
|
-
`<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>` + (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>`
|
|
2251
|
-
);
|
|
2322
|
+
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>` + (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>`);
|
|
2252
2323
|
}
|
|
2253
2324
|
for (const item of heldItems) {
|
|
2254
2325
|
const itemKey = `held:${item.label}`;
|
|
@@ -2283,7 +2354,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2283
2354
|
this.lastTrayKeys = nextTrayKeys;
|
|
2284
2355
|
this.els.tray.querySelectorAll("[data-ba]").forEach((btn) => {
|
|
2285
2356
|
btn.addEventListener("click", () => {
|
|
2286
|
-
this.baQty = Math.max(1, Math.min(
|
|
2357
|
+
this.baQty = Math.max(1, Math.min(this.maxTickets, this.baQty + Number(btn.dataset.ba)));
|
|
2287
2358
|
this.syncTray();
|
|
2288
2359
|
});
|
|
2289
2360
|
});
|
|
@@ -2291,6 +2362,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
2291
2362
|
this.baCat = e.target.value;
|
|
2292
2363
|
});
|
|
2293
2364
|
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
2365
|
+
if (this.pendingSelectionCount() > 0) {
|
|
2366
|
+
this.bestAvailableConfirm = true;
|
|
2367
|
+
this.syncTray();
|
|
2368
|
+
this.els.tray.querySelector("[data-ba-replace]")?.focus();
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2372
|
+
});
|
|
2373
|
+
this.els.tray.querySelector("[data-ba-cancel]")?.addEventListener("click", () => {
|
|
2374
|
+
this.bestAvailableConfirm = false;
|
|
2375
|
+
this.syncTray();
|
|
2376
|
+
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
2377
|
+
});
|
|
2378
|
+
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
2379
|
+
this.bestAvailableConfirm = false;
|
|
2294
2380
|
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2295
2381
|
});
|
|
2296
2382
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
@@ -2301,12 +2387,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
2301
2387
|
return;
|
|
2302
2388
|
}
|
|
2303
2389
|
const id = chip.dataset.seat;
|
|
2304
|
-
|
|
2390
|
+
const label = chip.querySelector("b")?.textContent ?? "Seat";
|
|
2391
|
+
const remove = () => {
|
|
2305
2392
|
this.controller.deselect([id]);
|
|
2393
|
+
this.toast(`${label} removed.`, "neutral", {
|
|
2394
|
+
label: "Undo",
|
|
2395
|
+
onClick: () => {
|
|
2396
|
+
const restored = this.controller.select([id]);
|
|
2397
|
+
this.toast(
|
|
2398
|
+
restored.length ? `${label} restored.` : `${label} is no longer available.`,
|
|
2399
|
+
restored.length ? "success" : "warning"
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
};
|
|
2404
|
+
if (this.reducedMotion()) {
|
|
2405
|
+
remove();
|
|
2306
2406
|
return;
|
|
2307
2407
|
}
|
|
2308
2408
|
chip.classList.add("sl-leave");
|
|
2309
|
-
this.scheduleMotion(
|
|
2409
|
+
this.scheduleMotion(remove, 150);
|
|
2310
2410
|
});
|
|
2311
2411
|
});
|
|
2312
2412
|
this.els.tray.querySelectorAll(".sl-chip .tier").forEach((sel) => {
|
|
@@ -2323,13 +2423,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
2323
2423
|
const areaEl = btn.closest(".sl-ga");
|
|
2324
2424
|
const id = areaEl.dataset.ga;
|
|
2325
2425
|
const area = gaAreas.find((a) => a.id === id);
|
|
2326
|
-
const
|
|
2426
|
+
const delta = Number(btn.dataset.d);
|
|
2427
|
+
if (delta > 0 && !this.canAddTicket()) return;
|
|
2428
|
+
const next = Math.max(0, Math.min(area?.available ?? 0, (this.gaQty.get(id) ?? 0) + delta));
|
|
2327
2429
|
this.gaQty.set(id, next);
|
|
2328
2430
|
this.syncTray();
|
|
2329
2431
|
});
|
|
2330
2432
|
});
|
|
2331
|
-
const gaTotal =
|
|
2332
|
-
const gaCount =
|
|
2433
|
+
const gaTotal = this.pendingGATotal(gaAreas);
|
|
2434
|
+
const gaCount = this.pendingGACount();
|
|
2333
2435
|
const heldTotal = heldItems.reduce((sum, item) => sum + item.unitPrice * (item.quantity ?? 1), 0);
|
|
2334
2436
|
const heldCount = heldItems.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
2335
2437
|
const freshSeats = seats.filter((seat) => !heldLabels.has(seat.label));
|
|
@@ -2349,10 +2451,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
2349
2451
|
if (this.hold) {
|
|
2350
2452
|
const securedCount = heldCount || this.hold.seats?.length || 0;
|
|
2351
2453
|
if (this.els.holdTitle) {
|
|
2352
|
-
this.els.holdTitle.textContent = `${securedCount}
|
|
2454
|
+
this.els.holdTitle.textContent = `${securedCount} secured`;
|
|
2353
2455
|
}
|
|
2354
2456
|
if (this.els.holdCopy) {
|
|
2355
|
-
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected
|
|
2457
|
+
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected` : "Checkout timer running";
|
|
2458
|
+
}
|
|
2459
|
+
const change = this.els.holdChange;
|
|
2460
|
+
if (change) {
|
|
2461
|
+
change.disabled = this.releasingHold;
|
|
2462
|
+
change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
|
|
2356
2463
|
}
|
|
2357
2464
|
}
|
|
2358
2465
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
@@ -2404,7 +2511,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
2404
2511
|
if (button?.isConnected) button.disabled = false;
|
|
2405
2512
|
}
|
|
2406
2513
|
}
|
|
2514
|
+
async handleChangeSeats() {
|
|
2515
|
+
if (!this.hold || this.releasingHold) return;
|
|
2516
|
+
this.releasingHold = true;
|
|
2517
|
+
const button = this.els.holdChange;
|
|
2518
|
+
if (button) {
|
|
2519
|
+
button.disabled = true;
|
|
2520
|
+
button.textContent = "Releasing\u2026";
|
|
2521
|
+
}
|
|
2522
|
+
try {
|
|
2523
|
+
await this.release();
|
|
2524
|
+
if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
|
|
2525
|
+
} finally {
|
|
2526
|
+
this.releasingHold = false;
|
|
2527
|
+
if (button?.isConnected) {
|
|
2528
|
+
button.disabled = false;
|
|
2529
|
+
button.textContent = "Change";
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2407
2533
|
async handleCta() {
|
|
2534
|
+
if (this.totalTicketCount() > this.maxTickets) {
|
|
2535
|
+
this.toast(`Remove tickets until your order has ${this.maxTickets} or fewer.`, "warning");
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2408
2538
|
const committed = this.committedSelection();
|
|
2409
2539
|
if (this.hold && !committed.some((s) => !(this.hold.items ?? []).some((i) => i.label === s.label))) {
|
|
2410
2540
|
const seats = this.hold.seats ?? committed;
|
|
@@ -2442,7 +2572,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
2442
2572
|
this.opts.onCheckout?.(hold, hold.seats ?? chosenSeats, this.buildHandoff(hold));
|
|
2443
2573
|
} catch (err) {
|
|
2444
2574
|
this.opts.onError?.(err);
|
|
2445
|
-
|
|
2575
|
+
const problem = err;
|
|
2576
|
+
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
2577
|
+
const message = problem.reason === "event_closed" ? "Seat sales have closed for this event." : labels.length ? `${labels.join(", ")} ${labels.length === 1 ? "is" : "are"} no longer available. Choose another ${labels.length === 1 ? "seat" : "group"}.` : "One or more seats were just taken. Please pick again.";
|
|
2578
|
+
this.toast(message, "error");
|
|
2446
2579
|
this.setCtaPhase("idle");
|
|
2447
2580
|
} finally {
|
|
2448
2581
|
this.holdingLabels.clear();
|
|
@@ -2565,15 +2698,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
2565
2698
|
hold ? this.buildHandoff(hold) : null
|
|
2566
2699
|
);
|
|
2567
2700
|
}
|
|
2568
|
-
toast(msg, tone = "neutral") {
|
|
2701
|
+
toast(msg, tone = "neutral", action) {
|
|
2569
2702
|
const el = this.els.toast;
|
|
2570
2703
|
if (!el) return;
|
|
2571
|
-
el.
|
|
2704
|
+
el.replaceChildren();
|
|
2705
|
+
const copy = document.createElement("span");
|
|
2706
|
+
copy.textContent = msg;
|
|
2707
|
+
el.appendChild(copy);
|
|
2708
|
+
el.classList.toggle("has-action", !!action);
|
|
2709
|
+
if (action) {
|
|
2710
|
+
const button = document.createElement("button");
|
|
2711
|
+
button.type = "button";
|
|
2712
|
+
button.className = "sl-toast-action";
|
|
2713
|
+
button.textContent = action.label;
|
|
2714
|
+
button.addEventListener("click", action.onClick, { once: true });
|
|
2715
|
+
el.appendChild(button);
|
|
2716
|
+
}
|
|
2572
2717
|
el.dataset.tone = tone;
|
|
2573
2718
|
el.classList.add("on");
|
|
2574
2719
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
2575
2720
|
this.toastTimer = setTimeout(() => {
|
|
2576
2721
|
el.classList.remove("on");
|
|
2722
|
+
el.classList.remove("has-action");
|
|
2577
2723
|
el.dataset.tone = "neutral";
|
|
2578
2724
|
}, 4200);
|
|
2579
2725
|
}
|
|
@@ -2623,7 +2769,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
2623
2769
|
}
|
|
2624
2770
|
async bestAvailable(qty, categoryKey) {
|
|
2625
2771
|
if (this.bestAvailableBusy) return null;
|
|
2772
|
+
qty = Math.max(1, Math.min(this.maxTickets, Math.floor(qty)));
|
|
2626
2773
|
if (this.confirmSeat) this.cancelConfirm();
|
|
2774
|
+
this.bestAvailableConfirm = false;
|
|
2627
2775
|
this.bestAvailableBusy = true;
|
|
2628
2776
|
const button = this.els.tray?.querySelector(".sl-ba-go");
|
|
2629
2777
|
if (button) {
|
|
@@ -2636,6 +2784,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2636
2784
|
this.hold = { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items };
|
|
2637
2785
|
this.handedOff = false;
|
|
2638
2786
|
this.bookedShown = false;
|
|
2787
|
+
this.gaQty.clear();
|
|
2639
2788
|
this.startHoldTimer(h.expiresAt);
|
|
2640
2789
|
this.flashHeldSeats(this.hold);
|
|
2641
2790
|
this.syncTray();
|
|
@@ -2645,14 +2794,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
2645
2794
|
return null;
|
|
2646
2795
|
} catch (err) {
|
|
2647
2796
|
this.opts.onError?.(err);
|
|
2648
|
-
|
|
2797
|
+
const reason = err?.reason;
|
|
2798
|
+
const message = reason === "not_enough_together" ? `We couldn't find ${qty} seats together. Try fewer seats or another ticket type.` : reason === "sold_out" ? "That ticket type is sold out. Try another ticket type." : reason === "event_closed" ? "Seat sales have closed for this event." : "Those seats are no longer available. Try another quantity or ticket type.";
|
|
2799
|
+
this.toast(message, "error");
|
|
2649
2800
|
return null;
|
|
2650
2801
|
} finally {
|
|
2651
2802
|
this.bestAvailableBusy = false;
|
|
2652
|
-
|
|
2653
|
-
button.disabled = false;
|
|
2654
|
-
button.textContent = "Best available";
|
|
2655
|
-
}
|
|
2803
|
+
this.syncTray();
|
|
2656
2804
|
}
|
|
2657
2805
|
}
|
|
2658
2806
|
async release() {
|