@seatlayer/js 0.46.0 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{hostedCheckout-PLHKPT6W.js → hostedCheckout-SHQCWN5Y.js} +27 -4
- package/dist/hostedCheckout-SHQCWN5Y.js.map +1 -0
- package/dist/index.cjs +420 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -11
- package/dist/index.d.ts +97 -11
- package/dist/index.js +400 -54
- 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`, {
|
|
@@ -4838,7 +4865,6 @@ var SeatingChart = class {
|
|
|
4838
4865
|
this.mount = null;
|
|
4839
4866
|
this.hostEl = null;
|
|
4840
4867
|
this.rendered = false;
|
|
4841
|
-
this.retryBtn = null;
|
|
4842
4868
|
this.mode_ = null;
|
|
4843
4869
|
this.tipEl = null;
|
|
4844
4870
|
this.tipPos = { x: 0, y: 0 };
|
|
@@ -5213,14 +5239,12 @@ var SeatingChart = class {
|
|
|
5213
5239
|
void this.render().catch((err) => this.opts.onError?.(err));
|
|
5214
5240
|
});
|
|
5215
5241
|
box.appendChild(btn);
|
|
5216
|
-
this.retryBtn = btn;
|
|
5217
5242
|
host.appendChild(box);
|
|
5218
5243
|
}
|
|
5219
5244
|
destroy() {
|
|
5220
5245
|
this.realtime?.stop();
|
|
5221
5246
|
this.realtime = null;
|
|
5222
5247
|
this.access?.clear();
|
|
5223
|
-
this.retryBtn = null;
|
|
5224
5248
|
if (this.hostEl && this.onTipMove) this.hostEl.removeEventListener("mousemove", this.onTipMove);
|
|
5225
5249
|
this.tipEl = null;
|
|
5226
5250
|
this.onTipMove = null;
|
|
@@ -5869,11 +5893,13 @@ var EmbeddedDesigner = class {
|
|
|
5869
5893
|
|
|
5870
5894
|
// src/SeatPicker.ts
|
|
5871
5895
|
var import_core2 = require("@seatlayer/core");
|
|
5896
|
+
var import_panorama = require("@seatlayer/core/view3d/crossfade/panorama");
|
|
5897
|
+
var import_panoramaDelivery = require("@seatlayer/core/view/panoramaDelivery");
|
|
5872
5898
|
var import_meta = {};
|
|
5873
5899
|
var DEFAULT_API_BASE2 = "https://api.seatlayer.io";
|
|
5874
5900
|
var DEFAULT_MAX_SELECTION2 = 10;
|
|
5875
5901
|
var EXTEND_PROMPT_MS = 6e4;
|
|
5876
|
-
var MAX_3D_SEATS_DEFAULT =
|
|
5902
|
+
var MAX_3D_SEATS_DEFAULT = 6e4;
|
|
5877
5903
|
var MAX_3D_SECTION_PILLS = 12;
|
|
5878
5904
|
function pointInPolygon(x, y, poly) {
|
|
5879
5905
|
let inside = false;
|
|
@@ -6217,7 +6243,12 @@ var CSS2 = (
|
|
|
6217
6243
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
6218
6244
|
two pieces of chrome free-float on top of each other. Regions never overlap:
|
|
6219
6245
|
the top strip splits into left/center/right; rails + corners own their edge. */
|
|
6220
|
-
|
|
6246
|
+
/* align-items:center, not the flex default of stretch. Without it a short pill
|
|
6247
|
+
beside a taller control (TEST MODE next to the Map/3D toggle) is stretched to
|
|
6248
|
+
the row's height, so two pills that should read as a matched pair end up
|
|
6249
|
+
different shapes on different baselines. Column regions set their own
|
|
6250
|
+
cross-axis alignment below and are unaffected. */
|
|
6251
|
+
.sl-anchor{position:absolute;z-index:5;display:flex;align-items:center;gap:8px;pointer-events:none}
|
|
6221
6252
|
.sl-anchor > *{pointer-events:auto}
|
|
6222
6253
|
.sl-anchor[data-region="top-left"]{top:12px;left:12px;flex-wrap:wrap;max-width:38%}
|
|
6223
6254
|
.sl-anchor[data-region="top-center"]{top:12px;left:50%;transform:translateX(-50%);flex-direction:column;
|
|
@@ -6233,10 +6264,17 @@ var CSS2 = (
|
|
|
6233
6264
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
6234
6265
|
|
|
6235
6266
|
/* TEST MODE badge \u2014 a small pill in the top-right region (shrinks on narrow) */
|
|
6236
|
-
|
|
6267
|
+
/* align-self:stretch, not a hardcoded height: the badge sits beside the Map/3D
|
|
6268
|
+
toggle, whose height comes from its own border + padding + button metrics.
|
|
6269
|
+
Stretching matches that row exactly and keeps matching if the toggle ever
|
|
6270
|
+
changes, while the badge's own inline-flex keeps the label centred inside
|
|
6271
|
+
whatever height it gets. Alone in the region it simply takes its natural
|
|
6272
|
+
size. */
|
|
6273
|
+
.sl-testbadge{display:inline-flex;align-items:center;align-self:stretch;padding:0 12px;border-radius:999px;
|
|
6274
|
+
font-size:10px;font-weight:800;letter-spacing:.1em;line-height:1;
|
|
6237
6275
|
text-transform:uppercase;white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
6238
6276
|
box-shadow:0 2px 8px rgba(0,0,0,.25)}
|
|
6239
|
-
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:
|
|
6277
|
+
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:0 8px;font-size:8.5px;letter-spacing:.06em}
|
|
6240
6278
|
|
|
6241
6279
|
/* zoom column (flows within the bottom-right region) */
|
|
6242
6280
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -6328,7 +6366,11 @@ var CSS2 = (
|
|
|
6328
6366
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
6329
6367
|
theme sets hideBadge. */
|
|
6330
6368
|
.sl-powered{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:10px;
|
|
6331
|
-
font-size:
|
|
6369
|
+
font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--sl-text);opacity:.72;
|
|
6370
|
+
text-decoration:none;padding:6px 10px;border-radius:999px;width:fit-content;margin-inline:auto;
|
|
6371
|
+
transition:opacity .15s ease,background-color .15s ease}
|
|
6372
|
+
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
6373
|
+
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
6332
6374
|
.sl-powered-mark{width:16px;height:16px;border-radius:4px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
6333
6375
|
background:#0c1220;color:#fcf7ee}
|
|
6334
6376
|
.sl-powered-mark svg{width:12px;height:11px}
|
|
@@ -6507,18 +6549,41 @@ var CSS2 = (
|
|
|
6507
6549
|
.sl-view3d{position:absolute;inset:0;z-index:4;opacity:0;touch-action:none;
|
|
6508
6550
|
transition:opacity .3s ease;background:radial-gradient(120% 120% at 50% 0%,#191f28 0%,#0d1014 70%)}
|
|
6509
6551
|
.sl-view3d canvas{display:block;width:100%;height:100%}
|
|
6552
|
+
.sl-view3d canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
6553
|
+
.sl-view3d-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;
|
|
6554
|
+
z-index:1;color:#d9e2f2;font-size:13px;font-weight:700;letter-spacing:.02em;pointer-events:none}
|
|
6555
|
+
.sl-view3d-loading::before{content:"";width:18px;height:18px;border-radius:50%;
|
|
6556
|
+
border:2px solid rgba(217,226,242,.28);border-top-color:#d9e2f2;animation:slSpin .8s linear infinite}
|
|
6510
6557
|
[data-view3d=on] .sl-chips,[data-view3d=on] .sl-rungs{display:none}
|
|
6511
6558
|
.sl-view3d-back{position:absolute;top:12px;left:12px;z-index:2;display:inline-flex;align-items:center;gap:6px;
|
|
6512
6559
|
padding:7px 13px 7px 9px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.03em;
|
|
6513
6560
|
color:#e6edf3;background:rgba(10,14,20,.62);border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
6514
6561
|
.sl-view3d-back:hover,.sl-view3d-back:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
6515
6562
|
.sl-view3d-back svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
6563
|
+
.sl-view3d-fs{position:absolute;top:12px;right:12px;z-index:2;min-width:44px;min-height:44px;padding:8px 12px;
|
|
6564
|
+
border-radius:999px;font-size:16px;color:#e6edf3;background:rgba(10,14,20,.62);
|
|
6565
|
+
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
6566
|
+
.sl-view3d-fs:hover,.sl-view3d-fs:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
6516
6567
|
/* Venue navigation inside 3D: levels (isolate a floor) and areas (fly to a zone).
|
|
6517
6568
|
Bottom-LEFT, clear of the module's own chips (Overview bottom-right, 360
|
|
6518
6569
|
bottom-centre) and of the bottom-sheeted confirm card. Horizontally scrollable
|
|
6519
|
-
so a venue with many zones never pushes the rail off a phone screen.
|
|
6570
|
+
so a venue with many zones never pushes the rail off a phone screen.
|
|
6571
|
+
|
|
6572
|
+
EVERY WIDTH HERE IS RELATIVE TO THE RAIL, NEVER TO THE WINDOW. These three
|
|
6573
|
+
rules used to size off 100vw, which contradicts the one contract this widget
|
|
6574
|
+
states about itself: it adapts to a full-screen takeover, an inline div in a
|
|
6575
|
+
content page, or a popup, and its breakpoints key off the CONTAINER, never the
|
|
6576
|
+
viewport. A 390 px picker embedded in a 1400 px page asked for
|
|
6577
|
+
calc(100vw - 180px) = 1220 inside a 390 px rail.
|
|
6578
|
+
|
|
6579
|
+
MEASURED, IT DID NOT OVERFLOW: the scroll parent is a flex container, so the
|
|
6580
|
+
over-large declaration was shrunk back to the rail and both the old and new
|
|
6581
|
+
rules resolve to 364 px in situ. So this is a latent correctness fix, not a
|
|
6582
|
+
visible bug -- the numbers were wrong and were being covered for by a
|
|
6583
|
+
flex-shrink one level up. Left as 100% because the next person to change that
|
|
6584
|
+
parent's display should not inherit a rule that only works by accident. */
|
|
6520
6585
|
.sl-view3d-nav{position:absolute;left:12px;bottom:16px;z-index:3;display:flex;flex-direction:column;gap:6px;
|
|
6521
|
-
max-width:calc(100% -
|
|
6586
|
+
max-width:calc(100% - 150px);pointer-events:none}
|
|
6522
6587
|
.sl-view3d-nav > div{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;pointer-events:auto;
|
|
6523
6588
|
padding:1px;-webkit-overflow-scrolling:touch}
|
|
6524
6589
|
.sl-view3d-nav > div::-webkit-scrollbar{display:none}
|
|
@@ -6527,6 +6592,21 @@ var CSS2 = (
|
|
|
6527
6592
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
6528
6593
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
6529
6594
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
6595
|
+
.sl-view3d-nav button:disabled{opacity:.45;cursor:not-allowed}
|
|
6596
|
+
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
6597
|
+
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
6598
|
+
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
6599
|
+
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
6600
|
+
.sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
|
|
6601
|
+
width:min(720px,100%);overflow:visible}
|
|
6602
|
+
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
6603
|
+
.sl-view3d-locator select{width:100%;min-width:0}
|
|
6604
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:100%;overflow-x:auto}
|
|
6605
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
6606
|
+
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
6607
|
+
Keep venue navigation in a separate top rail so those control families never
|
|
6608
|
+
stack over one another. */
|
|
6609
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav{top:68px;bottom:auto;max-width:calc(100% - 24px)}
|
|
6530
6610
|
/* While immersed, the 2D-only chrome is meaningless \u2014 hide it, keep Map|3D. */
|
|
6531
6611
|
.sl-picker[data-view3d="on"] .sl-rungs,
|
|
6532
6612
|
.sl-picker[data-view3d="on"] .sl-floors,
|
|
@@ -6605,7 +6685,6 @@ var CSS2 = (
|
|
|
6605
6685
|
.sl-confirm-thumb-badge{position:absolute;right:7px;top:7px;display:inline-flex;align-items:center;gap:5px;
|
|
6606
6686
|
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
6687
|
.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
6688
|
.sl-confirm-view{width:100%;margin-top:9px;padding:8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
6610
6689
|
color:var(--sl-text);font-weight:700;font-size:12px;display:flex;align-items:center;justify-content:center;gap:7px}
|
|
6611
6690
|
.sl-confirm-view:hover{border-color:var(--sl-muted)}
|
|
@@ -6700,6 +6779,11 @@ var CSS2 = (
|
|
|
6700
6779
|
/* modal host */
|
|
6701
6780
|
.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
6781
|
.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)}
|
|
6782
|
+
/* The frame already clips to 16px. A picker rounding itself to --sl-radius
|
|
6783
|
+
(14px) inside it leaves a sliver of scrim showing at each corner, which
|
|
6784
|
+
reads as a rendering fault rather than as a rounded card. One owner of the
|
|
6785
|
+
corners, and it is the frame. */
|
|
6786
|
+
.sl-modal-frame > .sl-picker{border-radius:0}
|
|
6703
6787
|
@media(max-width:640px){.sl-modal-scrim{padding:0}.sl-modal-frame{width:100%;height:100%;border-radius:0}}
|
|
6704
6788
|
`
|
|
6705
6789
|
);
|
|
@@ -6710,6 +6794,17 @@ function ensureStyle2() {
|
|
|
6710
6794
|
el2.textContent = CSS2;
|
|
6711
6795
|
document.head.appendChild(el2);
|
|
6712
6796
|
}
|
|
6797
|
+
function formatWhen(startsAt, timezone, locale) {
|
|
6798
|
+
const options = { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" };
|
|
6799
|
+
const at = new Date(startsAt);
|
|
6800
|
+
if (timezone) {
|
|
6801
|
+
try {
|
|
6802
|
+
return at.toLocaleString(locale, { ...options, timeZone: timezone });
|
|
6803
|
+
} catch {
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
return at.toLocaleString(locale, options);
|
|
6807
|
+
}
|
|
6713
6808
|
function resolveTokens(chart, host) {
|
|
6714
6809
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
6715
6810
|
const accentInk = host?.accentInk ?? chart?.accentInk ?? "#1a1200";
|
|
@@ -6904,6 +6999,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
6904
6999
|
currency: options.currency,
|
|
6905
7000
|
flashOnLiveChange: true,
|
|
6906
7001
|
colorblindSafe: this.cbSafe,
|
|
7002
|
+
// The drawn map's own overrides, when the host supplied them up front.
|
|
7003
|
+
// Everything else on `theme` is CSS and lands on the root instead.
|
|
7004
|
+
mapTheme: options.theme?.map ?? null,
|
|
6907
7005
|
onSelectionChange: () => {
|
|
6908
7006
|
this.syncTray();
|
|
6909
7007
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
@@ -6992,8 +7090,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
6992
7090
|
/**
|
|
6993
7091
|
* Eager sightline preview for the confirm card: the organizer's real view
|
|
6994
7092
|
* 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
|
|
7093
|
+
* forward view plus an approximate distance-to-stage line. On a stageless
|
|
7094
|
+
* chart both the distance claim and the generic stage silhouette are
|
|
6997
7095
|
* invented promises, so we suppress them; a real attached photo always shows.
|
|
6998
7096
|
* (OV-52)
|
|
6999
7097
|
*/
|
|
@@ -7012,7 +7110,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7012
7110
|
return "";
|
|
7013
7111
|
}
|
|
7014
7112
|
}
|
|
7015
|
-
const sightHtml = hasStage ? `<div class="sl-confirm-sight"
|
|
7113
|
+
const sightHtml = hasStage && distance != null ? `<div class="sl-confirm-sight">${(0, import_core2.t)("picker.sightline", { m: distance })}</div>` : "";
|
|
7016
7114
|
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
7115
|
return viewBtn + sightHtml;
|
|
7018
7116
|
}
|
|
@@ -7146,6 +7244,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
7146
7244
|
this.setFsFallback(false);
|
|
7147
7245
|
}
|
|
7148
7246
|
}
|
|
7247
|
+
syncFullscreenButtons() {
|
|
7248
|
+
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
7249
|
+
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
7250
|
+
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
7251
|
+
}
|
|
7149
7252
|
/**
|
|
7150
7253
|
* Native element-fullscreen was unavailable or rejected. When framed, a CSS
|
|
7151
7254
|
* `.sl-fs` overlay can't escape the iframe, so we ask the host page to pin us
|
|
@@ -7160,7 +7263,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7160
7263
|
setFramedFs(on) {
|
|
7161
7264
|
if (this.framedFs === on) return;
|
|
7162
7265
|
this.framedFs = on;
|
|
7163
|
-
this.
|
|
7266
|
+
this.syncFullscreenButtons();
|
|
7164
7267
|
this.postToHost({ type: "seatlayer:fullscreen", on });
|
|
7165
7268
|
if (on && !this.fsEscHandler) {
|
|
7166
7269
|
this.fsEscHandler = (e) => {
|
|
@@ -7177,7 +7280,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7177
7280
|
if (this.fsFallback === on) return;
|
|
7178
7281
|
this.fsFallback = on;
|
|
7179
7282
|
this.root?.classList.toggle("sl-fs", on);
|
|
7180
|
-
this.
|
|
7283
|
+
this.syncFullscreenButtons();
|
|
7181
7284
|
if (on && !this.fsEscHandler) {
|
|
7182
7285
|
this.fsEscHandler = (e) => {
|
|
7183
7286
|
if (e.key === "Escape" && !document.fullscreenElement) this.setFsFallback(false);
|
|
@@ -7367,7 +7470,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7367
7470
|
this.els.zfs.addEventListener("click", () => this.toggleFullscreen());
|
|
7368
7471
|
this.fsChangeHandler = () => {
|
|
7369
7472
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
7370
|
-
this.
|
|
7473
|
+
this.syncFullscreenButtons();
|
|
7371
7474
|
requestAnimationFrame(() => this.controller.zoomToFit());
|
|
7372
7475
|
};
|
|
7373
7476
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
@@ -7384,10 +7487,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
7384
7487
|
e.stopPropagation();
|
|
7385
7488
|
setSheet(root.dataset.sheet !== "open");
|
|
7386
7489
|
});
|
|
7490
|
+
this.els.peek?.addEventListener("click", (e) => {
|
|
7491
|
+
const go = e.target.closest(".sl-sheet-go");
|
|
7492
|
+
if (!go) return;
|
|
7493
|
+
e.stopPropagation();
|
|
7494
|
+
if (go.dataset.act === "checkout") void this.handleCta();
|
|
7495
|
+
else setSheet(true);
|
|
7496
|
+
});
|
|
7387
7497
|
let startY = 0;
|
|
7388
7498
|
let swiped = false;
|
|
7389
7499
|
let tracking = false;
|
|
7390
7500
|
head.addEventListener("pointerdown", (e) => {
|
|
7501
|
+
if (e.target.closest?.(".sl-seccard,.sl-sheet-toggle,.sl-sheet-go")) {
|
|
7502
|
+
tracking = false;
|
|
7503
|
+
return;
|
|
7504
|
+
}
|
|
7391
7505
|
tracking = true;
|
|
7392
7506
|
swiped = false;
|
|
7393
7507
|
startY = e.clientY;
|
|
@@ -7406,7 +7520,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7406
7520
|
});
|
|
7407
7521
|
head.addEventListener("pointerup", (e) => {
|
|
7408
7522
|
if (tracking && !swiped && Math.abs(e.clientY - startY) < 6) {
|
|
7409
|
-
|
|
7523
|
+
setSheet(root.dataset.sheet !== "open");
|
|
7410
7524
|
}
|
|
7411
7525
|
tracking = false;
|
|
7412
7526
|
head.releasePointerCapture?.(e.pointerId);
|
|
@@ -7459,7 +7573,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7459
7573
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
7460
7574
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
7461
7575
|
this.els.name.textContent = info.eventName ?? "";
|
|
7462
|
-
const when = info.startsAt ?
|
|
7576
|
+
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
7463
7577
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
7464
7578
|
this.buildBadge(chartTheme);
|
|
7465
7579
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -7600,12 +7714,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
7600
7714
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
7601
7715
|
const filters = this.els.filters;
|
|
7602
7716
|
if (filters) {
|
|
7717
|
+
if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
|
|
7603
7718
|
if (narrow) {
|
|
7604
7719
|
if (this.a11yChipsEl) filters.appendChild(this.a11yChipsEl);
|
|
7605
|
-
if (this.cbEl) filters.appendChild(this.cbEl);
|
|
7606
7720
|
} else {
|
|
7607
7721
|
if (this.a11yChipsEl) this.regions["top-left"]?.appendChild(this.a11yChipsEl);
|
|
7608
|
-
if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
|
|
7609
7722
|
}
|
|
7610
7723
|
const has = narrow && filters.children.length > 0;
|
|
7611
7724
|
filters.classList.toggle("has", has);
|
|
@@ -7712,8 +7825,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
7712
7825
|
if (this.badgeHidden(chartTheme)) return;
|
|
7713
7826
|
const foot = this.els.foot;
|
|
7714
7827
|
if (!foot) return;
|
|
7715
|
-
const el2 = document.createElement("
|
|
7828
|
+
const el2 = document.createElement("a");
|
|
7716
7829
|
el2.className = "sl-powered";
|
|
7830
|
+
el2.href = "https://seatlayer.io/?ref=picker";
|
|
7831
|
+
el2.target = "_blank";
|
|
7832
|
+
el2.rel = "noopener noreferrer";
|
|
7833
|
+
el2.setAttribute("aria-label", this.tf("picker.poweredBy", "Powered by SeatLayer"));
|
|
7717
7834
|
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
7835
|
foot.appendChild(el2);
|
|
7719
7836
|
}
|
|
@@ -8572,6 +8689,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8572
8689
|
el2.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
8573
8690
|
el2.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
8574
8691
|
if (this.buyerView === "venue3d") {
|
|
8692
|
+
this.dismissConfirm();
|
|
8575
8693
|
void this.view3dHandle?.flyToSeat(seat.id);
|
|
8576
8694
|
} else {
|
|
8577
8695
|
this.view3dReturnSeat = seat;
|
|
@@ -8650,13 +8768,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
8650
8768
|
const doc = this.controller.doc;
|
|
8651
8769
|
const activeId = this.controller.getActiveFloorId();
|
|
8652
8770
|
const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
|
|
8653
|
-
let
|
|
8771
|
+
let panoSource;
|
|
8654
8772
|
let caption;
|
|
8655
8773
|
let real = false;
|
|
8656
8774
|
if (seat.viewUrl) {
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8775
|
+
const view = {
|
|
8776
|
+
url: seat.viewUrl,
|
|
8777
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
8778
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
8779
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
8780
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
8781
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
8782
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
8783
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
8784
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
8785
|
+
};
|
|
8786
|
+
panoSource = view;
|
|
8787
|
+
caption = (0, import_panorama.seatViewDisclosure)(view);
|
|
8788
|
+
real = (0, import_panorama.isAuthoredSeatView)(view);
|
|
8660
8789
|
} else {
|
|
8661
8790
|
let pano2;
|
|
8662
8791
|
try {
|
|
@@ -8667,7 +8796,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8667
8796
|
return;
|
|
8668
8797
|
}
|
|
8669
8798
|
if (!this.root || !this.seatViewEnabled()) return;
|
|
8670
|
-
|
|
8799
|
+
panoSource = { url: pano2.url, generated: true };
|
|
8671
8800
|
caption = (0, import_core2.t)("picker.illustrationCaption", { m: pano2.distanceM });
|
|
8672
8801
|
}
|
|
8673
8802
|
this.closeSeatView();
|
|
@@ -8679,7 +8808,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
8679
8808
|
this.root.appendChild(el2);
|
|
8680
8809
|
this.viewEl = el2;
|
|
8681
8810
|
const pano = el2.querySelector(".sl-view-pano");
|
|
8682
|
-
|
|
8811
|
+
const delivery = (0, import_panoramaDelivery.planPanoramaDelivery)(panoSource, (0, import_panoramaDelivery.browserPanoramaConstraints)());
|
|
8812
|
+
const loadAbort = new AbortController();
|
|
8813
|
+
pano.style.backgroundImage = `url("${delivery.initialUrl}")`;
|
|
8814
|
+
let cancelUpgrade = () => {
|
|
8815
|
+
};
|
|
8816
|
+
if (delivery.upgradeUrl) {
|
|
8817
|
+
cancelUpgrade = (0, import_panoramaDelivery.schedulePanoramaUpgrade)(() => {
|
|
8818
|
+
void (0, import_panoramaDelivery.loadPanoramaImage)(delivery.upgradeUrl, loadAbort.signal).then(() => {
|
|
8819
|
+
if (!el2.isConnected || loadAbort.signal.aborted) return;
|
|
8820
|
+
pano.style.backgroundImage = `url("${delivery.upgradeUrl}")`;
|
|
8821
|
+
}).catch(() => {
|
|
8822
|
+
});
|
|
8823
|
+
});
|
|
8824
|
+
}
|
|
8683
8825
|
const VFOV_DEG = 70;
|
|
8684
8826
|
const MAX_PITCH_DEG = 35;
|
|
8685
8827
|
let zoom = 1;
|
|
@@ -8741,6 +8883,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
8741
8883
|
el2.addEventListener("keydown", onKey);
|
|
8742
8884
|
closeBtn.focus();
|
|
8743
8885
|
this.viewCleanup = () => {
|
|
8886
|
+
cancelUpgrade();
|
|
8887
|
+
loadAbort.abort();
|
|
8744
8888
|
pano.removeEventListener("pointerdown", onDown);
|
|
8745
8889
|
pano.removeEventListener("pointermove", onMove);
|
|
8746
8890
|
pano.removeEventListener("pointerup", onUp);
|
|
@@ -9105,10 +9249,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
9105
9249
|
if (previousCount === 0 && count > 0) this.animateOnce(this.els.cta, "sl-ready", 520);
|
|
9106
9250
|
if (this.els.peek) {
|
|
9107
9251
|
if (count) {
|
|
9108
|
-
|
|
9252
|
+
const holding = !!this.hold && !pendingCount;
|
|
9253
|
+
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>`;
|
|
9109
9254
|
} else {
|
|
9110
9255
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
9111
|
-
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<
|
|
9256
|
+
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>`;
|
|
9112
9257
|
}
|
|
9113
9258
|
}
|
|
9114
9259
|
this.lastTrayCount = count;
|
|
@@ -9544,6 +9689,58 @@ var SeatPicker = class _SeatPicker {
|
|
|
9544
9689
|
getSelection() {
|
|
9545
9690
|
return this.committedSelection();
|
|
9546
9691
|
}
|
|
9692
|
+
/**
|
|
9693
|
+
* Re-ink the DRAWN MAP after mount, so the canvas can follow a page palette
|
|
9694
|
+
* the host did not know at construction time.
|
|
9695
|
+
*
|
|
9696
|
+
* Deliberately narrower than the `theme` option: it takes only the map half.
|
|
9697
|
+
* The chrome half is CSS custom properties, which a host restyles from its
|
|
9698
|
+
* own stylesheet without asking the widget for anything — and a host that
|
|
9699
|
+
* used both mechanisms at once would have two things writing one token. This
|
|
9700
|
+
* method exists for the half CSS genuinely cannot reach: pixels Konva draws.
|
|
9701
|
+
*
|
|
9702
|
+
* The rebuild is the controller's (`setMapTheme`), which repaints statuses
|
|
9703
|
+
* from the map already in memory and restores the selection, so a buyer
|
|
9704
|
+
* mid-pick keeps their seats and no round trip is spent. A no-op when the
|
|
9705
|
+
* colours have not changed; safe to call on every render.
|
|
9706
|
+
*/
|
|
9707
|
+
setMapTheme(map) {
|
|
9708
|
+
if (this.destroyed) return;
|
|
9709
|
+
this.opts.theme = { ...this.opts.theme ?? {}, map: map ?? void 0 };
|
|
9710
|
+
this.controller.setMapTheme(map);
|
|
9711
|
+
}
|
|
9712
|
+
/**
|
|
9713
|
+
* Replace the host pricing override AFTER mount, and repaint everything that
|
|
9714
|
+
* shows a price.
|
|
9715
|
+
*
|
|
9716
|
+
* WHY IT CANNOT JUST BE A MOUNT OPTION. The prices a host knows are often not
|
|
9717
|
+
* the prices it knows AT MOUNT: SeatLayer's own event page learns them from
|
|
9718
|
+
* `GET /pub/events/:key/availability`, a separate cached read that lands a
|
|
9719
|
+
* round trip after the map does — deliberately, because the map is not
|
|
9720
|
+
* allowed to wait on it. Remounting the widget to hand it new options would
|
|
9721
|
+
* tear down a live hold, so the override is settable in place.
|
|
9722
|
+
*
|
|
9723
|
+
* The prices themselves are still the SERVER'S. This method takes an answer;
|
|
9724
|
+
* it never derives one. Nothing here re-reads a release, a window or a quota
|
|
9725
|
+
* — see `paidPrice`, the one funnel every displayed price flows through, and
|
|
9726
|
+
* note that the checkout handoff's line items do not come from here at all:
|
|
9727
|
+
* they are priced by the worker from its own hold. So the worst a wrong call
|
|
9728
|
+
* to this method can do is misprint a price, never mischarge one.
|
|
9729
|
+
*
|
|
9730
|
+
* A no-op when the map is unchanged, so a host may call it on every poll.
|
|
9731
|
+
*/
|
|
9732
|
+
setPricing(pricing) {
|
|
9733
|
+
const before = JSON.stringify(this.opts.pricing ?? null);
|
|
9734
|
+
const after = JSON.stringify(pricing ?? null);
|
|
9735
|
+
if (before === after) return;
|
|
9736
|
+
this.opts.pricing = pricing;
|
|
9737
|
+
if (this.destroyed || !this.els.prices) return;
|
|
9738
|
+
this.els.pricesSec?.querySelector(".sl-price-select")?.remove();
|
|
9739
|
+
this.buildPriceFilter();
|
|
9740
|
+
this.syncPrices();
|
|
9741
|
+
this.syncTray();
|
|
9742
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
9743
|
+
}
|
|
9547
9744
|
/** Current colorblind-safe render state, resolved from the stored buyer
|
|
9548
9745
|
* preference at mount. Host chrome (e.g. the Designer preview) reads this to
|
|
9549
9746
|
* surface the state rather than rendering colorblind colors silently. */
|
|
@@ -9595,10 +9792,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9595
9792
|
*
|
|
9596
9793
|
* Entering `'venue3d'` with `opts.flyToSeatId` runs the cinematic tour: the
|
|
9597
9794
|
* 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
|
|
9795
|
+
* 360° view-from-seat). When the widget is **already** in the 3D
|
|
9602
9796
|
* view, the camera simply flies to the requested seat — the GL scene is not
|
|
9603
9797
|
* torn down or rebuilt, so there is no flash or re-entry.
|
|
9604
9798
|
*
|
|
@@ -9607,7 +9801,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9607
9801
|
*
|
|
9608
9802
|
* @param view `'map'` for the flat picker, `'venue3d'` for the 3D venue.
|
|
9609
9803
|
* @param opts.flyToSeatId When entering (or already in) `'venue3d'`, the seat
|
|
9610
|
-
* id to fly the camera to — cinematic →
|
|
9804
|
+
* id to fly the camera to — cinematic → live seat view.
|
|
9611
9805
|
* Ignored when `view` is `'map'`.
|
|
9612
9806
|
*
|
|
9613
9807
|
* @example
|
|
@@ -9621,7 +9815,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
9621
9815
|
}
|
|
9622
9816
|
const flyToSeatId = opts?.flyToSeatId;
|
|
9623
9817
|
if (this.buyerView === "venue3d") {
|
|
9624
|
-
if (flyToSeatId)
|
|
9818
|
+
if (flyToSeatId) {
|
|
9819
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId: flyToSeatId });
|
|
9820
|
+
void this.view3dHandle?.flyToSeat(flyToSeatId);
|
|
9821
|
+
} else if (opts?.resetView) {
|
|
9822
|
+
this.view3dHandle?.focusOverview();
|
|
9823
|
+
}
|
|
9625
9824
|
return;
|
|
9626
9825
|
}
|
|
9627
9826
|
void this.enter3d(flyToSeatId);
|
|
@@ -9661,14 +9860,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
9661
9860
|
async seatViewFor3d(seatId) {
|
|
9662
9861
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
9663
9862
|
if (!seat) return null;
|
|
9664
|
-
if (seat.viewUrl) return {
|
|
9863
|
+
if (seat.viewUrl) return {
|
|
9864
|
+
url: seat.viewUrl,
|
|
9865
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
9866
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
9867
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
9868
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
9869
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
9870
|
+
...seat.viewMeta?.initialBearingDeg !== void 0 ? { initialBearingDeg: seat.viewMeta.initialBearingDeg } : {},
|
|
9871
|
+
...seat.viewMeta?.initialPitchDeg !== void 0 ? { initialPitchDeg: seat.viewMeta.initialPitchDeg } : {},
|
|
9872
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
9873
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
9874
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
9875
|
+
};
|
|
9665
9876
|
const doc = this.controller.doc;
|
|
9666
9877
|
if (!doc) return null;
|
|
9667
9878
|
const activeId = this.controller.getActiveFloorId();
|
|
9668
9879
|
const focal = seat.focalPoint ?? doc.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc.focalPoint ?? { x: 0, y: 0 };
|
|
9669
9880
|
try {
|
|
9670
9881
|
const { generateSeatPanorama } = await loadPanorama();
|
|
9671
|
-
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url };
|
|
9882
|
+
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url, generated: true };
|
|
9672
9883
|
} catch {
|
|
9673
9884
|
return null;
|
|
9674
9885
|
}
|
|
@@ -9690,14 +9901,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
9690
9901
|
}
|
|
9691
9902
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
9692
9903
|
if (!seat) return;
|
|
9693
|
-
const
|
|
9904
|
+
const table = this.controller.tableSelection(seatId);
|
|
9905
|
+
const already = this.committedSelection().some((s) => table ? s.label === table.label : s.id === seatId);
|
|
9694
9906
|
if (already) {
|
|
9695
9907
|
this.controller.deselect([seatId]);
|
|
9696
9908
|
this.dismissConfirm();
|
|
9697
9909
|
return;
|
|
9698
9910
|
}
|
|
9911
|
+
const added = this.controller.select([seatId]);
|
|
9912
|
+
if (!added.length) {
|
|
9913
|
+
this.syncSelectionTo3d();
|
|
9914
|
+
this.dismissConfirm();
|
|
9915
|
+
return;
|
|
9916
|
+
}
|
|
9917
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId });
|
|
9699
9918
|
this.flashPickedSeat(seatId);
|
|
9700
|
-
|
|
9919
|
+
if (table) {
|
|
9920
|
+
this.showTableDialog(table, false);
|
|
9921
|
+
this.syncSelectionTo3d();
|
|
9922
|
+
return;
|
|
9923
|
+
}
|
|
9701
9924
|
if (this.opts.confirmSelection !== false) this.showConfirm(seat);
|
|
9702
9925
|
else this.syncTray();
|
|
9703
9926
|
}
|
|
@@ -9717,12 +9940,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
9717
9940
|
*/
|
|
9718
9941
|
buildView3dNav(overlay, handle) {
|
|
9719
9942
|
const floors = handle.floors();
|
|
9720
|
-
const
|
|
9721
|
-
const
|
|
9943
|
+
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
9944
|
+
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
9945
|
+
const allSections = handle.sections().filter((s) => s.seatCount > 0);
|
|
9946
|
+
const sections = zones.length > 1 ? [] : allSections;
|
|
9722
9947
|
const wantFloors = floors.length > 1;
|
|
9723
9948
|
const wantZones = zones.length > 1;
|
|
9724
9949
|
const wantSections = sections.length > 1;
|
|
9725
|
-
|
|
9950
|
+
const wantLocator = allSections.length > 0 && handle.rows().length > 0;
|
|
9951
|
+
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
9726
9952
|
const nav = document.createElement("div");
|
|
9727
9953
|
nav.className = "sl-view3d-nav";
|
|
9728
9954
|
if (wantFloors) {
|
|
@@ -9759,15 +9985,112 @@ var SeatPicker = class _SeatPicker {
|
|
|
9759
9985
|
} })) : sections.map((s) => ({ id: s.id, label: s.label || s.id, go: () => {
|
|
9760
9986
|
handle.focusSection(s.id);
|
|
9761
9987
|
} }));
|
|
9762
|
-
|
|
9763
|
-
const
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9988
|
+
if (!wantZones && entries.length > MAX_3D_SECTION_PILLS) {
|
|
9989
|
+
const select = document.createElement("select");
|
|
9990
|
+
select.setAttribute("aria-label", this.tf("picker.jumpToSection", "Jump to section"));
|
|
9991
|
+
const placeholder = document.createElement("option");
|
|
9992
|
+
placeholder.value = "";
|
|
9993
|
+
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
9994
|
+
select.appendChild(placeholder);
|
|
9995
|
+
for (const entry of entries) {
|
|
9996
|
+
const option = document.createElement("option");
|
|
9997
|
+
option.value = entry.id;
|
|
9998
|
+
option.textContent = entry.label;
|
|
9999
|
+
select.appendChild(option);
|
|
10000
|
+
}
|
|
10001
|
+
select.addEventListener("change", () => {
|
|
10002
|
+
entries.find((entry) => entry.id === select.value)?.go();
|
|
10003
|
+
});
|
|
10004
|
+
row.appendChild(select);
|
|
10005
|
+
} else {
|
|
10006
|
+
for (const e of entries) {
|
|
10007
|
+
const b = document.createElement("button");
|
|
10008
|
+
b.type = "button";
|
|
10009
|
+
b.textContent = e.label;
|
|
10010
|
+
b.addEventListener("click", e.go);
|
|
10011
|
+
row.appendChild(b);
|
|
10012
|
+
}
|
|
9768
10013
|
}
|
|
9769
10014
|
nav.appendChild(row);
|
|
9770
10015
|
}
|
|
10016
|
+
if (wantLocator) {
|
|
10017
|
+
const locator = document.createElement("div");
|
|
10018
|
+
locator.className = "sl-view3d-locator";
|
|
10019
|
+
locator.setAttribute("role", "group");
|
|
10020
|
+
locator.setAttribute("aria-label", "Find an exact seat in 3D");
|
|
10021
|
+
const sectionSelect = document.createElement("select");
|
|
10022
|
+
sectionSelect.setAttribute("aria-label", "Choose section in 3D");
|
|
10023
|
+
const rowSelect = document.createElement("select");
|
|
10024
|
+
rowSelect.setAttribute("aria-label", "Choose row in 3D");
|
|
10025
|
+
const seatSelect = document.createElement("select");
|
|
10026
|
+
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
10027
|
+
const view = document.createElement("button");
|
|
10028
|
+
view.type = "button";
|
|
10029
|
+
view.textContent = "View seat";
|
|
10030
|
+
view.disabled = true;
|
|
10031
|
+
const fill = (select, placeholder, entries) => {
|
|
10032
|
+
select.replaceChildren();
|
|
10033
|
+
const first = document.createElement("option");
|
|
10034
|
+
first.value = "";
|
|
10035
|
+
first.textContent = placeholder;
|
|
10036
|
+
select.appendChild(first);
|
|
10037
|
+
for (const entry of entries) {
|
|
10038
|
+
const option = document.createElement("option");
|
|
10039
|
+
option.value = entry.id;
|
|
10040
|
+
option.textContent = entry.label;
|
|
10041
|
+
select.appendChild(option);
|
|
10042
|
+
}
|
|
10043
|
+
select.value = "";
|
|
10044
|
+
};
|
|
10045
|
+
fill(sectionSelect, "1. Section", allSections.map((section) => ({
|
|
10046
|
+
id: section.id,
|
|
10047
|
+
label: `${section.label} \xB7 ${section.seatCount.toLocaleString()} seats`
|
|
10048
|
+
})));
|
|
10049
|
+
fill(rowSelect, "2. Row", []);
|
|
10050
|
+
fill(seatSelect, "3. Seat", []);
|
|
10051
|
+
rowSelect.disabled = true;
|
|
10052
|
+
seatSelect.disabled = true;
|
|
10053
|
+
sectionSelect.addEventListener("change", () => {
|
|
10054
|
+
const sectionId = sectionSelect.value;
|
|
10055
|
+
view.disabled = true;
|
|
10056
|
+
fill(seatSelect, "3. Seat", []);
|
|
10057
|
+
seatSelect.disabled = true;
|
|
10058
|
+
if (!sectionId || !handle.focusSection(sectionId)) {
|
|
10059
|
+
fill(rowSelect, "2. Row", []);
|
|
10060
|
+
rowSelect.disabled = true;
|
|
10061
|
+
return;
|
|
10062
|
+
}
|
|
10063
|
+
const rows = handle.rows(sectionId);
|
|
10064
|
+
fill(rowSelect, "2. Row", rows.map((row) => ({
|
|
10065
|
+
id: row.id,
|
|
10066
|
+
label: `${row.label} \xB7 ${row.seatCount} seats`
|
|
10067
|
+
})));
|
|
10068
|
+
rowSelect.disabled = rows.length === 0;
|
|
10069
|
+
});
|
|
10070
|
+
rowSelect.addEventListener("change", () => {
|
|
10071
|
+
const rowId = rowSelect.value;
|
|
10072
|
+
view.disabled = true;
|
|
10073
|
+
if (!rowId || !handle.focusRow(rowId)) {
|
|
10074
|
+
fill(seatSelect, "3. Seat", []);
|
|
10075
|
+
seatSelect.disabled = true;
|
|
10076
|
+
return;
|
|
10077
|
+
}
|
|
10078
|
+
const seats = handle.seatsInRow(rowId);
|
|
10079
|
+
fill(seatSelect, "3. Seat", seats);
|
|
10080
|
+
seatSelect.disabled = seats.length === 0;
|
|
10081
|
+
});
|
|
10082
|
+
seatSelect.addEventListener("change", () => {
|
|
10083
|
+
const seatId = seatSelect.value;
|
|
10084
|
+
view.disabled = !seatId;
|
|
10085
|
+
handle.setSelection(seatId ? [seatId] : []);
|
|
10086
|
+
});
|
|
10087
|
+
view.addEventListener("click", () => {
|
|
10088
|
+
const seatId = seatSelect.value;
|
|
10089
|
+
if (seatId) void handle.flyToSeat(seatId);
|
|
10090
|
+
});
|
|
10091
|
+
locator.append(sectionSelect, rowSelect, seatSelect, view);
|
|
10092
|
+
nav.appendChild(locator);
|
|
10093
|
+
}
|
|
9771
10094
|
overlay.appendChild(nav);
|
|
9772
10095
|
}
|
|
9773
10096
|
async enter3d(flySeatId) {
|
|
@@ -9775,6 +10098,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9775
10098
|
const doc = this.controller.doc;
|
|
9776
10099
|
if (!doc) return;
|
|
9777
10100
|
this.buyerView = "venue3d";
|
|
10101
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", ...flySeatId ? { seatId: flySeatId } : {} });
|
|
9778
10102
|
this.root?.setAttribute("data-view3d", "on");
|
|
9779
10103
|
this.dismissConfirm();
|
|
9780
10104
|
this.syncProjection();
|
|
@@ -9788,6 +10112,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
9788
10112
|
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
10113
|
back.addEventListener("click", () => this.exit3d());
|
|
9790
10114
|
overlay.appendChild(back);
|
|
10115
|
+
const fullscreen = document.createElement("button");
|
|
10116
|
+
fullscreen.type = "button";
|
|
10117
|
+
fullscreen.className = "sl-view3d-fs";
|
|
10118
|
+
fullscreen.textContent = "\u26F6";
|
|
10119
|
+
fullscreen.setAttribute("aria-label", "Full screen");
|
|
10120
|
+
fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
|
|
10121
|
+
fullscreen.addEventListener("click", () => this.toggleFullscreen());
|
|
10122
|
+
overlay.appendChild(fullscreen);
|
|
10123
|
+
const loading = document.createElement("div");
|
|
10124
|
+
loading.className = "sl-view3d-loading";
|
|
10125
|
+
loading.setAttribute("role", "status");
|
|
10126
|
+
loading.setAttribute("aria-live", "polite");
|
|
10127
|
+
loading.textContent = this.tf("picker.loading3d", "Building the 3D venue\u2026");
|
|
10128
|
+
overlay.appendChild(loading);
|
|
9791
10129
|
this.els.map.appendChild(overlay);
|
|
9792
10130
|
this.view3dEl = overlay;
|
|
9793
10131
|
requestAnimationFrame(() => {
|
|
@@ -9798,8 +10136,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
9798
10136
|
const seats = (0, import_core2.expandChart)(doc);
|
|
9799
10137
|
const mod = await loadVenue3d();
|
|
9800
10138
|
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
9801
|
-
const
|
|
10139
|
+
const prepared = await mod.prepareVenue3D({ doc, seats });
|
|
10140
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
10141
|
+
const handle = mod.mountVenue3D(overlay, { doc, seats, prepared }, {
|
|
10142
|
+
// A strict contain fit turns a wide bowl into a postage stamp inside a
|
|
10143
|
+
// phone. The bounded portrait fit was validated against every UX-lab
|
|
10144
|
+
// fixture; keep editor previews strict while making buyer seats legible.
|
|
10145
|
+
portraitOverviewCrop: true,
|
|
9802
10146
|
onSeatPick: (id) => this.onView3dSeatPick(id),
|
|
10147
|
+
onSectionFocusChange: (sectionId) => {
|
|
10148
|
+
const select = overlay.querySelector(
|
|
10149
|
+
'select[aria-label="Choose section in 3D"]'
|
|
10150
|
+
);
|
|
10151
|
+
const next = sectionId ?? "";
|
|
10152
|
+
if (!select || select.value === next) return;
|
|
10153
|
+
select.value = next;
|
|
10154
|
+
select.dispatchEvent(new Event("change"));
|
|
10155
|
+
},
|
|
10156
|
+
onViewTargetChange: (seatId) => {
|
|
10157
|
+
overlay.querySelector(".sl-view3d-nav")?.classList.toggle("is-seat-focused", !!seatId);
|
|
10158
|
+
this.opts.onBuyerViewChange?.({
|
|
10159
|
+
view: "venue3d",
|
|
10160
|
+
...seatId ? { seatId } : {}
|
|
10161
|
+
});
|
|
10162
|
+
},
|
|
9803
10163
|
// Deferred off the tap gesture: generateSeatPanorama walks every seat
|
|
9804
10164
|
// (O(n) on a 13k chart), and the module prefetches at pick — by the
|
|
9805
10165
|
// time the flight lands (~2.5s) the idle render has long finished. A
|
|
@@ -9819,12 +10179,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
9819
10179
|
onAnalytics: (event, props) => this.emit3dAnalytics(event, props)
|
|
9820
10180
|
});
|
|
9821
10181
|
this.view3dHandle = handle;
|
|
10182
|
+
loading.remove();
|
|
9822
10183
|
this.pushAvailabilityTo3d();
|
|
9823
10184
|
this.syncSelectionTo3d();
|
|
9824
10185
|
this.buildView3dNav(overlay, handle);
|
|
9825
10186
|
if (flySeatId) void handle.flyToSeat(flySeatId);
|
|
9826
10187
|
} catch (err) {
|
|
10188
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
9827
10189
|
this.opts.onError?.(err);
|
|
10190
|
+
this.toast(this.tf("picker.unavailable3d", "3D could not start. The seat map is still available."), "warning");
|
|
9828
10191
|
this.exit3d();
|
|
9829
10192
|
}
|
|
9830
10193
|
}
|
|
@@ -9832,6 +10195,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
9832
10195
|
if (this.buyerView !== "venue3d" && !this.view3dEl) return;
|
|
9833
10196
|
this.view3dGen++;
|
|
9834
10197
|
this.buyerView = "map";
|
|
10198
|
+
this.opts.onBuyerViewChange?.({ view: "map" });
|
|
9835
10199
|
this.root?.removeAttribute("data-view3d");
|
|
9836
10200
|
try {
|
|
9837
10201
|
this.view3dHandle?.dispose();
|