@seatlayer/js 0.46.0 → 0.47.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/{hostedCheckout-PLHKPT6W.js → hostedCheckout-SHQCWN5Y.js} +27 -4
- package/dist/hostedCheckout-SHQCWN5Y.js.map +1 -0
- package/dist/index.cjs +389 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -10
- package/dist/index.d.ts +97 -10
- package/dist/index.js +369 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/hostedCheckout-PLHKPT6W.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -197,9 +197,13 @@ function mountCheckout(mount) {
|
|
|
197
197
|
const status = el(
|
|
198
198
|
"p",
|
|
199
199
|
"sl-hco-status",
|
|
200
|
-
`${body.seatCount} ${body.seatCount === 1 ? "seat" : "seats"} confirmed.
|
|
200
|
+
`${body.seatCount} ${body.seatCount === 1 ? "seat" : "seats"} confirmed. Your tickets \u2014 with their door QR codes \u2014 are in your email.`
|
|
201
201
|
);
|
|
202
202
|
const receipt = el("dl", "sl-hco-receipt");
|
|
203
|
+
const seatLabels = (body.tickets ?? []).map((t3) => t3.label);
|
|
204
|
+
if (seatLabels.length) {
|
|
205
|
+
receipt.append(el("dt", void 0, "Seats"), el("dd", void 0, seatLabels.join(", ")));
|
|
206
|
+
}
|
|
203
207
|
receipt.append(
|
|
204
208
|
el("dt", void 0, "Paid"),
|
|
205
209
|
el("dd", void 0, `${body.amountFormatted} ${body.currency}`),
|
|
@@ -209,7 +213,15 @@ function mountCheckout(mount) {
|
|
|
209
213
|
const close = el("button", "sl-hco-back", "Close");
|
|
210
214
|
close.type = "button";
|
|
211
215
|
close.addEventListener("click", cancel);
|
|
212
|
-
|
|
216
|
+
if (body.ticketUrl) {
|
|
217
|
+
const view = el("a", "sl-hco-tickets", "View tickets & QR codes");
|
|
218
|
+
view.href = body.ticketUrl;
|
|
219
|
+
view.target = "_blank";
|
|
220
|
+
view.rel = "noreferrer";
|
|
221
|
+
show(status, receipt, view, close);
|
|
222
|
+
} else {
|
|
223
|
+
show(status, receipt, close);
|
|
224
|
+
}
|
|
213
225
|
mount.onConfirmed(body);
|
|
214
226
|
return;
|
|
215
227
|
}
|
|
@@ -283,6 +295,13 @@ function mountCheckout(mount) {
|
|
|
283
295
|
"sl-hco-note",
|
|
284
296
|
`Your seats are held until ${until}. Payment is handled by ${provider === "razorpay" ? "Razorpay" : "Stripe"} \u2014 we never see your card details.`
|
|
285
297
|
);
|
|
298
|
+
const privacy = el("p", "sl-hco-note");
|
|
299
|
+
privacy.append("The event organizer and SeatLayer use your email to issue and manage your tickets. ");
|
|
300
|
+
const privacyLink = el("a", void 0, "Privacy Policy");
|
|
301
|
+
privacyLink.href = "https://seatlayer.io/privacy/";
|
|
302
|
+
privacyLink.target = "_blank";
|
|
303
|
+
privacyLink.rel = "noreferrer";
|
|
304
|
+
privacy.appendChild(privacyLink);
|
|
286
305
|
const emailValid = () => /.+@.+\..+/.test(email.value.trim());
|
|
287
306
|
email.addEventListener("input", () => {
|
|
288
307
|
pay.disabled = !emailValid();
|
|
@@ -337,7 +356,7 @@ function mountCheckout(mount) {
|
|
|
337
356
|
event.preventDefault();
|
|
338
357
|
if (emailValid()) void start();
|
|
339
358
|
});
|
|
340
|
-
form.append(emailLabel, email, nameLabel, name, pay, back, note);
|
|
359
|
+
form.append(emailLabel, email, nameLabel, name, privacy, pay, back, note);
|
|
341
360
|
const details = () => {
|
|
342
361
|
title.textContent = "Checkout";
|
|
343
362
|
show(summary, form);
|
|
@@ -381,12 +400,16 @@ var init_hostedCheckout = __esm({
|
|
|
381
400
|
.sl-hco-back{width:100%;margin-top:8px;padding:10px;font:inherit;font-size:14px;color:var(--sl-muted);
|
|
382
401
|
background:none;border:0;cursor:pointer;text-decoration:underline}
|
|
383
402
|
.sl-hco-note{margin:12px 0 0;font-size:12px;line-height:1.5;color:var(--sl-muted)}
|
|
403
|
+
.sl-hco-note a{color:inherit;text-decoration:underline;text-underline-offset:2px}
|
|
384
404
|
.sl-hco-status{margin:8px 0 0;font-size:14px;line-height:1.55}
|
|
385
405
|
.sl-hco-error{color:var(--sl-danger, #c0392b)}
|
|
386
406
|
.sl-hco-receipt{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:14px 0 0;font-size:13px}
|
|
387
407
|
.sl-hco-receipt dt{color:var(--sl-muted)}
|
|
388
408
|
.sl-hco-receipt dd{margin:0;text-align:right}
|
|
389
409
|
.sl-hco-ref{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all}
|
|
410
|
+
.sl-hco-tickets{display:block;width:100%;margin-top:14px;padding:12px;font:inherit;font-size:15px;
|
|
411
|
+
font-weight:650;text-align:center;text-decoration:none;color:var(--sl-accent-ink);
|
|
412
|
+
background:var(--sl-accent);border-radius:calc(var(--sl-radius) * .55)}
|
|
390
413
|
`;
|
|
391
414
|
}
|
|
392
415
|
});
|
|
@@ -4167,8 +4190,12 @@ var BuyerRealtimeClient = class {
|
|
|
4167
4190
|
const next = projectionFromSnapshot(frame);
|
|
4168
4191
|
const changes = diffProjections(this.projection, next);
|
|
4169
4192
|
this.projection = next;
|
|
4170
|
-
if (changes === null)
|
|
4171
|
-
|
|
4193
|
+
if (changes === null) {
|
|
4194
|
+
if (this.opts.sink.applyProjection) this.opts.sink.applyProjection(next);
|
|
4195
|
+
else void this.opts.sink.resync();
|
|
4196
|
+
} else if (changes.length) {
|
|
4197
|
+
this.opts.sink.applyStatuses(changes);
|
|
4198
|
+
}
|
|
4172
4199
|
return;
|
|
4173
4200
|
}
|
|
4174
4201
|
if (type === "delta" && Array.isArray(frame.changes)) {
|
|
@@ -4421,7 +4448,7 @@ var PubApi = class {
|
|
|
4421
4448
|
return this.request(`/pub/events/${encodeURIComponent(key)}/chart`);
|
|
4422
4449
|
}
|
|
4423
4450
|
objects(key) {
|
|
4424
|
-
return this.request(`/pub/events/${encodeURIComponent(key)}/objects`);
|
|
4451
|
+
return this.request(`/pub/events/${encodeURIComponent(key)}/objects?compact=1`);
|
|
4425
4452
|
}
|
|
4426
4453
|
hold(key, selections, ttlMs, replaceHoldId) {
|
|
4427
4454
|
return this.request(`/pub/events/${encodeURIComponent(key)}/hold`, {
|
|
@@ -5869,11 +5896,13 @@ var EmbeddedDesigner = class {
|
|
|
5869
5896
|
|
|
5870
5897
|
// src/SeatPicker.ts
|
|
5871
5898
|
var import_core2 = require("@seatlayer/core");
|
|
5899
|
+
var import_panorama = require("@seatlayer/core/view3d/crossfade/panorama");
|
|
5900
|
+
var import_panoramaDelivery = require("@seatlayer/core/view/panoramaDelivery");
|
|
5872
5901
|
var import_meta = {};
|
|
5873
5902
|
var DEFAULT_API_BASE2 = "https://api.seatlayer.io";
|
|
5874
5903
|
var DEFAULT_MAX_SELECTION2 = 10;
|
|
5875
5904
|
var EXTEND_PROMPT_MS = 6e4;
|
|
5876
|
-
var MAX_3D_SEATS_DEFAULT =
|
|
5905
|
+
var MAX_3D_SEATS_DEFAULT = 6e4;
|
|
5877
5906
|
var MAX_3D_SECTION_PILLS = 12;
|
|
5878
5907
|
function pointInPolygon(x, y, poly) {
|
|
5879
5908
|
let inside = false;
|
|
@@ -6217,7 +6246,12 @@ var CSS2 = (
|
|
|
6217
6246
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
6218
6247
|
two pieces of chrome free-float on top of each other. Regions never overlap:
|
|
6219
6248
|
the top strip splits into left/center/right; rails + corners own their edge. */
|
|
6220
|
-
|
|
6249
|
+
/* align-items:center, not the flex default of stretch. Without it a short pill
|
|
6250
|
+
beside a taller control (TEST MODE next to the Map/3D toggle) is stretched to
|
|
6251
|
+
the row's height, so two pills that should read as a matched pair end up
|
|
6252
|
+
different shapes on different baselines. Column regions set their own
|
|
6253
|
+
cross-axis alignment below and are unaffected. */
|
|
6254
|
+
.sl-anchor{position:absolute;z-index:5;display:flex;align-items:center;gap:8px;pointer-events:none}
|
|
6221
6255
|
.sl-anchor > *{pointer-events:auto}
|
|
6222
6256
|
.sl-anchor[data-region="top-left"]{top:12px;left:12px;flex-wrap:wrap;max-width:38%}
|
|
6223
6257
|
.sl-anchor[data-region="top-center"]{top:12px;left:50%;transform:translateX(-50%);flex-direction:column;
|
|
@@ -6233,10 +6267,17 @@ var CSS2 = (
|
|
|
6233
6267
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
6234
6268
|
|
|
6235
6269
|
/* TEST MODE badge \u2014 a small pill in the top-right region (shrinks on narrow) */
|
|
6236
|
-
|
|
6270
|
+
/* align-self:stretch, not a hardcoded height: the badge sits beside the Map/3D
|
|
6271
|
+
toggle, whose height comes from its own border + padding + button metrics.
|
|
6272
|
+
Stretching matches that row exactly and keeps matching if the toggle ever
|
|
6273
|
+
changes, while the badge's own inline-flex keeps the label centred inside
|
|
6274
|
+
whatever height it gets. Alone in the region it simply takes its natural
|
|
6275
|
+
size. */
|
|
6276
|
+
.sl-testbadge{display:inline-flex;align-items:center;align-self:stretch;padding:0 12px;border-radius:999px;
|
|
6277
|
+
font-size:10px;font-weight:800;letter-spacing:.1em;line-height:1;
|
|
6237
6278
|
text-transform:uppercase;white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
6238
6279
|
box-shadow:0 2px 8px rgba(0,0,0,.25)}
|
|
6239
|
-
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:
|
|
6280
|
+
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:0 8px;font-size:8.5px;letter-spacing:.06em}
|
|
6240
6281
|
|
|
6241
6282
|
/* zoom column (flows within the bottom-right region) */
|
|
6242
6283
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -6328,7 +6369,11 @@ var CSS2 = (
|
|
|
6328
6369
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
6329
6370
|
theme sets hideBadge. */
|
|
6330
6371
|
.sl-powered{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:10px;
|
|
6331
|
-
font-size:
|
|
6372
|
+
font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--sl-text);opacity:.72;
|
|
6373
|
+
text-decoration:none;padding:6px 10px;border-radius:999px;width:fit-content;margin-inline:auto;
|
|
6374
|
+
transition:opacity .15s ease,background-color .15s ease}
|
|
6375
|
+
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
6376
|
+
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
6332
6377
|
.sl-powered-mark{width:16px;height:16px;border-radius:4px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
6333
6378
|
background:#0c1220;color:#fcf7ee}
|
|
6334
6379
|
.sl-powered-mark svg{width:12px;height:11px}
|
|
@@ -6507,18 +6552,27 @@ var CSS2 = (
|
|
|
6507
6552
|
.sl-view3d{position:absolute;inset:0;z-index:4;opacity:0;touch-action:none;
|
|
6508
6553
|
transition:opacity .3s ease;background:radial-gradient(120% 120% at 50% 0%,#191f28 0%,#0d1014 70%)}
|
|
6509
6554
|
.sl-view3d canvas{display:block;width:100%;height:100%}
|
|
6555
|
+
.sl-view3d canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
6556
|
+
.sl-view3d-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;
|
|
6557
|
+
z-index:1;color:#d9e2f2;font-size:13px;font-weight:700;letter-spacing:.02em;pointer-events:none}
|
|
6558
|
+
.sl-view3d-loading::before{content:"";width:18px;height:18px;border-radius:50%;
|
|
6559
|
+
border:2px solid rgba(217,226,242,.28);border-top-color:#d9e2f2;animation:slSpin .8s linear infinite}
|
|
6510
6560
|
[data-view3d=on] .sl-chips,[data-view3d=on] .sl-rungs{display:none}
|
|
6511
6561
|
.sl-view3d-back{position:absolute;top:12px;left:12px;z-index:2;display:inline-flex;align-items:center;gap:6px;
|
|
6512
6562
|
padding:7px 13px 7px 9px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.03em;
|
|
6513
6563
|
color:#e6edf3;background:rgba(10,14,20,.62);border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
6514
6564
|
.sl-view3d-back:hover,.sl-view3d-back:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
6515
6565
|
.sl-view3d-back svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
6566
|
+
.sl-view3d-fs{position:absolute;top:12px;right:12px;z-index:2;min-width:44px;min-height:44px;padding:8px 12px;
|
|
6567
|
+
border-radius:999px;font-size:16px;color:#e6edf3;background:rgba(10,14,20,.62);
|
|
6568
|
+
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
6569
|
+
.sl-view3d-fs:hover,.sl-view3d-fs:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
6516
6570
|
/* Venue navigation inside 3D: levels (isolate a floor) and areas (fly to a zone).
|
|
6517
6571
|
Bottom-LEFT, clear of the module's own chips (Overview bottom-right, 360
|
|
6518
6572
|
bottom-centre) and of the bottom-sheeted confirm card. Horizontally scrollable
|
|
6519
6573
|
so a venue with many zones never pushes the rail off a phone screen. */
|
|
6520
6574
|
.sl-view3d-nav{position:absolute;left:12px;bottom:16px;z-index:3;display:flex;flex-direction:column;gap:6px;
|
|
6521
|
-
max-width:calc(100% -
|
|
6575
|
+
max-width:calc(100% - 150px);pointer-events:none}
|
|
6522
6576
|
.sl-view3d-nav > div{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;pointer-events:auto;
|
|
6523
6577
|
padding:1px;-webkit-overflow-scrolling:touch}
|
|
6524
6578
|
.sl-view3d-nav > div::-webkit-scrollbar{display:none}
|
|
@@ -6527,6 +6581,21 @@ var CSS2 = (
|
|
|
6527
6581
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
6528
6582
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
6529
6583
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
6584
|
+
.sl-view3d-nav button:disabled{opacity:.45;cursor:not-allowed}
|
|
6585
|
+
.sl-view3d-nav select{min-height:38px;max-width:min(280px,calc(100vw - 40px));padding:7px 34px 7px 12px;
|
|
6586
|
+
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
6587
|
+
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
6588
|
+
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
6589
|
+
.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,calc(100vw - 180px));overflow:visible}
|
|
6591
|
+
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
6592
|
+
.sl-view3d-locator select{width:100%;min-width:0}
|
|
6593
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:calc(100vw - 24px);overflow-x:auto}
|
|
6594
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
6595
|
+
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
6596
|
+
Keep venue navigation in a separate top rail so those control families never
|
|
6597
|
+
stack over one another. */
|
|
6598
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav{top:68px;bottom:auto;max-width:calc(100% - 24px)}
|
|
6530
6599
|
/* While immersed, the 2D-only chrome is meaningless \u2014 hide it, keep Map|3D. */
|
|
6531
6600
|
.sl-picker[data-view3d="on"] .sl-rungs,
|
|
6532
6601
|
.sl-picker[data-view3d="on"] .sl-floors,
|
|
@@ -6605,7 +6674,6 @@ var CSS2 = (
|
|
|
6605
6674
|
.sl-confirm-thumb-badge{position:absolute;right:7px;top:7px;display:inline-flex;align-items:center;gap:5px;
|
|
6606
6675
|
font-size:10px;font-weight:700;color:#fff;background:rgba(10,14,22,0.72);border-radius:12px;padding:4px 9px;backdrop-filter:blur(3px)}
|
|
6607
6676
|
.sl-confirm-sight{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--sl-muted);margin-bottom:2px}
|
|
6608
|
-
.sl-confirm-sight span{color:#22a06b;font-weight:800}
|
|
6609
6677
|
.sl-confirm-view{width:100%;margin-top:9px;padding:8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
6610
6678
|
color:var(--sl-text);font-weight:700;font-size:12px;display:flex;align-items:center;justify-content:center;gap:7px}
|
|
6611
6679
|
.sl-confirm-view:hover{border-color:var(--sl-muted)}
|
|
@@ -6700,6 +6768,11 @@ var CSS2 = (
|
|
|
6700
6768
|
/* modal host */
|
|
6701
6769
|
.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}
|
|
6702
6770
|
.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)}
|
|
6771
|
+
/* The frame already clips to 16px. A picker rounding itself to --sl-radius
|
|
6772
|
+
(14px) inside it leaves a sliver of scrim showing at each corner, which
|
|
6773
|
+
reads as a rendering fault rather than as a rounded card. One owner of the
|
|
6774
|
+
corners, and it is the frame. */
|
|
6775
|
+
.sl-modal-frame > .sl-picker{border-radius:0}
|
|
6703
6776
|
@media(max-width:640px){.sl-modal-scrim{padding:0}.sl-modal-frame{width:100%;height:100%;border-radius:0}}
|
|
6704
6777
|
`
|
|
6705
6778
|
);
|
|
@@ -6710,6 +6783,17 @@ function ensureStyle2() {
|
|
|
6710
6783
|
el2.textContent = CSS2;
|
|
6711
6784
|
document.head.appendChild(el2);
|
|
6712
6785
|
}
|
|
6786
|
+
function formatWhen(startsAt, timezone, locale) {
|
|
6787
|
+
const options = { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" };
|
|
6788
|
+
const at = new Date(startsAt);
|
|
6789
|
+
if (timezone) {
|
|
6790
|
+
try {
|
|
6791
|
+
return at.toLocaleString(locale, { ...options, timeZone: timezone });
|
|
6792
|
+
} catch {
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
return at.toLocaleString(locale, options);
|
|
6796
|
+
}
|
|
6713
6797
|
function resolveTokens(chart, host) {
|
|
6714
6798
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
6715
6799
|
const accentInk = host?.accentInk ?? chart?.accentInk ?? "#1a1200";
|
|
@@ -6904,6 +6988,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
6904
6988
|
currency: options.currency,
|
|
6905
6989
|
flashOnLiveChange: true,
|
|
6906
6990
|
colorblindSafe: this.cbSafe,
|
|
6991
|
+
// The drawn map's own overrides, when the host supplied them up front.
|
|
6992
|
+
// Everything else on `theme` is CSS and lands on the root instead.
|
|
6993
|
+
mapTheme: options.theme?.map ?? null,
|
|
6907
6994
|
onSelectionChange: () => {
|
|
6908
6995
|
this.syncTray();
|
|
6909
6996
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
@@ -6992,8 +7079,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
6992
7079
|
/**
|
|
6993
7080
|
* Eager sightline preview for the confirm card: the organizer's real view
|
|
6994
7081
|
* photo, or — only when the chart has an actual stage to look at — a generated
|
|
6995
|
-
* forward view plus an
|
|
6996
|
-
* chart both the distance
|
|
7082
|
+
* forward view plus an approximate distance-to-stage line. On a stageless
|
|
7083
|
+
* chart both the distance claim and the generic stage silhouette are
|
|
6997
7084
|
* invented promises, so we suppress them; a real attached photo always shows.
|
|
6998
7085
|
* (OV-52)
|
|
6999
7086
|
*/
|
|
@@ -7012,7 +7099,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7012
7099
|
return "";
|
|
7013
7100
|
}
|
|
7014
7101
|
}
|
|
7015
|
-
const sightHtml = hasStage ? `<div class="sl-confirm-sight"
|
|
7102
|
+
const sightHtml = hasStage && distance != null ? `<div class="sl-confirm-sight">${(0, import_core2.t)("picker.sightline", { m: distance })}</div>` : "";
|
|
7016
7103
|
const viewBtn = realPhoto ? `<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="${realPhoto}" alt="" /><span class="sl-confirm-thumb-badge">\u{1F52D} ${this.tf("picker.viewFromHere", "View from here")}</span></button>` : `<button type="button" class="sl-confirm-view sl-confirm-viewbtn" aria-label="${(0, import_core2.t)("picker.viewFromSeat", { label: seat.label })}"><span aria-hidden="true">\u{1F52D}</span><span>${this.tf("picker.viewFromHere", "View from here")}</span></button>`;
|
|
7017
7104
|
return viewBtn + sightHtml;
|
|
7018
7105
|
}
|
|
@@ -7146,6 +7233,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
7146
7233
|
this.setFsFallback(false);
|
|
7147
7234
|
}
|
|
7148
7235
|
}
|
|
7236
|
+
syncFullscreenButtons() {
|
|
7237
|
+
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
7238
|
+
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
7239
|
+
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
7240
|
+
}
|
|
7149
7241
|
/**
|
|
7150
7242
|
* Native element-fullscreen was unavailable or rejected. When framed, a CSS
|
|
7151
7243
|
* `.sl-fs` overlay can't escape the iframe, so we ask the host page to pin us
|
|
@@ -7160,7 +7252,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7160
7252
|
setFramedFs(on) {
|
|
7161
7253
|
if (this.framedFs === on) return;
|
|
7162
7254
|
this.framedFs = on;
|
|
7163
|
-
this.
|
|
7255
|
+
this.syncFullscreenButtons();
|
|
7164
7256
|
this.postToHost({ type: "seatlayer:fullscreen", on });
|
|
7165
7257
|
if (on && !this.fsEscHandler) {
|
|
7166
7258
|
this.fsEscHandler = (e) => {
|
|
@@ -7177,7 +7269,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7177
7269
|
if (this.fsFallback === on) return;
|
|
7178
7270
|
this.fsFallback = on;
|
|
7179
7271
|
this.root?.classList.toggle("sl-fs", on);
|
|
7180
|
-
this.
|
|
7272
|
+
this.syncFullscreenButtons();
|
|
7181
7273
|
if (on && !this.fsEscHandler) {
|
|
7182
7274
|
this.fsEscHandler = (e) => {
|
|
7183
7275
|
if (e.key === "Escape" && !document.fullscreenElement) this.setFsFallback(false);
|
|
@@ -7367,7 +7459,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7367
7459
|
this.els.zfs.addEventListener("click", () => this.toggleFullscreen());
|
|
7368
7460
|
this.fsChangeHandler = () => {
|
|
7369
7461
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
7370
|
-
this.
|
|
7462
|
+
this.syncFullscreenButtons();
|
|
7371
7463
|
requestAnimationFrame(() => this.controller.zoomToFit());
|
|
7372
7464
|
};
|
|
7373
7465
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
@@ -7459,7 +7551,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7459
7551
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
7460
7552
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
7461
7553
|
this.els.name.textContent = info.eventName ?? "";
|
|
7462
|
-
const when = info.startsAt ?
|
|
7554
|
+
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
7463
7555
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
7464
7556
|
this.buildBadge(chartTheme);
|
|
7465
7557
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -7712,8 +7804,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
7712
7804
|
if (this.badgeHidden(chartTheme)) return;
|
|
7713
7805
|
const foot = this.els.foot;
|
|
7714
7806
|
if (!foot) return;
|
|
7715
|
-
const el2 = document.createElement("
|
|
7807
|
+
const el2 = document.createElement("a");
|
|
7716
7808
|
el2.className = "sl-powered";
|
|
7809
|
+
el2.href = "https://seatlayer.io/?ref=picker";
|
|
7810
|
+
el2.target = "_blank";
|
|
7811
|
+
el2.rel = "noopener noreferrer";
|
|
7812
|
+
el2.setAttribute("aria-label", this.tf("picker.poweredBy", "Powered by SeatLayer"));
|
|
7717
7813
|
el2.innerHTML = `<span class="sl-powered-mark" aria-hidden="true">` + SEATLAYER_ATTRIBUTION_MARK_SVG + `</span><span>${this.tf("picker.poweredBy", "Powered by SeatLayer")}</span>`;
|
|
7718
7814
|
foot.appendChild(el2);
|
|
7719
7815
|
}
|
|
@@ -8572,6 +8668,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8572
8668
|
el2.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
8573
8669
|
el2.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
8574
8670
|
if (this.buyerView === "venue3d") {
|
|
8671
|
+
this.dismissConfirm();
|
|
8575
8672
|
void this.view3dHandle?.flyToSeat(seat.id);
|
|
8576
8673
|
} else {
|
|
8577
8674
|
this.view3dReturnSeat = seat;
|
|
@@ -8650,13 +8747,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
8650
8747
|
const doc = this.controller.doc;
|
|
8651
8748
|
const activeId = this.controller.getActiveFloorId();
|
|
8652
8749
|
const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
|
|
8653
|
-
let
|
|
8750
|
+
let panoSource;
|
|
8654
8751
|
let caption;
|
|
8655
8752
|
let real = false;
|
|
8656
8753
|
if (seat.viewUrl) {
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8754
|
+
const view = {
|
|
8755
|
+
url: seat.viewUrl,
|
|
8756
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
8757
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
8758
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
8759
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
8760
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
8761
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
8762
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
8763
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
8764
|
+
};
|
|
8765
|
+
panoSource = view;
|
|
8766
|
+
caption = (0, import_panorama.seatViewDisclosure)(view);
|
|
8767
|
+
real = (0, import_panorama.isAuthoredSeatView)(view);
|
|
8660
8768
|
} else {
|
|
8661
8769
|
let pano2;
|
|
8662
8770
|
try {
|
|
@@ -8667,7 +8775,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8667
8775
|
return;
|
|
8668
8776
|
}
|
|
8669
8777
|
if (!this.root || !this.seatViewEnabled()) return;
|
|
8670
|
-
|
|
8778
|
+
panoSource = { url: pano2.url, generated: true };
|
|
8671
8779
|
caption = (0, import_core2.t)("picker.illustrationCaption", { m: pano2.distanceM });
|
|
8672
8780
|
}
|
|
8673
8781
|
this.closeSeatView();
|
|
@@ -8679,7 +8787,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
8679
8787
|
this.root.appendChild(el2);
|
|
8680
8788
|
this.viewEl = el2;
|
|
8681
8789
|
const pano = el2.querySelector(".sl-view-pano");
|
|
8682
|
-
|
|
8790
|
+
const delivery = (0, import_panoramaDelivery.planPanoramaDelivery)(panoSource, (0, import_panoramaDelivery.browserPanoramaConstraints)());
|
|
8791
|
+
const loadAbort = new AbortController();
|
|
8792
|
+
pano.style.backgroundImage = `url("${delivery.initialUrl}")`;
|
|
8793
|
+
let cancelUpgrade = () => {
|
|
8794
|
+
};
|
|
8795
|
+
if (delivery.upgradeUrl) {
|
|
8796
|
+
cancelUpgrade = (0, import_panoramaDelivery.schedulePanoramaUpgrade)(() => {
|
|
8797
|
+
void (0, import_panoramaDelivery.loadPanoramaImage)(delivery.upgradeUrl, loadAbort.signal).then(() => {
|
|
8798
|
+
if (!el2.isConnected || loadAbort.signal.aborted) return;
|
|
8799
|
+
pano.style.backgroundImage = `url("${delivery.upgradeUrl}")`;
|
|
8800
|
+
}).catch(() => {
|
|
8801
|
+
});
|
|
8802
|
+
});
|
|
8803
|
+
}
|
|
8683
8804
|
const VFOV_DEG = 70;
|
|
8684
8805
|
const MAX_PITCH_DEG = 35;
|
|
8685
8806
|
let zoom = 1;
|
|
@@ -8741,6 +8862,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
8741
8862
|
el2.addEventListener("keydown", onKey);
|
|
8742
8863
|
closeBtn.focus();
|
|
8743
8864
|
this.viewCleanup = () => {
|
|
8865
|
+
cancelUpgrade();
|
|
8866
|
+
loadAbort.abort();
|
|
8744
8867
|
pano.removeEventListener("pointerdown", onDown);
|
|
8745
8868
|
pano.removeEventListener("pointermove", onMove);
|
|
8746
8869
|
pano.removeEventListener("pointerup", onUp);
|
|
@@ -9544,6 +9667,58 @@ var SeatPicker = class _SeatPicker {
|
|
|
9544
9667
|
getSelection() {
|
|
9545
9668
|
return this.committedSelection();
|
|
9546
9669
|
}
|
|
9670
|
+
/**
|
|
9671
|
+
* Re-ink the DRAWN MAP after mount, so the canvas can follow a page palette
|
|
9672
|
+
* the host did not know at construction time.
|
|
9673
|
+
*
|
|
9674
|
+
* Deliberately narrower than the `theme` option: it takes only the map half.
|
|
9675
|
+
* The chrome half is CSS custom properties, which a host restyles from its
|
|
9676
|
+
* own stylesheet without asking the widget for anything — and a host that
|
|
9677
|
+
* used both mechanisms at once would have two things writing one token. This
|
|
9678
|
+
* method exists for the half CSS genuinely cannot reach: pixels Konva draws.
|
|
9679
|
+
*
|
|
9680
|
+
* The rebuild is the controller's (`setMapTheme`), which repaints statuses
|
|
9681
|
+
* from the map already in memory and restores the selection, so a buyer
|
|
9682
|
+
* mid-pick keeps their seats and no round trip is spent. A no-op when the
|
|
9683
|
+
* colours have not changed; safe to call on every render.
|
|
9684
|
+
*/
|
|
9685
|
+
setMapTheme(map) {
|
|
9686
|
+
if (this.destroyed) return;
|
|
9687
|
+
this.opts.theme = { ...this.opts.theme ?? {}, map: map ?? void 0 };
|
|
9688
|
+
this.controller.setMapTheme(map);
|
|
9689
|
+
}
|
|
9690
|
+
/**
|
|
9691
|
+
* Replace the host pricing override AFTER mount, and repaint everything that
|
|
9692
|
+
* shows a price.
|
|
9693
|
+
*
|
|
9694
|
+
* WHY IT CANNOT JUST BE A MOUNT OPTION. The prices a host knows are often not
|
|
9695
|
+
* the prices it knows AT MOUNT: SeatLayer's own event page learns them from
|
|
9696
|
+
* `GET /pub/events/:key/availability`, a separate cached read that lands a
|
|
9697
|
+
* round trip after the map does — deliberately, because the map is not
|
|
9698
|
+
* allowed to wait on it. Remounting the widget to hand it new options would
|
|
9699
|
+
* tear down a live hold, so the override is settable in place.
|
|
9700
|
+
*
|
|
9701
|
+
* The prices themselves are still the SERVER'S. This method takes an answer;
|
|
9702
|
+
* it never derives one. Nothing here re-reads a release, a window or a quota
|
|
9703
|
+
* — see `paidPrice`, the one funnel every displayed price flows through, and
|
|
9704
|
+
* note that the checkout handoff's line items do not come from here at all:
|
|
9705
|
+
* they are priced by the worker from its own hold. So the worst a wrong call
|
|
9706
|
+
* to this method can do is misprint a price, never mischarge one.
|
|
9707
|
+
*
|
|
9708
|
+
* A no-op when the map is unchanged, so a host may call it on every poll.
|
|
9709
|
+
*/
|
|
9710
|
+
setPricing(pricing) {
|
|
9711
|
+
const before = JSON.stringify(this.opts.pricing ?? null);
|
|
9712
|
+
const after = JSON.stringify(pricing ?? null);
|
|
9713
|
+
if (before === after) return;
|
|
9714
|
+
this.opts.pricing = pricing;
|
|
9715
|
+
if (this.destroyed || !this.els.prices) return;
|
|
9716
|
+
this.els.pricesSec?.querySelector(".sl-price-select")?.remove();
|
|
9717
|
+
this.buildPriceFilter();
|
|
9718
|
+
this.syncPrices();
|
|
9719
|
+
this.syncTray();
|
|
9720
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
9721
|
+
}
|
|
9547
9722
|
/** Current colorblind-safe render state, resolved from the stored buyer
|
|
9548
9723
|
* preference at mount. Host chrome (e.g. the Designer preview) reads this to
|
|
9549
9724
|
* surface the state rather than rendering colorblind colors silently. */
|
|
@@ -9595,10 +9770,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9595
9770
|
*
|
|
9596
9771
|
* Entering `'venue3d'` with `opts.flyToSeatId` runs the cinematic tour: the
|
|
9597
9772
|
* camera flies to the seat and holds in the live scene (a chip offers the
|
|
9598
|
-
* 360° view-from-seat)
|
|
9599
|
-
* the widget enters 3D and auto-flies the camera to that seat, dissolving into
|
|
9600
|
-
* its view-from-seat panorama (the same chain as tapping "See the view from
|
|
9601
|
-
* here" on a seat's confirm card). When the widget is **already** in the 3D
|
|
9773
|
+
* 360° view-from-seat). When the widget is **already** in the 3D
|
|
9602
9774
|
* view, the camera simply flies to the requested seat — the GL scene is not
|
|
9603
9775
|
* torn down or rebuilt, so there is no flash or re-entry.
|
|
9604
9776
|
*
|
|
@@ -9607,7 +9779,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9607
9779
|
*
|
|
9608
9780
|
* @param view `'map'` for the flat picker, `'venue3d'` for the 3D venue.
|
|
9609
9781
|
* @param opts.flyToSeatId When entering (or already in) `'venue3d'`, the seat
|
|
9610
|
-
* id to fly the camera to — cinematic →
|
|
9782
|
+
* id to fly the camera to — cinematic → live seat view.
|
|
9611
9783
|
* Ignored when `view` is `'map'`.
|
|
9612
9784
|
*
|
|
9613
9785
|
* @example
|
|
@@ -9621,7 +9793,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
9621
9793
|
}
|
|
9622
9794
|
const flyToSeatId = opts?.flyToSeatId;
|
|
9623
9795
|
if (this.buyerView === "venue3d") {
|
|
9624
|
-
if (flyToSeatId)
|
|
9796
|
+
if (flyToSeatId) {
|
|
9797
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId: flyToSeatId });
|
|
9798
|
+
void this.view3dHandle?.flyToSeat(flyToSeatId);
|
|
9799
|
+
} else if (opts?.resetView) {
|
|
9800
|
+
this.view3dHandle?.focusOverview();
|
|
9801
|
+
}
|
|
9625
9802
|
return;
|
|
9626
9803
|
}
|
|
9627
9804
|
void this.enter3d(flyToSeatId);
|
|
@@ -9661,14 +9838,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
9661
9838
|
async seatViewFor3d(seatId) {
|
|
9662
9839
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
9663
9840
|
if (!seat) return null;
|
|
9664
|
-
if (seat.viewUrl) return {
|
|
9841
|
+
if (seat.viewUrl) return {
|
|
9842
|
+
url: seat.viewUrl,
|
|
9843
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
9844
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
9845
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
9846
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
9847
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
9848
|
+
...seat.viewMeta?.initialBearingDeg !== void 0 ? { initialBearingDeg: seat.viewMeta.initialBearingDeg } : {},
|
|
9849
|
+
...seat.viewMeta?.initialPitchDeg !== void 0 ? { initialPitchDeg: seat.viewMeta.initialPitchDeg } : {},
|
|
9850
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
9851
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
9852
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
9853
|
+
};
|
|
9665
9854
|
const doc = this.controller.doc;
|
|
9666
9855
|
if (!doc) return null;
|
|
9667
9856
|
const activeId = this.controller.getActiveFloorId();
|
|
9668
9857
|
const focal = seat.focalPoint ?? doc.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc.focalPoint ?? { x: 0, y: 0 };
|
|
9669
9858
|
try {
|
|
9670
9859
|
const { generateSeatPanorama } = await loadPanorama();
|
|
9671
|
-
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url };
|
|
9860
|
+
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url, generated: true };
|
|
9672
9861
|
} catch {
|
|
9673
9862
|
return null;
|
|
9674
9863
|
}
|
|
@@ -9690,14 +9879,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
9690
9879
|
}
|
|
9691
9880
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
9692
9881
|
if (!seat) return;
|
|
9693
|
-
const
|
|
9882
|
+
const table = this.controller.tableSelection(seatId);
|
|
9883
|
+
const already = this.committedSelection().some((s) => table ? s.label === table.label : s.id === seatId);
|
|
9694
9884
|
if (already) {
|
|
9695
9885
|
this.controller.deselect([seatId]);
|
|
9696
9886
|
this.dismissConfirm();
|
|
9697
9887
|
return;
|
|
9698
9888
|
}
|
|
9889
|
+
const added = this.controller.select([seatId]);
|
|
9890
|
+
if (!added.length) {
|
|
9891
|
+
this.syncSelectionTo3d();
|
|
9892
|
+
this.dismissConfirm();
|
|
9893
|
+
return;
|
|
9894
|
+
}
|
|
9895
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId });
|
|
9699
9896
|
this.flashPickedSeat(seatId);
|
|
9700
|
-
|
|
9897
|
+
if (table) {
|
|
9898
|
+
this.showTableDialog(table, false);
|
|
9899
|
+
this.syncSelectionTo3d();
|
|
9900
|
+
return;
|
|
9901
|
+
}
|
|
9701
9902
|
if (this.opts.confirmSelection !== false) this.showConfirm(seat);
|
|
9702
9903
|
else this.syncTray();
|
|
9703
9904
|
}
|
|
@@ -9717,12 +9918,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
9717
9918
|
*/
|
|
9718
9919
|
buildView3dNav(overlay, handle) {
|
|
9719
9920
|
const floors = handle.floors();
|
|
9720
|
-
const
|
|
9721
|
-
const
|
|
9921
|
+
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
9922
|
+
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
9923
|
+
const allSections = handle.sections().filter((s) => s.seatCount > 0);
|
|
9924
|
+
const sections = zones.length > 1 ? [] : allSections;
|
|
9722
9925
|
const wantFloors = floors.length > 1;
|
|
9723
9926
|
const wantZones = zones.length > 1;
|
|
9724
9927
|
const wantSections = sections.length > 1;
|
|
9725
|
-
|
|
9928
|
+
const wantLocator = allSections.length > 0 && handle.rows().length > 0;
|
|
9929
|
+
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
9726
9930
|
const nav = document.createElement("div");
|
|
9727
9931
|
nav.className = "sl-view3d-nav";
|
|
9728
9932
|
if (wantFloors) {
|
|
@@ -9759,15 +9963,112 @@ var SeatPicker = class _SeatPicker {
|
|
|
9759
9963
|
} })) : sections.map((s) => ({ id: s.id, label: s.label || s.id, go: () => {
|
|
9760
9964
|
handle.focusSection(s.id);
|
|
9761
9965
|
} }));
|
|
9762
|
-
|
|
9763
|
-
const
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9966
|
+
if (!wantZones && entries.length > MAX_3D_SECTION_PILLS) {
|
|
9967
|
+
const select = document.createElement("select");
|
|
9968
|
+
select.setAttribute("aria-label", this.tf("picker.jumpToSection", "Jump to section"));
|
|
9969
|
+
const placeholder = document.createElement("option");
|
|
9970
|
+
placeholder.value = "";
|
|
9971
|
+
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
9972
|
+
select.appendChild(placeholder);
|
|
9973
|
+
for (const entry of entries) {
|
|
9974
|
+
const option = document.createElement("option");
|
|
9975
|
+
option.value = entry.id;
|
|
9976
|
+
option.textContent = entry.label;
|
|
9977
|
+
select.appendChild(option);
|
|
9978
|
+
}
|
|
9979
|
+
select.addEventListener("change", () => {
|
|
9980
|
+
entries.find((entry) => entry.id === select.value)?.go();
|
|
9981
|
+
});
|
|
9982
|
+
row.appendChild(select);
|
|
9983
|
+
} else {
|
|
9984
|
+
for (const e of entries) {
|
|
9985
|
+
const b = document.createElement("button");
|
|
9986
|
+
b.type = "button";
|
|
9987
|
+
b.textContent = e.label;
|
|
9988
|
+
b.addEventListener("click", e.go);
|
|
9989
|
+
row.appendChild(b);
|
|
9990
|
+
}
|
|
9768
9991
|
}
|
|
9769
9992
|
nav.appendChild(row);
|
|
9770
9993
|
}
|
|
9994
|
+
if (wantLocator) {
|
|
9995
|
+
const locator = document.createElement("div");
|
|
9996
|
+
locator.className = "sl-view3d-locator";
|
|
9997
|
+
locator.setAttribute("role", "group");
|
|
9998
|
+
locator.setAttribute("aria-label", "Find an exact seat in 3D");
|
|
9999
|
+
const sectionSelect = document.createElement("select");
|
|
10000
|
+
sectionSelect.setAttribute("aria-label", "Choose section in 3D");
|
|
10001
|
+
const rowSelect = document.createElement("select");
|
|
10002
|
+
rowSelect.setAttribute("aria-label", "Choose row in 3D");
|
|
10003
|
+
const seatSelect = document.createElement("select");
|
|
10004
|
+
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
10005
|
+
const view = document.createElement("button");
|
|
10006
|
+
view.type = "button";
|
|
10007
|
+
view.textContent = "View seat";
|
|
10008
|
+
view.disabled = true;
|
|
10009
|
+
const fill = (select, placeholder, entries) => {
|
|
10010
|
+
select.replaceChildren();
|
|
10011
|
+
const first = document.createElement("option");
|
|
10012
|
+
first.value = "";
|
|
10013
|
+
first.textContent = placeholder;
|
|
10014
|
+
select.appendChild(first);
|
|
10015
|
+
for (const entry of entries) {
|
|
10016
|
+
const option = document.createElement("option");
|
|
10017
|
+
option.value = entry.id;
|
|
10018
|
+
option.textContent = entry.label;
|
|
10019
|
+
select.appendChild(option);
|
|
10020
|
+
}
|
|
10021
|
+
select.value = "";
|
|
10022
|
+
};
|
|
10023
|
+
fill(sectionSelect, "1. Section", allSections.map((section) => ({
|
|
10024
|
+
id: section.id,
|
|
10025
|
+
label: `${section.label} \xB7 ${section.seatCount.toLocaleString()} seats`
|
|
10026
|
+
})));
|
|
10027
|
+
fill(rowSelect, "2. Row", []);
|
|
10028
|
+
fill(seatSelect, "3. Seat", []);
|
|
10029
|
+
rowSelect.disabled = true;
|
|
10030
|
+
seatSelect.disabled = true;
|
|
10031
|
+
sectionSelect.addEventListener("change", () => {
|
|
10032
|
+
const sectionId = sectionSelect.value;
|
|
10033
|
+
view.disabled = true;
|
|
10034
|
+
fill(seatSelect, "3. Seat", []);
|
|
10035
|
+
seatSelect.disabled = true;
|
|
10036
|
+
if (!sectionId || !handle.focusSection(sectionId)) {
|
|
10037
|
+
fill(rowSelect, "2. Row", []);
|
|
10038
|
+
rowSelect.disabled = true;
|
|
10039
|
+
return;
|
|
10040
|
+
}
|
|
10041
|
+
const rows = handle.rows(sectionId);
|
|
10042
|
+
fill(rowSelect, "2. Row", rows.map((row) => ({
|
|
10043
|
+
id: row.id,
|
|
10044
|
+
label: `${row.label} \xB7 ${row.seatCount} seats`
|
|
10045
|
+
})));
|
|
10046
|
+
rowSelect.disabled = rows.length === 0;
|
|
10047
|
+
});
|
|
10048
|
+
rowSelect.addEventListener("change", () => {
|
|
10049
|
+
const rowId = rowSelect.value;
|
|
10050
|
+
view.disabled = true;
|
|
10051
|
+
if (!rowId || !handle.focusRow(rowId)) {
|
|
10052
|
+
fill(seatSelect, "3. Seat", []);
|
|
10053
|
+
seatSelect.disabled = true;
|
|
10054
|
+
return;
|
|
10055
|
+
}
|
|
10056
|
+
const seats = handle.seatsInRow(rowId);
|
|
10057
|
+
fill(seatSelect, "3. Seat", seats);
|
|
10058
|
+
seatSelect.disabled = seats.length === 0;
|
|
10059
|
+
});
|
|
10060
|
+
seatSelect.addEventListener("change", () => {
|
|
10061
|
+
const seatId = seatSelect.value;
|
|
10062
|
+
view.disabled = !seatId;
|
|
10063
|
+
handle.setSelection(seatId ? [seatId] : []);
|
|
10064
|
+
});
|
|
10065
|
+
view.addEventListener("click", () => {
|
|
10066
|
+
const seatId = seatSelect.value;
|
|
10067
|
+
if (seatId) void handle.flyToSeat(seatId);
|
|
10068
|
+
});
|
|
10069
|
+
locator.append(sectionSelect, rowSelect, seatSelect, view);
|
|
10070
|
+
nav.appendChild(locator);
|
|
10071
|
+
}
|
|
9771
10072
|
overlay.appendChild(nav);
|
|
9772
10073
|
}
|
|
9773
10074
|
async enter3d(flySeatId) {
|
|
@@ -9775,6 +10076,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9775
10076
|
const doc = this.controller.doc;
|
|
9776
10077
|
if (!doc) return;
|
|
9777
10078
|
this.buyerView = "venue3d";
|
|
10079
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", ...flySeatId ? { seatId: flySeatId } : {} });
|
|
9778
10080
|
this.root?.setAttribute("data-view3d", "on");
|
|
9779
10081
|
this.dismissConfirm();
|
|
9780
10082
|
this.syncProjection();
|
|
@@ -9788,6 +10090,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
9788
10090
|
back.innerHTML = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg><span>${this.tf("picker.backToMap", "Back to map")}</span>`;
|
|
9789
10091
|
back.addEventListener("click", () => this.exit3d());
|
|
9790
10092
|
overlay.appendChild(back);
|
|
10093
|
+
const fullscreen = document.createElement("button");
|
|
10094
|
+
fullscreen.type = "button";
|
|
10095
|
+
fullscreen.className = "sl-view3d-fs";
|
|
10096
|
+
fullscreen.textContent = "\u26F6";
|
|
10097
|
+
fullscreen.setAttribute("aria-label", "Full screen");
|
|
10098
|
+
fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
|
|
10099
|
+
fullscreen.addEventListener("click", () => this.toggleFullscreen());
|
|
10100
|
+
overlay.appendChild(fullscreen);
|
|
10101
|
+
const loading = document.createElement("div");
|
|
10102
|
+
loading.className = "sl-view3d-loading";
|
|
10103
|
+
loading.setAttribute("role", "status");
|
|
10104
|
+
loading.setAttribute("aria-live", "polite");
|
|
10105
|
+
loading.textContent = this.tf("picker.loading3d", "Building the 3D venue\u2026");
|
|
10106
|
+
overlay.appendChild(loading);
|
|
9791
10107
|
this.els.map.appendChild(overlay);
|
|
9792
10108
|
this.view3dEl = overlay;
|
|
9793
10109
|
requestAnimationFrame(() => {
|
|
@@ -9798,8 +10114,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
9798
10114
|
const seats = (0, import_core2.expandChart)(doc);
|
|
9799
10115
|
const mod = await loadVenue3d();
|
|
9800
10116
|
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
9801
|
-
const
|
|
10117
|
+
const prepared = await mod.prepareVenue3D({ doc, seats });
|
|
10118
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
10119
|
+
const handle = mod.mountVenue3D(overlay, { doc, seats, prepared }, {
|
|
10120
|
+
// A strict contain fit turns a wide bowl into a postage stamp inside a
|
|
10121
|
+
// phone. The bounded portrait fit was validated against every UX-lab
|
|
10122
|
+
// fixture; keep editor previews strict while making buyer seats legible.
|
|
10123
|
+
portraitOverviewCrop: true,
|
|
9802
10124
|
onSeatPick: (id) => this.onView3dSeatPick(id),
|
|
10125
|
+
onSectionFocusChange: (sectionId) => {
|
|
10126
|
+
const select = overlay.querySelector(
|
|
10127
|
+
'select[aria-label="Choose section in 3D"]'
|
|
10128
|
+
);
|
|
10129
|
+
const next = sectionId ?? "";
|
|
10130
|
+
if (!select || select.value === next) return;
|
|
10131
|
+
select.value = next;
|
|
10132
|
+
select.dispatchEvent(new Event("change"));
|
|
10133
|
+
},
|
|
10134
|
+
onViewTargetChange: (seatId) => {
|
|
10135
|
+
overlay.querySelector(".sl-view3d-nav")?.classList.toggle("is-seat-focused", !!seatId);
|
|
10136
|
+
this.opts.onBuyerViewChange?.({
|
|
10137
|
+
view: "venue3d",
|
|
10138
|
+
...seatId ? { seatId } : {}
|
|
10139
|
+
});
|
|
10140
|
+
},
|
|
9803
10141
|
// Deferred off the tap gesture: generateSeatPanorama walks every seat
|
|
9804
10142
|
// (O(n) on a 13k chart), and the module prefetches at pick — by the
|
|
9805
10143
|
// time the flight lands (~2.5s) the idle render has long finished. A
|
|
@@ -9819,12 +10157,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
9819
10157
|
onAnalytics: (event, props) => this.emit3dAnalytics(event, props)
|
|
9820
10158
|
});
|
|
9821
10159
|
this.view3dHandle = handle;
|
|
10160
|
+
loading.remove();
|
|
9822
10161
|
this.pushAvailabilityTo3d();
|
|
9823
10162
|
this.syncSelectionTo3d();
|
|
9824
10163
|
this.buildView3dNav(overlay, handle);
|
|
9825
10164
|
if (flySeatId) void handle.flyToSeat(flySeatId);
|
|
9826
10165
|
} catch (err) {
|
|
10166
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
9827
10167
|
this.opts.onError?.(err);
|
|
10168
|
+
this.toast(this.tf("picker.unavailable3d", "3D could not start. The seat map is still available."), "warning");
|
|
9828
10169
|
this.exit3d();
|
|
9829
10170
|
}
|
|
9830
10171
|
}
|
|
@@ -9832,6 +10173,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9832
10173
|
if (this.buyerView !== "venue3d" && !this.view3dEl) return;
|
|
9833
10174
|
this.view3dGen++;
|
|
9834
10175
|
this.buyerView = "map";
|
|
10176
|
+
this.opts.onBuyerViewChange?.({ view: "map" });
|
|
9835
10177
|
this.root?.removeAttribute("data-view3d");
|
|
9836
10178
|
try {
|
|
9837
10179
|
this.view3dHandle?.dispose();
|