@seatlayer/js 0.47.0 → 0.47.2
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 +330 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -2
- package/dist/index.d.ts +91 -2
- package/dist/index.js +327 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -3957,11 +3957,13 @@ __export(index_exports, {
|
|
|
3957
3957
|
markerOf: () => markerOf,
|
|
3958
3958
|
mutationCount: () => mutationCount,
|
|
3959
3959
|
needsMoveConfirmation: () => needsMoveConfirmation,
|
|
3960
|
+
parseTicketOfferAvailability: () => parseTicketOfferAvailability,
|
|
3960
3961
|
planAssignment: () => planAssignment,
|
|
3961
3962
|
retryAfterCopy: () => retryAfterCopy,
|
|
3962
3963
|
selectionSources: () => selectionSources,
|
|
3963
3964
|
stateBadge: () => stateBadge,
|
|
3964
|
-
suggestMarker: () => suggestMarker
|
|
3965
|
+
suggestMarker: () => suggestMarker,
|
|
3966
|
+
ticketOfferPrices: () => ticketOfferPrices
|
|
3965
3967
|
});
|
|
3966
3968
|
module.exports = __toCommonJS(index_exports);
|
|
3967
3969
|
|
|
@@ -4497,6 +4499,10 @@ var PubApi = class {
|
|
|
4497
4499
|
paymentOptions(key) {
|
|
4498
4500
|
return this.request(`/pub/events/${encodeURIComponent(key)}/payment-options`);
|
|
4499
4501
|
}
|
|
4502
|
+
/** Server-resolved active ticket offers and category prices. */
|
|
4503
|
+
availability(key, live = false) {
|
|
4504
|
+
return this.request(`/pub/events/${encodeURIComponent(key)}/availability${live ? "?live=1" : ""}`);
|
|
4505
|
+
}
|
|
4500
4506
|
/**
|
|
4501
4507
|
* Turn a live hold into an order and start a payment.
|
|
4502
4508
|
*
|
|
@@ -4865,7 +4871,6 @@ var SeatingChart = class {
|
|
|
4865
4871
|
this.mount = null;
|
|
4866
4872
|
this.hostEl = null;
|
|
4867
4873
|
this.rendered = false;
|
|
4868
|
-
this.retryBtn = null;
|
|
4869
4874
|
this.mode_ = null;
|
|
4870
4875
|
this.tipEl = null;
|
|
4871
4876
|
this.tipPos = { x: 0, y: 0 };
|
|
@@ -4994,7 +4999,7 @@ var SeatingChart = class {
|
|
|
4994
4999
|
this.tipEl.style.display = "none";
|
|
4995
5000
|
return;
|
|
4996
5001
|
}
|
|
4997
|
-
const
|
|
5002
|
+
const money2 = (() => {
|
|
4998
5003
|
try {
|
|
4999
5004
|
return new Intl.NumberFormat(void 0, { style: "currency", currency: details.currency }).format(details.price);
|
|
5000
5005
|
} catch {
|
|
@@ -5002,7 +5007,7 @@ var SeatingChart = class {
|
|
|
5002
5007
|
}
|
|
5003
5008
|
})();
|
|
5004
5009
|
const statusLine = details.status === "free" ? "" : `<div style="margin-top:5px;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:#fca5a5;font-weight:700">${details.status === "held" ? (0, import_core.t)("map.statusHeld") : (0, import_core.t)("map.statusTaken")}</div>`;
|
|
5005
|
-
this.tipEl.innerHTML = `<div style="font-weight:700;font-size:13px">${details.label}</div><div style="display:flex;align-items:center;gap:6px;margin-top:4px;color:#c7cddc"><span style="width:9px;height:9px;border-radius:50%;flex:none;background:${details.categoryColor}"></span><span>${details.categoryLabel}</span><span style="margin-left:auto;font-weight:700;color:#fff">${
|
|
5010
|
+
this.tipEl.innerHTML = `<div style="font-weight:700;font-size:13px">${details.label}</div><div style="display:flex;align-items:center;gap:6px;margin-top:4px;color:#c7cddc"><span style="width:9px;height:9px;border-radius:50%;flex:none;background:${details.categoryColor}"></span><span>${details.categoryLabel}</span><span style="margin-left:auto;font-weight:700;color:#fff">${money2}</span></div>` + statusLine;
|
|
5006
5011
|
this.tipEl.style.display = "block";
|
|
5007
5012
|
this.placeTooltip();
|
|
5008
5013
|
}
|
|
@@ -5240,14 +5245,12 @@ var SeatingChart = class {
|
|
|
5240
5245
|
void this.render().catch((err) => this.opts.onError?.(err));
|
|
5241
5246
|
});
|
|
5242
5247
|
box.appendChild(btn);
|
|
5243
|
-
this.retryBtn = btn;
|
|
5244
5248
|
host.appendChild(box);
|
|
5245
5249
|
}
|
|
5246
5250
|
destroy() {
|
|
5247
5251
|
this.realtime?.stop();
|
|
5248
5252
|
this.realtime = null;
|
|
5249
5253
|
this.access?.clear();
|
|
5250
|
-
this.retryBtn = null;
|
|
5251
5254
|
if (this.hostEl && this.onTipMove) this.hostEl.removeEventListener("mousemove", this.onTipMove);
|
|
5252
5255
|
this.tipEl = null;
|
|
5253
5256
|
this.onTipMove = null;
|
|
@@ -5898,6 +5901,136 @@ var EmbeddedDesigner = class {
|
|
|
5898
5901
|
var import_core2 = require("@seatlayer/core");
|
|
5899
5902
|
var import_panorama = require("@seatlayer/core/view3d/crossfade/panorama");
|
|
5900
5903
|
var import_panoramaDelivery = require("@seatlayer/core/view/panoramaDelivery");
|
|
5904
|
+
|
|
5905
|
+
// src/offerAvailability.ts
|
|
5906
|
+
var SALE_STATES = [
|
|
5907
|
+
"on-sale",
|
|
5908
|
+
"low",
|
|
5909
|
+
"sold-out",
|
|
5910
|
+
"presale",
|
|
5911
|
+
"closed"
|
|
5912
|
+
];
|
|
5913
|
+
function money(value) {
|
|
5914
|
+
if (value === null || value === void 0) return null;
|
|
5915
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5916
|
+
}
|
|
5917
|
+
function timestamp(value) {
|
|
5918
|
+
if (value === null || value === void 0) return null;
|
|
5919
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5920
|
+
}
|
|
5921
|
+
function parseSummary(value) {
|
|
5922
|
+
if (value == null) return null;
|
|
5923
|
+
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
5924
|
+
const source = value;
|
|
5925
|
+
const count = source.count;
|
|
5926
|
+
const index = source.index;
|
|
5927
|
+
if (typeof index !== "number" || !Number.isInteger(index) || index < 1) return void 0;
|
|
5928
|
+
if (typeof count !== "number" || !Number.isInteger(count) || count < index) return void 0;
|
|
5929
|
+
const remaining = source.remaining;
|
|
5930
|
+
if (remaining != null && (typeof remaining !== "number" || !Number.isInteger(remaining) || remaining < 0)) {
|
|
5931
|
+
return void 0;
|
|
5932
|
+
}
|
|
5933
|
+
const result = {
|
|
5934
|
+
index,
|
|
5935
|
+
count,
|
|
5936
|
+
remaining: remaining == null ? null : remaining
|
|
5937
|
+
};
|
|
5938
|
+
if (source.id !== void 0) {
|
|
5939
|
+
if (typeof source.id !== "string" || !source.id.trim()) return void 0;
|
|
5940
|
+
result.id = source.id.trim();
|
|
5941
|
+
}
|
|
5942
|
+
if (source.name !== void 0) {
|
|
5943
|
+
if (typeof source.name !== "string" || !source.name.trim()) return void 0;
|
|
5944
|
+
result.name = source.name.trim();
|
|
5945
|
+
}
|
|
5946
|
+
if (source.categoryKey !== void 0) {
|
|
5947
|
+
if (source.categoryKey !== null && (typeof source.categoryKey !== "string" || !source.categoryKey.trim())) {
|
|
5948
|
+
return void 0;
|
|
5949
|
+
}
|
|
5950
|
+
result.categoryKey = source.categoryKey == null ? null : source.categoryKey.trim();
|
|
5951
|
+
}
|
|
5952
|
+
for (const key of ["startsAt", "endsAt"]) {
|
|
5953
|
+
if (source[key] === void 0) continue;
|
|
5954
|
+
const parsed = timestamp(source[key]);
|
|
5955
|
+
if (parsed === void 0) return void 0;
|
|
5956
|
+
result[key] = parsed;
|
|
5957
|
+
}
|
|
5958
|
+
return result;
|
|
5959
|
+
}
|
|
5960
|
+
function parseTicketOfferAvailability(body) {
|
|
5961
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
5962
|
+
const raw = body;
|
|
5963
|
+
const state = SALE_STATES.find((candidate) => candidate === raw.state);
|
|
5964
|
+
if (!state) return null;
|
|
5965
|
+
const fromPrice = money(raw.fromPrice);
|
|
5966
|
+
const previousPrice = money(raw.previousPrice);
|
|
5967
|
+
if (fromPrice === void 0 || previousPrice === void 0) return null;
|
|
5968
|
+
const currency = raw.currency == null ? null : typeof raw.currency === "string" && raw.currency.trim() ? raw.currency.trim() : void 0;
|
|
5969
|
+
if (currency === void 0) return null;
|
|
5970
|
+
const release = parseSummary(raw.release);
|
|
5971
|
+
if (release === void 0) return null;
|
|
5972
|
+
const upcoming = raw.upcoming === void 0 ? null : parseSummary(raw.upcoming);
|
|
5973
|
+
if (upcoming === void 0) return null;
|
|
5974
|
+
let prices = [];
|
|
5975
|
+
if (raw.prices != null) {
|
|
5976
|
+
if (!Array.isArray(raw.prices)) return null;
|
|
5977
|
+
const parsed = [];
|
|
5978
|
+
for (const entry of raw.prices) {
|
|
5979
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
5980
|
+
const row = entry;
|
|
5981
|
+
const categoryKey = typeof row.categoryKey === "string" ? row.categoryKey.trim() : "";
|
|
5982
|
+
if (!categoryKey) return null;
|
|
5983
|
+
const price = money(row.price);
|
|
5984
|
+
const previous = money(row.previousPrice);
|
|
5985
|
+
if (price === void 0 || price === null || previous === void 0) return null;
|
|
5986
|
+
const item = { categoryKey, price, previousPrice: previous };
|
|
5987
|
+
if (row.offerId !== void 0) {
|
|
5988
|
+
if (typeof row.offerId !== "string" || !row.offerId.trim()) return null;
|
|
5989
|
+
item.offerId = row.offerId.trim();
|
|
5990
|
+
}
|
|
5991
|
+
if (row.offerName !== void 0) {
|
|
5992
|
+
if (typeof row.offerName !== "string" || !row.offerName.trim()) return null;
|
|
5993
|
+
item.offerName = row.offerName.trim();
|
|
5994
|
+
}
|
|
5995
|
+
if (row.remaining !== void 0) {
|
|
5996
|
+
if (row.remaining !== null && (typeof row.remaining !== "number" || !Number.isInteger(row.remaining) || row.remaining < 0)) return null;
|
|
5997
|
+
item.remaining = row.remaining == null ? null : row.remaining;
|
|
5998
|
+
}
|
|
5999
|
+
for (const key of ["startsAt", "endsAt"]) {
|
|
6000
|
+
if (row[key] === void 0) continue;
|
|
6001
|
+
const at = timestamp(row[key]);
|
|
6002
|
+
if (at === void 0) return null;
|
|
6003
|
+
item[key] = at;
|
|
6004
|
+
}
|
|
6005
|
+
parsed.push(item);
|
|
6006
|
+
}
|
|
6007
|
+
prices = parsed;
|
|
6008
|
+
}
|
|
6009
|
+
return { state, fromPrice, previousPrice, currency, release, upcoming, prices };
|
|
6010
|
+
}
|
|
6011
|
+
function nextOfferTransitionAt(availability, now) {
|
|
6012
|
+
if (!availability) return null;
|
|
6013
|
+
let next = null;
|
|
6014
|
+
const consider = (at) => {
|
|
6015
|
+
if (at != null && at > now && (next === null || at < next)) next = at;
|
|
6016
|
+
};
|
|
6017
|
+
for (const summary of [availability.release, availability.upcoming]) {
|
|
6018
|
+
consider(summary?.startsAt);
|
|
6019
|
+
consider(summary?.endsAt);
|
|
6020
|
+
}
|
|
6021
|
+
for (const price of availability.prices) {
|
|
6022
|
+
consider(price.startsAt);
|
|
6023
|
+
consider(price.endsAt);
|
|
6024
|
+
}
|
|
6025
|
+
return next;
|
|
6026
|
+
}
|
|
6027
|
+
function ticketOfferPrices(availability) {
|
|
6028
|
+
const map = {};
|
|
6029
|
+
for (const entry of availability?.prices ?? []) map[entry.categoryKey] = entry.price;
|
|
6030
|
+
return map;
|
|
6031
|
+
}
|
|
6032
|
+
|
|
6033
|
+
// src/SeatPicker.ts
|
|
5901
6034
|
var import_meta = {};
|
|
5902
6035
|
var DEFAULT_API_BASE2 = "https://api.seatlayer.io";
|
|
5903
6036
|
var DEFAULT_MAX_SELECTION2 = 10;
|
|
@@ -6119,6 +6252,15 @@ var CSS2 = (
|
|
|
6119
6252
|
.sl-cbbtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
6120
6253
|
|
|
6121
6254
|
/* price panel \u2014 one compact filter control replaces the wrapping price-chip row. */
|
|
6255
|
+
.sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
|
|
6256
|
+
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
6257
|
+
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
6258
|
+
.sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:9px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-accent)}
|
|
6259
|
+
.sl-offer-name{display:block;margin-top:2px;font-size:13px;font-weight:800;line-height:1.3}.sl-offer-line{display:block;margin-top:3px;font-size:11px;line-height:1.35;color:var(--sl-muted)}
|
|
6260
|
+
.sl-offer-info{position:relative;flex:none}.sl-offer-info>summary{list-style:none;width:25px;height:25px;border:1px solid var(--sl-line);border-radius:999px;
|
|
6261
|
+
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
6262
|
+
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
6263
|
+
.sl-offer-detail{margin-top:10px;padding-top:9px;border-top:1px solid var(--sl-line);font-size:10.5px;line-height:1.45;color:var(--sl-muted)}
|
|
6122
6264
|
.sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
6123
6265
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
6124
6266
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
@@ -6129,6 +6271,7 @@ var CSS2 = (
|
|
|
6129
6271
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
6130
6272
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
6131
6273
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
6274
|
+
.sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:9px;font-weight:750}
|
|
6132
6275
|
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
6133
6276
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
6134
6277
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
@@ -6570,7 +6713,21 @@ var CSS2 = (
|
|
|
6570
6713
|
/* Venue navigation inside 3D: levels (isolate a floor) and areas (fly to a zone).
|
|
6571
6714
|
Bottom-LEFT, clear of the module's own chips (Overview bottom-right, 360
|
|
6572
6715
|
bottom-centre) and of the bottom-sheeted confirm card. Horizontally scrollable
|
|
6573
|
-
so a venue with many zones never pushes the rail off a phone screen.
|
|
6716
|
+
so a venue with many zones never pushes the rail off a phone screen.
|
|
6717
|
+
|
|
6718
|
+
EVERY WIDTH HERE IS RELATIVE TO THE RAIL, NEVER TO THE WINDOW. These three
|
|
6719
|
+
rules used to size off 100vw, which contradicts the one contract this widget
|
|
6720
|
+
states about itself: it adapts to a full-screen takeover, an inline div in a
|
|
6721
|
+
content page, or a popup, and its breakpoints key off the CONTAINER, never the
|
|
6722
|
+
viewport. A 390 px picker embedded in a 1400 px page asked for
|
|
6723
|
+
calc(100vw - 180px) = 1220 inside a 390 px rail.
|
|
6724
|
+
|
|
6725
|
+
MEASURED, IT DID NOT OVERFLOW: the scroll parent is a flex container, so the
|
|
6726
|
+
over-large declaration was shrunk back to the rail and both the old and new
|
|
6727
|
+
rules resolve to 364 px in situ. So this is a latent correctness fix, not a
|
|
6728
|
+
visible bug -- the numbers were wrong and were being covered for by a
|
|
6729
|
+
flex-shrink one level up. Left as 100% because the next person to change that
|
|
6730
|
+
parent's display should not inherit a rule that only works by accident. */
|
|
6574
6731
|
.sl-view3d-nav{position:absolute;left:12px;bottom:16px;z-index:3;display:flex;flex-direction:column;gap:6px;
|
|
6575
6732
|
max-width:calc(100% - 150px);pointer-events:none}
|
|
6576
6733
|
.sl-view3d-nav > div{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;pointer-events:auto;
|
|
@@ -6582,15 +6739,15 @@ var CSS2 = (
|
|
|
6582
6739
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
6583
6740
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
6584
6741
|
.sl-view3d-nav button:disabled{opacity:.45;cursor:not-allowed}
|
|
6585
|
-
.sl-view3d-nav select{min-height:38px;max-width:
|
|
6742
|
+
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
6586
6743
|
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
6587
6744
|
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
6588
6745
|
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
6589
6746
|
.sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
|
|
6590
|
-
width:min(720px,
|
|
6747
|
+
width:min(720px,100%);overflow:visible}
|
|
6591
6748
|
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
6592
6749
|
.sl-view3d-locator select{width:100%;min-width:0}
|
|
6593
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:
|
|
6750
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:100%;overflow-x:auto}
|
|
6594
6751
|
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
6595
6752
|
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
6596
6753
|
Keep venue navigation in a separate top rail so those control families never
|
|
@@ -6840,10 +6997,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
6840
6997
|
this.ro = null;
|
|
6841
6998
|
this.holdTimer = null;
|
|
6842
6999
|
this.toastTimer = null;
|
|
7000
|
+
this.offerRefreshTimer = null;
|
|
7001
|
+
/** Armed only when the offer schedule has a known future transition (or as a
|
|
7002
|
+
* bounded retry after a failed read) — never a fixed-cadence poll. */
|
|
7003
|
+
this.offerBoundaryTimer = null;
|
|
7004
|
+
this.offerVisibilityHandler = null;
|
|
6843
7005
|
/** Short-lived UI motion timers; all are cancelled on destroy. */
|
|
6844
7006
|
this.motionTimers = /* @__PURE__ */ new Set();
|
|
6845
7007
|
// state
|
|
6846
7008
|
this.currency = "USD";
|
|
7009
|
+
this.eventTimezone = null;
|
|
7010
|
+
this.offerAvailability = null;
|
|
6847
7011
|
this.hold = null;
|
|
6848
7012
|
/** Latest server expiry for the open hold (moves on extend). */
|
|
6849
7013
|
this.holdExpiresAt = 0;
|
|
@@ -6957,6 +7121,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6957
7121
|
if (!options.event || typeof options.event !== "string") throw new Error("seatmap: `event` key is required");
|
|
6958
7122
|
if (!options.container) throw new Error("seatmap: `container` is required (or use SeatPicker.open())");
|
|
6959
7123
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
7124
|
+
this.hostPricing = options.pricing;
|
|
6960
7125
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
6961
7126
|
this.access = options.transport ? null : createBuyerAccessContext(options, {
|
|
6962
7127
|
onExpired: (event) => {
|
|
@@ -6998,6 +7163,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6998
7163
|
},
|
|
6999
7164
|
onStatusChange: () => {
|
|
7000
7165
|
this.syncPrices();
|
|
7166
|
+
this.scheduleOfferRefresh(true);
|
|
7001
7167
|
this.evictTakenSelections();
|
|
7002
7168
|
this.detectBooked();
|
|
7003
7169
|
this.refreshMinimap();
|
|
@@ -7419,6 +7585,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7419
7585
|
</div>
|
|
7420
7586
|
<div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
|
|
7421
7587
|
<div class="sl-filters" data-ref="filters"></div>
|
|
7588
|
+
<div class="sl-offer" data-ref="offer" role="status" aria-live="polite"></div>
|
|
7422
7589
|
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
|
|
7423
7590
|
<div class="sl-prices" data-ref="prices"></div>
|
|
7424
7591
|
<div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText">Live availability \u2014 seats update in real time</span></div>
|
|
@@ -7439,6 +7606,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
7439
7606
|
this.els[el2.dataset.ref] = el2;
|
|
7440
7607
|
});
|
|
7441
7608
|
this.mapHost = this.els.map;
|
|
7609
|
+
void this.refreshOfferAvailability(false);
|
|
7610
|
+
if (this.api.availability) {
|
|
7611
|
+
this.offerVisibilityHandler = () => {
|
|
7612
|
+
if (!document.hidden && !this.destroyed) void this.refreshOfferAvailability(false);
|
|
7613
|
+
};
|
|
7614
|
+
document.addEventListener("visibilitychange", this.offerVisibilityHandler);
|
|
7615
|
+
}
|
|
7442
7616
|
const applyLayout = () => {
|
|
7443
7617
|
const w = root.clientWidth;
|
|
7444
7618
|
if (w <= 0) return;
|
|
@@ -7476,10 +7650,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
7476
7650
|
e.stopPropagation();
|
|
7477
7651
|
setSheet(root.dataset.sheet !== "open");
|
|
7478
7652
|
});
|
|
7653
|
+
this.els.peek?.addEventListener("click", (e) => {
|
|
7654
|
+
const go = e.target.closest(".sl-sheet-go");
|
|
7655
|
+
if (!go) return;
|
|
7656
|
+
e.stopPropagation();
|
|
7657
|
+
if (go.dataset.act === "checkout") void this.handleCta();
|
|
7658
|
+
else setSheet(true);
|
|
7659
|
+
});
|
|
7479
7660
|
let startY = 0;
|
|
7480
7661
|
let swiped = false;
|
|
7481
7662
|
let tracking = false;
|
|
7482
7663
|
head.addEventListener("pointerdown", (e) => {
|
|
7664
|
+
if (e.target.closest?.(".sl-seccard,.sl-sheet-toggle,.sl-sheet-go")) {
|
|
7665
|
+
tracking = false;
|
|
7666
|
+
return;
|
|
7667
|
+
}
|
|
7483
7668
|
tracking = true;
|
|
7484
7669
|
swiped = false;
|
|
7485
7670
|
startY = e.clientY;
|
|
@@ -7498,7 +7683,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7498
7683
|
});
|
|
7499
7684
|
head.addEventListener("pointerup", (e) => {
|
|
7500
7685
|
if (tracking && !swiped && Math.abs(e.clientY - startY) < 6) {
|
|
7501
|
-
|
|
7686
|
+
setSheet(root.dataset.sheet !== "open");
|
|
7502
7687
|
}
|
|
7503
7688
|
tracking = false;
|
|
7504
7689
|
head.releasePointerCapture?.(e.pointerId);
|
|
@@ -7547,6 +7732,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7547
7732
|
const chartTheme = this.controller.doc?.theme;
|
|
7548
7733
|
Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
7549
7734
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
7735
|
+
this.eventTimezone = info.timezone ?? null;
|
|
7550
7736
|
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl;
|
|
7551
7737
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
7552
7738
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
@@ -7692,12 +7878,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
7692
7878
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
7693
7879
|
const filters = this.els.filters;
|
|
7694
7880
|
if (filters) {
|
|
7881
|
+
if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
|
|
7695
7882
|
if (narrow) {
|
|
7696
7883
|
if (this.a11yChipsEl) filters.appendChild(this.a11yChipsEl);
|
|
7697
|
-
if (this.cbEl) filters.appendChild(this.cbEl);
|
|
7698
7884
|
} else {
|
|
7699
7885
|
if (this.a11yChipsEl) this.regions["top-left"]?.appendChild(this.a11yChipsEl);
|
|
7700
|
-
if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
|
|
7701
7886
|
}
|
|
7702
7887
|
const has = narrow && filters.children.length > 0;
|
|
7703
7888
|
filters.classList.toggle("has", has);
|
|
@@ -8888,6 +9073,119 @@ var SeatPicker = class _SeatPicker {
|
|
|
8888
9073
|
return `${n} ${this.currency}`;
|
|
8889
9074
|
}
|
|
8890
9075
|
}
|
|
9076
|
+
/**
|
|
9077
|
+
* Sleep until the offer schedule's next known transition, then re-read.
|
|
9078
|
+
*
|
|
9079
|
+
* A far-away boundary is capped: the wake re-reads, learns the (unchanged)
|
|
9080
|
+
* schedule, and re-arms — so a picker left open for days still tracks an
|
|
9081
|
+
* organizer's schedule edits at a cost of one request every few hours. No
|
|
9082
|
+
* future transition means no timer at all; an event with no releases does
|
|
9083
|
+
* zero background traffic. A wake in a hidden tab fetches nothing — the
|
|
9084
|
+
* visibilitychange handler owns catching that tab up.
|
|
9085
|
+
*/
|
|
9086
|
+
scheduleOfferBoundary(availability) {
|
|
9087
|
+
if (this.offerBoundaryTimer) clearTimeout(this.offerBoundaryTimer);
|
|
9088
|
+
this.offerBoundaryTimer = null;
|
|
9089
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9090
|
+
const now = Date.now();
|
|
9091
|
+
const boundary = nextOfferTransitionAt(availability, now);
|
|
9092
|
+
if (boundary == null) return;
|
|
9093
|
+
const MAX_SLEEP_MS = 6 * 36e5;
|
|
9094
|
+
const delay = Math.min(Math.max(boundary - now + 1e3, 1e3), MAX_SLEEP_MS);
|
|
9095
|
+
this.offerBoundaryTimer = setTimeout(() => {
|
|
9096
|
+
this.offerBoundaryTimer = null;
|
|
9097
|
+
if (document.hidden) return;
|
|
9098
|
+
void this.refreshOfferAvailability(false);
|
|
9099
|
+
}, delay);
|
|
9100
|
+
}
|
|
9101
|
+
/** Debounce the no-store offer read behind a burst of seat-status frames. */
|
|
9102
|
+
scheduleOfferRefresh(live) {
|
|
9103
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9104
|
+
if (this.offerRefreshTimer) clearTimeout(this.offerRefreshTimer);
|
|
9105
|
+
this.offerRefreshTimer = setTimeout(() => {
|
|
9106
|
+
this.offerRefreshTimer = null;
|
|
9107
|
+
void this.refreshOfferAvailability(live);
|
|
9108
|
+
}, live ? 180 : 0);
|
|
9109
|
+
}
|
|
9110
|
+
/**
|
|
9111
|
+
* Pull the server's resolved answer. A failed refresh keeps the last truthful
|
|
9112
|
+
* answer: flashing back to a chart price while checkout still charges an
|
|
9113
|
+
* offer is worse than a temporarily stale remaining count.
|
|
9114
|
+
*/
|
|
9115
|
+
async refreshOfferAvailability(live) {
|
|
9116
|
+
if (!this.api.availability || this.destroyed) return;
|
|
9117
|
+
try {
|
|
9118
|
+
const body = await this.api.availability(this.opts.event, live);
|
|
9119
|
+
if (this.destroyed) return;
|
|
9120
|
+
const availability = parseTicketOfferAvailability(body);
|
|
9121
|
+
if (!availability) return;
|
|
9122
|
+
this.offerAvailability = availability;
|
|
9123
|
+
this.scheduleOfferBoundary(availability);
|
|
9124
|
+
const server = ticketOfferPrices(availability);
|
|
9125
|
+
const merged = { ...this.hostPricing?.prices ?? {}, ...server };
|
|
9126
|
+
const pricing = Object.keys(merged).length > 0 || this.hostPricing?.formatter ? { prices: merged, ...this.hostPricing?.formatter ? { formatter: this.hostPricing.formatter } : {} } : void 0;
|
|
9127
|
+
this.setPricing(pricing);
|
|
9128
|
+
this.syncOffer();
|
|
9129
|
+
this.opts.onOfferAvailabilityChange?.(availability);
|
|
9130
|
+
} catch {
|
|
9131
|
+
if (!this.destroyed && !this.offerBoundaryTimer && !document.hidden) {
|
|
9132
|
+
this.offerBoundaryTimer = setTimeout(() => {
|
|
9133
|
+
this.offerBoundaryTimer = null;
|
|
9134
|
+
if (document.hidden) return;
|
|
9135
|
+
void this.refreshOfferAvailability(false);
|
|
9136
|
+
}, 3e4);
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
}
|
|
9140
|
+
offerPrice(categoryKey) {
|
|
9141
|
+
if (!categoryKey) return null;
|
|
9142
|
+
return this.offerAvailability?.prices.find((entry) => entry.categoryKey === categoryKey) ?? null;
|
|
9143
|
+
}
|
|
9144
|
+
/** The compact current/upcoming offer card above Ticket prices. */
|
|
9145
|
+
syncOffer() {
|
|
9146
|
+
const host = this.els.offer;
|
|
9147
|
+
if (!host) return;
|
|
9148
|
+
const availability = this.offerAvailability;
|
|
9149
|
+
const active = availability?.release ?? null;
|
|
9150
|
+
const upcoming = !active ? availability?.upcoming ?? null : null;
|
|
9151
|
+
if (!availability || availability.state === "closed" || availability.state === "sold-out" || !active && !upcoming) {
|
|
9152
|
+
host.classList.remove("has");
|
|
9153
|
+
host.replaceChildren();
|
|
9154
|
+
return;
|
|
9155
|
+
}
|
|
9156
|
+
const offer = active ?? upcoming;
|
|
9157
|
+
const main = document.createElement("div");
|
|
9158
|
+
main.className = "sl-offer-main";
|
|
9159
|
+
const copy = document.createElement("div");
|
|
9160
|
+
copy.className = "sl-offer-copy";
|
|
9161
|
+
const kicker = document.createElement("span");
|
|
9162
|
+
kicker.className = "sl-offer-kicker";
|
|
9163
|
+
kicker.textContent = active ? "Current ticket offer" : "Upcoming ticket offer";
|
|
9164
|
+
const name = document.createElement("strong");
|
|
9165
|
+
name.className = "sl-offer-name";
|
|
9166
|
+
name.textContent = offer.name || (active ? "Current offer" : "Scheduled offer");
|
|
9167
|
+
const line = document.createElement("span");
|
|
9168
|
+
line.className = "sl-offer-line";
|
|
9169
|
+
const facts = [];
|
|
9170
|
+
if (active && availability.fromPrice != null) facts.push(this.money(availability.fromPrice / 100));
|
|
9171
|
+
if (active && offer.remaining != null) facts.push(`${offer.remaining} available`);
|
|
9172
|
+
if (active && offer.endsAt != null) facts.push(`until ${formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale)}`);
|
|
9173
|
+
if (upcoming?.startsAt != null) facts.push(`starts ${formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale)}`);
|
|
9174
|
+
line.textContent = facts.join(" \xB7 ");
|
|
9175
|
+
copy.append(kicker, name, line);
|
|
9176
|
+
const info = document.createElement("details");
|
|
9177
|
+
info.className = "sl-offer-info";
|
|
9178
|
+
const summary = document.createElement("summary");
|
|
9179
|
+
summary.setAttribute("aria-label", `How the ${name.textContent} offer works`);
|
|
9180
|
+
summary.textContent = "i";
|
|
9181
|
+
const detail = document.createElement("div");
|
|
9182
|
+
detail.className = "sl-offer-detail";
|
|
9183
|
+
detail.textContent = active ? `This price applies automatically to eligible seats. Tickets in active carts temporarily reduce the available quantity; released or expired holds return it. When the offer ends, the next matching offer or normal ticket price takes over.` : `Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.`;
|
|
9184
|
+
info.append(summary, detail);
|
|
9185
|
+
main.append(copy, info);
|
|
9186
|
+
host.replaceChildren(main);
|
|
9187
|
+
host.classList.add("has");
|
|
9188
|
+
}
|
|
8891
9189
|
/**
|
|
8892
9190
|
* The price the buyer will actually pay for a category (+tier): the host's
|
|
8893
9191
|
* `pricing` override when present, else the chart's stored price. Every
|
|
@@ -8914,9 +9212,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
8914
9212
|
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
8915
9213
|
this.els.prices.innerHTML = shown.map((c) => {
|
|
8916
9214
|
const price = this.catPrice(c);
|
|
9215
|
+
const offer = this.offerPrice(c.key);
|
|
9216
|
+
const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
|
|
8917
9217
|
const active = this.focusedCatKey === c.key;
|
|
8918
9218
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
8919
|
-
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>`;
|
|
9219
|
+
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? "Show all seats" : `Show ${c.label} seats on the map`)}"><span class="sl-dot" style="background:${escapeOption(c.color)}"></span><span class="sl-price-label">${escapeOption(c.label)}` + (offer?.offerName ? `<small class="sl-price-offer">${escapeOption(offer.offerName)}</small>` : "") + `</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (previous != null ? `<span class="sl-price-was">${escapeOption(this.money(previous))}</span>` : "") + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
8920
9220
|
}).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>`;
|
|
8921
9221
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
8922
9222
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
@@ -9228,10 +9528,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
9228
9528
|
if (previousCount === 0 && count > 0) this.animateOnce(this.els.cta, "sl-ready", 520);
|
|
9229
9529
|
if (this.els.peek) {
|
|
9230
9530
|
if (count) {
|
|
9231
|
-
|
|
9531
|
+
const holding = !!this.hold && !pendingCount;
|
|
9532
|
+
this.els.peek.innerHTML = `<span>${count} ${count === 1 ? "ticket" : "tickets"} \xB7 ${this.money(total)}</span><button type="button" class="go sl-sheet-go" data-act="${holding ? "checkout" : "open"}">${this.hold ? pendingCount ? "Secure more" : "Continue" : "Review"}</button>`;
|
|
9232
9533
|
} else {
|
|
9233
9534
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
9234
|
-
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<
|
|
9535
|
+
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<button type="button" class="go sl-sheet-go" data-act="open">\u2726 Best seats</button>`;
|
|
9235
9536
|
}
|
|
9236
9537
|
}
|
|
9237
9538
|
this.lastTrayCount = count;
|
|
@@ -9571,6 +9872,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9571
9872
|
}
|
|
9572
9873
|
emitHoldChange() {
|
|
9573
9874
|
const hold = this.hold;
|
|
9875
|
+
this.scheduleOfferRefresh(true);
|
|
9574
9876
|
this.opts.onHoldChange?.(
|
|
9575
9877
|
hold,
|
|
9576
9878
|
hold?.seats ?? [],
|
|
@@ -10300,6 +10602,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
10300
10602
|
flashOnLiveChange: true,
|
|
10301
10603
|
onStatusChange: () => {
|
|
10302
10604
|
this.syncPrices();
|
|
10605
|
+
this.scheduleOfferRefresh(true);
|
|
10303
10606
|
this.detectBooked();
|
|
10304
10607
|
this.refreshMinimap();
|
|
10305
10608
|
this.pushAvailabilityTo3d();
|
|
@@ -10435,6 +10738,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
10435
10738
|
this.stopHoldTimer();
|
|
10436
10739
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
10437
10740
|
if (this.liveTimer) clearTimeout(this.liveTimer);
|
|
10741
|
+
if (this.offerRefreshTimer) clearTimeout(this.offerRefreshTimer);
|
|
10742
|
+
if (this.offerBoundaryTimer) clearTimeout(this.offerBoundaryTimer);
|
|
10743
|
+
this.offerRefreshTimer = null;
|
|
10744
|
+
this.offerBoundaryTimer = null;
|
|
10745
|
+
if (this.offerVisibilityHandler) {
|
|
10746
|
+
document.removeEventListener("visibilitychange", this.offerVisibilityHandler);
|
|
10747
|
+
this.offerVisibilityHandler = null;
|
|
10748
|
+
}
|
|
10438
10749
|
for (const timer of this.motionTimers) clearTimeout(timer);
|
|
10439
10750
|
this.motionTimers.clear();
|
|
10440
10751
|
this.ro?.disconnect();
|
|
@@ -13109,10 +13420,12 @@ init_manageApi();
|
|
|
13109
13420
|
markerOf,
|
|
13110
13421
|
mutationCount,
|
|
13111
13422
|
needsMoveConfirmation,
|
|
13423
|
+
parseTicketOfferAvailability,
|
|
13112
13424
|
planAssignment,
|
|
13113
13425
|
retryAfterCopy,
|
|
13114
13426
|
selectionSources,
|
|
13115
13427
|
stateBadge,
|
|
13116
|
-
suggestMarker
|
|
13428
|
+
suggestMarker,
|
|
13429
|
+
ticketOfferPrices
|
|
13117
13430
|
});
|
|
13118
13431
|
//# sourceMappingURL=index.cjs.map
|