@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.js
CHANGED
|
@@ -270,8 +270,12 @@ var BuyerRealtimeClient = class {
|
|
|
270
270
|
const next = projectionFromSnapshot(frame);
|
|
271
271
|
const changes = diffProjections(this.projection, next);
|
|
272
272
|
this.projection = next;
|
|
273
|
-
if (changes === null)
|
|
274
|
-
|
|
273
|
+
if (changes === null) {
|
|
274
|
+
if (this.opts.sink.applyProjection) this.opts.sink.applyProjection(next);
|
|
275
|
+
else void this.opts.sink.resync();
|
|
276
|
+
} else if (changes.length) {
|
|
277
|
+
this.opts.sink.applyStatuses(changes);
|
|
278
|
+
}
|
|
275
279
|
return;
|
|
276
280
|
}
|
|
277
281
|
if (type === "delta" && Array.isArray(frame.changes)) {
|
|
@@ -524,7 +528,7 @@ var PubApi = class {
|
|
|
524
528
|
return this.request(`/pub/events/${encodeURIComponent(key)}/chart`);
|
|
525
529
|
}
|
|
526
530
|
objects(key) {
|
|
527
|
-
return this.request(`/pub/events/${encodeURIComponent(key)}/objects`);
|
|
531
|
+
return this.request(`/pub/events/${encodeURIComponent(key)}/objects?compact=1`);
|
|
528
532
|
}
|
|
529
533
|
hold(key, selections, ttlMs, replaceHoldId) {
|
|
530
534
|
return this.request(`/pub/events/${encodeURIComponent(key)}/hold`, {
|
|
@@ -1981,10 +1985,17 @@ import {
|
|
|
1981
1985
|
t as t2,
|
|
1982
1986
|
tCount
|
|
1983
1987
|
} from "@seatlayer/core";
|
|
1988
|
+
import { isAuthoredSeatView, seatViewDisclosure } from "@seatlayer/core/view3d/crossfade/panorama";
|
|
1989
|
+
import {
|
|
1990
|
+
browserPanoramaConstraints,
|
|
1991
|
+
loadPanoramaImage,
|
|
1992
|
+
planPanoramaDelivery,
|
|
1993
|
+
schedulePanoramaUpgrade
|
|
1994
|
+
} from "@seatlayer/core/view/panoramaDelivery";
|
|
1984
1995
|
var DEFAULT_API_BASE2 = "https://api.seatlayer.io";
|
|
1985
1996
|
var DEFAULT_MAX_SELECTION2 = 10;
|
|
1986
1997
|
var EXTEND_PROMPT_MS = 6e4;
|
|
1987
|
-
var MAX_3D_SEATS_DEFAULT =
|
|
1998
|
+
var MAX_3D_SEATS_DEFAULT = 6e4;
|
|
1988
1999
|
var MAX_3D_SECTION_PILLS = 12;
|
|
1989
2000
|
function pointInPolygon(x, y, poly) {
|
|
1990
2001
|
let inside = false;
|
|
@@ -2067,7 +2078,7 @@ async function loadHostedCheckout() {
|
|
|
2067
2078
|
cdnChunkUrl("seatlayer-checkout.mjs")
|
|
2068
2079
|
);
|
|
2069
2080
|
}
|
|
2070
|
-
return import("./hostedCheckout-
|
|
2081
|
+
return import("./hostedCheckout-SHQCWN5Y.js");
|
|
2071
2082
|
}
|
|
2072
2083
|
function paymentsOffReason(reason) {
|
|
2073
2084
|
return reason === "unavailable_for_event" || reason === "payments_off_for_event" ? reason : "not_configured";
|
|
@@ -2328,7 +2339,12 @@ var CSS = (
|
|
|
2328
2339
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
2329
2340
|
two pieces of chrome free-float on top of each other. Regions never overlap:
|
|
2330
2341
|
the top strip splits into left/center/right; rails + corners own their edge. */
|
|
2331
|
-
|
|
2342
|
+
/* align-items:center, not the flex default of stretch. Without it a short pill
|
|
2343
|
+
beside a taller control (TEST MODE next to the Map/3D toggle) is stretched to
|
|
2344
|
+
the row's height, so two pills that should read as a matched pair end up
|
|
2345
|
+
different shapes on different baselines. Column regions set their own
|
|
2346
|
+
cross-axis alignment below and are unaffected. */
|
|
2347
|
+
.sl-anchor{position:absolute;z-index:5;display:flex;align-items:center;gap:8px;pointer-events:none}
|
|
2332
2348
|
.sl-anchor > *{pointer-events:auto}
|
|
2333
2349
|
.sl-anchor[data-region="top-left"]{top:12px;left:12px;flex-wrap:wrap;max-width:38%}
|
|
2334
2350
|
.sl-anchor[data-region="top-center"]{top:12px;left:50%;transform:translateX(-50%);flex-direction:column;
|
|
@@ -2344,10 +2360,17 @@ var CSS = (
|
|
|
2344
2360
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
2345
2361
|
|
|
2346
2362
|
/* TEST MODE badge \u2014 a small pill in the top-right region (shrinks on narrow) */
|
|
2347
|
-
|
|
2363
|
+
/* align-self:stretch, not a hardcoded height: the badge sits beside the Map/3D
|
|
2364
|
+
toggle, whose height comes from its own border + padding + button metrics.
|
|
2365
|
+
Stretching matches that row exactly and keeps matching if the toggle ever
|
|
2366
|
+
changes, while the badge's own inline-flex keeps the label centred inside
|
|
2367
|
+
whatever height it gets. Alone in the region it simply takes its natural
|
|
2368
|
+
size. */
|
|
2369
|
+
.sl-testbadge{display:inline-flex;align-items:center;align-self:stretch;padding:0 12px;border-radius:999px;
|
|
2370
|
+
font-size:10px;font-weight:800;letter-spacing:.1em;line-height:1;
|
|
2348
2371
|
text-transform:uppercase;white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
2349
2372
|
box-shadow:0 2px 8px rgba(0,0,0,.25)}
|
|
2350
|
-
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:
|
|
2373
|
+
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:0 8px;font-size:8.5px;letter-spacing:.06em}
|
|
2351
2374
|
|
|
2352
2375
|
/* zoom column (flows within the bottom-right region) */
|
|
2353
2376
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -2439,7 +2462,11 @@ var CSS = (
|
|
|
2439
2462
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
2440
2463
|
theme sets hideBadge. */
|
|
2441
2464
|
.sl-powered{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:10px;
|
|
2442
|
-
font-size:
|
|
2465
|
+
font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--sl-text);opacity:.72;
|
|
2466
|
+
text-decoration:none;padding:6px 10px;border-radius:999px;width:fit-content;margin-inline:auto;
|
|
2467
|
+
transition:opacity .15s ease,background-color .15s ease}
|
|
2468
|
+
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
2469
|
+
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
2443
2470
|
.sl-powered-mark{width:16px;height:16px;border-radius:4px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
2444
2471
|
background:#0c1220;color:#fcf7ee}
|
|
2445
2472
|
.sl-powered-mark svg{width:12px;height:11px}
|
|
@@ -2618,18 +2645,27 @@ var CSS = (
|
|
|
2618
2645
|
.sl-view3d{position:absolute;inset:0;z-index:4;opacity:0;touch-action:none;
|
|
2619
2646
|
transition:opacity .3s ease;background:radial-gradient(120% 120% at 50% 0%,#191f28 0%,#0d1014 70%)}
|
|
2620
2647
|
.sl-view3d canvas{display:block;width:100%;height:100%}
|
|
2648
|
+
.sl-view3d canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
2649
|
+
.sl-view3d-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;
|
|
2650
|
+
z-index:1;color:#d9e2f2;font-size:13px;font-weight:700;letter-spacing:.02em;pointer-events:none}
|
|
2651
|
+
.sl-view3d-loading::before{content:"";width:18px;height:18px;border-radius:50%;
|
|
2652
|
+
border:2px solid rgba(217,226,242,.28);border-top-color:#d9e2f2;animation:slSpin .8s linear infinite}
|
|
2621
2653
|
[data-view3d=on] .sl-chips,[data-view3d=on] .sl-rungs{display:none}
|
|
2622
2654
|
.sl-view3d-back{position:absolute;top:12px;left:12px;z-index:2;display:inline-flex;align-items:center;gap:6px;
|
|
2623
2655
|
padding:7px 13px 7px 9px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.03em;
|
|
2624
2656
|
color:#e6edf3;background:rgba(10,14,20,.62);border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
2625
2657
|
.sl-view3d-back:hover,.sl-view3d-back:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
2626
2658
|
.sl-view3d-back svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2659
|
+
.sl-view3d-fs{position:absolute;top:12px;right:12px;z-index:2;min-width:44px;min-height:44px;padding:8px 12px;
|
|
2660
|
+
border-radius:999px;font-size:16px;color:#e6edf3;background:rgba(10,14,20,.62);
|
|
2661
|
+
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
2662
|
+
.sl-view3d-fs:hover,.sl-view3d-fs:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
2627
2663
|
/* Venue navigation inside 3D: levels (isolate a floor) and areas (fly to a zone).
|
|
2628
2664
|
Bottom-LEFT, clear of the module's own chips (Overview bottom-right, 360
|
|
2629
2665
|
bottom-centre) and of the bottom-sheeted confirm card. Horizontally scrollable
|
|
2630
2666
|
so a venue with many zones never pushes the rail off a phone screen. */
|
|
2631
2667
|
.sl-view3d-nav{position:absolute;left:12px;bottom:16px;z-index:3;display:flex;flex-direction:column;gap:6px;
|
|
2632
|
-
max-width:calc(100% -
|
|
2668
|
+
max-width:calc(100% - 150px);pointer-events:none}
|
|
2633
2669
|
.sl-view3d-nav > div{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;pointer-events:auto;
|
|
2634
2670
|
padding:1px;-webkit-overflow-scrolling:touch}
|
|
2635
2671
|
.sl-view3d-nav > div::-webkit-scrollbar{display:none}
|
|
@@ -2638,6 +2674,21 @@ var CSS = (
|
|
|
2638
2674
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
2639
2675
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
2640
2676
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
2677
|
+
.sl-view3d-nav button:disabled{opacity:.45;cursor:not-allowed}
|
|
2678
|
+
.sl-view3d-nav select{min-height:38px;max-width:min(280px,calc(100vw - 40px));padding:7px 34px 7px 12px;
|
|
2679
|
+
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
2680
|
+
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
2681
|
+
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
2682
|
+
.sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
|
|
2683
|
+
width:min(720px,calc(100vw - 180px));overflow:visible}
|
|
2684
|
+
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
2685
|
+
.sl-view3d-locator select{width:100%;min-width:0}
|
|
2686
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:calc(100vw - 24px);overflow-x:auto}
|
|
2687
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
2688
|
+
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
2689
|
+
Keep venue navigation in a separate top rail so those control families never
|
|
2690
|
+
stack over one another. */
|
|
2691
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav{top:68px;bottom:auto;max-width:calc(100% - 24px)}
|
|
2641
2692
|
/* While immersed, the 2D-only chrome is meaningless \u2014 hide it, keep Map|3D. */
|
|
2642
2693
|
.sl-picker[data-view3d="on"] .sl-rungs,
|
|
2643
2694
|
.sl-picker[data-view3d="on"] .sl-floors,
|
|
@@ -2716,7 +2767,6 @@ var CSS = (
|
|
|
2716
2767
|
.sl-confirm-thumb-badge{position:absolute;right:7px;top:7px;display:inline-flex;align-items:center;gap:5px;
|
|
2717
2768
|
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)}
|
|
2718
2769
|
.sl-confirm-sight{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--sl-muted);margin-bottom:2px}
|
|
2719
|
-
.sl-confirm-sight span{color:#22a06b;font-weight:800}
|
|
2720
2770
|
.sl-confirm-view{width:100%;margin-top:9px;padding:8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
2721
2771
|
color:var(--sl-text);font-weight:700;font-size:12px;display:flex;align-items:center;justify-content:center;gap:7px}
|
|
2722
2772
|
.sl-confirm-view:hover{border-color:var(--sl-muted)}
|
|
@@ -2811,6 +2861,11 @@ var CSS = (
|
|
|
2811
2861
|
/* modal host */
|
|
2812
2862
|
.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}
|
|
2813
2863
|
.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)}
|
|
2864
|
+
/* The frame already clips to 16px. A picker rounding itself to --sl-radius
|
|
2865
|
+
(14px) inside it leaves a sliver of scrim showing at each corner, which
|
|
2866
|
+
reads as a rendering fault rather than as a rounded card. One owner of the
|
|
2867
|
+
corners, and it is the frame. */
|
|
2868
|
+
.sl-modal-frame > .sl-picker{border-radius:0}
|
|
2814
2869
|
@media(max-width:640px){.sl-modal-scrim{padding:0}.sl-modal-frame{width:100%;height:100%;border-radius:0}}
|
|
2815
2870
|
`
|
|
2816
2871
|
);
|
|
@@ -2821,6 +2876,17 @@ function ensureStyle() {
|
|
|
2821
2876
|
el.textContent = CSS;
|
|
2822
2877
|
document.head.appendChild(el);
|
|
2823
2878
|
}
|
|
2879
|
+
function formatWhen(startsAt, timezone, locale) {
|
|
2880
|
+
const options = { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" };
|
|
2881
|
+
const at = new Date(startsAt);
|
|
2882
|
+
if (timezone) {
|
|
2883
|
+
try {
|
|
2884
|
+
return at.toLocaleString(locale, { ...options, timeZone: timezone });
|
|
2885
|
+
} catch {
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
return at.toLocaleString(locale, options);
|
|
2889
|
+
}
|
|
2824
2890
|
function resolveTokens(chart, host) {
|
|
2825
2891
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
2826
2892
|
const accentInk = host?.accentInk ?? chart?.accentInk ?? "#1a1200";
|
|
@@ -3015,6 +3081,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
3015
3081
|
currency: options.currency,
|
|
3016
3082
|
flashOnLiveChange: true,
|
|
3017
3083
|
colorblindSafe: this.cbSafe,
|
|
3084
|
+
// The drawn map's own overrides, when the host supplied them up front.
|
|
3085
|
+
// Everything else on `theme` is CSS and lands on the root instead.
|
|
3086
|
+
mapTheme: options.theme?.map ?? null,
|
|
3018
3087
|
onSelectionChange: () => {
|
|
3019
3088
|
this.syncTray();
|
|
3020
3089
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
@@ -3103,8 +3172,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3103
3172
|
/**
|
|
3104
3173
|
* Eager sightline preview for the confirm card: the organizer's real view
|
|
3105
3174
|
* photo, or — only when the chart has an actual stage to look at — a generated
|
|
3106
|
-
* forward view plus an
|
|
3107
|
-
* chart both the distance
|
|
3175
|
+
* forward view plus an approximate distance-to-stage line. On a stageless
|
|
3176
|
+
* chart both the distance claim and the generic stage silhouette are
|
|
3108
3177
|
* invented promises, so we suppress them; a real attached photo always shows.
|
|
3109
3178
|
* (OV-52)
|
|
3110
3179
|
*/
|
|
@@ -3123,7 +3192,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3123
3192
|
return "";
|
|
3124
3193
|
}
|
|
3125
3194
|
}
|
|
3126
|
-
const sightHtml = hasStage ? `<div class="sl-confirm-sight"
|
|
3195
|
+
const sightHtml = hasStage && distance != null ? `<div class="sl-confirm-sight">${t2("picker.sightline", { m: distance })}</div>` : "";
|
|
3127
3196
|
const viewBtn = realPhoto ? `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${t2("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="${t2("picker.viewFromSeat", { label: seat.label })}"><span aria-hidden="true">\u{1F52D}</span><span>${this.tf("picker.viewFromHere", "View from here")}</span></button>`;
|
|
3128
3197
|
return viewBtn + sightHtml;
|
|
3129
3198
|
}
|
|
@@ -3257,6 +3326,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
3257
3326
|
this.setFsFallback(false);
|
|
3258
3327
|
}
|
|
3259
3328
|
}
|
|
3329
|
+
syncFullscreenButtons() {
|
|
3330
|
+
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
3331
|
+
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
3332
|
+
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
3333
|
+
}
|
|
3260
3334
|
/**
|
|
3261
3335
|
* Native element-fullscreen was unavailable or rejected. When framed, a CSS
|
|
3262
3336
|
* `.sl-fs` overlay can't escape the iframe, so we ask the host page to pin us
|
|
@@ -3271,7 +3345,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3271
3345
|
setFramedFs(on) {
|
|
3272
3346
|
if (this.framedFs === on) return;
|
|
3273
3347
|
this.framedFs = on;
|
|
3274
|
-
this.
|
|
3348
|
+
this.syncFullscreenButtons();
|
|
3275
3349
|
this.postToHost({ type: "seatlayer:fullscreen", on });
|
|
3276
3350
|
if (on && !this.fsEscHandler) {
|
|
3277
3351
|
this.fsEscHandler = (e) => {
|
|
@@ -3288,7 +3362,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3288
3362
|
if (this.fsFallback === on) return;
|
|
3289
3363
|
this.fsFallback = on;
|
|
3290
3364
|
this.root?.classList.toggle("sl-fs", on);
|
|
3291
|
-
this.
|
|
3365
|
+
this.syncFullscreenButtons();
|
|
3292
3366
|
if (on && !this.fsEscHandler) {
|
|
3293
3367
|
this.fsEscHandler = (e) => {
|
|
3294
3368
|
if (e.key === "Escape" && !document.fullscreenElement) this.setFsFallback(false);
|
|
@@ -3478,7 +3552,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3478
3552
|
this.els.zfs.addEventListener("click", () => this.toggleFullscreen());
|
|
3479
3553
|
this.fsChangeHandler = () => {
|
|
3480
3554
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
3481
|
-
this.
|
|
3555
|
+
this.syncFullscreenButtons();
|
|
3482
3556
|
requestAnimationFrame(() => this.controller.zoomToFit());
|
|
3483
3557
|
};
|
|
3484
3558
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
@@ -3570,7 +3644,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3570
3644
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
3571
3645
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
3572
3646
|
this.els.name.textContent = info.eventName ?? "";
|
|
3573
|
-
const when = info.startsAt ?
|
|
3647
|
+
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
3574
3648
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
3575
3649
|
this.buildBadge(chartTheme);
|
|
3576
3650
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -3823,8 +3897,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3823
3897
|
if (this.badgeHidden(chartTheme)) return;
|
|
3824
3898
|
const foot = this.els.foot;
|
|
3825
3899
|
if (!foot) return;
|
|
3826
|
-
const el = document.createElement("
|
|
3900
|
+
const el = document.createElement("a");
|
|
3827
3901
|
el.className = "sl-powered";
|
|
3902
|
+
el.href = "https://seatlayer.io/?ref=picker";
|
|
3903
|
+
el.target = "_blank";
|
|
3904
|
+
el.rel = "noopener noreferrer";
|
|
3905
|
+
el.setAttribute("aria-label", this.tf("picker.poweredBy", "Powered by SeatLayer"));
|
|
3828
3906
|
el.innerHTML = `<span class="sl-powered-mark" aria-hidden="true">` + SEATLAYER_ATTRIBUTION_MARK_SVG + `</span><span>${this.tf("picker.poweredBy", "Powered by SeatLayer")}</span>`;
|
|
3829
3907
|
foot.appendChild(el);
|
|
3830
3908
|
}
|
|
@@ -4683,6 +4761,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4683
4761
|
el.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
4684
4762
|
el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
4685
4763
|
if (this.buyerView === "venue3d") {
|
|
4764
|
+
this.dismissConfirm();
|
|
4686
4765
|
void this.view3dHandle?.flyToSeat(seat.id);
|
|
4687
4766
|
} else {
|
|
4688
4767
|
this.view3dReturnSeat = seat;
|
|
@@ -4761,13 +4840,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
4761
4840
|
const doc = this.controller.doc;
|
|
4762
4841
|
const activeId = this.controller.getActiveFloorId();
|
|
4763
4842
|
const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
|
|
4764
|
-
let
|
|
4843
|
+
let panoSource;
|
|
4765
4844
|
let caption;
|
|
4766
4845
|
let real = false;
|
|
4767
4846
|
if (seat.viewUrl) {
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4847
|
+
const view = {
|
|
4848
|
+
url: seat.viewUrl,
|
|
4849
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
4850
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
4851
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
4852
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
4853
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
4854
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
4855
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
4856
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
4857
|
+
};
|
|
4858
|
+
panoSource = view;
|
|
4859
|
+
caption = seatViewDisclosure(view);
|
|
4860
|
+
real = isAuthoredSeatView(view);
|
|
4771
4861
|
} else {
|
|
4772
4862
|
let pano2;
|
|
4773
4863
|
try {
|
|
@@ -4778,7 +4868,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4778
4868
|
return;
|
|
4779
4869
|
}
|
|
4780
4870
|
if (!this.root || !this.seatViewEnabled()) return;
|
|
4781
|
-
|
|
4871
|
+
panoSource = { url: pano2.url, generated: true };
|
|
4782
4872
|
caption = t2("picker.illustrationCaption", { m: pano2.distanceM });
|
|
4783
4873
|
}
|
|
4784
4874
|
this.closeSeatView();
|
|
@@ -4790,7 +4880,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
4790
4880
|
this.root.appendChild(el);
|
|
4791
4881
|
this.viewEl = el;
|
|
4792
4882
|
const pano = el.querySelector(".sl-view-pano");
|
|
4793
|
-
|
|
4883
|
+
const delivery = planPanoramaDelivery(panoSource, browserPanoramaConstraints());
|
|
4884
|
+
const loadAbort = new AbortController();
|
|
4885
|
+
pano.style.backgroundImage = `url("${delivery.initialUrl}")`;
|
|
4886
|
+
let cancelUpgrade = () => {
|
|
4887
|
+
};
|
|
4888
|
+
if (delivery.upgradeUrl) {
|
|
4889
|
+
cancelUpgrade = schedulePanoramaUpgrade(() => {
|
|
4890
|
+
void loadPanoramaImage(delivery.upgradeUrl, loadAbort.signal).then(() => {
|
|
4891
|
+
if (!el.isConnected || loadAbort.signal.aborted) return;
|
|
4892
|
+
pano.style.backgroundImage = `url("${delivery.upgradeUrl}")`;
|
|
4893
|
+
}).catch(() => {
|
|
4894
|
+
});
|
|
4895
|
+
});
|
|
4896
|
+
}
|
|
4794
4897
|
const VFOV_DEG = 70;
|
|
4795
4898
|
const MAX_PITCH_DEG = 35;
|
|
4796
4899
|
let zoom = 1;
|
|
@@ -4852,6 +4955,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
4852
4955
|
el.addEventListener("keydown", onKey);
|
|
4853
4956
|
closeBtn.focus();
|
|
4854
4957
|
this.viewCleanup = () => {
|
|
4958
|
+
cancelUpgrade();
|
|
4959
|
+
loadAbort.abort();
|
|
4855
4960
|
pano.removeEventListener("pointerdown", onDown);
|
|
4856
4961
|
pano.removeEventListener("pointermove", onMove);
|
|
4857
4962
|
pano.removeEventListener("pointerup", onUp);
|
|
@@ -5655,6 +5760,58 @@ var SeatPicker = class _SeatPicker {
|
|
|
5655
5760
|
getSelection() {
|
|
5656
5761
|
return this.committedSelection();
|
|
5657
5762
|
}
|
|
5763
|
+
/**
|
|
5764
|
+
* Re-ink the DRAWN MAP after mount, so the canvas can follow a page palette
|
|
5765
|
+
* the host did not know at construction time.
|
|
5766
|
+
*
|
|
5767
|
+
* Deliberately narrower than the `theme` option: it takes only the map half.
|
|
5768
|
+
* The chrome half is CSS custom properties, which a host restyles from its
|
|
5769
|
+
* own stylesheet without asking the widget for anything — and a host that
|
|
5770
|
+
* used both mechanisms at once would have two things writing one token. This
|
|
5771
|
+
* method exists for the half CSS genuinely cannot reach: pixels Konva draws.
|
|
5772
|
+
*
|
|
5773
|
+
* The rebuild is the controller's (`setMapTheme`), which repaints statuses
|
|
5774
|
+
* from the map already in memory and restores the selection, so a buyer
|
|
5775
|
+
* mid-pick keeps their seats and no round trip is spent. A no-op when the
|
|
5776
|
+
* colours have not changed; safe to call on every render.
|
|
5777
|
+
*/
|
|
5778
|
+
setMapTheme(map) {
|
|
5779
|
+
if (this.destroyed) return;
|
|
5780
|
+
this.opts.theme = { ...this.opts.theme ?? {}, map: map ?? void 0 };
|
|
5781
|
+
this.controller.setMapTheme(map);
|
|
5782
|
+
}
|
|
5783
|
+
/**
|
|
5784
|
+
* Replace the host pricing override AFTER mount, and repaint everything that
|
|
5785
|
+
* shows a price.
|
|
5786
|
+
*
|
|
5787
|
+
* WHY IT CANNOT JUST BE A MOUNT OPTION. The prices a host knows are often not
|
|
5788
|
+
* the prices it knows AT MOUNT: SeatLayer's own event page learns them from
|
|
5789
|
+
* `GET /pub/events/:key/availability`, a separate cached read that lands a
|
|
5790
|
+
* round trip after the map does — deliberately, because the map is not
|
|
5791
|
+
* allowed to wait on it. Remounting the widget to hand it new options would
|
|
5792
|
+
* tear down a live hold, so the override is settable in place.
|
|
5793
|
+
*
|
|
5794
|
+
* The prices themselves are still the SERVER'S. This method takes an answer;
|
|
5795
|
+
* it never derives one. Nothing here re-reads a release, a window or a quota
|
|
5796
|
+
* — see `paidPrice`, the one funnel every displayed price flows through, and
|
|
5797
|
+
* note that the checkout handoff's line items do not come from here at all:
|
|
5798
|
+
* they are priced by the worker from its own hold. So the worst a wrong call
|
|
5799
|
+
* to this method can do is misprint a price, never mischarge one.
|
|
5800
|
+
*
|
|
5801
|
+
* A no-op when the map is unchanged, so a host may call it on every poll.
|
|
5802
|
+
*/
|
|
5803
|
+
setPricing(pricing) {
|
|
5804
|
+
const before = JSON.stringify(this.opts.pricing ?? null);
|
|
5805
|
+
const after = JSON.stringify(pricing ?? null);
|
|
5806
|
+
if (before === after) return;
|
|
5807
|
+
this.opts.pricing = pricing;
|
|
5808
|
+
if (this.destroyed || !this.els.prices) return;
|
|
5809
|
+
this.els.pricesSec?.querySelector(".sl-price-select")?.remove();
|
|
5810
|
+
this.buildPriceFilter();
|
|
5811
|
+
this.syncPrices();
|
|
5812
|
+
this.syncTray();
|
|
5813
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
5814
|
+
}
|
|
5658
5815
|
/** Current colorblind-safe render state, resolved from the stored buyer
|
|
5659
5816
|
* preference at mount. Host chrome (e.g. the Designer preview) reads this to
|
|
5660
5817
|
* surface the state rather than rendering colorblind colors silently. */
|
|
@@ -5706,10 +5863,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5706
5863
|
*
|
|
5707
5864
|
* Entering `'venue3d'` with `opts.flyToSeatId` runs the cinematic tour: the
|
|
5708
5865
|
* camera flies to the seat and holds in the live scene (a chip offers the
|
|
5709
|
-
* 360° view-from-seat)
|
|
5710
|
-
* the widget enters 3D and auto-flies the camera to that seat, dissolving into
|
|
5711
|
-
* its view-from-seat panorama (the same chain as tapping "See the view from
|
|
5712
|
-
* here" on a seat's confirm card). When the widget is **already** in the 3D
|
|
5866
|
+
* 360° view-from-seat). When the widget is **already** in the 3D
|
|
5713
5867
|
* view, the camera simply flies to the requested seat — the GL scene is not
|
|
5714
5868
|
* torn down or rebuilt, so there is no flash or re-entry.
|
|
5715
5869
|
*
|
|
@@ -5718,7 +5872,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5718
5872
|
*
|
|
5719
5873
|
* @param view `'map'` for the flat picker, `'venue3d'` for the 3D venue.
|
|
5720
5874
|
* @param opts.flyToSeatId When entering (or already in) `'venue3d'`, the seat
|
|
5721
|
-
* id to fly the camera to — cinematic →
|
|
5875
|
+
* id to fly the camera to — cinematic → live seat view.
|
|
5722
5876
|
* Ignored when `view` is `'map'`.
|
|
5723
5877
|
*
|
|
5724
5878
|
* @example
|
|
@@ -5732,7 +5886,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
5732
5886
|
}
|
|
5733
5887
|
const flyToSeatId = opts?.flyToSeatId;
|
|
5734
5888
|
if (this.buyerView === "venue3d") {
|
|
5735
|
-
if (flyToSeatId)
|
|
5889
|
+
if (flyToSeatId) {
|
|
5890
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId: flyToSeatId });
|
|
5891
|
+
void this.view3dHandle?.flyToSeat(flyToSeatId);
|
|
5892
|
+
} else if (opts?.resetView) {
|
|
5893
|
+
this.view3dHandle?.focusOverview();
|
|
5894
|
+
}
|
|
5736
5895
|
return;
|
|
5737
5896
|
}
|
|
5738
5897
|
void this.enter3d(flyToSeatId);
|
|
@@ -5772,14 +5931,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
5772
5931
|
async seatViewFor3d(seatId) {
|
|
5773
5932
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
5774
5933
|
if (!seat) return null;
|
|
5775
|
-
if (seat.viewUrl) return {
|
|
5934
|
+
if (seat.viewUrl) return {
|
|
5935
|
+
url: seat.viewUrl,
|
|
5936
|
+
...seat.viewMeta?.previewUrl ? { previewUrl: seat.viewMeta.previewUrl } : {},
|
|
5937
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
5938
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
5939
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
5940
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
5941
|
+
...seat.viewMeta?.initialBearingDeg !== void 0 ? { initialBearingDeg: seat.viewMeta.initialBearingDeg } : {},
|
|
5942
|
+
...seat.viewMeta?.initialPitchDeg !== void 0 ? { initialPitchDeg: seat.viewMeta.initialPitchDeg } : {},
|
|
5943
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
5944
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
5945
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
5946
|
+
};
|
|
5776
5947
|
const doc = this.controller.doc;
|
|
5777
5948
|
if (!doc) return null;
|
|
5778
5949
|
const activeId = this.controller.getActiveFloorId();
|
|
5779
5950
|
const focal = seat.focalPoint ?? doc.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc.focalPoint ?? { x: 0, y: 0 };
|
|
5780
5951
|
try {
|
|
5781
5952
|
const { generateSeatPanorama } = await loadPanorama();
|
|
5782
|
-
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url };
|
|
5953
|
+
return { url: generateSeatPanorama(seat, focal, this.allSeats()).url, generated: true };
|
|
5783
5954
|
} catch {
|
|
5784
5955
|
return null;
|
|
5785
5956
|
}
|
|
@@ -5801,14 +5972,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
5801
5972
|
}
|
|
5802
5973
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
5803
5974
|
if (!seat) return;
|
|
5804
|
-
const
|
|
5975
|
+
const table = this.controller.tableSelection(seatId);
|
|
5976
|
+
const already = this.committedSelection().some((s) => table ? s.label === table.label : s.id === seatId);
|
|
5805
5977
|
if (already) {
|
|
5806
5978
|
this.controller.deselect([seatId]);
|
|
5807
5979
|
this.dismissConfirm();
|
|
5808
5980
|
return;
|
|
5809
5981
|
}
|
|
5982
|
+
const added = this.controller.select([seatId]);
|
|
5983
|
+
if (!added.length) {
|
|
5984
|
+
this.syncSelectionTo3d();
|
|
5985
|
+
this.dismissConfirm();
|
|
5986
|
+
return;
|
|
5987
|
+
}
|
|
5988
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", seatId });
|
|
5810
5989
|
this.flashPickedSeat(seatId);
|
|
5811
|
-
|
|
5990
|
+
if (table) {
|
|
5991
|
+
this.showTableDialog(table, false);
|
|
5992
|
+
this.syncSelectionTo3d();
|
|
5993
|
+
return;
|
|
5994
|
+
}
|
|
5812
5995
|
if (this.opts.confirmSelection !== false) this.showConfirm(seat);
|
|
5813
5996
|
else this.syncTray();
|
|
5814
5997
|
}
|
|
@@ -5828,12 +6011,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
5828
6011
|
*/
|
|
5829
6012
|
buildView3dNav(overlay, handle) {
|
|
5830
6013
|
const floors = handle.floors();
|
|
5831
|
-
const
|
|
5832
|
-
const
|
|
6014
|
+
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
6015
|
+
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
6016
|
+
const allSections = handle.sections().filter((s) => s.seatCount > 0);
|
|
6017
|
+
const sections = zones.length > 1 ? [] : allSections;
|
|
5833
6018
|
const wantFloors = floors.length > 1;
|
|
5834
6019
|
const wantZones = zones.length > 1;
|
|
5835
6020
|
const wantSections = sections.length > 1;
|
|
5836
|
-
|
|
6021
|
+
const wantLocator = allSections.length > 0 && handle.rows().length > 0;
|
|
6022
|
+
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
5837
6023
|
const nav = document.createElement("div");
|
|
5838
6024
|
nav.className = "sl-view3d-nav";
|
|
5839
6025
|
if (wantFloors) {
|
|
@@ -5870,15 +6056,112 @@ var SeatPicker = class _SeatPicker {
|
|
|
5870
6056
|
} })) : sections.map((s) => ({ id: s.id, label: s.label || s.id, go: () => {
|
|
5871
6057
|
handle.focusSection(s.id);
|
|
5872
6058
|
} }));
|
|
5873
|
-
|
|
5874
|
-
const
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
6059
|
+
if (!wantZones && entries.length > MAX_3D_SECTION_PILLS) {
|
|
6060
|
+
const select = document.createElement("select");
|
|
6061
|
+
select.setAttribute("aria-label", this.tf("picker.jumpToSection", "Jump to section"));
|
|
6062
|
+
const placeholder = document.createElement("option");
|
|
6063
|
+
placeholder.value = "";
|
|
6064
|
+
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
6065
|
+
select.appendChild(placeholder);
|
|
6066
|
+
for (const entry of entries) {
|
|
6067
|
+
const option = document.createElement("option");
|
|
6068
|
+
option.value = entry.id;
|
|
6069
|
+
option.textContent = entry.label;
|
|
6070
|
+
select.appendChild(option);
|
|
6071
|
+
}
|
|
6072
|
+
select.addEventListener("change", () => {
|
|
6073
|
+
entries.find((entry) => entry.id === select.value)?.go();
|
|
6074
|
+
});
|
|
6075
|
+
row.appendChild(select);
|
|
6076
|
+
} else {
|
|
6077
|
+
for (const e of entries) {
|
|
6078
|
+
const b = document.createElement("button");
|
|
6079
|
+
b.type = "button";
|
|
6080
|
+
b.textContent = e.label;
|
|
6081
|
+
b.addEventListener("click", e.go);
|
|
6082
|
+
row.appendChild(b);
|
|
6083
|
+
}
|
|
5879
6084
|
}
|
|
5880
6085
|
nav.appendChild(row);
|
|
5881
6086
|
}
|
|
6087
|
+
if (wantLocator) {
|
|
6088
|
+
const locator = document.createElement("div");
|
|
6089
|
+
locator.className = "sl-view3d-locator";
|
|
6090
|
+
locator.setAttribute("role", "group");
|
|
6091
|
+
locator.setAttribute("aria-label", "Find an exact seat in 3D");
|
|
6092
|
+
const sectionSelect = document.createElement("select");
|
|
6093
|
+
sectionSelect.setAttribute("aria-label", "Choose section in 3D");
|
|
6094
|
+
const rowSelect = document.createElement("select");
|
|
6095
|
+
rowSelect.setAttribute("aria-label", "Choose row in 3D");
|
|
6096
|
+
const seatSelect = document.createElement("select");
|
|
6097
|
+
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
6098
|
+
const view = document.createElement("button");
|
|
6099
|
+
view.type = "button";
|
|
6100
|
+
view.textContent = "View seat";
|
|
6101
|
+
view.disabled = true;
|
|
6102
|
+
const fill = (select, placeholder, entries) => {
|
|
6103
|
+
select.replaceChildren();
|
|
6104
|
+
const first = document.createElement("option");
|
|
6105
|
+
first.value = "";
|
|
6106
|
+
first.textContent = placeholder;
|
|
6107
|
+
select.appendChild(first);
|
|
6108
|
+
for (const entry of entries) {
|
|
6109
|
+
const option = document.createElement("option");
|
|
6110
|
+
option.value = entry.id;
|
|
6111
|
+
option.textContent = entry.label;
|
|
6112
|
+
select.appendChild(option);
|
|
6113
|
+
}
|
|
6114
|
+
select.value = "";
|
|
6115
|
+
};
|
|
6116
|
+
fill(sectionSelect, "1. Section", allSections.map((section) => ({
|
|
6117
|
+
id: section.id,
|
|
6118
|
+
label: `${section.label} \xB7 ${section.seatCount.toLocaleString()} seats`
|
|
6119
|
+
})));
|
|
6120
|
+
fill(rowSelect, "2. Row", []);
|
|
6121
|
+
fill(seatSelect, "3. Seat", []);
|
|
6122
|
+
rowSelect.disabled = true;
|
|
6123
|
+
seatSelect.disabled = true;
|
|
6124
|
+
sectionSelect.addEventListener("change", () => {
|
|
6125
|
+
const sectionId = sectionSelect.value;
|
|
6126
|
+
view.disabled = true;
|
|
6127
|
+
fill(seatSelect, "3. Seat", []);
|
|
6128
|
+
seatSelect.disabled = true;
|
|
6129
|
+
if (!sectionId || !handle.focusSection(sectionId)) {
|
|
6130
|
+
fill(rowSelect, "2. Row", []);
|
|
6131
|
+
rowSelect.disabled = true;
|
|
6132
|
+
return;
|
|
6133
|
+
}
|
|
6134
|
+
const rows = handle.rows(sectionId);
|
|
6135
|
+
fill(rowSelect, "2. Row", rows.map((row) => ({
|
|
6136
|
+
id: row.id,
|
|
6137
|
+
label: `${row.label} \xB7 ${row.seatCount} seats`
|
|
6138
|
+
})));
|
|
6139
|
+
rowSelect.disabled = rows.length === 0;
|
|
6140
|
+
});
|
|
6141
|
+
rowSelect.addEventListener("change", () => {
|
|
6142
|
+
const rowId = rowSelect.value;
|
|
6143
|
+
view.disabled = true;
|
|
6144
|
+
if (!rowId || !handle.focusRow(rowId)) {
|
|
6145
|
+
fill(seatSelect, "3. Seat", []);
|
|
6146
|
+
seatSelect.disabled = true;
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
const seats = handle.seatsInRow(rowId);
|
|
6150
|
+
fill(seatSelect, "3. Seat", seats);
|
|
6151
|
+
seatSelect.disabled = seats.length === 0;
|
|
6152
|
+
});
|
|
6153
|
+
seatSelect.addEventListener("change", () => {
|
|
6154
|
+
const seatId = seatSelect.value;
|
|
6155
|
+
view.disabled = !seatId;
|
|
6156
|
+
handle.setSelection(seatId ? [seatId] : []);
|
|
6157
|
+
});
|
|
6158
|
+
view.addEventListener("click", () => {
|
|
6159
|
+
const seatId = seatSelect.value;
|
|
6160
|
+
if (seatId) void handle.flyToSeat(seatId);
|
|
6161
|
+
});
|
|
6162
|
+
locator.append(sectionSelect, rowSelect, seatSelect, view);
|
|
6163
|
+
nav.appendChild(locator);
|
|
6164
|
+
}
|
|
5882
6165
|
overlay.appendChild(nav);
|
|
5883
6166
|
}
|
|
5884
6167
|
async enter3d(flySeatId) {
|
|
@@ -5886,6 +6169,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5886
6169
|
const doc = this.controller.doc;
|
|
5887
6170
|
if (!doc) return;
|
|
5888
6171
|
this.buyerView = "venue3d";
|
|
6172
|
+
this.opts.onBuyerViewChange?.({ view: "venue3d", ...flySeatId ? { seatId: flySeatId } : {} });
|
|
5889
6173
|
this.root?.setAttribute("data-view3d", "on");
|
|
5890
6174
|
this.dismissConfirm();
|
|
5891
6175
|
this.syncProjection();
|
|
@@ -5899,6 +6183,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
5899
6183
|
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>`;
|
|
5900
6184
|
back.addEventListener("click", () => this.exit3d());
|
|
5901
6185
|
overlay.appendChild(back);
|
|
6186
|
+
const fullscreen = document.createElement("button");
|
|
6187
|
+
fullscreen.type = "button";
|
|
6188
|
+
fullscreen.className = "sl-view3d-fs";
|
|
6189
|
+
fullscreen.textContent = "\u26F6";
|
|
6190
|
+
fullscreen.setAttribute("aria-label", "Full screen");
|
|
6191
|
+
fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
|
|
6192
|
+
fullscreen.addEventListener("click", () => this.toggleFullscreen());
|
|
6193
|
+
overlay.appendChild(fullscreen);
|
|
6194
|
+
const loading = document.createElement("div");
|
|
6195
|
+
loading.className = "sl-view3d-loading";
|
|
6196
|
+
loading.setAttribute("role", "status");
|
|
6197
|
+
loading.setAttribute("aria-live", "polite");
|
|
6198
|
+
loading.textContent = this.tf("picker.loading3d", "Building the 3D venue\u2026");
|
|
6199
|
+
overlay.appendChild(loading);
|
|
5902
6200
|
this.els.map.appendChild(overlay);
|
|
5903
6201
|
this.view3dEl = overlay;
|
|
5904
6202
|
requestAnimationFrame(() => {
|
|
@@ -5909,8 +6207,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
5909
6207
|
const seats = expandChart(doc);
|
|
5910
6208
|
const mod = await loadVenue3d();
|
|
5911
6209
|
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
5912
|
-
const
|
|
6210
|
+
const prepared = await mod.prepareVenue3D({ doc, seats });
|
|
6211
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
6212
|
+
const handle = mod.mountVenue3D(overlay, { doc, seats, prepared }, {
|
|
6213
|
+
// A strict contain fit turns a wide bowl into a postage stamp inside a
|
|
6214
|
+
// phone. The bounded portrait fit was validated against every UX-lab
|
|
6215
|
+
// fixture; keep editor previews strict while making buyer seats legible.
|
|
6216
|
+
portraitOverviewCrop: true,
|
|
5913
6217
|
onSeatPick: (id) => this.onView3dSeatPick(id),
|
|
6218
|
+
onSectionFocusChange: (sectionId) => {
|
|
6219
|
+
const select = overlay.querySelector(
|
|
6220
|
+
'select[aria-label="Choose section in 3D"]'
|
|
6221
|
+
);
|
|
6222
|
+
const next = sectionId ?? "";
|
|
6223
|
+
if (!select || select.value === next) return;
|
|
6224
|
+
select.value = next;
|
|
6225
|
+
select.dispatchEvent(new Event("change"));
|
|
6226
|
+
},
|
|
6227
|
+
onViewTargetChange: (seatId) => {
|
|
6228
|
+
overlay.querySelector(".sl-view3d-nav")?.classList.toggle("is-seat-focused", !!seatId);
|
|
6229
|
+
this.opts.onBuyerViewChange?.({
|
|
6230
|
+
view: "venue3d",
|
|
6231
|
+
...seatId ? { seatId } : {}
|
|
6232
|
+
});
|
|
6233
|
+
},
|
|
5914
6234
|
// Deferred off the tap gesture: generateSeatPanorama walks every seat
|
|
5915
6235
|
// (O(n) on a 13k chart), and the module prefetches at pick — by the
|
|
5916
6236
|
// time the flight lands (~2.5s) the idle render has long finished. A
|
|
@@ -5930,12 +6250,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
5930
6250
|
onAnalytics: (event, props) => this.emit3dAnalytics(event, props)
|
|
5931
6251
|
});
|
|
5932
6252
|
this.view3dHandle = handle;
|
|
6253
|
+
loading.remove();
|
|
5933
6254
|
this.pushAvailabilityTo3d();
|
|
5934
6255
|
this.syncSelectionTo3d();
|
|
5935
6256
|
this.buildView3dNav(overlay, handle);
|
|
5936
6257
|
if (flySeatId) void handle.flyToSeat(flySeatId);
|
|
5937
6258
|
} catch (err) {
|
|
6259
|
+
if (gen !== this.view3dGen || this.buyerView !== "venue3d" || this.view3dEl !== overlay) return;
|
|
5938
6260
|
this.opts.onError?.(err);
|
|
6261
|
+
this.toast(this.tf("picker.unavailable3d", "3D could not start. The seat map is still available."), "warning");
|
|
5939
6262
|
this.exit3d();
|
|
5940
6263
|
}
|
|
5941
6264
|
}
|
|
@@ -5943,6 +6266,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5943
6266
|
if (this.buyerView !== "venue3d" && !this.view3dEl) return;
|
|
5944
6267
|
this.view3dGen++;
|
|
5945
6268
|
this.buyerView = "map";
|
|
6269
|
+
this.opts.onBuyerViewChange?.({ view: "map" });
|
|
5946
6270
|
this.root?.removeAttribute("data-view3d");
|
|
5947
6271
|
try {
|
|
5948
6272
|
this.view3dHandle?.dispose();
|