@seatlayer/js 0.6.0 → 0.7.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 +174 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +175 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -377,6 +377,7 @@ var EmbeddedDesigner = class {
|
|
|
377
377
|
// src/SeatPicker.ts
|
|
378
378
|
import {
|
|
379
379
|
PickerController as PickerController2,
|
|
380
|
+
expandChart,
|
|
380
381
|
loadLocale as loadLocale2,
|
|
381
382
|
setStringOverrides as setStringOverrides2,
|
|
382
383
|
t as t2
|
|
@@ -430,6 +431,8 @@ var CSS = `
|
|
|
430
431
|
.sl-picker[data-layout="narrow"] .sl-body{flex-direction:column}
|
|
431
432
|
.sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
|
|
432
433
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;max-height:46%;border-left:0;border-top:1px solid var(--sl-line)}
|
|
434
|
+
.sl-picker[data-layout="narrow"] .sl-tray{flex:none}
|
|
435
|
+
.sl-picker[data-layout="narrow"] .sl-foot{position:sticky;bottom:0;background:var(--sl-bg)}
|
|
433
436
|
|
|
434
437
|
/* price panel */
|
|
435
438
|
.sl-sec{padding:14px 16px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
@@ -494,6 +497,39 @@ var CSS = `
|
|
|
494
497
|
.sl-boot-retry{margin-top:4px;padding:9px 20px;border-radius:var(--sl-r-sm);background:var(--sl-accent);
|
|
495
498
|
color:var(--sl-accent-ink);font-weight:700;font-size:13px}
|
|
496
499
|
|
|
500
|
+
/* a11y filter chips (over the map, top-left) */
|
|
501
|
+
.sl-chips{position:absolute;top:12px;left:12px;z-index:5;display:flex;gap:6px;flex-wrap:wrap;max-width:70%}
|
|
502
|
+
.sl-chip-f{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:999px;font-size:12px;font-weight:700;
|
|
503
|
+
background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-muted);transition:color .15s,border-color .15s}
|
|
504
|
+
.sl-chip-f:hover{color:var(--sl-text)}
|
|
505
|
+
.sl-chip-f.on{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
506
|
+
|
|
507
|
+
/* confirm popover */
|
|
508
|
+
.sl-confirm{position:absolute;z-index:9;min-width:190px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
509
|
+
border-radius:12px;padding:12px;box-shadow:0 18px 50px -18px rgba(0,0,0,.7);transform:translate(-50%,calc(-100% - 14px))}
|
|
510
|
+
.sl-confirm-label{font-weight:800;font-size:14px}
|
|
511
|
+
.sl-confirm-meta{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--sl-muted);margin-top:4px}
|
|
512
|
+
.sl-confirm-meta b{color:var(--sl-text);margin-left:auto}
|
|
513
|
+
.sl-confirm-row{display:flex;gap:8px;margin-top:10px}
|
|
514
|
+
.sl-confirm-row button{flex:1;padding:8px;border-radius:8px;font-weight:700;font-size:12.5px}
|
|
515
|
+
.sl-confirm-add{background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
516
|
+
.sl-confirm-cancel{border:1px solid var(--sl-line);color:var(--sl-muted)}
|
|
517
|
+
.sl-confirm-cancel:hover{color:var(--sl-text)}
|
|
518
|
+
|
|
519
|
+
/* best-available row */
|
|
520
|
+
.sl-ba{display:flex;align-items:center;gap:8px;padding:9px 11px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm)}
|
|
521
|
+
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:7px;
|
|
522
|
+
font:inherit;font-size:12px;padding:5px 6px;max-width:110px}
|
|
523
|
+
.sl-ba-qty{display:flex;align-items:center;gap:7px}
|
|
524
|
+
.sl-ba-qty button{width:24px;height:24px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
525
|
+
font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center}
|
|
526
|
+
.sl-ba-qty span{min-width:14px;text-align:center;font-weight:800}
|
|
527
|
+
.sl-ba-go{margin-left:auto;padding:7px 12px;border-radius:999px;border:1px solid var(--sl-line);font-weight:700;font-size:12px;transition:border-color .15s}
|
|
528
|
+
.sl-ba-go:hover{border-color:var(--sl-muted)}
|
|
529
|
+
|
|
530
|
+
/* screen-reader live region */
|
|
531
|
+
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
532
|
+
|
|
497
533
|
/* modal host */
|
|
498
534
|
.sl-modal-scrim{position:fixed;inset:0;z-index:2147483000;background:rgba(5,7,12,.66);display:flex;align-items:center;justify-content:center;padding:18px}
|
|
499
535
|
.sl-modal-frame{width:min(1200px,100%);height:min(820px,100%);border-radius:16px;overflow:hidden;box-shadow:0 40px 120px -30px rgba(0,0,0,.8)}
|
|
@@ -538,6 +574,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
538
574
|
this.gaQty = /* @__PURE__ */ new Map();
|
|
539
575
|
this.tipEl = null;
|
|
540
576
|
this.tipPos = { x: 0, y: 0 };
|
|
577
|
+
this.confirmEl = null;
|
|
578
|
+
this.confirmSeat = null;
|
|
579
|
+
this.srEl = null;
|
|
580
|
+
this.a11yFilter = "all";
|
|
581
|
+
this.baQty = 2;
|
|
582
|
+
this.baCat = "";
|
|
541
583
|
// modal plumbing (set by open())
|
|
542
584
|
this.modalScrim = null;
|
|
543
585
|
this.prevFocus = null;
|
|
@@ -567,6 +609,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
567
609
|
this.syncTray();
|
|
568
610
|
this.opts.onHoldExpired?.();
|
|
569
611
|
},
|
|
612
|
+
confirmSelection: options.confirmSelection,
|
|
613
|
+
onSelect: (seat) => {
|
|
614
|
+
if (this.opts.confirmSelection) this.showConfirm(seat);
|
|
615
|
+
},
|
|
616
|
+
onViewChange: () => this.reanchorConfirm(),
|
|
617
|
+
onFocusSeat: (seat) => this.announceSeat(seat),
|
|
570
618
|
onSeatHover: (d) => this.updateTooltip(d),
|
|
571
619
|
onHint: (m) => {
|
|
572
620
|
if (m) this.toast(m);
|
|
@@ -700,10 +748,92 @@ var SeatPicker = class _SeatPicker {
|
|
|
700
748
|
this.els.name.textContent = info.eventName ?? "";
|
|
701
749
|
const when = info.startsAt ? new Date(info.startsAt).toLocaleString(this.opts.locale, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }) : "";
|
|
702
750
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
751
|
+
const present = /* @__PURE__ */ new Set();
|
|
752
|
+
if (this.controller.doc) {
|
|
753
|
+
for (const seat of expandChart(this.controller.doc)) {
|
|
754
|
+
for (const type of seat.accessibility ?? []) present.add(type);
|
|
755
|
+
if (seat.accessible && !seat.accessibility?.length) present.add("wheelchair");
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (present.size) {
|
|
759
|
+
const chips = document.createElement("div");
|
|
760
|
+
chips.className = "sl-chips";
|
|
761
|
+
const GLYPH = { wheelchair: "\u267F", companion: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}" };
|
|
762
|
+
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-f="${key}">${label}</button>`;
|
|
763
|
+
chips.innerHTML = mk("all", "All seats") + [...present].map((type) => mk(type, `${GLYPH[type] ? GLYPH[type] + " " : ""}${type[0].toUpperCase()}${type.slice(1).replace(/-/g, " ")}`)).join("");
|
|
764
|
+
this.els.map.appendChild(chips);
|
|
765
|
+
chips.querySelectorAll("button").forEach((btn) => {
|
|
766
|
+
btn.addEventListener("click", () => {
|
|
767
|
+
const f = btn.dataset.f;
|
|
768
|
+
this.a11yFilter = f;
|
|
769
|
+
chips.querySelectorAll("button").forEach((b) => b.classList.toggle("on", b === btn));
|
|
770
|
+
this.controller.setAccessibilityFilter(f === "all" ? null : [f]);
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
const cb = document.createElement("button");
|
|
775
|
+
cb.type = "button";
|
|
776
|
+
cb.setAttribute("aria-label", "Toggle colorblind-friendly colors");
|
|
777
|
+
cb.setAttribute("aria-pressed", String(!!this.opts.colorblindSafe));
|
|
778
|
+
cb.innerHTML = '<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>';
|
|
779
|
+
this.els.zfit.parentElement.appendChild(cb);
|
|
780
|
+
let cbOn = !!this.opts.colorblindSafe;
|
|
781
|
+
cb.addEventListener("click", () => {
|
|
782
|
+
cbOn = !cbOn;
|
|
783
|
+
cb.setAttribute("aria-pressed", String(cbOn));
|
|
784
|
+
this.controller.setColorblindSafe(cbOn);
|
|
785
|
+
});
|
|
786
|
+
this.srEl = document.createElement("div");
|
|
787
|
+
this.srEl.className = "sl-sr";
|
|
788
|
+
this.srEl.setAttribute("aria-live", "polite");
|
|
789
|
+
root.appendChild(this.srEl);
|
|
703
790
|
this.syncPrices();
|
|
704
791
|
this.syncTray();
|
|
705
792
|
return this;
|
|
706
793
|
}
|
|
794
|
+
/** aria-live readout when keyboard focus lands on a seat. */
|
|
795
|
+
announceSeat(seat) {
|
|
796
|
+
if (!this.srEl) return;
|
|
797
|
+
if (!seat) {
|
|
798
|
+
this.srEl.textContent = "";
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
|
|
802
|
+
const status = this.controller.getStatus(seat.id) ?? "free";
|
|
803
|
+
const statusText = status === "free" ? "available" : status === "held" ? "on hold" : "taken";
|
|
804
|
+
const price = cat?.tiers?.length ? cat.tiers[0].price : cat?.price;
|
|
805
|
+
this.srEl.textContent = `Seat ${seat.label}, ${cat?.label ?? seat.categoryKey}${price != null ? `, ${this.money(price)}` : ""}, ${statusText}`;
|
|
806
|
+
}
|
|
807
|
+
// ---- confirm popover (opt-in confirmSelection mode) ------------------------
|
|
808
|
+
showConfirm(seat) {
|
|
809
|
+
this.closeConfirm();
|
|
810
|
+
if (this.tipEl) this.tipEl.style.display = "none";
|
|
811
|
+
const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
|
|
812
|
+
const price = cat?.tiers?.length ? cat.tiers[0].price : cat?.price;
|
|
813
|
+
const el = document.createElement("div");
|
|
814
|
+
el.className = "sl-confirm";
|
|
815
|
+
el.innerHTML = `<div class="sl-confirm-label">${seat.label}</div><div class="sl-confirm-meta"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span>${cat?.label ?? seat.categoryKey}${price != null ? `<b>${this.money(price)}</b>` : ""}</div><div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add">Add seat</button></div>`;
|
|
816
|
+
this.els.map.appendChild(el);
|
|
817
|
+
this.confirmEl = el;
|
|
818
|
+
this.confirmSeat = seat;
|
|
819
|
+
this.reanchorConfirm();
|
|
820
|
+
el.querySelector(".sl-confirm-add").addEventListener("click", () => this.closeConfirm());
|
|
821
|
+
el.querySelector(".sl-confirm-cancel").addEventListener("click", () => {
|
|
822
|
+
this.controller.deselect([seat.id]);
|
|
823
|
+
this.closeConfirm();
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
reanchorConfirm() {
|
|
827
|
+
if (!this.confirmEl || !this.confirmSeat) return;
|
|
828
|
+
const p = this.controller.worldToScreen({ x: this.confirmSeat.x, y: this.confirmSeat.y });
|
|
829
|
+
this.confirmEl.style.left = `${p.x}px`;
|
|
830
|
+
this.confirmEl.style.top = `${p.y}px`;
|
|
831
|
+
}
|
|
832
|
+
closeConfirm() {
|
|
833
|
+
this.confirmEl?.remove();
|
|
834
|
+
this.confirmEl = null;
|
|
835
|
+
this.confirmSeat = null;
|
|
836
|
+
}
|
|
707
837
|
// ---- chrome sync ----------------------------------------------------------
|
|
708
838
|
money(n) {
|
|
709
839
|
try {
|
|
@@ -718,8 +848,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
718
848
|
const left = this.controller.categoryAvailability();
|
|
719
849
|
this.els.prices.innerHTML = doc.categories.map((c) => {
|
|
720
850
|
const price = c.tiers?.length ? c.tiers[0].price : c.price;
|
|
721
|
-
return `<div class="sl-price-row"><span class="sl-dot" style="background:${c.color}"></span><span class="sl-price-label">${c.label}</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
851
|
+
return `<div class="sl-price-row" data-cat="${c.key}"><span class="sl-dot" style="background:${c.color}"></span><span class="sl-price-label">${c.label}</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
722
852
|
}).join("");
|
|
853
|
+
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
854
|
+
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
855
|
+
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
856
|
+
});
|
|
723
857
|
}
|
|
724
858
|
/** A live delta took one of OUR selected (not yet held) seats — evict + tell the buyer. */
|
|
725
859
|
evictTakenSelections() {
|
|
@@ -733,13 +867,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
733
867
|
if (!this.els.tray) return;
|
|
734
868
|
const seats = this.controller.getSelection();
|
|
735
869
|
const gaAreas = this.controller.getGAAreas();
|
|
870
|
+
const heldItems = this.hold?.items ?? [];
|
|
736
871
|
const parts = [];
|
|
737
|
-
if (!seats.length && !gaAreas.length) {
|
|
872
|
+
if (!seats.length && !heldItems.length && !gaAreas.length) {
|
|
738
873
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map, or let us pick the best available for you.</div>`);
|
|
739
|
-
} else if (!seats.length) {
|
|
874
|
+
} else if (!seats.length && !heldItems.length) {
|
|
740
875
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
|
|
741
876
|
}
|
|
742
|
-
for (const
|
|
877
|
+
for (const item of heldItems) {
|
|
878
|
+
const cat = this.controller.doc?.categories.find((c) => c.key === item.categoryKey);
|
|
879
|
+
parts.push(
|
|
880
|
+
`<div class="sl-chip"><b>${item.label}</b><span class="cat">${cat?.label ?? item.categoryKey}</span><span class="amt">${this.money(item.unitPrice * (item.quantity ?? 1))}</span></div>`
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
884
|
+
for (const s of seats.filter((seat) => !heldLabels.has(seat.label))) {
|
|
743
885
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
744
886
|
parts.push(
|
|
745
887
|
`<div class="sl-chip" data-seat="${s.id}"><b>${s.label}</b><span class="cat">${cat?.label ?? s.categoryKey}</span><span class="amt">${this.money(s.price)}</span><button type="button" class="rm" aria-label="Remove ${s.label}"><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></div>`
|
|
@@ -751,7 +893,25 @@ var SeatPicker = class _SeatPicker {
|
|
|
751
893
|
`<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.label}</div><div class="sl-ga-sub">${this.money(area.price)} \xB7 ${area.available} left</div></div><div class="sl-ga-qty"><button type="button" data-d="-1" aria-label="Fewer">\u2212</button><span>${qty}</span><button type="button" data-d="1" aria-label="More">+</button></div></div>`
|
|
752
894
|
);
|
|
753
895
|
}
|
|
896
|
+
if (!this.hold) {
|
|
897
|
+
const cats = this.controller.doc?.categories ?? [];
|
|
898
|
+
parts.push(
|
|
899
|
+
`<div class="sl-ba">` + (cats.length > 1 ? `<select aria-label="Category" data-ba-cat><option value="">Any tier</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : "") + `<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">Best available</button></div>`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
754
902
|
this.els.tray.innerHTML = parts.join("");
|
|
903
|
+
this.els.tray.querySelectorAll("[data-ba]").forEach((btn) => {
|
|
904
|
+
btn.addEventListener("click", () => {
|
|
905
|
+
this.baQty = Math.max(1, Math.min(8, this.baQty + Number(btn.dataset.ba)));
|
|
906
|
+
this.syncTray();
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
this.els.tray.querySelector("[data-ba-cat]")?.addEventListener("change", (e) => {
|
|
910
|
+
this.baCat = e.target.value;
|
|
911
|
+
});
|
|
912
|
+
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
913
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
914
|
+
});
|
|
755
915
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
756
916
|
btn.addEventListener("click", () => {
|
|
757
917
|
const id = btn.closest(".sl-chip").dataset.seat;
|
|
@@ -770,17 +930,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
770
930
|
});
|
|
771
931
|
const gaTotal = gaAreas.reduce((sum, a) => sum + a.price * (this.gaQty.get(a.id) ?? 0), 0);
|
|
772
932
|
const gaCount = [...this.gaQty.values()].reduce((a, b) => a + b, 0);
|
|
773
|
-
const
|
|
774
|
-
const
|
|
933
|
+
const heldTotal = heldItems.reduce((sum, item) => sum + item.unitPrice * (item.quantity ?? 1), 0);
|
|
934
|
+
const heldCount = heldItems.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
935
|
+
const freshSeats = seats.filter((seat) => !heldLabels.has(seat.label));
|
|
936
|
+
const total = freshSeats.reduce((sum, s) => sum + s.price, 0) + gaTotal + heldTotal;
|
|
937
|
+
const count = freshSeats.length + gaCount + heldCount;
|
|
775
938
|
this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
|
|
776
939
|
this.els.total.textContent = count ? this.money(total) : "";
|
|
777
940
|
const cta = this.els.cta;
|
|
778
941
|
cta.disabled = count === 0;
|
|
779
|
-
cta.textContent = count ? "Hold seats & checkout" : "Select seats";
|
|
942
|
+
cta.textContent = this.hold ? "Continue to checkout" : count ? "Hold seats & checkout" : "Select seats";
|
|
780
943
|
this.opts.onSelectionChange?.(seats);
|
|
781
944
|
}
|
|
782
945
|
async handleCta() {
|
|
783
946
|
const cta = this.els.cta;
|
|
947
|
+
if (this.hold && !this.controller.getSelection().some((s) => !(this.hold.items ?? []).some((i) => i.label === s.label))) {
|
|
948
|
+
this.opts.onCheckout?.(this.hold, this.controller.getSelection());
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
784
951
|
cta.disabled = true;
|
|
785
952
|
cta.textContent = "Holding\u2026";
|
|
786
953
|
try {
|
|
@@ -888,6 +1055,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
888
1055
|
}
|
|
889
1056
|
destroy() {
|
|
890
1057
|
this.destroyed = true;
|
|
1058
|
+
this.closeConfirm();
|
|
891
1059
|
this.stopHoldTimer();
|
|
892
1060
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
893
1061
|
this.ro?.disconnect();
|