@seatlayer/js 0.48.2 → 0.50.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/README.md +16 -2
- package/dist/{channelsMode-NCH5GYHN.js → channelsMode-S4GEWCWS.js} +2 -3
- package/dist/{chunk-KNMZQZXR.js → chunk-5URCWBHM.js} +490 -5
- package/dist/chunk-5URCWBHM.js.map +1 -0
- package/dist/{hostedCheckout-F6C6VCCG.js → hostedCheckout-GVS6UKYA.js} +3 -1
- package/dist/hostedCheckout-GVS6UKYA.js.map +1 -0
- package/dist/index.cjs +5367 -10881
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -679
- package/dist/index.d.ts +112 -679
- package/dist/index.js +977 -153
- package/dist/index.js.map +1 -1
- package/dist/manager.cjs +128 -20
- package/dist/manager.cjs.map +1 -1
- package/dist/manager.d.cts +2275 -1
- package/dist/manager.d.ts +2275 -1
- package/dist/manager.js +2771 -6
- package/dist/manager.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-H4OJF6LE.js +0 -492
- package/dist/chunk-H4OJF6LE.js.map +0 -1
- package/dist/chunk-KNMZQZXR.js.map +0 -1
- package/dist/chunk-SD6GL3DT.js +0 -2681
- package/dist/chunk-SD6GL3DT.js.map +0 -1
- package/dist/hostedCheckout-F6C6VCCG.js.map +0 -1
- package/dist/manager-DEjbKiqJ.d.cts +0 -1602
- package/dist/manager-DEjbKiqJ.d.ts +0 -1602
- /package/dist/{channelsMode-NCH5GYHN.js.map → channelsMode-S4GEWCWS.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,38 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SeatManager
|
|
3
|
-
} from "./chunk-SD6GL3DT.js";
|
|
4
|
-
import {
|
|
5
|
-
ACCESS_LINK_DEFAULTS,
|
|
6
|
-
ChannelsMode,
|
|
7
|
-
PUBLIC_CHANNEL_ID,
|
|
8
|
-
PUBLIC_CHANNEL_NAME,
|
|
9
|
-
accessIntentDescription,
|
|
10
|
-
accessIntentLabel,
|
|
11
|
-
accessLine,
|
|
12
|
-
accessLinkBadge,
|
|
13
|
-
accessLinkErrorCopy,
|
|
14
|
-
accessLinkIsLive,
|
|
15
|
-
accessLinkPolicyLines,
|
|
16
|
-
bucketRows,
|
|
17
|
-
bucketRowsHtml,
|
|
18
|
-
dropReviewRows,
|
|
19
|
-
intentForbidsCopy,
|
|
20
|
-
intentSwitchBlockedCopy,
|
|
21
|
-
isPublicChannelId,
|
|
22
|
-
markerLetter,
|
|
23
|
-
markerOf,
|
|
24
|
-
mutationCount,
|
|
25
|
-
needsMoveConfirmation,
|
|
26
|
-
planAssignment,
|
|
27
|
-
retryAfterCopy,
|
|
28
|
-
selectionSources,
|
|
29
|
-
stateBadge,
|
|
30
|
-
suggestMarker
|
|
31
|
-
} from "./chunk-KNMZQZXR.js";
|
|
32
|
-
import {
|
|
33
|
-
ManageApi,
|
|
34
|
-
ManageApiError
|
|
35
|
-
} from "./chunk-H4OJF6LE.js";
|
|
36
1
|
import {
|
|
37
2
|
__privateAdd,
|
|
38
3
|
__privateGet,
|
|
@@ -1448,6 +1413,12 @@ var EmbeddedDesigner = class {
|
|
|
1448
1413
|
*/
|
|
1449
1414
|
this.autoRecoverUsed = false;
|
|
1450
1415
|
this.phase = "loading";
|
|
1416
|
+
/**
|
|
1417
|
+
* Set only after an identity-checked `ready`. Unlike `phase`, this remains true
|
|
1418
|
+
* if a later fatal error renders the error card, so no subsequent callback can
|
|
1419
|
+
* shed the chart/workspace identity the live session already established.
|
|
1420
|
+
*/
|
|
1421
|
+
this.identityEstablished = false;
|
|
1451
1422
|
this.restoreContainerPosition = null;
|
|
1452
1423
|
// Host-side fullscreen pin: saved state we restore on `off`/Escape/destroy.
|
|
1453
1424
|
this.pinned = false;
|
|
@@ -1514,14 +1485,21 @@ var EmbeddedDesigner = class {
|
|
|
1514
1485
|
expiresAt: typeof data.expiresAt === "number" ? data.expiresAt : void 0,
|
|
1515
1486
|
code: typeof data.code === "string" ? data.code : void 0,
|
|
1516
1487
|
message: typeof data.message === "string" ? data.message : void 0,
|
|
1517
|
-
meta: data.meta
|
|
1488
|
+
meta: data.meta,
|
|
1489
|
+
fatal: typeof data.fatal === "boolean" ? data.fatal : void 0,
|
|
1490
|
+
action: typeof data.action === "string" ? data.action : void 0
|
|
1518
1491
|
};
|
|
1519
|
-
|
|
1492
|
+
const identityRequired = this.identityEstablished || message.type === "seatlayer.designer.ready" || message.type === "seatlayer.designer.saved" || message.type === "seatlayer.designer.published" || message.type === "seatlayer.designer.close";
|
|
1493
|
+
const chartMismatch = this.options.expectedChartId !== void 0 && message.chartId !== this.options.expectedChartId && (identityRequired || message.chartId !== void 0);
|
|
1494
|
+
const workspaceMismatch = this.options.expectedWorkspaceId !== void 0 && message.workspaceId !== this.options.expectedWorkspaceId && (identityRequired || message.workspaceId !== void 0);
|
|
1495
|
+
if (chartMismatch || workspaceMismatch) {
|
|
1520
1496
|
this.showError("mismatch");
|
|
1521
1497
|
return;
|
|
1522
1498
|
}
|
|
1523
1499
|
switch (message.type) {
|
|
1524
1500
|
case "seatlayer.designer.ready":
|
|
1501
|
+
this.identityEstablished = true;
|
|
1502
|
+
this.sessionExpiresAt = message.expiresAt;
|
|
1525
1503
|
this.phase = "ready";
|
|
1526
1504
|
this.clearTimeoutTimer();
|
|
1527
1505
|
this.removeOverlay();
|
|
@@ -1603,6 +1581,25 @@ var EmbeddedDesigner = class {
|
|
|
1603
1581
|
getIframe() {
|
|
1604
1582
|
return this.frame;
|
|
1605
1583
|
}
|
|
1584
|
+
/** Update iframe sizing without replacing the live Designer session. */
|
|
1585
|
+
setSizing(height, minHeight) {
|
|
1586
|
+
this.options = { ...this.options, height, minHeight };
|
|
1587
|
+
if (!this.frame) return;
|
|
1588
|
+
this.stopFill();
|
|
1589
|
+
this.lastAutoHeight = "";
|
|
1590
|
+
if (this.fillEnabled()) {
|
|
1591
|
+
if (!this.pinned) this.setFrameHeight("100%");
|
|
1592
|
+
this.startFill();
|
|
1593
|
+
} else if (!this.pinned) {
|
|
1594
|
+
this.setFrameHeight(`${height}px`);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
/** Update renewal/expiry-recovery policy without replacing the iframe. */
|
|
1598
|
+
setRelaunchPolicy(onRequestRelaunch, autoRenewSession) {
|
|
1599
|
+
this.options = { ...this.options, onRequestRelaunch, autoRenewSession };
|
|
1600
|
+
this.clearRenewTimer();
|
|
1601
|
+
if (this.phase === "ready") this.scheduleRenewal(this.sessionExpiresAt);
|
|
1602
|
+
}
|
|
1606
1603
|
destroy() {
|
|
1607
1604
|
window.removeEventListener("message", this.handleMessage);
|
|
1608
1605
|
this.stopFill();
|
|
@@ -1617,6 +1614,8 @@ var EmbeddedDesigner = class {
|
|
|
1617
1614
|
this.fillMode = null;
|
|
1618
1615
|
this.designerOrigin = "";
|
|
1619
1616
|
this.phase = "loading";
|
|
1617
|
+
this.identityEstablished = false;
|
|
1618
|
+
this.sessionExpiresAt = void 0;
|
|
1620
1619
|
this.lastAutoHeight = "";
|
|
1621
1620
|
}
|
|
1622
1621
|
loadingStateEnabled() {
|
|
@@ -1767,6 +1766,7 @@ var EmbeddedDesigner = class {
|
|
|
1767
1766
|
else this.frame.setAttribute("style", this.frameStyleBeforeFs);
|
|
1768
1767
|
if (this.fillEnabled()) this.applyFill();
|
|
1769
1768
|
else if (this.autoResizeEnabled() && this.lastAutoHeight) this.setFrameHeight(this.lastAutoHeight);
|
|
1769
|
+
else if (typeof this.options.height === "number") this.setFrameHeight(`${this.options.height}px`);
|
|
1770
1770
|
}
|
|
1771
1771
|
this.frameStyleBeforeFs = null;
|
|
1772
1772
|
if (this.docOverflowBeforeFs !== null) {
|
|
@@ -2031,6 +2031,7 @@ import {
|
|
|
2031
2031
|
tCount
|
|
2032
2032
|
} from "@seatlayer/core";
|
|
2033
2033
|
import { isAuthoredSeatView, seatViewDisclosure } from "@seatlayer/core/view3d/crossfade/panorama";
|
|
2034
|
+
import { seatConfidenceDisclosure } from "@seatlayer/core/core/seatConfidence";
|
|
2034
2035
|
import {
|
|
2035
2036
|
browserPanoramaConstraints,
|
|
2036
2037
|
loadPanoramaImage,
|
|
@@ -2328,7 +2329,7 @@ async function loadHostedCheckout() {
|
|
|
2328
2329
|
cdnChunkUrl("seatlayer-checkout.mjs")
|
|
2329
2330
|
);
|
|
2330
2331
|
}
|
|
2331
|
-
return import("./hostedCheckout-
|
|
2332
|
+
return import("./hostedCheckout-GVS6UKYA.js");
|
|
2332
2333
|
}
|
|
2333
2334
|
function paymentsOffReason(reason) {
|
|
2334
2335
|
return reason === "unavailable_for_event" || reason === "payments_off_for_event" ? reason : "not_configured";
|
|
@@ -2344,7 +2345,12 @@ var CSS = (
|
|
|
2344
2345
|
never inherits (or fights) the host page's own timing. Values mirror
|
|
2345
2346
|
docs/motion-system-2026-08-01.md \xA72. */
|
|
2346
2347
|
--slm-mo-instant:80ms;--slm-mo-quick:140ms;--slm-mo-base:200ms;--slm-mo-slow:320ms;
|
|
2347
|
-
--slm-mo-out:cubic-bezier(0.2,0.8,0.2,1);--slm-mo-exit:cubic-bezier(0.4,0,1,1)
|
|
2348
|
+
--slm-mo-out:cubic-bezier(0.2,0.8,0.2,1);--slm-mo-exit:cubic-bezier(0.4,0,1,1);
|
|
2349
|
+
/* Semantic palette tokens, defined here (not resolveTokens) because they are
|
|
2350
|
+
the WIDGET's meanings, not an org's brand: premium gold, environment
|
|
2351
|
+
warning, danger, success. Hosts can still override them with CSS. */
|
|
2352
|
+
--sl-premium:#e8c15a;--sl-premium-deep:#c9a24b;--sl-premium-ink:#1c1608;
|
|
2353
|
+
--sl-warn:#d8a425;--sl-danger:#e5484d;--sl-success:#22a06b}
|
|
2348
2354
|
.sl-picker *{box-sizing:border-box;margin:0;padding:0}
|
|
2349
2355
|
.sl-picker button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
|
|
2350
2356
|
|
|
@@ -2370,21 +2376,41 @@ var CSS = (
|
|
|
2370
2376
|
.sl-close.on{display:inline-flex}
|
|
2371
2377
|
.sl-close svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
|
|
2372
2378
|
|
|
2373
|
-
/* A page or popup may already own the event heading
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
.
|
|
2377
|
-
|
|
2378
|
-
.
|
|
2379
|
-
.
|
|
2380
|
-
.sl-picker[data-event-details-hidden="true"] .sl-
|
|
2381
|
-
.sl-picker[data-event-details-hidden="true"] .sl-
|
|
2379
|
+
/* A page or popup may already own the event heading \u2014 but "already" is true at
|
|
2380
|
+
load and false thirty seconds later: the buyer scrolls, the sheet opens over
|
|
2381
|
+
the map, fullscreen removes the page, and two-tab comparison shopping is
|
|
2382
|
+
normal ticket behaviour. So identity DEMOTES instead of disappearing: one
|
|
2383
|
+
compact line (thumb + name) that answers "which event is this money for"
|
|
2384
|
+
without competing with the host's own heading. The hold clock keeps a
|
|
2385
|
+
labelled row around it instead of floating in a dark corner. */
|
|
2386
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head{padding-block:7px}
|
|
2387
|
+
.sl-picker[data-event-details-hidden="true"] .sl-logo{width:22px;height:22px;border-radius:6px;font-size:11px}
|
|
2388
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head-name{font-size:12.5px}
|
|
2389
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head-meta{display:none}
|
|
2382
2390
|
|
|
2383
2391
|
/* body */
|
|
2384
2392
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
2385
2393
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
2386
2394
|
.sl-map-host{position:absolute;inset:0}
|
|
2387
2395
|
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow:hidden}
|
|
2396
|
+
/* Wide-layout panel collapse: THE CHART IS THE PRODUCT, and a fixed 300px
|
|
2397
|
+
panel holding an empty cart is the map's room spent on nothing. The panel
|
|
2398
|
+
slides to zero (children keep their laid-out width so text doesn't rewrap
|
|
2399
|
+
mid-slide); the "Tickets" pill in the top-right region brings it back, and
|
|
2400
|
+
the first seat landing in the cart reopens it automatically \u2014 a collapsed
|
|
2401
|
+
panel must never hide a checkout. */
|
|
2402
|
+
.sl-picker[data-layout="wide"] .sl-side{transition:width var(--slm-mo-slow) var(--slm-mo-out)}
|
|
2403
|
+
.sl-picker[data-layout="wide"][data-side-collapsed="true"] .sl-side{width:0;min-width:0;border-left-width:0}
|
|
2404
|
+
/* min-width:0 above is load-bearing: the panel is a flex item, and its
|
|
2405
|
+
automatic minimum size (from these 300px-wide children) would otherwise
|
|
2406
|
+
clamp the explicit width:0 straight back to 300 \u2014 verified in-browser. */
|
|
2407
|
+
.sl-picker[data-layout="wide"][data-side-collapsed="true"] .sl-side>*{min-width:300px}
|
|
2408
|
+
.sl-side-toggle{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;padding:0 12px;
|
|
2409
|
+
border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;white-space:nowrap;
|
|
2410
|
+
background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
|
|
2411
|
+
.sl-side-toggle:hover{border-color:var(--sl-muted)}
|
|
2412
|
+
.sl-side-toggle svg{width:13px;height:13px;stroke:currentColor;stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2413
|
+
.sl-picker[data-layout="narrow"] .sl-side-toggle{display:none}
|
|
2388
2414
|
|
|
2389
2415
|
/* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
|
|
2390
2416
|
container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
|
|
@@ -2467,6 +2493,15 @@ var CSS = (
|
|
|
2467
2493
|
the buyer explicitly opens the ticket panel. */
|
|
2468
2494
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filtersec.has{display:block!important}
|
|
2469
2495
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filters.has{display:flex!important}
|
|
2496
|
+
/* The price list is the mobile buyer's ONLY legend \u2014 colour \u2192 ticket type \u2192
|
|
2497
|
+
price \u2192 how many are left. Hiding it in the peek state is right (map-first);
|
|
2498
|
+
never restoring it forced phone buyers to tap dots to learn prices. The OPEN
|
|
2499
|
+
sheet brings it back, exactly like the filters row above. The tray hint comes
|
|
2500
|
+
back with it: the first-time buyer on a phone needs the instruction more than
|
|
2501
|
+
the desktop buyer, not less. */
|
|
2502
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices-sec{display:flex!important}
|
|
2503
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices{display:flex!important}
|
|
2504
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-tray-hint{display:block!important}
|
|
2470
2505
|
.sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
2471
2506
|
color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
2472
2507
|
.sl-cbbtn:hover{border-color:var(--sl-muted)}
|
|
@@ -2476,13 +2511,13 @@ var CSS = (
|
|
|
2476
2511
|
.sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
|
|
2477
2512
|
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
2478
2513
|
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
2479
|
-
.sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:
|
|
2514
|
+
.sl-offer-copy{min-width:0}.sl-offer-kicker{display:block;font-size:10px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-accent)}
|
|
2480
2515
|
.sl-offer-name{display:block;margin-top:2px;font-size:13px;font-weight:800;line-height:1.3}.sl-offer-line{display:block;margin-top:3px;font-size:11px;line-height:1.35;color:var(--sl-muted)}
|
|
2481
2516
|
.sl-offer-info{position:relative;flex:none}.sl-offer-info>summary{list-style:none;width:25px;height:25px;border:1px solid var(--sl-line);border-radius:999px;
|
|
2482
2517
|
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
2483
2518
|
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
2484
2519
|
.sl-offer-detail{margin-top:10px;padding-top:9px;border-top:1px solid var(--sl-line);font-size:10.5px;line-height:1.45;color:var(--sl-muted)}
|
|
2485
|
-
.sl-sec{padding:14px 14px 4px;font-size:
|
|
2520
|
+
.sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
2486
2521
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
2487
2522
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
2488
2523
|
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
@@ -2492,7 +2527,7 @@ var CSS = (
|
|
|
2492
2527
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
2493
2528
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
2494
2529
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
2495
|
-
.sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:
|
|
2530
|
+
.sl-price-was{margin-left:auto;color:var(--sl-muted);font-size:10px;text-decoration:line-through}.sl-price-offer{display:block;color:var(--sl-accent);font-size:10px;font-weight:750}
|
|
2496
2531
|
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
2497
2532
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
2498
2533
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
@@ -2535,7 +2570,7 @@ var CSS = (
|
|
|
2535
2570
|
.sl-chip-id .fld{min-width:0}
|
|
2536
2571
|
.sl-chip-id .fld.sec{flex:1}
|
|
2537
2572
|
.sl-chip-id .fld.mid{flex:none;text-align:center}
|
|
2538
|
-
.sl-chip-eb{display:block;font-size:
|
|
2573
|
+
.sl-chip-eb{display:block;font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);margin-bottom:1px}
|
|
2539
2574
|
.sl-chip-id .val{display:block;font-weight:600;font-size:13px;line-height:1.25;white-space:nowrap}
|
|
2540
2575
|
.sl-chip-id .fld.sec .val{overflow:hidden;text-overflow:ellipsis}
|
|
2541
2576
|
.sl-chip-sub{display:flex;align-items:center;gap:6px;min-width:0}
|
|
@@ -2545,7 +2580,7 @@ var CSS = (
|
|
|
2545
2580
|
.sl-chip .rm,.sl-chip .view{flex:1;min-height:26px;border-radius:0;display:flex;align-items:center;justify-content:center;
|
|
2546
2581
|
color:var(--sl-muted);transition:color .15s,background .15s}
|
|
2547
2582
|
.sl-chip .view{border-top:1px solid var(--sl-line)}
|
|
2548
|
-
.sl-chip .rm:hover,.sl-chip .rm:focus-visible{color
|
|
2583
|
+
.sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:var(--sl-danger);background:color-mix(in srgb,var(--sl-danger) 9%,transparent)}
|
|
2549
2584
|
.sl-chip .view:hover,.sl-chip .view:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
|
|
2550
2585
|
.sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
|
|
2551
2586
|
.sl-chip .view svg{width:13px;height:13px;stroke:currentColor;stroke-width:1.8;fill:none}
|
|
@@ -2555,7 +2590,7 @@ var CSS = (
|
|
|
2555
2590
|
.sl-live{display:none;align-items:center;gap:7px;margin:10px 14px 0;padding:7px 9px;flex:none;
|
|
2556
2591
|
border:1px solid var(--sl-line);border-radius:8px;background:color-mix(in srgb,var(--sl-accent) 4%,var(--sl-surface));
|
|
2557
2592
|
font-size:11px;color:var(--sl-muted)}
|
|
2558
|
-
.sl-live .dot{width:6px;height:6px;border-radius:999px;background
|
|
2593
|
+
.sl-live .dot{width:6px;height:6px;border-radius:999px;background:var(--sl-success);box-shadow:0 0 6px color-mix(in srgb,var(--sl-success) 75%,transparent);flex:none}
|
|
2559
2594
|
.sl-live span:last-child{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2560
2595
|
.sl-live.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
2561
2596
|
|
|
@@ -2625,17 +2660,17 @@ var CSS = (
|
|
|
2625
2660
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-left"]{max-width:30%}
|
|
2626
2661
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
2627
2662
|
|
|
2628
|
-
/* TEST MODE is environment context, not an action. A
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2663
|
+
/* TEST MODE is environment context, not an action. A small squared chip that
|
|
2664
|
+
flows FIRST in the top-left anchor region: the old rotated corner ribbon sat
|
|
2665
|
+
at a negative offset and was clipped by the widget root's rounded
|
|
2666
|
+
overflow:hidden (its ends cut mid-word), and its accent fill made an
|
|
2667
|
+
environment flag wear the same gold as "buy". Amber warning tone, dashed
|
|
2668
|
+
border \u2014 reads as a tag, never as a control; the a11y chips wrap beside it
|
|
2669
|
+
in the same region, so no displacement hack is needed. */
|
|
2670
|
+
.sl-testbadge{display:inline-flex;align-items:center;padding:5px 10px;border-radius:7px;pointer-events:none;
|
|
2671
|
+
font-size:10px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;white-space:nowrap;
|
|
2672
|
+
color:var(--sl-warn);background:color-mix(in srgb,var(--sl-warn) 13%,var(--sl-surface));
|
|
2673
|
+
border:1px dashed color-mix(in srgb,var(--sl-warn) 55%,transparent)}
|
|
2639
2674
|
|
|
2640
2675
|
/* zoom column (flows within the bottom-right region) */
|
|
2641
2676
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -2645,6 +2680,18 @@ var CSS = (
|
|
|
2645
2680
|
color:var(--sl-text);font-size:17px;font-weight:700;display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
2646
2681
|
.sl-zoom button:hover{border-color:var(--sl-muted)}
|
|
2647
2682
|
.sl-zoom svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2683
|
+
/* Full-screen promotion on phones: the capability existed (native + iOS overlay
|
|
2684
|
+
+ framed-host paths) but hid as the 4th unlabelled glyph in a corner stack.
|
|
2685
|
+
On narrow layouts dockLayoutChrome moves the SAME button into the top-right
|
|
2686
|
+
region beside Map|3D and this class dresses it as a labelled pill \u2014 the one
|
|
2687
|
+
accelerator with the highest payoff on a small screen gets words. */
|
|
2688
|
+
.sl-zfs-lbl{display:none}
|
|
2689
|
+
.sl-fs-pill.sl-fs-pill{display:inline-flex;align-items:center;justify-content:center;width:auto;min-height:40px;height:auto;
|
|
2690
|
+
padding:0 13px;gap:6px;border-radius:999px;font-size:11px;font-weight:800;
|
|
2691
|
+
background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
|
|
2692
|
+
.sl-fs-pill.sl-fs-pill:hover{border-color:var(--sl-muted)}
|
|
2693
|
+
.sl-fs-pill svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2694
|
+
.sl-fs-pill .sl-zfs-lbl{display:inline;letter-spacing:.02em;white-space:nowrap}
|
|
2648
2695
|
|
|
2649
2696
|
/* toast + boot states (toast flows in the bottom-center region) */
|
|
2650
2697
|
.sl-toast{transform:translateY(6px) scale(.98);max-width:100%;
|
|
@@ -2655,9 +2702,9 @@ var CSS = (
|
|
|
2655
2702
|
.sl-toast.has-action{pointer-events:auto;display:flex;align-items:center;gap:12px;padding-right:8px}
|
|
2656
2703
|
.sl-toast-action{min-height:30px;padding:5px 10px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
2657
2704
|
font:inherit;font-weight:800}
|
|
2658
|
-
.sl-toast[data-tone="error"]{border-color
|
|
2705
|
+
.sl-toast[data-tone="error"]{border-color:var(--sl-danger)}
|
|
2659
2706
|
.sl-toast[data-tone="warning"]{border-color:var(--sl-accent)}
|
|
2660
|
-
.sl-toast[data-tone="success"]{border-color
|
|
2707
|
+
.sl-toast[data-tone="success"]{border-color:var(--sl-success)}
|
|
2661
2708
|
.sl-toast.on[data-tone="error"]{animation:slToastNudge .32s ease-out}
|
|
2662
2709
|
.sl-boot{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;align-items:center;justify-content:center;
|
|
2663
2710
|
gap:10px;background:var(--sl-bg);font-size:13px;font-weight:600;color:var(--sl-muted)}
|
|
@@ -2699,10 +2746,9 @@ var CSS = (
|
|
|
2699
2746
|
.sl-booked.on .sl-booked-title{animation-delay:.22s}
|
|
2700
2747
|
.sl-booked.on .sl-booked-sub{animation-delay:.3s}
|
|
2701
2748
|
|
|
2702
|
-
/* sold-out overlay \u2014 every SEATED category's live availability is 0.
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
the moment WS frees a seat up. */
|
|
2749
|
+
/* sold-out overlay \u2014 every SEATED category's live availability is 0. This is
|
|
2750
|
+
informational only: no waitlist workflow exists. Suppressed when GA areas
|
|
2751
|
+
exist (GA capacity isn't seat-counted). Clears live when WS frees a seat. */
|
|
2706
2752
|
.sl-soldout{position:absolute;inset:0;z-index:10;display:none;flex-direction:column;align-items:center;
|
|
2707
2753
|
justify-content:center;text-align:center;gap:8px;padding:24px;
|
|
2708
2754
|
background:color-mix(in srgb,var(--sl-bg) 82%,transparent);backdrop-filter:blur(4px)}
|
|
@@ -2710,9 +2756,6 @@ var CSS = (
|
|
|
2710
2756
|
.sl-soldout-eyebrow{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sl-accent);font-weight:800}
|
|
2711
2757
|
.sl-soldout-title{font-size:32px;font-weight:800;color:var(--sl-text);line-height:1.05}
|
|
2712
2758
|
.sl-soldout-copy{max-width:360px;font-size:13px;color:var(--sl-muted);line-height:1.5}
|
|
2713
|
-
.sl-picker .sl-soldout-btn{margin-top:10px;min-height:40px;padding:10px 18px;border-radius:var(--sl-r-sm);
|
|
2714
|
-
background:var(--sl-surface);color:var(--sl-muted);border:1px solid var(--sl-line);font-weight:800;font-size:13px;
|
|
2715
|
-
cursor:not-allowed;opacity:.85}
|
|
2716
2759
|
|
|
2717
2760
|
/* sales-closed pill (header) \u2014 persistent read-only state when the event's sales
|
|
2718
2761
|
window is closed at load or closes live mid-session. Neutral (not accent) so it
|
|
@@ -2757,7 +2800,7 @@ var CSS = (
|
|
|
2757
2800
|
.sl-confirm-field{min-width:0;padding:12px 11px 10px;border-right:1px solid var(--sl-line)}
|
|
2758
2801
|
.sl-confirm-field:last-child{border-right:0;text-align:center}
|
|
2759
2802
|
.sl-confirm-field:nth-child(2){text-align:center}
|
|
2760
|
-
.sl-confirm-key{display:block;font-size:
|
|
2803
|
+
.sl-confirm-key{display:block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
|
|
2761
2804
|
.sl-confirm-value{display:block;margin-top:4px;color:var(--sl-text);font-size:17px;line-height:1.1;font-weight:850;
|
|
2762
2805
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2763
2806
|
/* Long venue section names must read in full: smaller type + up to two lines
|
|
@@ -2822,7 +2865,7 @@ var CSS = (
|
|
|
2822
2865
|
.sl-tip-field{min-width:0;padding:6px 9px;border-right:1px solid var(--sl-line)}
|
|
2823
2866
|
.sl-tip-field:last-child{border-right:0;text-align:center}
|
|
2824
2867
|
.sl-tip-grid:not(.one) .sl-tip-field:nth-child(2){text-align:center}
|
|
2825
|
-
.sl-tip-key{display:block;font-size:
|
|
2868
|
+
.sl-tip-key{display:block;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--sl-muted);font-weight:800}
|
|
2826
2869
|
.sl-tip-val{display:block;margin-top:2px;color:var(--sl-text);font-size:13px;line-height:1.1;font-weight:750;
|
|
2827
2870
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2828
2871
|
.sl-tip-cat{display:flex;align-items:center;gap:7px;padding:6px 10px;font-size:11px;
|
|
@@ -2830,7 +2873,7 @@ var CSS = (
|
|
|
2830
2873
|
.sl-tip-dot{width:8px;height:8px;border-radius:50%;flex:none}
|
|
2831
2874
|
.sl-tip-name{color:var(--sl-muted);flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2832
2875
|
.sl-tip-amt{margin-left:auto;font-weight:800;color:var(--sl-text);font-variant-numeric:tabular-nums;font-size:12px}
|
|
2833
|
-
.sl-tip-status{padding:5px 10px 7px;font-size:
|
|
2876
|
+
.sl-tip-status{padding:5px 10px 7px;font-size:10px;letter-spacing:.09em;text-transform:uppercase;font-weight:700;color:var(--sl-muted)}
|
|
2834
2877
|
|
|
2835
2878
|
/* Best available is a first-class shortcut, not an anonymous utility row. */
|
|
2836
2879
|
.sl-ba{position:relative;flex:none;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:7px;
|
|
@@ -2846,12 +2889,12 @@ var CSS = (
|
|
|
2846
2889
|
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
2847
2890
|
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
2848
2891
|
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
2849
|
-
color
|
|
2850
|
-
border:1px solid color-mix(in srgb
|
|
2851
|
-
.sl-ba-premium .star{font-size:12px;line-height:1;color
|
|
2892
|
+
color:var(--sl-premium-deep);background:color-mix(in srgb,var(--sl-premium) 10%,var(--sl-surface));
|
|
2893
|
+
border:1px solid color-mix(in srgb,var(--sl-premium) 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
2894
|
+
.sl-ba-premium .star{font-size:12px;line-height:1;color:var(--sl-premium)}
|
|
2852
2895
|
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
2853
|
-
.sl-ba-premium.on{color
|
|
2854
|
-
box-shadow:0 6px 16px color-mix(in srgb
|
|
2896
|
+
.sl-ba-premium.on{color:var(--sl-premium-ink);background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
|
|
2897
|
+
box-shadow:0 6px 16px color-mix(in srgb,var(--sl-premium) 26%,transparent)}
|
|
2855
2898
|
.sl-ba-premium.on .star{color:#5a4410}
|
|
2856
2899
|
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
2857
2900
|
font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
|
|
@@ -2863,7 +2906,15 @@ var CSS = (
|
|
|
2863
2906
|
color:var(--sl-accent-ink);font-weight:800;font-size:12px;transition:filter .15s,opacity .15s;display:flex;align-items:center;justify-content:center;gap:6px;
|
|
2864
2907
|
box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
|
|
2865
2908
|
.sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
|
|
2866
|
-
|
|
2909
|
+
/* ONE disabled language, shared with the checkout CTA: surface + muted ink +
|
|
2910
|
+
not-allowed. A gold button at 62% still reads as gold, and cursor:wait means
|
|
2911
|
+
"working" \u2014 that pairing made a dead control look live. The wait cursor is
|
|
2912
|
+
reserved for the genuinely in-flight search, which keeps its accent fill
|
|
2913
|
+
via .sl-busy. */
|
|
2914
|
+
.sl-picker .sl-ba-go:disabled{background:var(--sl-surface);color:var(--sl-muted);cursor:not-allowed;
|
|
2915
|
+
filter:none;box-shadow:none}
|
|
2916
|
+
.sl-picker .sl-ba-go.sl-busy:disabled{background:var(--sl-accent);color:var(--sl-accent-ink);opacity:.85;cursor:wait;
|
|
2917
|
+
box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
|
|
2867
2918
|
.sl-ba-replace{grid-column:1/-1;padding:3px 0 1px}
|
|
2868
2919
|
.sl-ba-replace b{display:block;font-size:12.5px}
|
|
2869
2920
|
.sl-ba-replace span{display:block;margin-top:3px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
@@ -2880,6 +2931,35 @@ var CSS = (
|
|
|
2880
2931
|
.sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
|
|
2881
2932
|
.sl-picker[data-layout="narrow"] .sl-ba-go{min-height:40px}
|
|
2882
2933
|
|
|
2934
|
+
/* Sales-closed is a designed state, not a set of disables: one statement in
|
|
2935
|
+
the tray where the buying flow was, carrying the event's own date so the
|
|
2936
|
+
buyer knows which night the verdict belongs to. Neutral like the header
|
|
2937
|
+
pill \u2014 "unavailable" never wears the accent. */
|
|
2938
|
+
.sl-closed-note{flex:none;display:flex;flex-direction:column;gap:4px;padding:13px;border-radius:13px;
|
|
2939
|
+
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-text) 5%,var(--sl-surface))}
|
|
2940
|
+
.sl-closed-note b{font-size:13px;font-weight:850}
|
|
2941
|
+
.sl-closed-note span{font-size:11.5px;line-height:1.45;color:var(--sl-muted)}
|
|
2942
|
+
.sl-closed-note .when{display:block;margin-top:2px;font-size:11.5px;font-weight:700;color:var(--sl-text)}
|
|
2943
|
+
|
|
2944
|
+
/* 44px HIT FLOOR ON TOUCH. Visual sizes stay exactly as designed \u2014 the target
|
|
2945
|
+
grows through an invisible centred pseudo-element, the same grow-the-target-
|
|
2946
|
+
not-the-ink pattern the hosted event page documents for its footer links.
|
|
2947
|
+
Applied only to controls with clearance around them; tightly stacked pairs
|
|
2948
|
+
(the cart chip's remove/view rail) instead get real height on narrow. */
|
|
2949
|
+
@media (pointer:coarse){
|
|
2950
|
+
.sl-zoom button,.sl-cbbtn,.sl-close,.sl-ga-qty button,.sl-ba-qty button,.sl-price-more,
|
|
2951
|
+
.sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle{position:relative}
|
|
2952
|
+
.sl-zoom button::after,.sl-cbbtn::after,.sl-close::after,.sl-ga-qty button::after,.sl-ba-qty button::after,
|
|
2953
|
+
.sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after{
|
|
2954
|
+
content:'';position:absolute;top:50%;left:50%;width:max(100%,44px);height:max(100%,44px);
|
|
2955
|
+
transform:translate(-50%,-50%)}
|
|
2956
|
+
/* The chip rail stacks remove over view in ~53px \u2014 pseudo-targets would
|
|
2957
|
+
overlap and make a destructive tap ambiguous. Give the pair real height. */
|
|
2958
|
+
.sl-picker[data-layout="narrow"] .sl-chip{min-height:64px}
|
|
2959
|
+
.sl-picker[data-layout="narrow"] .sl-chip .rm,
|
|
2960
|
+
.sl-picker[data-layout="narrow"] .sl-chip .view{min-height:32px}
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2883
2963
|
/* screen-reader live region */
|
|
2884
2964
|
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
2885
2965
|
|
|
@@ -2914,6 +2994,12 @@ var CSS = (
|
|
|
2914
2994
|
Map|3D toggle and the seat confirm both stay usable over it. */
|
|
2915
2995
|
.sl-view3d{position:absolute;inset:0;z-index:4;opacity:0;touch-action:none;
|
|
2916
2996
|
transition:opacity .3s ease;background:radial-gradient(120% 120% at 50% 0%,#191f28 0%,#0d1014 70%)}
|
|
2997
|
+
.sl-view3d.has-comparison,.sl-view3d.has-passport{z-index:20}
|
|
2998
|
+
/* Confirm mode normally disables the entire GL sibling. Modal surfaces live
|
|
2999
|
+
inside that sibling, so explicitly restore pointer input only while one is
|
|
3000
|
+
open; their own inert contract keeps the venue underneath unavailable. */
|
|
3001
|
+
.sl-picker[data-confirming="true"] .sl-view3d.has-comparison,
|
|
3002
|
+
.sl-picker[data-confirming="true"] .sl-view3d.has-passport{pointer-events:auto}
|
|
2917
3003
|
.sl-view3d canvas{display:block;width:100%;height:100%}
|
|
2918
3004
|
.sl-view3d canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
2919
3005
|
.sl-view3d-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:10px;
|
|
@@ -2922,10 +3008,13 @@ var CSS = (
|
|
|
2922
3008
|
border:2px solid rgba(217,226,242,.28);border-top-color:#d9e2f2;animation:slSpin .8s linear infinite}
|
|
2923
3009
|
[data-view3d=on] .sl-chips,[data-view3d=on] .sl-rungs{display:none}
|
|
2924
3010
|
.sl-view3d-back{position:absolute;top:12px;left:12px;z-index:2;display:inline-flex;align-items:center;gap:6px;
|
|
2925
|
-
padding:7px 13px 7px 9px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.03em;
|
|
3011
|
+
min-height:44px;padding:7px 13px 7px 9px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.03em;
|
|
2926
3012
|
color:#e6edf3;background:rgba(10,14,20,.62);border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
2927
3013
|
.sl-view3d-back:hover,.sl-view3d-back:focus-visible{background:rgba(16,22,30,.82);border-color:rgba(255,255,255,.4)}
|
|
2928
3014
|
.sl-view3d-back svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3015
|
+
/* Map|3D already exits an overview to the 2D map. Reserve Back for the one
|
|
3016
|
+
state where it adds meaning: returning from an exact seat to the venue. */
|
|
3017
|
+
.sl-view3d:not(.is-seat-focused) .sl-view3d-back{display:none}
|
|
2929
3018
|
.sl-view3d-fs{position:absolute;top:12px;right:12px;z-index:2;min-width:44px;min-height:44px;padding:8px 12px;
|
|
2930
3019
|
border-radius:999px;font-size:16px;color:#e6edf3;background:rgba(10,14,20,.62);
|
|
2931
3020
|
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
@@ -2959,6 +3048,7 @@ var CSS = (
|
|
|
2959
3048
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
2960
3049
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
2961
3050
|
.sl-view3d-nav button:disabled{opacity:.45;cursor:not-allowed}
|
|
3051
|
+
.sl-view3d-nav-toggle{display:none!important}
|
|
2962
3052
|
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
2963
3053
|
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
2964
3054
|
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
@@ -2972,7 +3062,22 @@ var CSS = (
|
|
|
2972
3062
|
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
2973
3063
|
Keep venue navigation in a separate top rail so those control families never
|
|
2974
3064
|
stack over one another. */
|
|
2975
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav{top:
|
|
3065
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav{left:120px;top:12px;bottom:auto;max-width:calc(100% - 132px)}
|
|
3066
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav-toggle{display:inline-flex!important;align-items:center;pointer-events:auto;min-height:44px}
|
|
3067
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav:not(.is-open)>div{display:none!important}
|
|
3068
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open{left:12px;top:68px;max-width:calc(100% - 24px);padding:8px;
|
|
3069
|
+
border:1px solid rgba(150,165,205,.35);border-radius:14px;background:rgba(8,12,22,.9);backdrop-filter:blur(10px)}
|
|
3070
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open>div{display:flex}
|
|
3071
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator{display:grid;grid-template-columns:1fr 1fr;width:100%;overflow:visible}
|
|
3072
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator select{width:100%;min-width:0;flex:auto}
|
|
3073
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator button{width:100%}
|
|
3074
|
+
/* Seat-eye is a decision state, not another venue-navigation state. Once the
|
|
3075
|
+
buyer arrives, clear the floor/area/locator rails and the module's duplicate
|
|
3076
|
+
Overview action. The picker-owned Back button becomes the one predictable
|
|
3077
|
+
escape: seat -> venue -> 2D map. */
|
|
3078
|
+
.sl-view3d.is-seat-focused .sl-view3d-nav,
|
|
3079
|
+
.sl-view3d.is-seat-focused .sl-3d-overview-control,
|
|
3080
|
+
.sl-view3d.is-seat-focused .sl-view3d-compare-saved{display:none!important}
|
|
2976
3081
|
/* While immersed, the 2D-only chrome is meaningless \u2014 hide it, keep Map|3D. */
|
|
2977
3082
|
.sl-picker[data-view3d="on"] .sl-rungs,
|
|
2978
3083
|
.sl-picker[data-view3d="on"] .sl-floors,
|
|
@@ -2983,6 +3088,130 @@ var CSS = (
|
|
|
2983
3088
|
.sl-picker[data-view3d="on"] .sl-confirm{left:50%!important;top:auto!important;bottom:16px;
|
|
2984
3089
|
transform:translateX(-50%);width:min(342px,calc(100% - 24px))}
|
|
2985
3090
|
.sl-picker[data-view3d="on"] .sl-confirm[data-placement]{transform:translateX(-50%)}
|
|
3091
|
+
.sl-confirm-inspect-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:9px}
|
|
3092
|
+
.sl-confirm-inspect-row .sl-confirm-3d{margin-top:0;min-height:44px}
|
|
3093
|
+
.sl-confirm-compare{min-height:44px;padding:8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
3094
|
+
display:flex;align-items:center;justify-content:center;gap:7px;font-size:12px;font-weight:800;
|
|
3095
|
+
color:var(--sl-text);background:transparent}
|
|
3096
|
+
.sl-confirm-compare:hover,.sl-confirm-compare:focus-visible{border-color:var(--sl-accent);
|
|
3097
|
+
background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
|
|
3098
|
+
.sl-confirm-compare:disabled{opacity:.62;cursor:default}
|
|
3099
|
+
.sl-confirm-confidence{width:100%;min-height:44px;margin-top:8px;padding:8px 10px;border-radius:9px;
|
|
3100
|
+
border:1px solid color-mix(in srgb,var(--sl-accent) 35%,var(--sl-line));display:flex;align-items:center;
|
|
3101
|
+
justify-content:space-between;gap:10px;text-align:left;color:var(--sl-text);
|
|
3102
|
+
background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface))}
|
|
3103
|
+
.sl-confirm-confidence>span{min-width:0}
|
|
3104
|
+
.sl-confirm-confidence strong,.sl-confirm-confidence small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3105
|
+
.sl-confirm-confidence strong{font-size:11px}.sl-confirm-confidence small{margin-top:2px;color:var(--sl-muted);font-size:9.5px}
|
|
3106
|
+
.sl-confirm-confidence em{display:none;font-style:normal}
|
|
3107
|
+
.sl-confirm-confidence>b{flex:none;font-size:11px;color:var(--sl-accent)}
|
|
3108
|
+
.sl-confirm-confidence:hover,.sl-confirm-confidence:focus-visible{border-color:var(--sl-accent)}
|
|
3109
|
+
/* The production 3D decision dock is deliberately denser than the 2D popup:
|
|
3110
|
+
the venue remains the main content and the two inspection actions share one
|
|
3111
|
+
row. Truth-bearing accessibility/restriction copy is never hidden. */
|
|
3112
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm{bottom:10px}
|
|
3113
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-field{padding:8px 9px 7px}
|
|
3114
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-value{font-size:14px;margin-top:2px}
|
|
3115
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-field:first-child .sl-confirm-value{font-size:12px}
|
|
3116
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-cat{padding:7px 10px}
|
|
3117
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-price{font-size:15px}
|
|
3118
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-body{padding:8px 10px 9px}
|
|
3119
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"] .sl-confirm-row{margin-top:7px}
|
|
3120
|
+
/* A short embedded/mobile picker cannot afford a full decision sheet over a
|
|
3121
|
+
285px map. Keep three 44px action rows and move all disclosure into the
|
|
3122
|
+
passport instead of hiding it without a route back. */
|
|
3123
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm{bottom:6px}
|
|
3124
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-grid,
|
|
3125
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-cat,
|
|
3126
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-body>.sl-cx{display:none}
|
|
3127
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-body{padding:6px 8px 7px}
|
|
3128
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-confidence{margin-top:0}
|
|
3129
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-confidence em{display:block;margin-bottom:2px;
|
|
3130
|
+
color:var(--sl-text);font-size:12px;font-weight:850;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3131
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-confidence strong{font-size:9.5px}
|
|
3132
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-confidence small{display:none}
|
|
3133
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-inspect-row{margin-top:5px}
|
|
3134
|
+
.sl-picker[data-view3d="on"][data-layout="narrow"][data-density="compact"] .sl-confirm-row{margin-top:5px}
|
|
3135
|
+
|
|
3136
|
+
/* Saved comparison lives in the top journey row, away from checkout, arrival
|
|
3137
|
+
controls and the bottom-right privacy position. */
|
|
3138
|
+
.sl-view3d-compare-saved{position:absolute;top:12px;left:136px;z-index:5;display:flex;align-items:stretch;
|
|
3139
|
+
max-width:180px;min-height:38px;border-radius:999px;overflow:hidden;color:#e6edf3;
|
|
3140
|
+
background:rgba(10,14,20,.72);border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px)}
|
|
3141
|
+
.sl-view3d-compare-saved button{min-width:0;padding:7px 10px;color:inherit;font-size:11px;font-weight:800;white-space:nowrap}
|
|
3142
|
+
.sl-view3d-compare-saved .main{overflow:hidden;text-overflow:ellipsis}
|
|
3143
|
+
.sl-view3d-compare-saved .clear{width:34px;padding:7px;border-left:1px solid rgba(255,255,255,.18)}
|
|
3144
|
+
.sl-view3d-compare-saved button:hover,.sl-view3d-compare-saved button:focus-visible{background:rgba(255,255,255,.1)}
|
|
3145
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-compare-saved{left:126px;max-width:calc(100% - 194px);min-height:44px}
|
|
3146
|
+
|
|
3147
|
+
/* An unavailable seat is still inspectable. This compact, non-modal status
|
|
3148
|
+
card explains the exact chair the buyer touched without selecting it or
|
|
3149
|
+
obscuring the venue with the full purchase confirmation sheet. */
|
|
3150
|
+
.sl-view3d-unavailable{position:absolute;left:50%;bottom:18px;z-index:9;display:grid;
|
|
3151
|
+
grid-template-columns:minmax(0,1fr) auto;gap:4px 14px;width:min(330px,calc(100% - 24px));
|
|
3152
|
+
padding:13px 14px;border:1px solid rgba(255,255,255,.22);border-radius:14px;
|
|
3153
|
+
color:#eef3fb;background:rgba(10,14,22,.94);box-shadow:0 18px 48px rgba(0,0,0,.48);
|
|
3154
|
+
backdrop-filter:blur(10px);transform:translateX(-50%)}
|
|
3155
|
+
.sl-view3d-unavailable[data-state="held"]{border-color:rgba(242,168,56,.7)}
|
|
3156
|
+
.sl-view3d-unavailable[data-state="sold"],.sl-view3d-unavailable[data-state="dimmed"]{border-color:rgba(160,170,188,.48)}
|
|
3157
|
+
.sl-view3d-unavailable-copy{min-width:0}
|
|
3158
|
+
.sl-view3d-unavailable-eyebrow{display:block;font-size:9px;line-height:1.2;letter-spacing:.13em;
|
|
3159
|
+
text-transform:uppercase;color:#aab7cc;font-weight:850}
|
|
3160
|
+
.sl-view3d-unavailable strong{display:block;margin-top:3px;font-size:17px;line-height:1.2}
|
|
3161
|
+
.sl-view3d-unavailable p{grid-column:1/-1;margin:4px 0 0;color:#b9c4d7;font-size:11px;line-height:1.4}
|
|
3162
|
+
.sl-view3d-unavailable button{align-self:start;min-width:44px;min-height:44px;margin:-5px -6px 0 0;
|
|
3163
|
+
border-radius:999px;color:#eef3fb;font-size:18px;border:1px solid rgba(255,255,255,.18)}
|
|
3164
|
+
.sl-view3d-unavailable button:hover,.sl-view3d-unavailable button:focus-visible{background:rgba(255,255,255,.1)}
|
|
3165
|
+
.sl-picker[data-layout="narrow"] .sl-view3d-unavailable{bottom:10px;padding-bottom:max(13px,env(safe-area-inset-bottom))}
|
|
3166
|
+
|
|
3167
|
+
.sl-view3d-compare-shell{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:16px}
|
|
3168
|
+
.sl-view3d-compare-scrim{position:absolute;inset:0;background:rgba(3,6,12,.74);backdrop-filter:blur(5px)}
|
|
3169
|
+
.sl-view3d-compare{position:relative;width:min(720px,100%);max-height:min(680px,calc(100% - 20px));overflow:auto;
|
|
3170
|
+
border:1px solid rgba(160,177,214,.34);border-radius:18px;background:var(--sl-bg);color:var(--sl-text);
|
|
3171
|
+
box-shadow:0 28px 90px rgba(0,0,0,.55);padding:18px}
|
|
3172
|
+
.sl-view3d-compare>header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
|
|
3173
|
+
.sl-view3d-compare>header span{display:block;font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:850}
|
|
3174
|
+
.sl-view3d-compare>header strong{display:block;margin-top:4px;font-size:20px}
|
|
3175
|
+
.sl-view3d-compare>header button{min-width:44px;min-height:44px;border-radius:999px;border:1px solid var(--sl-line);color:var(--sl-text)}
|
|
3176
|
+
.sl-view3d-compare-note{margin:12px 0;color:var(--sl-muted);font-size:12px;line-height:1.45}
|
|
3177
|
+
.sl-view3d-compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
|
3178
|
+
.sl-view3d-compare article{min-width:0;padding:14px;border:1px solid var(--sl-line);border-radius:13px;background:var(--sl-surface)}
|
|
3179
|
+
.sl-view3d-compare article>span{font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--sl-muted);font-weight:850}
|
|
3180
|
+
.sl-view3d-compare article>strong{display:block;margin-top:3px;font-size:20px}
|
|
3181
|
+
.sl-view3d-compare article>small{display:block;margin-top:3px;color:var(--sl-muted)}
|
|
3182
|
+
.sl-view3d-compare dl{margin:12px 0 0}
|
|
3183
|
+
.sl-view3d-compare dl div{display:grid;grid-template-columns:minmax(90px,.8fr) minmax(0,1.2fr);gap:10px;padding:8px 0;border-top:1px solid var(--sl-line)}
|
|
3184
|
+
.sl-view3d-compare dt{font-size:10.5px;color:var(--sl-muted)}
|
|
3185
|
+
.sl-view3d-compare dd{margin:0;text-align:right;font-size:11px;font-weight:750;overflow-wrap:anywhere}
|
|
3186
|
+
.sl-view3d-compare-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:12px}
|
|
3187
|
+
.sl-view3d-compare-actions button{min-height:44px;border-radius:9px;border:1px solid var(--sl-line);font-size:12px;font-weight:800}
|
|
3188
|
+
.sl-view3d-compare-actions .select{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3189
|
+
.sl-view3d-compare-actions button:disabled{opacity:.5;cursor:not-allowed}
|
|
3190
|
+
.sl-view3d-passport-shell{position:absolute;inset:0;z-index:40;display:grid;place-items:center;padding:16px}
|
|
3191
|
+
.sl-view3d-passport-scrim{position:absolute;inset:0;background:rgba(3,6,12,.8);backdrop-filter:blur(6px)}
|
|
3192
|
+
.sl-view3d-passport{position:relative;width:min(540px,100%);max-height:min(700px,calc(100% - 20px));overflow:auto;
|
|
3193
|
+
padding:18px;border:1px solid rgba(160,177,214,.38);border-radius:18px;background:var(--sl-bg);color:var(--sl-text);
|
|
3194
|
+
box-shadow:0 28px 90px rgba(0,0,0,.6)}
|
|
3195
|
+
.sl-view3d-passport>header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
|
|
3196
|
+
.sl-view3d-passport>header span{display:block;font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:850}
|
|
3197
|
+
.sl-view3d-passport>header strong{display:block;margin-top:4px;font-size:20px}
|
|
3198
|
+
.sl-view3d-passport>header button{min-width:44px;min-height:44px;border:1px solid var(--sl-line);border-radius:999px;color:var(--sl-text)}
|
|
3199
|
+
.sl-view3d-passport-summary{margin:14px 0;padding:12px;border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 9%,var(--sl-surface));
|
|
3200
|
+
border:1px solid color-mix(in srgb,var(--sl-accent) 30%,var(--sl-line))}
|
|
3201
|
+
.sl-view3d-passport-summary strong{display:block;font-size:15px}.sl-view3d-passport-summary span{display:block;margin-top:4px;font-size:11px;color:var(--sl-muted)}
|
|
3202
|
+
.sl-view3d-passport dl{margin:0}.sl-view3d-passport dl div{display:grid;grid-template-columns:minmax(105px,.75fr) minmax(0,1.25fr);
|
|
3203
|
+
gap:12px;padding:9px 0;border-top:1px solid var(--sl-line)}
|
|
3204
|
+
.sl-view3d-passport dt{font-size:10.5px;color:var(--sl-muted)}.sl-view3d-passport dd{margin:0;text-align:right;font-size:11px;font-weight:750;overflow-wrap:anywhere}
|
|
3205
|
+
.sl-view3d-passport h4{margin:14px 0 6px;font-size:11px}.sl-view3d-passport ul{margin:0;padding-left:18px;color:var(--sl-muted);font-size:10.5px;line-height:1.5}
|
|
3206
|
+
.sl-view3d-passport-note{margin:14px 0 0;color:var(--sl-muted);font-size:10.5px;line-height:1.45}
|
|
3207
|
+
@media(max-width:640px){
|
|
3208
|
+
.sl-view3d-compare-shell{padding:max(10px,env(safe-area-inset-top)) 10px max(10px,env(safe-area-inset-bottom))}
|
|
3209
|
+
.sl-view3d-compare{width:100%;max-height:100%;padding:14px 14px max(86px,calc(14px + env(safe-area-inset-bottom)));border-radius:15px}
|
|
3210
|
+
.sl-view3d-compare-grid{grid-template-columns:1fr}
|
|
3211
|
+
.sl-view3d-compare article{padding:12px}
|
|
3212
|
+
.sl-view3d-passport-shell{padding:max(10px,env(safe-area-inset-top)) 10px max(10px,env(safe-area-inset-bottom))}
|
|
3213
|
+
.sl-view3d-passport{width:100%;max-height:100%;padding:14px 14px max(24px,calc(14px + env(safe-area-inset-bottom)));border-radius:15px}
|
|
3214
|
+
}
|
|
2986
3215
|
|
|
2987
3216
|
/* Plain "View from here" action shown when no real photo exists (the synthetic
|
|
2988
3217
|
thumb is suppressed at card size \u2014 full-screen is where it earns its keep). */
|
|
@@ -3100,8 +3329,12 @@ var CSS = (
|
|
|
3100
3329
|
.sl-picker[data-layout="narrow"] .sl-minimap{display:none}
|
|
3101
3330
|
|
|
3102
3331
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
3103
|
-
|
|
3104
|
-
|
|
3332
|
+
/* Out-of-band rows are exactly the rows a buyer reads while deciding whether
|
|
3333
|
+
to widen a filter: whole-row opacity .4 took the label to ~2:1. Dim the
|
|
3334
|
+
SWATCH and soften the numerals; the words stay legible muted ink. */
|
|
3335
|
+
.sl-price-row.sl-dim .sl-dot{opacity:.35}
|
|
3336
|
+
.sl-price-row.sl-dim .sl-price-label,.sl-price-row.sl-dim .sl-price-left,.sl-price-row.sl-dim .sl-price-amt{color:var(--sl-muted)}
|
|
3337
|
+
.sl-seccard-mix-item.sl-dim{opacity:.55}
|
|
3105
3338
|
|
|
3106
3339
|
/* Buyer-journey motion: every animation explains a state transition (selected,
|
|
3107
3340
|
held, checkout handoff, conflict or booked). No decorative infinite motion
|
|
@@ -3227,6 +3460,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3227
3460
|
// state
|
|
3228
3461
|
this.currency = "USD";
|
|
3229
3462
|
this.eventTimezone = null;
|
|
3463
|
+
/** The event's formatted date-time (event-zone), reused by the closed-state note. */
|
|
3464
|
+
this.eventWhenText = null;
|
|
3230
3465
|
this.offerAvailability = null;
|
|
3231
3466
|
this.hold = null;
|
|
3232
3467
|
/** Latest server expiry for the open hold (moves on extend). */
|
|
@@ -3282,6 +3517,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
3282
3517
|
this.view3dGen = 0;
|
|
3283
3518
|
/** Seat whose 2D confirm card launched "See it in 3D"; re-shown on return. */
|
|
3284
3519
|
this.view3dReturnSeat = null;
|
|
3520
|
+
/** Current premium 3D journey depth. `null` is the venue; a seat id is the
|
|
3521
|
+
* fixed seat-eye state. It lets Back unwind one step before leaving 3D. */
|
|
3522
|
+
this.view3dTargetSeatId = null;
|
|
3523
|
+
/** Inspection-only comparison. These ids never represent cart selection. */
|
|
3524
|
+
this.view3dCompareSeatIds = [];
|
|
3525
|
+
this.view3dCompareChip = null;
|
|
3526
|
+
this.view3dCompareEl = null;
|
|
3527
|
+
this.view3dCompareCleanup = null;
|
|
3528
|
+
this.view3dPassportEl = null;
|
|
3529
|
+
this.view3dPassportCleanup = null;
|
|
3285
3530
|
this.floorsEl = null;
|
|
3286
3531
|
this.secCardEl = null;
|
|
3287
3532
|
this.viewEl = null;
|
|
@@ -3323,6 +3568,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
3323
3568
|
this.fsEscHandler = null;
|
|
3324
3569
|
/** Host-level event chrome owns the duplicate identity outside full screen. */
|
|
3325
3570
|
this.eventDetailsHidden = false;
|
|
3571
|
+
/** Wide-layout ticket panel collapsed (map owns the full width). */
|
|
3572
|
+
this.sideCollapsed = false;
|
|
3573
|
+
this.sideToggleEl = null;
|
|
3326
3574
|
/** True once we've asked the host page to pin us fullscreen (framed, no native). */
|
|
3327
3575
|
this.framedFs = false;
|
|
3328
3576
|
/** Last height (px) posted to a host frame; dedupes redundant reports. */
|
|
@@ -3632,9 +3880,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3632
3880
|
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
3633
3881
|
const hideEventDetails = this.eventDetailsHidden && !active;
|
|
3634
3882
|
this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
|
|
3635
|
-
this.els.logo?.toggleAttribute("hidden", hideEventDetails);
|
|
3636
|
-
this.els.headInfo?.toggleAttribute("hidden", hideEventDetails);
|
|
3637
3883
|
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
3884
|
+
this.els.zfs?.setAttribute("title", active ? "Exit full screen" : "Full screen");
|
|
3885
|
+
const zfsLabel = this.els.zfs?.querySelector(".sl-zfs-lbl");
|
|
3886
|
+
if (zfsLabel) zfsLabel.textContent = active ? "Exit full screen" : "Full screen";
|
|
3638
3887
|
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
3639
3888
|
}
|
|
3640
3889
|
/**
|
|
@@ -3692,24 +3941,73 @@ var SeatPicker = class _SeatPicker {
|
|
|
3692
3941
|
/** Mount the full picker as a document-level modal. Resolves after render. */
|
|
3693
3942
|
static async open(options) {
|
|
3694
3943
|
ensureStyle();
|
|
3944
|
+
const priorFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
3695
3945
|
const scrim = document.createElement("div");
|
|
3696
3946
|
scrim.className = "sl-modal-scrim";
|
|
3697
3947
|
const frame = document.createElement("div");
|
|
3698
3948
|
frame.className = "sl-modal-frame";
|
|
3949
|
+
frame.setAttribute("role", "dialog");
|
|
3950
|
+
frame.setAttribute("aria-modal", "true");
|
|
3951
|
+
frame.setAttribute("aria-label", "Seat selection");
|
|
3952
|
+
frame.tabIndex = -1;
|
|
3699
3953
|
scrim.appendChild(frame);
|
|
3700
3954
|
document.body.appendChild(scrim);
|
|
3701
3955
|
const prevOverflow = document.body.style.overflow;
|
|
3702
3956
|
document.body.style.overflow = "hidden";
|
|
3703
3957
|
const picker = new _SeatPicker({ ...options, container: frame });
|
|
3704
3958
|
picker.modalScrim = scrim;
|
|
3705
|
-
picker.prevFocus =
|
|
3959
|
+
picker.prevFocus = priorFocus;
|
|
3960
|
+
const focusableSelector = [
|
|
3961
|
+
"a[href]",
|
|
3962
|
+
"area[href]",
|
|
3963
|
+
"button",
|
|
3964
|
+
"input",
|
|
3965
|
+
"select",
|
|
3966
|
+
"textarea",
|
|
3967
|
+
"iframe",
|
|
3968
|
+
"object",
|
|
3969
|
+
"embed",
|
|
3970
|
+
"summary",
|
|
3971
|
+
"audio[controls]",
|
|
3972
|
+
"video[controls]",
|
|
3973
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
3974
|
+
"[tabindex]"
|
|
3975
|
+
].join(",");
|
|
3976
|
+
const activeDialog = () => {
|
|
3977
|
+
const nested = [...frame.querySelectorAll('[role="dialog"][aria-modal="true"]')].filter((dialog) => dialog.isConnected && !dialog.closest('[hidden], [aria-hidden="true"], [inert]'));
|
|
3978
|
+
return nested[nested.length - 1] ?? frame;
|
|
3979
|
+
};
|
|
3980
|
+
const hiddenWithin = (element, scope) => {
|
|
3981
|
+
let current = element;
|
|
3982
|
+
while (current) {
|
|
3983
|
+
const style = window.getComputedStyle(current);
|
|
3984
|
+
if (current.hidden || current.getAttribute("aria-hidden") === "true" || current.hasAttribute("inert") || style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") return true;
|
|
3985
|
+
if (current === scope) return false;
|
|
3986
|
+
current = current.parentElement;
|
|
3987
|
+
}
|
|
3988
|
+
return true;
|
|
3989
|
+
};
|
|
3990
|
+
const tabbableWithin = (scope) => [...scope.querySelectorAll(focusableSelector)].filter((element) => element.tabIndex >= 0 && !element.matches(":disabled") && !hiddenWithin(element, scope));
|
|
3991
|
+
const focusEdge = (scope, backwards) => {
|
|
3992
|
+
const tabbable = tabbableWithin(scope);
|
|
3993
|
+
const target = backwards ? tabbable[tabbable.length - 1] : tabbable[0];
|
|
3994
|
+
if (target) target.focus({ preventScroll: true });
|
|
3995
|
+
else {
|
|
3996
|
+
if (!scope.hasAttribute("tabindex")) scope.tabIndex = -1;
|
|
3997
|
+
scope.focus({ preventScroll: true });
|
|
3998
|
+
}
|
|
3999
|
+
};
|
|
3706
4000
|
let closing = false;
|
|
3707
4001
|
const close = () => {
|
|
3708
4002
|
if (closing) return;
|
|
3709
4003
|
closing = true;
|
|
3710
4004
|
document.body.style.overflow = prevOverflow;
|
|
3711
|
-
|
|
3712
|
-
scrim.
|
|
4005
|
+
if (picker.escHandler) document.removeEventListener("keydown", picker.escHandler);
|
|
4006
|
+
scrim.remove();
|
|
4007
|
+
picker.modalScrim = null;
|
|
4008
|
+
const restoreTarget = picker.prevFocus;
|
|
4009
|
+
picker.prevFocus = null;
|
|
4010
|
+
if (restoreTarget?.isConnected) restoreTarget.focus({ preventScroll: true });
|
|
3713
4011
|
const finish = () => {
|
|
3714
4012
|
picker.destroy();
|
|
3715
4013
|
options.onClose?.();
|
|
@@ -3722,6 +4020,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
3722
4020
|
if (e.target === scrim) close();
|
|
3723
4021
|
});
|
|
3724
4022
|
picker.escHandler = (e) => {
|
|
4023
|
+
if (e.key === "Tab") {
|
|
4024
|
+
if (e.defaultPrevented) return;
|
|
4025
|
+
const scope = activeDialog();
|
|
4026
|
+
const tabbable = tabbableWithin(scope);
|
|
4027
|
+
const first = tabbable[0];
|
|
4028
|
+
const last = tabbable[tabbable.length - 1];
|
|
4029
|
+
const active = document.activeElement;
|
|
4030
|
+
if (!first || !last) {
|
|
4031
|
+
e.preventDefault();
|
|
4032
|
+
focusEdge(scope, e.shiftKey);
|
|
4033
|
+
} else if (active === scope || !active || !scope.contains(active)) {
|
|
4034
|
+
e.preventDefault();
|
|
4035
|
+
(e.shiftKey ? last : first).focus({ preventScroll: true });
|
|
4036
|
+
} else if (e.shiftKey && active === first) {
|
|
4037
|
+
e.preventDefault();
|
|
4038
|
+
last.focus({ preventScroll: true });
|
|
4039
|
+
} else if (!e.shiftKey && active === last) {
|
|
4040
|
+
e.preventDefault();
|
|
4041
|
+
first.focus({ preventScroll: true });
|
|
4042
|
+
}
|
|
4043
|
+
return;
|
|
4044
|
+
}
|
|
3725
4045
|
if (e.key !== "Escape") return;
|
|
3726
4046
|
if (picker.tableDialog) {
|
|
3727
4047
|
e.preventDefault();
|
|
@@ -3734,6 +4054,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3734
4054
|
picker.bestAvailableConfirm = false;
|
|
3735
4055
|
picker.syncTray();
|
|
3736
4056
|
} else {
|
|
4057
|
+
e.preventDefault();
|
|
3737
4058
|
close();
|
|
3738
4059
|
}
|
|
3739
4060
|
};
|
|
@@ -3741,6 +4062,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3741
4062
|
await picker.render();
|
|
3742
4063
|
picker.els.close?.classList.add("on");
|
|
3743
4064
|
picker.els.close?.addEventListener("click", close);
|
|
4065
|
+
focusEdge(activeDialog(), false);
|
|
3744
4066
|
return picker;
|
|
3745
4067
|
}
|
|
3746
4068
|
async render() {
|
|
@@ -3794,13 +4116,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
3794
4116
|
<div class="sl-map">
|
|
3795
4117
|
<div class="sl-map-host" data-ref="map"></div>
|
|
3796
4118
|
<div class="sl-zoom" data-ref="zoom">
|
|
3797
|
-
<button type="button" aria-label="Zoom in" data-ref="zin">+</button>
|
|
3798
|
-
<button type="button" aria-label="Zoom out" data-ref="zout">\u2212</button>
|
|
3799
|
-
<button type="button" aria-label="Fit to screen" data-ref="zfit">
|
|
4119
|
+
<button type="button" aria-label="Zoom in" title="Zoom in" data-ref="zin">+</button>
|
|
4120
|
+
<button type="button" aria-label="Zoom out" title="Zoom out" data-ref="zout">\u2212</button>
|
|
4121
|
+
<button type="button" aria-label="Fit to screen" title="Fit to screen" data-ref="zfit">
|
|
3800
4122
|
<svg viewBox="0 0 24 24"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
|
|
3801
4123
|
</button>
|
|
3802
|
-
<button type="button" aria-label="Full screen" aria-pressed="false" data-ref="zfs">
|
|
4124
|
+
<button type="button" aria-label="Full screen" title="Full screen" aria-pressed="false" data-ref="zfs">
|
|
3803
4125
|
<svg viewBox="0 0 24 24"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
|
4126
|
+
<span class="sl-zfs-lbl" aria-hidden="true">Full screen</span>
|
|
3804
4127
|
</button>
|
|
3805
4128
|
</div>
|
|
3806
4129
|
<div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>Loading seat map\u2026</div>
|
|
@@ -3852,8 +4175,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3852
4175
|
if (w <= 0) return;
|
|
3853
4176
|
this.reportFramedHeight();
|
|
3854
4177
|
const next = w < 640 ? "narrow" : "wide";
|
|
3855
|
-
|
|
4178
|
+
const density = root.clientHeight < 560 ? "compact" : "comfortable";
|
|
4179
|
+
if (root.dataset.layout === next && root.dataset.density === density) return;
|
|
3856
4180
|
root.dataset.layout = next;
|
|
4181
|
+
root.dataset.density = density;
|
|
3857
4182
|
if (next === "narrow" && !root.dataset.sheet) root.dataset.sheet = "peek";
|
|
3858
4183
|
this.dockLayoutChrome();
|
|
3859
4184
|
};
|
|
@@ -3962,17 +4287,18 @@ var SeatPicker = class _SeatPicker {
|
|
|
3962
4287
|
badge.className = "sl-testbadge";
|
|
3963
4288
|
badge.textContent = t2("picker.testMode");
|
|
3964
4289
|
badge.setAttribute("aria-label", t2("picker.testMode"));
|
|
3965
|
-
this.
|
|
4290
|
+
this.regions["top-left"].appendChild(badge);
|
|
3966
4291
|
}
|
|
3967
4292
|
const chartTheme = this.controller.doc?.theme;
|
|
3968
4293
|
Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
3969
4294
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
3970
4295
|
this.eventTimezone = info.timezone ?? null;
|
|
3971
|
-
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl;
|
|
4296
|
+
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl ?? info.posterUrl ?? null;
|
|
3972
4297
|
if (logoUrl) this.els.logo.innerHTML = `<img src="${logoUrl}" alt="">`;
|
|
3973
4298
|
else this.els.logo.textContent = (this.opts.theme?.brandName ?? chartTheme?.brandName ?? info.eventName ?? "?").slice(0, 1).toUpperCase();
|
|
3974
4299
|
this.els.name.textContent = info.eventName ?? "";
|
|
3975
4300
|
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
4301
|
+
this.eventWhenText = when || null;
|
|
3976
4302
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
3977
4303
|
this.buildBadge(chartTheme);
|
|
3978
4304
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -4063,6 +4389,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
4063
4389
|
this.buildExtendPrompt();
|
|
4064
4390
|
this.buildBookedOverlay();
|
|
4065
4391
|
this.buildSoldoutOverlay();
|
|
4392
|
+
this.buildPanelToggle();
|
|
4393
|
+
if (this.opts.panelCollapsed) this.setPanelCollapsed(true);
|
|
4066
4394
|
this.dockLayoutChrome();
|
|
4067
4395
|
await this.restoreRememberedHold();
|
|
4068
4396
|
if (this.destroyed) return this;
|
|
@@ -4109,8 +4437,63 @@ var SeatPicker = class _SeatPicker {
|
|
|
4109
4437
|
* re-render the section card in the form the layout wants (docked card/pill
|
|
4110
4438
|
* on wide, sheet strip on narrow). Runs on every layout flip + once post-render.
|
|
4111
4439
|
*/
|
|
4440
|
+
/**
|
|
4441
|
+
* The wide-layout panel toggle: a labelled pill in the top-right region
|
|
4442
|
+
* beside Map|3D. Collapsing gives the map the panel's 300px; the pill's own
|
|
4443
|
+
* label is the way back ("Tickets"), and syncTray reopens the panel when the
|
|
4444
|
+
* first seat lands so the checkout can never be hidden behind a collapse.
|
|
4445
|
+
*/
|
|
4446
|
+
buildPanelToggle() {
|
|
4447
|
+
if (!this.regions["top-right"]) return;
|
|
4448
|
+
const btn = document.createElement("button");
|
|
4449
|
+
btn.type = "button";
|
|
4450
|
+
btn.className = "sl-side-toggle";
|
|
4451
|
+
btn.addEventListener("click", () => this.setPanelCollapsed(!this.sideCollapsed));
|
|
4452
|
+
this.sideToggleEl = btn;
|
|
4453
|
+
this.regions["top-right"].appendChild(btn);
|
|
4454
|
+
this.syncPanelToggle();
|
|
4455
|
+
}
|
|
4456
|
+
syncPanelToggle() {
|
|
4457
|
+
const btn = this.sideToggleEl;
|
|
4458
|
+
if (!btn) return;
|
|
4459
|
+
const collapsed = this.sideCollapsed;
|
|
4460
|
+
btn.setAttribute("aria-expanded", String(!collapsed));
|
|
4461
|
+
btn.setAttribute("aria-label", collapsed ? "Show the ticket panel" : "Hide the ticket panel");
|
|
4462
|
+
btn.innerHTML = collapsed ? '<svg viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></svg><span>Tickets</span>' : '<svg viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg><span>Hide panel</span>';
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* Collapse or reopen the wide-layout ticket panel in place (host-drivable —
|
|
4466
|
+
* a map-first embed can mount collapsed via `panelCollapsed` and reopen on
|
|
4467
|
+
* its own cue). Inert while collapsed so a keyboard buyer cannot tab into a
|
|
4468
|
+
* zero-width panel; narrow layouts ignore the state entirely (the sheet is
|
|
4469
|
+
* already the collapse) but keep it for the next flip back to wide.
|
|
4470
|
+
*/
|
|
4471
|
+
setPanelCollapsed(collapsed) {
|
|
4472
|
+
if (this.destroyed) return;
|
|
4473
|
+
this.sideCollapsed = collapsed;
|
|
4474
|
+
this.applyPanelCollapsed();
|
|
4475
|
+
this.scheduleMotion(() => this.controller.zoomToFit(), 340);
|
|
4476
|
+
}
|
|
4477
|
+
/** Idempotent DOM apply, also run on every layout flip (inert must lift on narrow). */
|
|
4478
|
+
applyPanelCollapsed() {
|
|
4479
|
+
const wide = this.root?.dataset.layout !== "narrow";
|
|
4480
|
+
this.root?.setAttribute("data-side-collapsed", String(this.sideCollapsed));
|
|
4481
|
+
this.els.side?.toggleAttribute("inert", this.sideCollapsed && wide);
|
|
4482
|
+
this.syncPanelToggle();
|
|
4483
|
+
}
|
|
4112
4484
|
dockLayoutChrome() {
|
|
4113
4485
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
4486
|
+
this.applyPanelCollapsed();
|
|
4487
|
+
const zfs = this.els.zfs;
|
|
4488
|
+
if (zfs) {
|
|
4489
|
+
if (narrow && this.regions["top-right"]) {
|
|
4490
|
+
zfs.classList.add("sl-fs-pill");
|
|
4491
|
+
this.regions["top-right"].appendChild(zfs);
|
|
4492
|
+
} else if (!narrow && this.els.zoom && zfs.parentElement !== this.els.zoom) {
|
|
4493
|
+
zfs.classList.remove("sl-fs-pill");
|
|
4494
|
+
this.els.zoom.appendChild(zfs);
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4114
4497
|
const filters = this.els.filters;
|
|
4115
4498
|
if (filters) {
|
|
4116
4499
|
if (this.cbEl) this.els.zoom?.appendChild(this.cbEl);
|
|
@@ -4158,14 +4541,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
4158
4541
|
const v = t2(key);
|
|
4159
4542
|
return v === key ? fallback : v;
|
|
4160
4543
|
}
|
|
4161
|
-
/** Sold-out overlay —
|
|
4544
|
+
/** Sold-out overlay — an informational state with no unavailable action. */
|
|
4162
4545
|
buildSoldoutOverlay() {
|
|
4163
4546
|
if (!this.els.map) return;
|
|
4164
4547
|
const el = document.createElement("div");
|
|
4165
4548
|
el.className = "sl-soldout";
|
|
4166
4549
|
el.setAttribute("role", "status");
|
|
4167
4550
|
const name = (this.controller.doc?.theme?.brandName ?? this.opts.theme?.brandName ?? this.els.name?.textContent ?? this.tf("picker.soldOutEyebrow", "This event")).toUpperCase();
|
|
4168
|
-
el.innerHTML = `<div class="sl-soldout-eyebrow">${name}</div><div class="sl-soldout-title">${this.tf("picker.soldOutTitle", "Sold out")}</div><p class="sl-soldout-copy">${this.tf("picker.soldOutCopy", "
|
|
4551
|
+
el.innerHTML = `<div class="sl-soldout-eyebrow">${name}</div><div class="sl-soldout-title">${this.tf("picker.soldOutTitle", "Sold out")}</div><p class="sl-soldout-copy">${this.tf("picker.soldOutCopy", "No reserved seats are currently available for this event.")}</p>`;
|
|
4169
4552
|
this.els.map.appendChild(el);
|
|
4170
4553
|
this.soldoutEl = el;
|
|
4171
4554
|
}
|
|
@@ -4213,6 +4596,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4213
4596
|
text.textContent = this.tf("picker.salesClosedPill", "Sales are closed");
|
|
4214
4597
|
}
|
|
4215
4598
|
this.root?.setAttribute("data-sales-closed", String(this.salesClosed));
|
|
4599
|
+
if (this.salesClosed && this.srEl) {
|
|
4600
|
+
this.srEl.textContent = this.tf("picker.salesClosedToast", "Sales are closed for this event.");
|
|
4601
|
+
}
|
|
4216
4602
|
this.syncCta();
|
|
4217
4603
|
this.syncTray();
|
|
4218
4604
|
}
|
|
@@ -5058,6 +5444,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5058
5444
|
this.confirmEl = null;
|
|
5059
5445
|
this.confirmSeat = seat;
|
|
5060
5446
|
this.root?.setAttribute("data-confirming", "true");
|
|
5447
|
+
this.els.side?.toggleAttribute("inert", true);
|
|
5448
|
+
Object.values(this.regions).forEach((region) => region.toggleAttribute("inert", true));
|
|
5061
5449
|
this.controller.setSelectionFocus(seat.id);
|
|
5062
5450
|
if (previousId && previousId !== seat.id) this.controller.deselect([previousId]);
|
|
5063
5451
|
if (this.tipEl) this.tipEl.style.display = "none";
|
|
@@ -5079,10 +5467,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
5079
5467
|
const el = document.createElement("div");
|
|
5080
5468
|
el.className = "sl-confirm";
|
|
5081
5469
|
el.setAttribute("role", "dialog");
|
|
5082
|
-
el.setAttribute("aria-modal", "true");
|
|
5083
5470
|
el.setAttribute("aria-label", `Confirm seat ${seat.label}`);
|
|
5084
5471
|
el.style.setProperty("--sl-cat", cat?.color ?? "#6e7bff");
|
|
5085
|
-
el.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + this.see3dConfirmHtml() + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
|
|
5472
|
+
el.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() && this.buyerView !== "venue3d" ? this.confirmThumbHtml(seat) : "") + (this.buyerView === "venue3d" ? `${this.seatConfidenceConfirmHtml(seat, `${details?.displayLabel ?? seat.displayLabel ?? seat.label} \xB7 ${price == null ? this.tf("picker.priceNotSupplied", "Price not supplied") : this.money(price)}`)}<div class="sl-confirm-inspect-row">${this.view3dCompareConfirmHtml(seat)}${this.see3dConfirmHtml()}</div>` : this.see3dConfirmHtml()) + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
|
|
5086
5473
|
this.els.map.appendChild(el);
|
|
5087
5474
|
this.confirmEl = el;
|
|
5088
5475
|
const thumb = el.querySelector(".sl-confirm-thumb");
|
|
@@ -5094,6 +5481,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
5094
5481
|
}
|
|
5095
5482
|
this.reanchorConfirm();
|
|
5096
5483
|
el.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
5484
|
+
el.querySelector(".sl-confirm-confidence")?.addEventListener("click", (event) => {
|
|
5485
|
+
this.openSeatConfidencePassport(seat, event.currentTarget instanceof HTMLElement ? event.currentTarget : null);
|
|
5486
|
+
});
|
|
5487
|
+
el.querySelector(".sl-confirm-compare")?.addEventListener("click", () => this.saveView3dComparisonSeat(seat));
|
|
5097
5488
|
el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
5098
5489
|
if (this.buyerView === "venue3d") {
|
|
5099
5490
|
this.dismissConfirm();
|
|
@@ -5130,6 +5521,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5130
5521
|
this.confirmEl = null;
|
|
5131
5522
|
this.confirmSeat = null;
|
|
5132
5523
|
this.root?.removeAttribute("data-confirming");
|
|
5524
|
+
Object.values(this.regions).forEach((region) => region.toggleAttribute("inert", false));
|
|
5525
|
+
this.applyPanelCollapsed();
|
|
5133
5526
|
this.controller.setSelectionFocus(null);
|
|
5134
5527
|
}
|
|
5135
5528
|
commitConfirm() {
|
|
@@ -5137,12 +5530,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
5137
5530
|
this.dismissConfirm();
|
|
5138
5531
|
this.collapseSectionCard();
|
|
5139
5532
|
this.syncTray();
|
|
5533
|
+
this.syncSelectionTo3d();
|
|
5140
5534
|
}
|
|
5141
5535
|
cancelConfirm() {
|
|
5142
5536
|
const seat = this.confirmSeat;
|
|
5143
5537
|
if (!seat) return;
|
|
5144
5538
|
this.controller.deselect([seat.id]);
|
|
5145
5539
|
if (this.confirmSeat) this.dismissConfirm();
|
|
5540
|
+
this.syncSelectionTo3d();
|
|
5146
5541
|
this.root?.focus({ preventScroll: true });
|
|
5147
5542
|
}
|
|
5148
5543
|
closeConfirm() {
|
|
@@ -5566,19 +5961,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
5566
5961
|
const heldItems = this.hold?.items ?? [];
|
|
5567
5962
|
const parts = [];
|
|
5568
5963
|
const nextTrayKeys = /* @__PURE__ */ new Set();
|
|
5569
|
-
if (
|
|
5964
|
+
if (this.salesClosed && !seats.length && !heldItems.length) {
|
|
5965
|
+
const closedWhen = this.eventWhenText ? `<span class="when">${String(this.eventWhenText).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c])}</span>` : "";
|
|
5966
|
+
parts.push(
|
|
5967
|
+
`<div class="sl-closed-note" role="status"><b>${this.tf("picker.salesClosedPill", "Sales are closed")}</b><span>${this.tf("picker.salesClosedCopy", "Ticket sales for this event have ended.")}${closedWhen}</span></div>`
|
|
5968
|
+
);
|
|
5969
|
+
} else if (!seats.length && !heldItems.length && !gaAreas.length) {
|
|
5570
5970
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map, or let us pick the best available for you.</div>`);
|
|
5571
5971
|
} else if (!seats.length && !heldItems.length) {
|
|
5572
5972
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
|
|
5573
5973
|
}
|
|
5574
5974
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
5575
|
-
if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
5975
|
+
if (!this.salesClosed && !this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
5576
5976
|
const cats = this.controller.doc?.categories ?? [];
|
|
5577
5977
|
const zones = this.controller.getBestAvailableZones();
|
|
5578
5978
|
if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
|
|
5579
5979
|
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Replace your current choices?</div><div class="sl-ba-replace"><b>We\u2019ll find ${this.baQty} seats together.</b><span>Your manually selected tickets will be removed only after a new group is secured.</span></div><div class="sl-ba-actions"><button type="button" data-ba-cancel>Keep mine</button><button type="button" class="replace" data-ba-replace>Find new seats</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Find the best seats together</div><div class="sl-ba-copy"><span class="wide">We\u2019ll choose the closest available group for you.</span><span class="narrow">Closest available group, chosen instantly.</span></div>` + // Premium quick-pick — present only when the chart actually has premium
|
|
5580
5980
|
// seats (same present-only philosophy as the a11y filter chips).
|
|
5581
|
-
(this.controller.hasPremiumSeats() ? `<button type="button" class="sl-ba-premium${this.baPremium ? " on" : ""}" data-ba-premium aria-pressed="${this.baPremium ? "true" : "false"}"><span class="star" aria-hidden="true">\u2605</span>${this.tf("picker.bestSeatsPremium", "Best seats")}</button>` : "") + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + (zones.length ? `<select aria-label="Preferred venue zone" data-ba-zone><option value="">Any venue zone</option>` + zones.map((zone) => `<option value="${escapeOption(zone.id)}"${this.baZone === zone.id ? " selected" : ""}>${escapeOption(zone.label)}</option>`).join("") + `</select>` : "") + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
|
|
5981
|
+
(this.controller.hasPremiumSeats() ? `<button type="button" class="sl-ba-premium${this.baPremium ? " on" : ""}" data-ba-premium aria-pressed="${this.baPremium ? "true" : "false"}"><span class="star" aria-hidden="true">\u2605</span>${this.tf("picker.bestSeatsPremium", "Best seats")}</button>` : "") + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + (zones.length ? `<select aria-label="Preferred venue zone" data-ba-zone><option value="">Any venue zone</option>` + zones.map((zone) => `<option value="${escapeOption(zone.id)}"${this.baZone === zone.id ? " selected" : ""}>${escapeOption(zone.label)}</option>`).join("") + `</select>` : "") + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go${this.bestAvailableBusy ? " sl-busy" : ""}"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
|
|
5582
5982
|
}
|
|
5583
5983
|
const idGrid = (seatId, label, objectType, quantity = 1, objectId, identity) => {
|
|
5584
5984
|
const esc = (value) => String(value ?? "\u2014").replace(/[&<>"]/g, (char) => ({
|
|
@@ -5788,11 +6188,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
5788
6188
|
}
|
|
5789
6189
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
5790
6190
|
if (total !== previousTotal) this.animateOnce(this.els.total, "sl-value-pop", 380);
|
|
5791
|
-
if (previousCount === 0 && count > 0)
|
|
6191
|
+
if (previousCount === 0 && count > 0) {
|
|
6192
|
+
this.animateOnce(this.els.cta, "sl-ready", 520);
|
|
6193
|
+
if (this.sideCollapsed && this.root?.dataset.layout !== "narrow") this.setPanelCollapsed(false);
|
|
6194
|
+
}
|
|
5792
6195
|
if (this.els.peek) {
|
|
5793
6196
|
if (count) {
|
|
5794
6197
|
const holding = !!this.hold && !pendingCount;
|
|
5795
6198
|
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>`;
|
|
6199
|
+
} else if (this.salesClosed) {
|
|
6200
|
+
this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
|
|
5796
6201
|
} else {
|
|
5797
6202
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
5798
6203
|
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>`;
|
|
@@ -6439,16 +6844,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6439
6844
|
return null;
|
|
6440
6845
|
}
|
|
6441
6846
|
}
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
} catch {
|
|
6450
|
-
return null;
|
|
6451
|
-
}
|
|
6847
|
+
return {
|
|
6848
|
+
url: "",
|
|
6849
|
+
generated: true,
|
|
6850
|
+
mediaKind: "model",
|
|
6851
|
+
coverage: "exact-seat",
|
|
6852
|
+
sourceLabel: this.tf("picker.chartDerivedModel", "Chart-derived model")
|
|
6853
|
+
};
|
|
6452
6854
|
}
|
|
6453
6855
|
/** Route the module's decoupled analytics into the host callback, tagged buyer. */
|
|
6454
6856
|
emit3dAnalytics(event, props) {
|
|
@@ -6472,8 +6874,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
6472
6874
|
if (already) {
|
|
6473
6875
|
this.controller.deselect([seatId]);
|
|
6474
6876
|
this.dismissConfirm();
|
|
6877
|
+
this.syncSelectionTo3d();
|
|
6475
6878
|
return;
|
|
6476
6879
|
}
|
|
6880
|
+
const visualState = this.seatState3dFor(seat);
|
|
6881
|
+
if (visualState !== "available") {
|
|
6882
|
+
this.showUnavailable3dSeat(seat, visualState);
|
|
6883
|
+
this.syncSelectionTo3d();
|
|
6884
|
+
return;
|
|
6885
|
+
}
|
|
6886
|
+
this.dismissUnavailable3dSeat();
|
|
6477
6887
|
const added = this.controller.select([seatId]);
|
|
6478
6888
|
if (!added.length) {
|
|
6479
6889
|
this.syncSelectionTo3d();
|
|
@@ -6489,6 +6899,391 @@ var SeatPicker = class _SeatPicker {
|
|
|
6489
6899
|
}
|
|
6490
6900
|
if (this.opts.confirmSelection !== false) this.showConfirm(seat);
|
|
6491
6901
|
else this.syncTray();
|
|
6902
|
+
this.syncSelectionTo3d();
|
|
6903
|
+
}
|
|
6904
|
+
/** Explain a visible-but-unselectable 3D seat without entering the booking
|
|
6905
|
+
* flow. Category colour remains visible in the venue; this card names the
|
|
6906
|
+
* availability state explicitly so yellow never has to carry both meanings. */
|
|
6907
|
+
showUnavailable3dSeat(seat, visualState) {
|
|
6908
|
+
const overlay = this.view3dEl;
|
|
6909
|
+
if (!overlay) return;
|
|
6910
|
+
this.dismissUnavailable3dSeat();
|
|
6911
|
+
const status = this.controller.getStatus(seat.id);
|
|
6912
|
+
const details = this.controller.seatDetails(seat.id);
|
|
6913
|
+
const identity = details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
6914
|
+
const section = details?.sectionLabel;
|
|
6915
|
+
const row = this.rowShort(details);
|
|
6916
|
+
const location2 = [section, row ? `Row ${row}` : null, identity].filter(Boolean).join(" \xB7 ");
|
|
6917
|
+
const copy = status === "held" ? {
|
|
6918
|
+
title: this.tf("picker.temporarilyHeld", "Temporarily held"),
|
|
6919
|
+
message: this.tf("picker.heldSeatExplanation", "Another buyer is holding this seat. It may become available again.")
|
|
6920
|
+
} : status === "booked" ? {
|
|
6921
|
+
title: this.tf("picker.sold", "Sold"),
|
|
6922
|
+
message: this.tf("picker.soldSeatExplanation", "This seat has already been booked.")
|
|
6923
|
+
} : status === "not_for_sale" ? {
|
|
6924
|
+
title: this.tf("picker.notForSale", "Not for sale"),
|
|
6925
|
+
message: this.tf("picker.notForSaleExplanation", "This seat is not included in the current sale.")
|
|
6926
|
+
} : {
|
|
6927
|
+
title: this.tf("picker.filteredSeat", "Unavailable with current filters"),
|
|
6928
|
+
message: this.tf("picker.filteredSeatExplanation", "Change the active price or view filters to make this seat selectable.")
|
|
6929
|
+
};
|
|
6930
|
+
const card = document.createElement("div");
|
|
6931
|
+
card.className = "sl-view3d-unavailable";
|
|
6932
|
+
card.dataset.state = visualState;
|
|
6933
|
+
card.setAttribute("role", "status");
|
|
6934
|
+
card.setAttribute("aria-live", "polite");
|
|
6935
|
+
const eyebrow = document.createElement("span");
|
|
6936
|
+
eyebrow.className = "sl-view3d-unavailable-eyebrow";
|
|
6937
|
+
eyebrow.textContent = location2 || identity;
|
|
6938
|
+
const title = document.createElement("strong");
|
|
6939
|
+
title.textContent = copy.title;
|
|
6940
|
+
const content = document.createElement("div");
|
|
6941
|
+
content.className = "sl-view3d-unavailable-copy";
|
|
6942
|
+
content.append(eyebrow, title);
|
|
6943
|
+
const close = document.createElement("button");
|
|
6944
|
+
close.type = "button";
|
|
6945
|
+
close.setAttribute("aria-label", this.tf("picker.closeSeatStatus", "Close seat status"));
|
|
6946
|
+
close.textContent = "\xD7";
|
|
6947
|
+
const message = document.createElement("p");
|
|
6948
|
+
message.textContent = copy.message;
|
|
6949
|
+
card.append(content, close, message);
|
|
6950
|
+
close.addEventListener("click", () => card.remove());
|
|
6951
|
+
overlay.appendChild(card);
|
|
6952
|
+
this.announceSeat(seat);
|
|
6953
|
+
}
|
|
6954
|
+
dismissUnavailable3dSeat() {
|
|
6955
|
+
this.view3dEl?.querySelector(".sl-view3d-unavailable")?.remove();
|
|
6956
|
+
}
|
|
6957
|
+
/** Comparison belongs to inspection, never selection. The confirm candidate
|
|
6958
|
+
* remains excluded from checkout until Select; saving it releases that
|
|
6959
|
+
* candidate before any comparison state is created. */
|
|
6960
|
+
view3dCompareConfirmHtml(seat) {
|
|
6961
|
+
if (this.buyerView !== "venue3d") return "";
|
|
6962
|
+
const saved = this.view3dCompareSeatIds;
|
|
6963
|
+
const included = saved.includes(seat.id);
|
|
6964
|
+
const label = included ? saved.length > 1 ? this.tf("picker.openComparison", "Open comparison") : this.tf("picker.savedForComparison", "Saved for comparison") : saved.length ? this.tf("picker.compareWithSaved", "Compare with saved") : this.tf("picker.saveToCompare", "Save to compare");
|
|
6965
|
+
return `<button type="button" class="sl-confirm-compare"${included && saved.length === 1 ? " disabled" : ""}><span aria-hidden="true">\u21C4</span><span>${this.escCx(label)}</span></button>`;
|
|
6966
|
+
}
|
|
6967
|
+
seatConfidenceConfirmHtml(seat, compactSummary) {
|
|
6968
|
+
if (this.buyerView !== "venue3d") return "";
|
|
6969
|
+
const disclosure = seatConfidenceDisclosure(seat.confidenceEvidence);
|
|
6970
|
+
const detail = disclosure.modeledTarget ?? disclosure.reality;
|
|
6971
|
+
return `<button type="button" class="sl-confirm-confidence" aria-label="Open seat confidence passport for ${this.escCx(seat.displayLabel ?? seat.label)}"><span><em>${this.escCx(compactSummary)}</em><strong>${this.escCx(disclosure.headline)}</strong><small>${this.escCx(detail)}</small></span><b>Passport</b></button>`;
|
|
6972
|
+
}
|
|
6973
|
+
saveView3dComparisonSeat(seat) {
|
|
6974
|
+
if (this.buyerView !== "venue3d") return;
|
|
6975
|
+
const previous = this.view3dCompareSeatIds;
|
|
6976
|
+
if (!previous.includes(seat.id)) {
|
|
6977
|
+
this.view3dCompareSeatIds = previous.length === 0 ? [seat.id] : [previous[0], seat.id];
|
|
6978
|
+
}
|
|
6979
|
+
if (this.confirmSeat?.id === seat.id) {
|
|
6980
|
+
this.controller.deselect([seat.id]);
|
|
6981
|
+
this.dismissConfirm();
|
|
6982
|
+
this.syncSelectionTo3d();
|
|
6983
|
+
this.syncTray();
|
|
6984
|
+
}
|
|
6985
|
+
this.syncView3dCompareChip();
|
|
6986
|
+
this.emit3dAnalytics("3d_comparison_saved", {
|
|
6987
|
+
seatId: seat.id,
|
|
6988
|
+
count: this.view3dCompareSeatIds.length
|
|
6989
|
+
});
|
|
6990
|
+
if (this.view3dCompareSeatIds.length > 1) this.openView3dComparison();
|
|
6991
|
+
else this.toast(this.tf("picker.chooseAnotherToCompare", "Seat saved. Choose another seat to compare."), "success");
|
|
6992
|
+
}
|
|
6993
|
+
clearView3dComparison() {
|
|
6994
|
+
this.closeView3dComparison(false);
|
|
6995
|
+
this.view3dCompareSeatIds = [];
|
|
6996
|
+
this.view3dCompareChip?.remove();
|
|
6997
|
+
this.view3dCompareChip = null;
|
|
6998
|
+
this.emit3dAnalytics("3d_comparison_cleared");
|
|
6999
|
+
}
|
|
7000
|
+
syncView3dCompareChip() {
|
|
7001
|
+
const overlay = this.view3dEl;
|
|
7002
|
+
if (!overlay || this.view3dCompareSeatIds.length === 0) {
|
|
7003
|
+
this.view3dCompareChip?.remove();
|
|
7004
|
+
this.view3dCompareChip = null;
|
|
7005
|
+
return;
|
|
7006
|
+
}
|
|
7007
|
+
let chip = this.view3dCompareChip;
|
|
7008
|
+
if (!chip) {
|
|
7009
|
+
chip = document.createElement("div");
|
|
7010
|
+
chip.className = "sl-view3d-compare-saved";
|
|
7011
|
+
chip.setAttribute("role", "group");
|
|
7012
|
+
chip.setAttribute("aria-label", "Saved seat comparison");
|
|
7013
|
+
const main2 = document.createElement("button");
|
|
7014
|
+
main2.type = "button";
|
|
7015
|
+
main2.className = "main";
|
|
7016
|
+
main2.addEventListener("click", () => {
|
|
7017
|
+
if (this.view3dCompareSeatIds.length > 1) this.openView3dComparison();
|
|
7018
|
+
else this.toast(this.tf("picker.chooseAnotherToCompare", "Choose another seat to compare."), "neutral");
|
|
7019
|
+
});
|
|
7020
|
+
const clear = document.createElement("button");
|
|
7021
|
+
clear.type = "button";
|
|
7022
|
+
clear.className = "clear";
|
|
7023
|
+
clear.textContent = "\xD7";
|
|
7024
|
+
clear.setAttribute("aria-label", "Clear saved seat comparison");
|
|
7025
|
+
clear.addEventListener("click", () => this.clearView3dComparison());
|
|
7026
|
+
chip.append(main2, clear);
|
|
7027
|
+
overlay.appendChild(chip);
|
|
7028
|
+
this.view3dCompareChip = chip;
|
|
7029
|
+
}
|
|
7030
|
+
const count = this.view3dCompareSeatIds.length;
|
|
7031
|
+
const main = chip.querySelector(".main");
|
|
7032
|
+
if (main) {
|
|
7033
|
+
main.textContent = count > 1 ? `Compare ${count}` : "1 seat saved";
|
|
7034
|
+
main.setAttribute("aria-label", count > 1 ? `Open comparison of ${count} seats` : "One seat saved; choose another to compare");
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
view3dComparisonSnapshot(seatId) {
|
|
7038
|
+
const seat = this.allSeats().find((candidate) => candidate.id === seatId);
|
|
7039
|
+
if (!seat) return null;
|
|
7040
|
+
const details = this.controller.seatDetails(seat.id);
|
|
7041
|
+
const cat = this.controller.doc?.categories.find((candidate) => candidate.key === seat.categoryKey);
|
|
7042
|
+
const chartPrice = details?.price ?? (cat?.tiers?.length ? cat.tiers[0].price : cat?.price);
|
|
7043
|
+
const price = chartPrice != null ? this.paidPrice(seat.categoryKey, details?.tierId ?? cat?.tiers?.[0]?.id ?? null, chartPrice) : void 0;
|
|
7044
|
+
const status = this.controller.getStatus(seat.id);
|
|
7045
|
+
const availability = status === "held" ? this.tf("picker.temporarilyHeld", "Temporarily held") : status === "booked" ? this.tf("picker.sold", "Sold") : status === "not_for_sale" ? this.tf("picker.notForSale", "Not for sale") : this.tf("picker.available", "Available");
|
|
7046
|
+
const viewSource = seat.viewUrl ? seatViewDisclosure({
|
|
7047
|
+
url: seat.viewUrl,
|
|
7048
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
7049
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
7050
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
7051
|
+
}) : this.tf("picker.chartDerivedSeatEye", "Live 3D \xB7 chart-derived seat-eye \xB7 not surveyed");
|
|
7052
|
+
const limited = this.limitedViewLabel(seat.commercial) || this.tf("picker.noAuthoredRestriction", "No organizer-authored restriction");
|
|
7053
|
+
const accessibility = details?.wheelchairSpaceType ? `${this.wheelchairProvisionLabel(details.wheelchairSpaceType)} \xB7 metadata, not access certification` : this.tf("picker.noAccessibilityMetadata", "No accessibility metadata supplied");
|
|
7054
|
+
const confidence = seatConfidenceDisclosure(seat.confidenceEvidence);
|
|
7055
|
+
return {
|
|
7056
|
+
seat,
|
|
7057
|
+
label: details?.displayLabel ?? seat.displayLabel ?? seat.label,
|
|
7058
|
+
section: details?.sectionLabel ?? seat.sectionId ?? "\u2014",
|
|
7059
|
+
row: this.rowShort(details) ?? details?.rowLabel ?? "\u2014",
|
|
7060
|
+
category: details?.categoryLabel ?? cat?.label ?? seat.categoryKey,
|
|
7061
|
+
price: price == null ? this.tf("picker.priceNotSupplied", "Not supplied") : this.money(price),
|
|
7062
|
+
availability,
|
|
7063
|
+
selectable: status == null || status === "free",
|
|
7064
|
+
viewSource,
|
|
7065
|
+
limited,
|
|
7066
|
+
accessibility,
|
|
7067
|
+
confidence
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
7070
|
+
openSeatConfidencePassport(seat, returnFocus = null) {
|
|
7071
|
+
const overlay = this.view3dEl;
|
|
7072
|
+
if (!overlay) return;
|
|
7073
|
+
this.closeSeatConfidencePassport(false);
|
|
7074
|
+
const disclosure = seatConfidenceDisclosure(seat.confidenceEvidence);
|
|
7075
|
+
const evidence = seat.confidenceEvidence;
|
|
7076
|
+
const details = this.controller.seatDetails(seat.id);
|
|
7077
|
+
const safe = (value) => this.escCx(value);
|
|
7078
|
+
const limitations = disclosure.limitations.length ? `<h4>Known limits</h4><ul>${disclosure.limitations.map((item) => `<li>${safe(item)}</li>`).join("")}</ul>` : "";
|
|
7079
|
+
const modeledTarget = disclosure.modeledTarget ? `<div><dt>Modeled target</dt><dd>${safe(disclosure.modeledTarget)}</dd></div>` : "";
|
|
7080
|
+
const evidenceRows = evidence ? `<div><dt>Evidence ID</dt><dd>${safe(evidence.evidenceId)}</dd></div><div><dt>Model version</dt><dd>${safe(evidence.modelVersion)}</dd></div><div><dt>Event configuration</dt><dd>${safe(evidence.eventConfigurationId ?? "Not configuration-specific")}</dd></div><div><dt>Approval</dt><dd>${safe(evidence.approvedByRole ?? "No external approval supplied")}</dd></div>` + (evidence.validUntil ? `<div><dt>Valid until</dt><dd>${safe(evidence.validUntil.slice(0, 10))}</dd></div>` : "") : `<div><dt>Evidence ID</dt><dd>None supplied</dd></div>`;
|
|
7081
|
+
const restriction = this.limitedViewLabel(seat.commercial) || this.tf("picker.noAuthoredRestriction", "No organizer-authored restriction");
|
|
7082
|
+
const commercialRows = `<div><dt>View restriction</dt><dd>${safe(restriction)}</dd></div>` + (seat.commercial?.note ? `<div><dt>Organizer note</dt><dd>${safe(seat.commercial.note)}</dd></div>` : "");
|
|
7083
|
+
const shell = document.createElement("div");
|
|
7084
|
+
shell.className = "sl-view3d-passport-shell";
|
|
7085
|
+
shell.innerHTML = `<div class="sl-view3d-passport-scrim" aria-hidden="true"></div><section class="sl-view3d-passport" role="dialog" aria-modal="true" aria-labelledby="sl-view3d-passport-title"><header><div><span>Seat confidence passport</span><strong id="sl-view3d-passport-title">${safe(details?.displayLabel ?? seat.displayLabel ?? seat.label)}</strong></div><button type="button" data-close aria-label="Close seat confidence passport">\xD7</button></header><div class="sl-view3d-passport-summary"><strong>${safe(disclosure.headline)}</strong><span>${safe(disclosure.coverage)} \xB7 ${safe(disclosure.freshness)}</span></div><dl><div><dt>Model status</dt><dd>${safe(disclosure.model)}</dd></div><div><dt>Reality evidence</dt><dd>${safe(disclosure.reality)}</dd></div><div><dt>Source</dt><dd>${safe(disclosure.provenance)}</dd></div>` + commercialRows + modeledTarget + evidenceRows + `</dl>${limitations}<p class="sl-view3d-passport-note">This passport describes supplied evidence and known limits. It does not guarantee that every temporary obstruction or real-world condition is knowable before the event build.</p></section>`;
|
|
7086
|
+
const background = [.../* @__PURE__ */ new Set([
|
|
7087
|
+
...overlay.children,
|
|
7088
|
+
...[...this.els.map.children].filter((element) => element !== overlay)
|
|
7089
|
+
])].filter((element) => element instanceof HTMLElement);
|
|
7090
|
+
const prior = background.map((element) => ({
|
|
7091
|
+
element,
|
|
7092
|
+
inert: element.inert,
|
|
7093
|
+
ariaHidden: element.getAttribute("aria-hidden")
|
|
7094
|
+
}));
|
|
7095
|
+
for (const element of background) {
|
|
7096
|
+
element.inert = true;
|
|
7097
|
+
element.setAttribute("aria-hidden", "true");
|
|
7098
|
+
}
|
|
7099
|
+
overlay.appendChild(shell);
|
|
7100
|
+
overlay.classList.add("has-passport");
|
|
7101
|
+
this.view3dPassportEl = shell;
|
|
7102
|
+
const dialog = shell.querySelector(".sl-view3d-passport");
|
|
7103
|
+
const controls = () => [...dialog.querySelectorAll('button:not(:disabled),[href],[tabindex]:not([tabindex="-1"])')];
|
|
7104
|
+
const onKey = (event) => {
|
|
7105
|
+
if (event.key === "Escape") {
|
|
7106
|
+
event.preventDefault();
|
|
7107
|
+
event.stopPropagation();
|
|
7108
|
+
event.stopImmediatePropagation();
|
|
7109
|
+
this.closeSeatConfidencePassport();
|
|
7110
|
+
return;
|
|
7111
|
+
}
|
|
7112
|
+
if (event.key !== "Tab") return;
|
|
7113
|
+
const focusable = controls();
|
|
7114
|
+
if (!focusable.length) return;
|
|
7115
|
+
const first = focusable[0];
|
|
7116
|
+
const last = focusable[focusable.length - 1];
|
|
7117
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
7118
|
+
event.preventDefault();
|
|
7119
|
+
last.focus();
|
|
7120
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
7121
|
+
event.preventDefault();
|
|
7122
|
+
first.focus();
|
|
7123
|
+
}
|
|
7124
|
+
};
|
|
7125
|
+
window.addEventListener("keydown", onKey, true);
|
|
7126
|
+
this.view3dPassportCleanup = () => {
|
|
7127
|
+
window.removeEventListener("keydown", onKey, true);
|
|
7128
|
+
for (const state of prior) {
|
|
7129
|
+
state.element.inert = state.inert;
|
|
7130
|
+
if (state.ariaHidden === null) state.element.removeAttribute("aria-hidden");
|
|
7131
|
+
else state.element.setAttribute("aria-hidden", state.ariaHidden);
|
|
7132
|
+
}
|
|
7133
|
+
const returnCandidate = returnFocus?.isConnected ? returnFocus : this.confirmEl?.querySelector(".sl-confirm-confidence") ?? this.view3dCompareEl?.querySelector("[data-passport-seat]") ?? null;
|
|
7134
|
+
const returnSurface = returnCandidate?.closest(".sl-confirm,.sl-view3d-compare");
|
|
7135
|
+
if (returnSurface?.isConnected) {
|
|
7136
|
+
returnSurface.inert = false;
|
|
7137
|
+
returnSurface.removeAttribute("aria-hidden");
|
|
7138
|
+
}
|
|
7139
|
+
shell.remove();
|
|
7140
|
+
overlay.classList.remove("has-passport");
|
|
7141
|
+
if (this.view3dPassportEl === shell) this.view3dPassportEl = null;
|
|
7142
|
+
const fallback = returnCandidate ?? this.view3dCompareEl?.querySelector("[data-passport-seat]") ?? this.confirmEl?.querySelector(".sl-confirm-confidence") ?? this.view3dCompareChip?.querySelector(".main");
|
|
7143
|
+
(returnFocus?.isConnected ? returnFocus : fallback)?.focus();
|
|
7144
|
+
};
|
|
7145
|
+
shell.addEventListener("click", (event) => {
|
|
7146
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
7147
|
+
if (target?.closest("[data-close]") || target?.classList.contains("sl-view3d-passport-scrim")) {
|
|
7148
|
+
this.closeSeatConfidencePassport();
|
|
7149
|
+
}
|
|
7150
|
+
});
|
|
7151
|
+
requestAnimationFrame(() => controls()[0]?.focus());
|
|
7152
|
+
this.emit3dAnalytics("3d_confidence_passport_opened", {
|
|
7153
|
+
seatId: seat.id,
|
|
7154
|
+
evidenceId: evidence?.evidenceId ?? null,
|
|
7155
|
+
eventConfigurationId: evidence?.eventConfigurationId ?? null,
|
|
7156
|
+
modelLevel: evidence?.modelLevel ?? "unverified",
|
|
7157
|
+
realityLevel: evidence?.realityLevel ?? "none"
|
|
7158
|
+
});
|
|
7159
|
+
}
|
|
7160
|
+
closeSeatConfidencePassport(restoreFocus = true) {
|
|
7161
|
+
const cleanup = this.view3dPassportCleanup;
|
|
7162
|
+
this.view3dPassportCleanup = null;
|
|
7163
|
+
if (!cleanup) {
|
|
7164
|
+
this.view3dPassportEl?.remove();
|
|
7165
|
+
this.view3dPassportEl = null;
|
|
7166
|
+
this.view3dEl?.classList.remove("has-passport");
|
|
7167
|
+
return;
|
|
7168
|
+
}
|
|
7169
|
+
if (!restoreFocus) (document.activeElement instanceof HTMLElement ? document.activeElement : null)?.blur();
|
|
7170
|
+
cleanup();
|
|
7171
|
+
if (!restoreFocus) this.root?.focus({ preventScroll: true });
|
|
7172
|
+
}
|
|
7173
|
+
openView3dComparison() {
|
|
7174
|
+
const overlay = this.view3dEl;
|
|
7175
|
+
if (!overlay || this.view3dCompareSeatIds.length < 2) return;
|
|
7176
|
+
this.closeView3dComparison(false);
|
|
7177
|
+
const snapshots = this.view3dCompareSeatIds.map((seatId) => this.view3dComparisonSnapshot(seatId)).filter((value) => !!value);
|
|
7178
|
+
if (snapshots.length < 2) {
|
|
7179
|
+
this.clearView3dComparison();
|
|
7180
|
+
return;
|
|
7181
|
+
}
|
|
7182
|
+
const safe = (value) => this.escCx(value);
|
|
7183
|
+
const shell = document.createElement("div");
|
|
7184
|
+
shell.className = "sl-view3d-compare-shell";
|
|
7185
|
+
const cards = snapshots.map((snapshot, index) => `<article><span>Seat ${index === 0 ? "A" : "B"}</span><strong>${safe(snapshot.label)}</strong><small>Section ${safe(snapshot.section)} \xB7 Row ${safe(snapshot.row)}</small><dl><div><dt>Current price</dt><dd>${safe(snapshot.price)}</dd></div><div><dt>Ticket type</dt><dd>${safe(snapshot.category)}</dd></div><div><dt>Availability</dt><dd>${safe(snapshot.availability)}</dd></div><div><dt>View source</dt><dd>${safe(snapshot.viewSource)}</dd></div><div><dt>View restriction</dt><dd>${safe(snapshot.limited)}</dd></div><div><dt>Seat confidence</dt><dd>${safe(snapshot.confidence.headline)}</dd></div><div><dt>Reality check</dt><dd>${safe(snapshot.confidence.reality)}</dd></div><div><dt>Accessibility</dt><dd>${safe(snapshot.accessibility)}</dd></div></dl><div class="sl-view3d-compare-actions"><button type="button" data-passport-seat="${safe(snapshot.seat.id)}">Passport</button><button type="button" data-view-seat="${safe(snapshot.seat.id)}">View seat</button><button type="button" class="select" data-select-seat="${safe(snapshot.seat.id)}"${snapshot.selectable ? "" : " disabled"}>Select seat</button></div></article>`).join("");
|
|
7186
|
+
shell.innerHTML = `<div class="sl-view3d-compare-scrim" aria-hidden="true"></div><section class="sl-view3d-compare" role="dialog" aria-modal="true" aria-labelledby="sl-view3d-compare-title"><header><div><span>Seat inspection</span><strong id="sl-view3d-compare-title">Compare disclosed attributes</strong></div><button type="button" data-close aria-label="Close seat comparison">\xD7</button></header><p class="sl-view3d-compare-note">Current price and availability come from this picker. Modeled views are chart-derived unless organizer media is labeled; SeatLayer does not invent why a seat has its price.</p><div class="sl-view3d-compare-grid">${cards}</div></section>`;
|
|
7187
|
+
const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
7188
|
+
const background = [...overlay.children].filter((element) => element instanceof HTMLElement);
|
|
7189
|
+
const prior = background.map((element) => ({
|
|
7190
|
+
element,
|
|
7191
|
+
inert: element.inert,
|
|
7192
|
+
ariaHidden: element.getAttribute("aria-hidden")
|
|
7193
|
+
}));
|
|
7194
|
+
for (const element of background) {
|
|
7195
|
+
element.inert = true;
|
|
7196
|
+
element.setAttribute("aria-hidden", "true");
|
|
7197
|
+
}
|
|
7198
|
+
overlay.appendChild(shell);
|
|
7199
|
+
overlay.classList.add("has-comparison");
|
|
7200
|
+
this.view3dCompareEl = shell;
|
|
7201
|
+
const dialog = shell.querySelector(".sl-view3d-compare");
|
|
7202
|
+
const controls = () => [...dialog.querySelectorAll('button:not(:disabled),[href],[tabindex]:not([tabindex="-1"])')];
|
|
7203
|
+
const onKey = (event) => {
|
|
7204
|
+
if (event.key === "Escape") {
|
|
7205
|
+
event.preventDefault();
|
|
7206
|
+
this.closeView3dComparison();
|
|
7207
|
+
return;
|
|
7208
|
+
}
|
|
7209
|
+
if (event.key !== "Tab") return;
|
|
7210
|
+
const focusable = controls();
|
|
7211
|
+
if (!focusable.length) return;
|
|
7212
|
+
const first = focusable[0];
|
|
7213
|
+
const last = focusable[focusable.length - 1];
|
|
7214
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
7215
|
+
event.preventDefault();
|
|
7216
|
+
last.focus();
|
|
7217
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
7218
|
+
event.preventDefault();
|
|
7219
|
+
first.focus();
|
|
7220
|
+
}
|
|
7221
|
+
};
|
|
7222
|
+
window.addEventListener("keydown", onKey);
|
|
7223
|
+
this.view3dCompareCleanup = () => {
|
|
7224
|
+
window.removeEventListener("keydown", onKey);
|
|
7225
|
+
for (const state of prior) {
|
|
7226
|
+
state.element.inert = state.inert;
|
|
7227
|
+
if (state.ariaHidden === null) state.element.removeAttribute("aria-hidden");
|
|
7228
|
+
else state.element.setAttribute("aria-hidden", state.ariaHidden);
|
|
7229
|
+
}
|
|
7230
|
+
shell.remove();
|
|
7231
|
+
overlay.classList.remove("has-comparison");
|
|
7232
|
+
if (this.view3dCompareEl === shell) this.view3dCompareEl = null;
|
|
7233
|
+
if (previousFocus?.isConnected) previousFocus.focus();
|
|
7234
|
+
else this.view3dCompareChip?.querySelector(".main")?.focus();
|
|
7235
|
+
};
|
|
7236
|
+
shell.querySelector("[data-close]")?.addEventListener("click", () => this.closeView3dComparison());
|
|
7237
|
+
shell.querySelectorAll("[data-passport-seat]").forEach((button) => button.addEventListener("click", () => {
|
|
7238
|
+
const seatId = button.dataset.passportSeat;
|
|
7239
|
+
const seat = seatId ? this.allSeats().find((candidate) => candidate.id === seatId) : void 0;
|
|
7240
|
+
if (seat) this.openSeatConfidencePassport(seat, button);
|
|
7241
|
+
}));
|
|
7242
|
+
shell.querySelectorAll("[data-view-seat]").forEach((button) => button.addEventListener("click", () => {
|
|
7243
|
+
const seatId = button.dataset.viewSeat;
|
|
7244
|
+
this.closeView3dComparison(false);
|
|
7245
|
+
if (seatId) void this.view3dHandle?.flyToSeat(seatId);
|
|
7246
|
+
}));
|
|
7247
|
+
shell.querySelectorAll("[data-select-seat]").forEach((button) => button.addEventListener("click", () => {
|
|
7248
|
+
const seatId = button.dataset.selectSeat;
|
|
7249
|
+
if (seatId) this.selectComparedSeat(seatId);
|
|
7250
|
+
}));
|
|
7251
|
+
requestAnimationFrame(() => controls()[0]?.focus());
|
|
7252
|
+
this.emit3dAnalytics("3d_comparison_opened", { seatIds: this.view3dCompareSeatIds.slice() });
|
|
7253
|
+
}
|
|
7254
|
+
closeView3dComparison(restoreFocus = true) {
|
|
7255
|
+
const cleanup = this.view3dCompareCleanup;
|
|
7256
|
+
this.view3dCompareCleanup = null;
|
|
7257
|
+
if (!cleanup) {
|
|
7258
|
+
this.view3dCompareEl?.remove();
|
|
7259
|
+
this.view3dCompareEl = null;
|
|
7260
|
+
this.view3dEl?.classList.remove("has-comparison");
|
|
7261
|
+
return;
|
|
7262
|
+
}
|
|
7263
|
+
if (!restoreFocus) {
|
|
7264
|
+
const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
7265
|
+
active?.blur();
|
|
7266
|
+
}
|
|
7267
|
+
cleanup();
|
|
7268
|
+
if (!restoreFocus) this.root?.focus({ preventScroll: true });
|
|
7269
|
+
}
|
|
7270
|
+
selectComparedSeat(seatId) {
|
|
7271
|
+
if (this.salesClosed) {
|
|
7272
|
+
this.toast(this.tf("picker.salesClosedToast", "Sales are closed for this event."), "warning");
|
|
7273
|
+
return;
|
|
7274
|
+
}
|
|
7275
|
+
const seat = this.allSeats().find((candidate) => candidate.id === seatId);
|
|
7276
|
+
if (!seat) return;
|
|
7277
|
+
this.closeView3dComparison(false);
|
|
7278
|
+
const added = this.controller.select([seatId]);
|
|
7279
|
+
if (!added.length) {
|
|
7280
|
+
this.syncSelectionTo3d();
|
|
7281
|
+
this.toast(this.tf("picker.seatNoLongerAvailable", "That seat is no longer available."), "warning");
|
|
7282
|
+
return;
|
|
7283
|
+
}
|
|
7284
|
+
this.syncSelectionTo3d();
|
|
7285
|
+
this.showConfirm(seat);
|
|
7286
|
+
this.emit3dAnalytics("3d_comparison_selected", { seatId });
|
|
6492
7287
|
}
|
|
6493
7288
|
/**
|
|
6494
7289
|
* The venue-navigation rail inside 3D: levels and areas.
|
|
@@ -6517,6 +7312,25 @@ var SeatPicker = class _SeatPicker {
|
|
|
6517
7312
|
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
6518
7313
|
const nav = document.createElement("div");
|
|
6519
7314
|
nav.className = "sl-view3d-nav";
|
|
7315
|
+
const finderToggle = document.createElement("button");
|
|
7316
|
+
finderToggle.type = "button";
|
|
7317
|
+
finderToggle.className = "sl-view3d-nav-toggle";
|
|
7318
|
+
finderToggle.setAttribute("aria-expanded", "false");
|
|
7319
|
+
const setFinderOpen = (open) => {
|
|
7320
|
+
nav.classList.toggle("is-open", open);
|
|
7321
|
+
finderToggle.setAttribute("aria-expanded", String(open));
|
|
7322
|
+
finderToggle.textContent = open ? "Close seat finder" : "Find a seat";
|
|
7323
|
+
};
|
|
7324
|
+
finderToggle.addEventListener("click", () => setFinderOpen(!nav.classList.contains("is-open")));
|
|
7325
|
+
nav.addEventListener("keydown", (event) => {
|
|
7326
|
+
if (event.key !== "Escape" || !nav.classList.contains("is-open")) return;
|
|
7327
|
+
event.preventDefault();
|
|
7328
|
+
event.stopPropagation();
|
|
7329
|
+
setFinderOpen(false);
|
|
7330
|
+
finderToggle.focus();
|
|
7331
|
+
});
|
|
7332
|
+
setFinderOpen(false);
|
|
7333
|
+
nav.appendChild(finderToggle);
|
|
6520
7334
|
if (wantFloors) {
|
|
6521
7335
|
const row = document.createElement("div");
|
|
6522
7336
|
row.setAttribute("role", "group");
|
|
@@ -6534,7 +7348,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
6534
7348
|
b.textContent = label;
|
|
6535
7349
|
b.dataset.floor = index === null ? "" : String(index);
|
|
6536
7350
|
b.setAttribute("aria-pressed", String(index === null));
|
|
6537
|
-
b.addEventListener("click", () =>
|
|
7351
|
+
b.addEventListener("click", () => {
|
|
7352
|
+
select(index);
|
|
7353
|
+
setFinderOpen(false);
|
|
7354
|
+
});
|
|
6538
7355
|
pills.push(b);
|
|
6539
7356
|
row.appendChild(b);
|
|
6540
7357
|
};
|
|
@@ -6573,7 +7390,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
6573
7390
|
const b = document.createElement("button");
|
|
6574
7391
|
b.type = "button";
|
|
6575
7392
|
b.textContent = e.label;
|
|
6576
|
-
b.addEventListener("click",
|
|
7393
|
+
b.addEventListener("click", () => {
|
|
7394
|
+
e.go();
|
|
7395
|
+
setFinderOpen(false);
|
|
7396
|
+
});
|
|
6577
7397
|
row.appendChild(b);
|
|
6578
7398
|
}
|
|
6579
7399
|
}
|
|
@@ -6592,7 +7412,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6592
7412
|
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
6593
7413
|
const view = document.createElement("button");
|
|
6594
7414
|
view.type = "button";
|
|
6595
|
-
view.textContent = "
|
|
7415
|
+
view.textContent = "Inspect seat";
|
|
6596
7416
|
view.disabled = true;
|
|
6597
7417
|
const fill = (select, placeholder, entries) => {
|
|
6598
7418
|
select.replaceChildren();
|
|
@@ -6648,11 +7468,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6648
7468
|
seatSelect.addEventListener("change", () => {
|
|
6649
7469
|
const seatId = seatSelect.value;
|
|
6650
7470
|
view.disabled = !seatId;
|
|
6651
|
-
handle.setSelection(seatId ? [seatId] : []);
|
|
6652
7471
|
});
|
|
6653
7472
|
view.addEventListener("click", () => {
|
|
6654
7473
|
const seatId = seatSelect.value;
|
|
6655
|
-
if (seatId)
|
|
7474
|
+
if (seatId) {
|
|
7475
|
+
setFinderOpen(false);
|
|
7476
|
+
this.onView3dSeatPick(seatId);
|
|
7477
|
+
}
|
|
6656
7478
|
});
|
|
6657
7479
|
locator.append(sectionSelect, rowSelect, seatSelect, view);
|
|
6658
7480
|
nav.appendChild(locator);
|
|
@@ -6664,6 +7486,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6664
7486
|
const doc = this.controller.doc;
|
|
6665
7487
|
if (!doc) return;
|
|
6666
7488
|
this.buyerView = "venue3d";
|
|
7489
|
+
this.view3dTargetSeatId = null;
|
|
6667
7490
|
this.opts.onBuyerViewChange?.({ view: "venue3d", ...flySeatId ? { seatId: flySeatId } : {} });
|
|
6668
7491
|
this.root?.setAttribute("data-view3d", "on");
|
|
6669
7492
|
this.dismissConfirm();
|
|
@@ -6676,7 +7499,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
6676
7499
|
back.type = "button";
|
|
6677
7500
|
back.className = "sl-view3d-back";
|
|
6678
7501
|
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>`;
|
|
6679
|
-
back.
|
|
7502
|
+
const backLabel = back.querySelector("span");
|
|
7503
|
+
const setJourneyTarget = (seatId) => {
|
|
7504
|
+
this.view3dTargetSeatId = seatId;
|
|
7505
|
+
const atSeat = !!seatId;
|
|
7506
|
+
overlay.classList.toggle("is-seat-focused", atSeat);
|
|
7507
|
+
const label = atSeat ? this.tf("picker.backToVenue", "Back to venue") : this.tf("picker.backToMap", "Back to map");
|
|
7508
|
+
if (backLabel) backLabel.textContent = label;
|
|
7509
|
+
back.setAttribute("aria-label", label);
|
|
7510
|
+
};
|
|
7511
|
+
back.addEventListener("click", () => {
|
|
7512
|
+
if (this.view3dTargetSeatId && this.view3dHandle) {
|
|
7513
|
+
this.view3dHandle.focusOverview();
|
|
7514
|
+
return;
|
|
7515
|
+
}
|
|
7516
|
+
this.exit3d();
|
|
7517
|
+
});
|
|
6680
7518
|
overlay.appendChild(back);
|
|
6681
7519
|
const fullscreen = document.createElement("button");
|
|
6682
7520
|
fullscreen.type = "button";
|
|
@@ -6694,6 +7532,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6694
7532
|
overlay.appendChild(loading);
|
|
6695
7533
|
this.els.map.appendChild(overlay);
|
|
6696
7534
|
this.view3dEl = overlay;
|
|
7535
|
+
this.syncView3dCompareChip();
|
|
6697
7536
|
requestAnimationFrame(() => {
|
|
6698
7537
|
overlay.style.opacity = "1";
|
|
6699
7538
|
});
|
|
@@ -6709,7 +7548,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6709
7548
|
// phone. The bounded portrait fit was validated against every UX-lab
|
|
6710
7549
|
// fixture; keep editor previews strict while making buyer seats legible.
|
|
6711
7550
|
portraitOverviewCrop: true,
|
|
7551
|
+
// Premium buyer mode lands at the modeled seated-eye and locks the
|
|
7552
|
+
// venue orbit there. Explicit look-around rotates at that same origin;
|
|
7553
|
+
// zooming cannot escape through the shell or reveal backstage geometry.
|
|
7554
|
+
arriveAtSeatEye: true,
|
|
7555
|
+
seatViewActionLabel: (seatId) => this.allSeats().find((seat) => seat.id === seatId)?.viewUrl ? this.tf("picker.openAuthored360", "Open venue 360\xB0") : this.tf("picker.lookAroundLive3d", "Look around in live 3D"),
|
|
6712
7556
|
onSeatPick: (id) => this.onView3dSeatPick(id),
|
|
7557
|
+
onSeatInspect: (id) => this.onView3dSeatPick(id),
|
|
6713
7558
|
onSectionFocusChange: (sectionId) => {
|
|
6714
7559
|
const select = overlay.querySelector(
|
|
6715
7560
|
'select[aria-label="Choose section in 3D"]'
|
|
@@ -6721,6 +7566,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6721
7566
|
},
|
|
6722
7567
|
onViewTargetChange: (seatId) => {
|
|
6723
7568
|
overlay.querySelector(".sl-view3d-nav")?.classList.toggle("is-seat-focused", !!seatId);
|
|
7569
|
+
setJourneyTarget(seatId);
|
|
6724
7570
|
this.opts.onBuyerViewChange?.({
|
|
6725
7571
|
view: "venue3d",
|
|
6726
7572
|
...seatId ? { seatId } : {}
|
|
@@ -6761,8 +7607,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6761
7607
|
if (this.buyerView !== "venue3d" && !this.view3dEl) return;
|
|
6762
7608
|
this.view3dGen++;
|
|
6763
7609
|
this.buyerView = "map";
|
|
7610
|
+
this.view3dTargetSeatId = null;
|
|
6764
7611
|
this.opts.onBuyerViewChange?.({ view: "map" });
|
|
6765
7612
|
this.root?.removeAttribute("data-view3d");
|
|
7613
|
+
this.closeSeatConfidencePassport(false);
|
|
7614
|
+
this.closeView3dComparison(false);
|
|
7615
|
+
this.view3dCompareChip?.remove();
|
|
7616
|
+
this.view3dCompareChip = null;
|
|
6766
7617
|
try {
|
|
6767
7618
|
this.view3dHandle?.dispose();
|
|
6768
7619
|
} catch {
|
|
@@ -6802,6 +7653,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6802
7653
|
const button = this.els.tray?.querySelector(".sl-ba-go");
|
|
6803
7654
|
if (button) {
|
|
6804
7655
|
button.disabled = true;
|
|
7656
|
+
button.classList.add("sl-busy");
|
|
6805
7657
|
button.innerHTML = '<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026';
|
|
6806
7658
|
}
|
|
6807
7659
|
try {
|
|
@@ -7048,7 +7900,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
7048
7900
|
if (this.modalScrim) {
|
|
7049
7901
|
this.modalScrim.remove();
|
|
7050
7902
|
this.modalScrim = null;
|
|
7051
|
-
this.prevFocus?.focus
|
|
7903
|
+
if (this.prevFocus?.isConnected) this.prevFocus.focus({ preventScroll: true });
|
|
7904
|
+
this.prevFocus = null;
|
|
7052
7905
|
}
|
|
7053
7906
|
}
|
|
7054
7907
|
};
|
|
@@ -7139,46 +7992,17 @@ function attachPickerFrame(iframe, opts = {}) {
|
|
|
7139
7992
|
};
|
|
7140
7993
|
}
|
|
7141
7994
|
export {
|
|
7142
|
-
ACCESS_LINK_DEFAULTS,
|
|
7143
7995
|
ApiError,
|
|
7144
7996
|
BuyerAccessContext,
|
|
7145
7997
|
BuyerAccessUnavailableError,
|
|
7146
7998
|
BuyerRealtimeClient,
|
|
7147
|
-
ChannelsMode,
|
|
7148
7999
|
EmbeddedDesigner,
|
|
7149
|
-
ManageApi,
|
|
7150
|
-
ManageApiError,
|
|
7151
|
-
PUBLIC_CHANNEL_ID,
|
|
7152
|
-
PUBLIC_CHANNEL_NAME,
|
|
7153
|
-
SeatManager,
|
|
7154
8000
|
SeatPicker,
|
|
7155
8001
|
SeatingChart,
|
|
7156
|
-
accessIntentDescription,
|
|
7157
|
-
accessIntentLabel,
|
|
7158
|
-
accessLine,
|
|
7159
|
-
accessLinkBadge,
|
|
7160
|
-
accessLinkErrorCopy,
|
|
7161
|
-
accessLinkIsLive,
|
|
7162
|
-
accessLinkPolicyLines,
|
|
7163
8002
|
attachPickerFrame,
|
|
7164
|
-
bucketRows,
|
|
7165
|
-
bucketRowsHtml,
|
|
7166
8003
|
createBuyerAccessContext,
|
|
7167
8004
|
createControllerSink,
|
|
7168
|
-
dropReviewRows,
|
|
7169
|
-
intentForbidsCopy,
|
|
7170
|
-
intentSwitchBlockedCopy,
|
|
7171
|
-
isPublicChannelId,
|
|
7172
|
-
markerLetter,
|
|
7173
|
-
markerOf,
|
|
7174
|
-
mutationCount,
|
|
7175
|
-
needsMoveConfirmation,
|
|
7176
8005
|
parseTicketOfferAvailability,
|
|
7177
|
-
planAssignment,
|
|
7178
|
-
retryAfterCopy,
|
|
7179
|
-
selectionSources,
|
|
7180
|
-
stateBadge,
|
|
7181
|
-
suggestMarker,
|
|
7182
8006
|
ticketOfferPrices
|
|
7183
8007
|
};
|
|
7184
8008
|
//# sourceMappingURL=index.js.map
|