@seatlayer/js 0.19.0 → 0.21.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 +91 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +91 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -909,20 +909,30 @@ var CSS = `
|
|
|
909
909
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
910
910
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
911
911
|
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
912
|
-
.sl-prices{padding:
|
|
912
|
+
.sl-prices{display:flex;flex-direction:column;padding:4px 14px 8px;border-bottom:1px solid var(--sl-line)}
|
|
913
913
|
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
914
|
-
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px
|
|
914
|
+
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px;
|
|
915
|
+
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
916
|
+
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
917
|
+
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
918
|
+
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
915
919
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
916
920
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
917
921
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
918
922
|
.sl-price-amt{font-weight:800;font-variant-numeric:tabular-nums}
|
|
919
|
-
|
|
920
|
-
.sl-
|
|
921
|
-
.sl-
|
|
923
|
+
/* long category lists: capped by default, scroll once expanded */
|
|
924
|
+
.sl-prices.sl-expanded{max-height:196px;overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable}
|
|
925
|
+
.sl-price-more{display:flex;align-items:center;min-height:26px;padding:0;
|
|
926
|
+
color:var(--sl-muted);font-size:11px;font-weight:750;transition:color .15s}
|
|
927
|
+
.sl-price-more:hover,.sl-price-more:focus-visible{color:var(--sl-text)}
|
|
928
|
+
/* held/sold key \u2014 one quiet caption line; the map itself teaches these states */
|
|
929
|
+
.sl-status-key{display:flex;gap:11px;flex-wrap:wrap;padding:5px 0 0;margin-top:4px;border-top:1px solid var(--sl-line);color:var(--sl-muted);font-size:10px}
|
|
930
|
+
.sl-status-item{display:inline-flex;align-items:center;gap:5px}
|
|
931
|
+
.sl-status-icon{width:13px;height:13px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;
|
|
922
932
|
color:#fff;background:#6b7280;line-height:1}
|
|
923
|
-
.sl-status-icon svg{width:
|
|
933
|
+
.sl-status-icon svg{width:8px;height:8px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
924
934
|
.sl-status-icon.sold{background:#8b93a0}
|
|
925
|
-
.sl-status-icon.sold svg{width:
|
|
935
|
+
.sl-status-icon.sold svg{width:9px;height:9px;stroke-width:2.4}
|
|
926
936
|
|
|
927
937
|
/* tray */
|
|
928
938
|
.sl-seats-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
@@ -961,12 +971,15 @@ var CSS = `
|
|
|
961
971
|
.sl-chip .view:hover,.sl-chip .view:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
|
|
962
972
|
.sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
|
|
963
973
|
.sl-chip .view svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.8;fill:none}
|
|
964
|
-
/* live-activity strip \u2014 narrates WS availability deltas (social proof + urgency)
|
|
965
|
-
|
|
974
|
+
/* live-activity strip \u2014 narrates WS availability deltas (social proof + urgency).
|
|
975
|
+
Hidden until a delta actually happens: a static "seats update in real time"
|
|
976
|
+
banner is dead vertical space, a "2 seats just taken" flash is a signal. */
|
|
977
|
+
.sl-live{display:none;align-items:center;gap:7px;margin:10px 14px 0;padding:7px 9px;flex:none;
|
|
966
978
|
border:1px solid var(--sl-line);border-radius:8px;background:color-mix(in srgb,var(--sl-accent) 4%,var(--sl-surface));
|
|
967
979
|
font-size:11px;color:var(--sl-muted)}
|
|
968
980
|
.sl-live .dot{width:6px;height:6px;border-radius:999px;background:#22a06b;box-shadow:0 0 6px rgba(34,160,107,.75);flex:none}
|
|
969
981
|
.sl-live span:last-child{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
982
|
+
.sl-live.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
970
983
|
|
|
971
984
|
/* GA rows */
|
|
972
985
|
.sl-ga{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px dashed var(--sl-line);border-radius:var(--sl-r-sm)}
|
|
@@ -1152,13 +1165,18 @@ var CSS = `
|
|
|
1152
1165
|
border-radius:15px;box-shadow:0 24px 64px -18px rgba(0,0,0,.72);transform:translate(-50%,calc(-100% - 16px));
|
|
1153
1166
|
animation:slConfirmIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
1154
1167
|
.sl-confirm[data-placement="below"]{transform:translate(-50%,16px);animation:slConfirmBelowIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
1155
|
-
.sl-confirm-grid{display:grid;grid-template-columns:
|
|
1168
|
+
.sl-confirm-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(52px,auto) minmax(52px,auto);border-bottom:1px solid var(--sl-line)}
|
|
1156
1169
|
.sl-confirm-field{min-width:0;padding:12px 11px 10px;border-right:1px solid var(--sl-line)}
|
|
1157
1170
|
.sl-confirm-field:last-child{border-right:0;text-align:center}
|
|
1158
1171
|
.sl-confirm-field:nth-child(2){text-align:center}
|
|
1159
1172
|
.sl-confirm-key{display:block;font-size:8.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
|
|
1160
1173
|
.sl-confirm-value{display:block;margin-top:4px;color:var(--sl-text);font-size:17px;line-height:1.1;font-weight:850;
|
|
1161
1174
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1175
|
+
/* Long venue section names must read in full: smaller type + up to two lines
|
|
1176
|
+
beats an ellipsis at identity-confirmation time. Row/seat stay big \u2014 they're
|
|
1177
|
+
short and they're what the buyer double-checks against the map. */
|
|
1178
|
+
.sl-confirm-field:first-child .sl-confirm-value{font-size:13.5px;line-height:1.25;white-space:normal;
|
|
1179
|
+
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
1162
1180
|
.sl-confirm-cat{display:flex;align-items:center;gap:8px;padding:10px 12px;background:color-mix(in srgb,var(--sl-cat) 76%,var(--sl-surface))}
|
|
1163
1181
|
.sl-confirm-cat .sl-dot{border:2px solid rgba(255,255,255,.78);width:11px;height:11px}
|
|
1164
1182
|
.sl-confirm-cat-name{font-size:13.5px;font-weight:800;color:#fff;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
@@ -1452,6 +1470,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
1452
1470
|
this.miniTf = null;
|
|
1453
1471
|
// F4 price-band filter — active band's category keys (null = all prices)
|
|
1454
1472
|
this.priceBandKeys = null;
|
|
1473
|
+
this.focusedCatKey = null;
|
|
1474
|
+
this.pricesExpanded = false;
|
|
1455
1475
|
/** Last surfaced section summary (re-rendered when the price band changes). */
|
|
1456
1476
|
this.lastSection = null;
|
|
1457
1477
|
/** Section card collapsed to its slim pill (seat-picking has begun). */
|
|
@@ -1486,6 +1506,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
1486
1506
|
/** Set by open(): closes the modal (scroll restore + destroy + onClose). */
|
|
1487
1507
|
this.closeModal = null;
|
|
1488
1508
|
this.lastCatAvail = null;
|
|
1509
|
+
this.lastAvailFloorId = "";
|
|
1510
|
+
this.availQuietUntil = 0;
|
|
1511
|
+
this.liveTimer = null;
|
|
1489
1512
|
if (!options || typeof options !== "object") throw new Error("seatmap: options object is required");
|
|
1490
1513
|
if (!options.event || typeof options.event !== "string") throw new Error("seatmap: `event` key is required");
|
|
1491
1514
|
if (!options.container) throw new Error("seatmap: `container` is required (or use SeatPicker.open())");
|
|
@@ -1578,7 +1601,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1578
1601
|
return "";
|
|
1579
1602
|
}
|
|
1580
1603
|
}
|
|
1581
|
-
const sight = distance != null ?
|
|
1604
|
+
const sight = distance != null ? (0, import_core2.t)("picker.sightline", { m: distance }) : this.tf("picker.sightlineClear", "Clear sightline");
|
|
1582
1605
|
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>`;
|
|
1583
1606
|
}
|
|
1584
1607
|
/** True when the picker is rendered inside an iframe (snippet embed at /e/:key). */
|
|
@@ -2574,9 +2597,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
2574
2597
|
select.addEventListener("change", () => {
|
|
2575
2598
|
const band = bands.find((candidate) => candidate.id === select.value);
|
|
2576
2599
|
const keys = band?.keys ?? null;
|
|
2600
|
+
this.focusedCatKey = null;
|
|
2577
2601
|
this.priceBandKeys = keys ? new Set(keys) : null;
|
|
2578
2602
|
this.controller.setCategoryFilter(keys);
|
|
2579
2603
|
this.controller.focusCategoryFilter(keys);
|
|
2604
|
+
this.syncFloors();
|
|
2605
|
+
this.syncRung();
|
|
2606
|
+
this.refreshMinimap();
|
|
2580
2607
|
this.syncPrices();
|
|
2581
2608
|
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
2582
2609
|
});
|
|
@@ -2995,15 +3022,51 @@ var SeatPicker = class _SeatPicker {
|
|
|
2995
3022
|
const left = this.controller.categoryAvailability();
|
|
2996
3023
|
this.narrateAvailability(doc.categories, left);
|
|
2997
3024
|
this.syncSoldout(doc.categories, left);
|
|
2998
|
-
|
|
3025
|
+
const PRICE_LIMIT = 5;
|
|
3026
|
+
const overflow = doc.categories.length - PRICE_LIMIT;
|
|
3027
|
+
const collapsed = overflow > 1 && !this.pricesExpanded;
|
|
3028
|
+
const shown = collapsed ? doc.categories.slice(0, PRICE_LIMIT) : doc.categories;
|
|
3029
|
+
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
3030
|
+
this.els.prices.innerHTML = shown.map((c) => {
|
|
2999
3031
|
const price = this.catPrice(c);
|
|
3032
|
+
const active = this.focusedCatKey === c.key;
|
|
3000
3033
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
3001
|
-
return `<div class="sl-price-row${dim ? " sl-dim" : ""}" 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>`;
|
|
3002
|
-
}).join("") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon" aria-hidden="true"><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></i>Temporarily held</span><span class="sl-status-item"><i class="sl-status-icon sold" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M7 17L17 7"/></svg></i>Sold</span></div>`;
|
|
3034
|
+
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${c.key}" role="button" tabindex="0" aria-pressed="${active}" title="${active ? "Show all seats" : `Show ${c.label} seats on the map`}"><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>`;
|
|
3035
|
+
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? `Show all ${doc.categories.length} ticket types` : "Show fewer") + `</button>` : "") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon" aria-hidden="true"><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></i>Temporarily held</span><span class="sl-status-item"><i class="sl-status-icon sold" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M7 17L17 7"/></svg></i>Sold</span></div>`;
|
|
3003
3036
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
3004
3037
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
3005
3038
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
3039
|
+
const toggle = () => this.focusCategory(row.dataset.cat ?? "");
|
|
3040
|
+
row.addEventListener("click", toggle);
|
|
3041
|
+
row.addEventListener("keydown", (e) => {
|
|
3042
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3043
|
+
e.preventDefault();
|
|
3044
|
+
toggle();
|
|
3045
|
+
}
|
|
3046
|
+
});
|
|
3006
3047
|
});
|
|
3048
|
+
this.els.prices.querySelector(".sl-price-more")?.addEventListener("click", () => {
|
|
3049
|
+
this.pricesExpanded = !this.pricesExpanded;
|
|
3050
|
+
this.syncPrices();
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
/** Tap a price row → filter + frame that category on the map; tap again to
|
|
3054
|
+
* clear. Shares `priceBandKeys` with the band selector so the row-dim state
|
|
3055
|
+
* has one source of truth (and each control resets the other). */
|
|
3056
|
+
focusCategory(key) {
|
|
3057
|
+
if (!key) return;
|
|
3058
|
+
const next = this.focusedCatKey === key ? null : key;
|
|
3059
|
+
this.focusedCatKey = next;
|
|
3060
|
+
this.priceBandKeys = next ? /* @__PURE__ */ new Set([next]) : null;
|
|
3061
|
+
const select = this.els.pricesSec?.querySelector(".sl-price-select");
|
|
3062
|
+
if (select) select.value = "all";
|
|
3063
|
+
this.controller.setCategoryFilter(next ? [next] : null);
|
|
3064
|
+
this.controller.focusCategoryFilter(next ? [next] : null);
|
|
3065
|
+
this.syncFloors();
|
|
3066
|
+
this.syncRung();
|
|
3067
|
+
this.refreshMinimap();
|
|
3068
|
+
this.syncPrices();
|
|
3069
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
3007
3070
|
}
|
|
3008
3071
|
/**
|
|
3009
3072
|
* Live-activity strip: turn WS availability deltas into one quiet line of
|
|
@@ -3015,13 +3078,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
3015
3078
|
const textEl = this.els.liveText;
|
|
3016
3079
|
const prev = this.lastCatAvail;
|
|
3017
3080
|
this.lastCatAvail = { ...left };
|
|
3018
|
-
|
|
3081
|
+
const floorId = this.controller.getActiveFloorId();
|
|
3082
|
+
if (floorId !== this.lastAvailFloorId) {
|
|
3083
|
+
this.lastAvailFloorId = floorId;
|
|
3084
|
+
this.availQuietUntil = performance.now() + 2e3;
|
|
3085
|
+
}
|
|
3086
|
+
if (!textEl || !prev || performance.now() < this.availQuietUntil) return;
|
|
3019
3087
|
for (const cat of categories) {
|
|
3020
3088
|
const before = prev[cat.key];
|
|
3021
3089
|
const now = left[cat.key] ?? 0;
|
|
3022
3090
|
if (before === void 0 || now >= before) continue;
|
|
3023
3091
|
const taken = before - now;
|
|
3024
3092
|
textEl.textContent = `${taken} seat${taken === 1 ? "" : "s"} just taken in ${cat.label} \xB7 ${now} left`;
|
|
3093
|
+
this.els.live?.classList.remove("on");
|
|
3094
|
+
void this.els.live?.offsetWidth;
|
|
3095
|
+
this.els.live?.classList.add("on");
|
|
3096
|
+
if (this.liveTimer) clearTimeout(this.liveTimer);
|
|
3097
|
+
this.liveTimer = setTimeout(() => this.els.live?.classList.remove("on"), 8e3);
|
|
3025
3098
|
return;
|
|
3026
3099
|
}
|
|
3027
3100
|
}
|
|
@@ -3049,7 +3122,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3049
3122
|
} else if (!seats.length && !heldItems.length) {
|
|
3050
3123
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
|
|
3051
3124
|
}
|
|
3052
|
-
|
|
3125
|
+
const noPicks = !seats.length && !heldItems.length;
|
|
3126
|
+
if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
3053
3127
|
const cats = this.controller.doc?.categories ?? [];
|
|
3054
3128
|
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>`);
|
|
3055
3129
|
}
|
|
@@ -3613,6 +3687,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3613
3687
|
this.closeSeatView();
|
|
3614
3688
|
this.stopHoldTimer();
|
|
3615
3689
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
3690
|
+
if (this.liveTimer) clearTimeout(this.liveTimer);
|
|
3616
3691
|
for (const timer of this.motionTimers) clearTimeout(timer);
|
|
3617
3692
|
this.motionTimers.clear();
|
|
3618
3693
|
this.ro?.disconnect();
|