@seatlayer/js 0.50.2 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1007 -459
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -8
- package/dist/index.d.ts +53 -8
- package/dist/index.js +1011 -459
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2114,6 +2114,13 @@ var BuyerAssetObjectUrls = class {
|
|
|
2114
2114
|
}
|
|
2115
2115
|
};
|
|
2116
2116
|
|
|
2117
|
+
// src/SeatPicker.ts
|
|
2118
|
+
import {
|
|
2119
|
+
createMinimapTransform,
|
|
2120
|
+
minimapPointToWorld,
|
|
2121
|
+
minimapViewportPlan
|
|
2122
|
+
} from "@seatlayer/core/picker/minimapGeometry";
|
|
2123
|
+
|
|
2117
2124
|
// src/offerAvailability.ts
|
|
2118
2125
|
var SALE_STATES = [
|
|
2119
2126
|
"on-sale",
|
|
@@ -2248,6 +2255,16 @@ var DEFAULT_MAX_SELECTION2 = 10;
|
|
|
2248
2255
|
var EXTEND_PROMPT_MS = 6e4;
|
|
2249
2256
|
var MAX_3D_SEATS_DEFAULT = 6e4;
|
|
2250
2257
|
var MAX_3D_SECTION_PILLS = 12;
|
|
2258
|
+
function tfStatic(key, fallback) {
|
|
2259
|
+
const v = t2(key);
|
|
2260
|
+
return v === key ? fallback : v;
|
|
2261
|
+
}
|
|
2262
|
+
var chartsLoadedThisPage = /* @__PURE__ */ new Set();
|
|
2263
|
+
function markChartLoaded(eventKey) {
|
|
2264
|
+
const seen = chartsLoadedThisPage.has(eventKey);
|
|
2265
|
+
chartsLoadedThisPage.add(eventKey);
|
|
2266
|
+
return seen;
|
|
2267
|
+
}
|
|
2251
2268
|
function pointInPolygon(x, y, poly) {
|
|
2252
2269
|
let inside = false;
|
|
2253
2270
|
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
@@ -2350,9 +2367,13 @@ var CSS = (
|
|
|
2350
2367
|
the WIDGET's meanings, not an org's brand: premium gold, environment
|
|
2351
2368
|
warning, danger, success. Hosts can still override them with CSS. */
|
|
2352
2369
|
--sl-premium:#e8c15a;--sl-premium-deep:#c9a24b;--sl-premium-ink:#1c1608;
|
|
2353
|
-
--sl-
|
|
2370
|
+
--sl-premium-text:color-mix(in srgb,var(--sl-premium-deep) 48%,var(--sl-text));
|
|
2371
|
+
--sl-warn:#d8a425;--sl-warn-text:color-mix(in srgb,var(--sl-warn) 48%,var(--sl-text));
|
|
2372
|
+
--sl-danger:#e5484d;--sl-success:#22a06b}
|
|
2354
2373
|
.sl-picker *{box-sizing:border-box;margin:0;padding:0}
|
|
2355
|
-
.
|
|
2374
|
+
/* Keep the reset intentionally specificity-free. Standalone map and panel
|
|
2375
|
+
controls must be able to establish their own token surfaces in either theme. */
|
|
2376
|
+
.sl-picker :where(button){font:inherit;color:inherit;background:none;border:0;cursor:pointer}
|
|
2356
2377
|
|
|
2357
2378
|
/* header */
|
|
2358
2379
|
.sl-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--sl-line);flex:none}
|
|
@@ -2422,11 +2443,30 @@ var CSS = (
|
|
|
2422
2443
|
.sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
|
|
2423
2444
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
2424
2445
|
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
2425
|
-
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(
|
|
2446
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(380px,64%)}
|
|
2447
|
+
/* With the price list expanded the sheet takes the with-selection height so
|
|
2448
|
+
the tray (cart + best-seats) is never crushed under the fixed-size list. */
|
|
2449
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-side{height:min(480px,74%)}
|
|
2426
2450
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:76px;overflow:hidden}
|
|
2427
2451
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
2428
|
-
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:
|
|
2452
|
+
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:96px;overflow-y:auto;overscroll-behavior:contain}
|
|
2453
|
+
.sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-tray{min-height:48px}
|
|
2454
|
+
.sl-picker[data-layout="narrow"] .sl-prices{max-height:150px;overflow-y:auto;overscroll-behavior:contain}
|
|
2429
2455
|
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
2456
|
+
/* NARROW FOOT SAYS EVERYTHING ONCE (owner report: with a hold the footer ate
|
|
2457
|
+
~190px and the ticket list got a sliver). The sheet head already carries
|
|
2458
|
+
"N tickets \xB7 $total" and the header pill already ticks the clock \u2014 so the
|
|
2459
|
+
total row hides, the secured note collapses to one compact line, and the
|
|
2460
|
+
badge tucks in. The tray gets the pixels; a thin scrollbar shows there is
|
|
2461
|
+
more to scroll. */
|
|
2462
|
+
.sl-picker[data-layout="narrow"] .sl-total{display:none}
|
|
2463
|
+
.sl-picker[data-layout="narrow"] .sl-foot{padding:8px 12px 8px}
|
|
2464
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note{margin-bottom:6px;padding:5px 9px}
|
|
2465
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note svg{display:none}
|
|
2466
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note b{display:inline;margin-right:6px;font-size:11px}
|
|
2467
|
+
.sl-picker[data-layout="narrow"] .sl-hold-copy{display:inline;white-space:normal;font-size:10.5px}
|
|
2468
|
+
.sl-picker[data-layout="narrow"] .sl-powered{margin-top:4px;padding:2px 8px;font-size:11px}
|
|
2469
|
+
.sl-picker[data-layout="narrow"] .sl-tray{scrollbar-width:thin}
|
|
2430
2470
|
.sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
|
|
2431
2471
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
|
|
2432
2472
|
.sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
|
|
@@ -2434,6 +2474,7 @@ var CSS = (
|
|
|
2434
2474
|
.sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
|
|
2435
2475
|
.sl-picker[data-layout="narrow"] .sl-filters{order:4}
|
|
2436
2476
|
.sl-picker[data-layout="narrow"] .sl-prices-sec{order:5}
|
|
2477
|
+
.sl-picker[data-layout="narrow"] .sl-prices-hint{order:6}
|
|
2437
2478
|
.sl-picker[data-layout="narrow"] .sl-pricef{order:6}
|
|
2438
2479
|
.sl-picker[data-layout="narrow"] .sl-prices{order:7}
|
|
2439
2480
|
.sl-picker[data-layout="narrow"] .sl-foot{order:8}
|
|
@@ -2441,6 +2482,7 @@ var CSS = (
|
|
|
2441
2482
|
.sl-picker[data-layout="narrow"] .sl-filtersec,
|
|
2442
2483
|
.sl-picker[data-layout="narrow"] .sl-filters,
|
|
2443
2484
|
.sl-picker[data-layout="narrow"] .sl-prices-sec,
|
|
2485
|
+
.sl-picker[data-layout="narrow"] .sl-prices-hint,
|
|
2444
2486
|
.sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
|
|
2445
2487
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
|
|
2446
2488
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
|
|
@@ -2458,7 +2500,13 @@ var CSS = (
|
|
|
2458
2500
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
2459
2501
|
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
2460
2502
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
2461
|
-
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:
|
|
2503
|
+
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:52px;padding:3px 10px 5px}
|
|
2504
|
+
/* OPEN sheet: the head is context, not controls \u2014 the pill it carries at peek
|
|
2505
|
+
("Best seats"/"Continue") duplicates the card and CTA right below it, and
|
|
2506
|
+
every saved pixel is tray room (owner call 2026-08-14). */
|
|
2507
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-head{min-height:42px}
|
|
2508
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-bar{min-height:32px}
|
|
2509
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-peek .go{display:none}
|
|
2462
2510
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-sheet-head{height:100%}
|
|
2463
2511
|
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:1px auto 5px}
|
|
2464
2512
|
.sl-sheet-bar{display:flex;align-items:center;gap:8px;min-height:44px}
|
|
@@ -2499,9 +2547,19 @@ var CSS = (
|
|
|
2499
2547
|
sheet brings it back, exactly like the filters row above. The tray hint comes
|
|
2500
2548
|
back with it: the first-time buyer on a phone needs the instruction more than
|
|
2501
2549
|
the desktop buyer, not less. */
|
|
2502
|
-
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices-sec{display:flex!important}
|
|
2503
|
-
|
|
2550
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-sec{display:flex!important}
|
|
2551
|
+
/* MOBILE SPACE BELONGS TO THE CART (owner call 2026-08-14): the price list is
|
|
2552
|
+
one tap away behind the "Ticket prices" row, not a permanent tenant. Zone
|
|
2553
|
+
flags and section taps already answer "what does it cost" in context. */
|
|
2554
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-hint{display:block!important}
|
|
2555
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices{display:flex!important}
|
|
2504
2556
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-tray-hint{display:block!important}
|
|
2557
|
+
/* The row reads as the toggle it is on narrow: chevron + pointer. */
|
|
2558
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec{cursor:pointer}
|
|
2559
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec::after{content:'';width:8px;height:8px;flex:none;margin-left:auto;
|
|
2560
|
+
border-right:2px solid var(--sl-muted);border-bottom:2px solid var(--sl-muted);
|
|
2561
|
+
transform:rotate(45deg);transition:transform .2s cubic-bezier(.2,.8,.2,1)}
|
|
2562
|
+
.sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-prices-sec::after{transform:rotate(-135deg)}
|
|
2505
2563
|
.sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
2506
2564
|
color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
2507
2565
|
.sl-cbbtn:hover{border-color:var(--sl-muted)}
|
|
@@ -2511,24 +2569,30 @@ var CSS = (
|
|
|
2511
2569
|
.sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
|
|
2512
2570
|
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
2513
2571
|
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
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)}
|
|
2572
|
+
.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-text)}
|
|
2515
2573
|
.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)}
|
|
2516
2574
|
.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;
|
|
2517
2575
|
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
2518
|
-
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
2576
|
+
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
2519
2577
|
.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)}
|
|
2520
2578
|
.sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
2521
2579
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
2522
2580
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
2523
|
-
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
2581
|
+
background-color:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
2582
|
+
/* One sentence that teaches the whole colour code \u2014 a first-time buyer sees
|
|
2583
|
+
"Head Tables \xB7 $165" and coloured dots with nothing connecting them. Gone
|
|
2584
|
+
the moment they have seats (they've learned it) and while sales are closed. */
|
|
2585
|
+
.sl-prices-hint{padding:0 14px 7px;font-size:11px;line-height:1.45;color:var(--sl-muted)}
|
|
2586
|
+
.sl-picker[data-has-selection="true"] .sl-prices-hint,
|
|
2587
|
+
.sl-picker[data-sales-closed="true"] .sl-prices-hint{display:none}
|
|
2524
2588
|
.sl-prices{display:flex;flex-direction:column;padding:4px 14px 8px;border-bottom:1px solid var(--sl-line)}
|
|
2525
2589
|
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
2526
2590
|
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px;
|
|
2527
2591
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
2528
2592
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
2529
2593
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
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}
|
|
2531
|
-
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
2594
|
+
.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-text);font-size:10px;font-weight:750}
|
|
2595
|
+
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent-text)}
|
|
2532
2596
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
2533
2597
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
2534
2598
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
@@ -2563,7 +2627,7 @@ var CSS = (
|
|
|
2563
2627
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
|
|
2564
2628
|
.sl-ticket-state{width:17px;height:17px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
2565
2629
|
background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
2566
|
-
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent)}
|
|
2630
|
+
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent-text)}
|
|
2567
2631
|
.sl-ticket-state svg{width:10px;height:10px;stroke:currentColor;stroke-width:2.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2568
2632
|
.sl-chip-main{min-width:0;padding:8px 10px 8px 11px;display:flex;flex-direction:column;justify-content:center;gap:5px}
|
|
2569
2633
|
.sl-chip-id{display:flex;gap:12px;min-width:0}
|
|
@@ -2606,7 +2670,7 @@ var CSS = (
|
|
|
2606
2670
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
2607
2671
|
|
|
2608
2672
|
/* footer */
|
|
2609
|
-
.sl-foot{position:relative;z-index:2;padding:
|
|
2673
|
+
.sl-foot{position:relative;z-index:2;padding:10px 16px 8px;border-top:1px solid var(--sl-line);flex:none;
|
|
2610
2674
|
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
2611
2675
|
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
2612
2676
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
@@ -2620,14 +2684,13 @@ var CSS = (
|
|
|
2620
2684
|
.sl-hold-note>span{flex:1;min-width:0}
|
|
2621
2685
|
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
2622
2686
|
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
2623
|
-
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
2687
|
+
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
2624
2688
|
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
2625
2689
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
2626
2690
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
2627
2691
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
2628
|
-
/* Primary checkout CTA.
|
|
2629
|
-
|
|
2630
|
-
and the button renders as plain text with no accent fill. */
|
|
2692
|
+
/* Primary checkout CTA. The extra root scope documents that this is widget
|
|
2693
|
+
chrome and keeps the contract resilient when embedded beside host styles. */
|
|
2631
2694
|
.sl-picker .sl-cta{display:flex;align-items:center;justify-content:center;width:100%;min-height:44px;
|
|
2632
2695
|
padding:12px 16px;border-radius:var(--sl-r-sm);font-weight:800;font-size:14px;line-height:1.1;
|
|
2633
2696
|
background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
@@ -2638,8 +2701,8 @@ var CSS = (
|
|
|
2638
2701
|
.sl-cta-spin,.sl-ba-spin{width:14px;height:14px;border-radius:50%;border:2px solid currentColor;border-right-color:transparent;
|
|
2639
2702
|
animation:slspin .7s linear infinite;flex:none}
|
|
2640
2703
|
/* Disabled ("Select seats"): quieter, but still a full-width button shape. */
|
|
2641
|
-
.sl-picker .sl-cta:disabled{background:var(--sl-surface);color:var(--sl-muted);
|
|
2642
|
-
cursor:not-allowed;filter:none;transform:none}
|
|
2704
|
+
.sl-picker .sl-cta:disabled{background:color-mix(in srgb,var(--sl-bg) 35%,var(--sl-surface));color:var(--sl-muted);
|
|
2705
|
+
box-shadow:inset 0 0 0 1px var(--sl-line);opacity:1;cursor:not-allowed;filter:none;transform:none}
|
|
2643
2706
|
|
|
2644
2707
|
/* Chrome anchor regions (Feature 6) \u2014 every interactive map overlay is APPENDED
|
|
2645
2708
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
@@ -2669,7 +2732,7 @@ var CSS = (
|
|
|
2669
2732
|
in the same region, so no displacement hack is needed. */
|
|
2670
2733
|
.sl-testbadge{display:inline-flex;align-items:center;padding:5px 10px;border-radius:7px;pointer-events:none;
|
|
2671
2734
|
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));
|
|
2735
|
+
color:var(--sl-warn-text);background:color-mix(in srgb,var(--sl-warn) 13%,var(--sl-surface));
|
|
2673
2736
|
border:1px dashed color-mix(in srgb,var(--sl-warn) 55%,transparent)}
|
|
2674
2737
|
|
|
2675
2738
|
/* zoom column (flows within the bottom-right region) */
|
|
@@ -2680,11 +2743,11 @@ var CSS = (
|
|
|
2680
2743
|
color:var(--sl-text);font-size:17px;font-weight:700;display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
2681
2744
|
.sl-zoom button:hover{border-color:var(--sl-muted)}
|
|
2682
2745
|
.sl-zoom svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2683
|
-
/* Full-screen promotion
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
region beside Map|3D
|
|
2687
|
-
|
|
2746
|
+
/* Full-screen promotion: the capability existed (native + iOS overlay +
|
|
2747
|
+
framed-host paths) but hid as the 4th unlabelled glyph in a corner stack \u2014
|
|
2748
|
+
even the owner could not pick it out. dockLayoutChrome docks the SAME
|
|
2749
|
+
button into the top-right region beside Map|3D on EVERY layout and this
|
|
2750
|
+
class dresses it as a labelled pill. */
|
|
2688
2751
|
.sl-zfs-lbl{display:none}
|
|
2689
2752
|
.sl-fs-pill.sl-fs-pill{display:inline-flex;align-items:center;justify-content:center;width:auto;min-height:40px;height:auto;
|
|
2690
2753
|
padding:0 13px;gap:6px;border-radius:999px;font-size:11px;font-weight:800;
|
|
@@ -2692,6 +2755,16 @@ var CSS = (
|
|
|
2692
2755
|
.sl-fs-pill.sl-fs-pill:hover{border-color:var(--sl-muted)}
|
|
2693
2756
|
.sl-fs-pill svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2694
2757
|
.sl-fs-pill .sl-zfs-lbl{display:inline;letter-spacing:.02em;white-space:nowrap}
|
|
2758
|
+
/* In the header, icons carry it (owner call): square Full screen glyph, and
|
|
2759
|
+
on narrow a price-tag toggle that opens the sheet with the list expanded. */
|
|
2760
|
+
.sl-head .sl-fs-pill{min-height:34px;min-width:34px;width:34px;padding:0;flex:none}
|
|
2761
|
+
.sl-head .sl-fs-pill .sl-zfs-lbl{display:none}
|
|
2762
|
+
.sl-prices-btn{display:none;align-items:center;justify-content:center;width:34px;min-height:34px;flex:none;
|
|
2763
|
+
border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
|
|
2764
|
+
.sl-prices-btn:hover{border-color:var(--sl-muted)}
|
|
2765
|
+
.sl-prices-btn svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2766
|
+
.sl-prices-btn[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
2767
|
+
.sl-picker[data-layout="narrow"] .sl-prices-btn{display:inline-flex}
|
|
2695
2768
|
|
|
2696
2769
|
/* toast + boot states (toast flows in the bottom-center region) */
|
|
2697
2770
|
.sl-toast{transform:translateY(6px) scale(.98);max-width:100%;
|
|
@@ -2726,7 +2799,7 @@ var CSS = (
|
|
|
2726
2799
|
.sl-extend-btn{flex:none;padding:8px 14px;border-radius:999px;font-weight:800;font-size:12.5px;
|
|
2727
2800
|
background:var(--sl-accent);color:var(--sl-accent-ink);transition:filter .15s,opacity .15s}
|
|
2728
2801
|
.sl-extend-btn:hover{filter:brightness(1.08)}
|
|
2729
|
-
.sl-extend-btn:disabled{opacity:.
|
|
2802
|
+
.sl-extend-btn:disabled{opacity:.62;cursor:not-allowed}
|
|
2730
2803
|
|
|
2731
2804
|
/* booked confirmation overlay (covers the widget once the held seats are sold) */
|
|
2732
2805
|
.sl-booked{position:absolute;inset:0;z-index:11;display:flex;flex-direction:column;align-items:center;
|
|
@@ -2753,7 +2826,7 @@ var CSS = (
|
|
|
2753
2826
|
justify-content:center;text-align:center;gap:8px;padding:24px;
|
|
2754
2827
|
background:color-mix(in srgb,var(--sl-bg) 82%,transparent);backdrop-filter:blur(4px)}
|
|
2755
2828
|
.sl-soldout.on{display:flex}
|
|
2756
|
-
.sl-soldout-eyebrow{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sl-accent);font-weight:800}
|
|
2829
|
+
.sl-soldout-eyebrow{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sl-accent-text);font-weight:800}
|
|
2757
2830
|
.sl-soldout-title{font-size:32px;font-weight:800;color:var(--sl-text);line-height:1.05}
|
|
2758
2831
|
.sl-soldout-copy{max-width:360px;font-size:13px;color:var(--sl-muted);line-height:1.5}
|
|
2759
2832
|
|
|
@@ -2769,9 +2842,9 @@ var CSS = (
|
|
|
2769
2842
|
/* "Powered by SeatLayer" attribution badge (side-panel foot) \u2014 the canonical
|
|
2770
2843
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
2771
2844
|
theme sets hideBadge. */
|
|
2772
|
-
.sl-powered{display:flex;align-items:center;justify-content:center;gap:
|
|
2773
|
-
font-size:
|
|
2774
|
-
text-decoration:none;padding:
|
|
2845
|
+
.sl-powered{display:flex;align-items:center;justify-content:center;gap:5px;margin-top:2px;
|
|
2846
|
+
font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--sl-text);opacity:.72;
|
|
2847
|
+
text-decoration:none;padding:2px 8px;border-radius:999px;width:fit-content;margin-inline:auto;
|
|
2775
2848
|
transition:opacity .15s ease,background-color .15s ease}
|
|
2776
2849
|
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
2777
2850
|
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
@@ -2809,18 +2882,44 @@ var CSS = (
|
|
|
2809
2882
|
.sl-confirm-field:first-child .sl-confirm-value{font-size:13.5px;line-height:1.25;white-space:normal;
|
|
2810
2883
|
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
2811
2884
|
.sl-confirm-cat{display:flex;align-items:center;gap:8px;padding:10px 12px;background:color-mix(in srgb,var(--sl-cat) 76%,var(--sl-surface))}
|
|
2812
|
-
.sl-confirm-cat .sl-dot{border:2px solid
|
|
2813
|
-
.sl-confirm-cat-name{font-size:13.5px;font-weight:800;color
|
|
2814
|
-
.sl-confirm-price{font-size:17px;font-weight:850;color
|
|
2885
|
+
.sl-confirm-cat .sl-dot{border:2px solid color-mix(in srgb,var(--sl-cat-ink) 78%,transparent);width:11px;height:11px}
|
|
2886
|
+
.sl-confirm-cat-name{font-size:13.5px;font-weight:800;color:var(--sl-cat-ink);flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2887
|
+
.sl-confirm-price{font-size:17px;font-weight:850;color:var(--sl-cat-ink);font-variant-numeric:tabular-nums}
|
|
2815
2888
|
.sl-confirm-body{padding:11px 12px 12px}
|
|
2889
|
+
.sl-confirm-tiers{display:grid;gap:6px;margin:0 0 9px;padding:0;border:0}
|
|
2890
|
+
.sl-confirm-tiers legend{margin:0 0 6px;padding:0;color:var(--sl-muted);font-size:10px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}
|
|
2891
|
+
.sl-confirm-tier{display:flex;align-items:center;gap:9px;width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--sl-line);
|
|
2892
|
+
border-radius:9px;color:var(--sl-text);background:color-mix(in srgb,var(--sl-bg) 72%,var(--sl-surface));text-align:left}
|
|
2893
|
+
.sl-confirm-tier:hover{border-color:color-mix(in srgb,var(--sl-accent) 58%,var(--sl-line))}
|
|
2894
|
+
.sl-confirm-tier[aria-pressed="true"]{border-color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 13%,var(--sl-surface));
|
|
2895
|
+
box-shadow:inset 3px 0 0 var(--sl-accent)}
|
|
2896
|
+
.sl-confirm-tier-copy{min-width:0;flex:1;display:grid;gap:2px}
|
|
2897
|
+
.sl-confirm-tier-name{min-width:0;font-size:12.5px;font-weight:800}
|
|
2898
|
+
.sl-confirm-tier-note,.sl-tier-note{color:var(--sl-muted);font-size:10.5px;line-height:1.3}
|
|
2899
|
+
.sl-confirm-tier[aria-pressed="true"] .sl-confirm-tier-note{color:var(--sl-text)}
|
|
2900
|
+
.sl-confirm-tier-price{font-size:13px;font-weight:850;font-variant-numeric:tabular-nums}
|
|
2816
2901
|
.sl-confirm-row{display:flex;gap:8px;margin-top:10px}
|
|
2817
2902
|
.sl-confirm-row button{flex:1;min-height:44px;padding:9px 12px;border-radius:9px;font-weight:800;font-size:13px}
|
|
2818
2903
|
.sl-confirm-add{background:var(--sl-accent)!important;color:var(--sl-accent-ink)!important;display:flex;align-items:center;justify-content:center;gap:7px}
|
|
2819
2904
|
.sl-confirm-add svg{width:16px;height:16px;stroke:currentColor;stroke-width:2.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2820
2905
|
.sl-confirm-cancel{background:color-mix(in srgb,var(--sl-line) 44%,transparent)!important;border:1px solid var(--sl-line)!important;color:var(--sl-muted)!important}
|
|
2821
2906
|
.sl-confirm-cancel:hover{color:var(--sl-text)}
|
|
2822
|
-
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(
|
|
2907
|
+
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(310px,calc(100% - 24px));
|
|
2823
2908
|
transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
2909
|
+
/* Phone-size confirm card (owner call 2026-08-14): same content \u2014 identity,
|
|
2910
|
+
price band, view photo, 3D link \u2014 on a tighter grid. The values stay large
|
|
2911
|
+
enough to double-check against the map; buttons keep tap-size. */
|
|
2912
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-field{padding:9px 9px 8px}
|
|
2913
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-value{font-size:15px}
|
|
2914
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-cat{padding:8px 10px}
|
|
2915
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-cat-name{font-size:12.5px}
|
|
2916
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-price{font-size:15px}
|
|
2917
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-body{padding:9px 10px 10px}
|
|
2918
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-tier{min-height:38px;padding:7px 9px}
|
|
2919
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-thumbwrap{height:62px;margin-bottom:6px}
|
|
2920
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-3d{margin-top:7px;padding:6px}
|
|
2921
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-row{margin-top:8px}
|
|
2922
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-row button{min-height:42px}
|
|
2824
2923
|
|
|
2825
2924
|
/* Atomic whole/variable-table chooser. Lives inside the widget so the same
|
|
2826
2925
|
accessible dialog works in inline, modal, desktop and 390px mobile hosts. */
|
|
@@ -2840,7 +2939,7 @@ var CSS = (
|
|
|
2840
2939
|
.sl-table-stepper{display:grid;grid-template-columns:48px 1fr 48px;align-items:center;border:1px solid var(--sl-line);
|
|
2841
2940
|
border-radius:12px;overflow:hidden;background:var(--sl-bg)}
|
|
2842
2941
|
.sl-table-stepper button{height:48px;font-size:24px;font-weight:700;background:color-mix(in srgb,var(--sl-line) 34%,transparent)!important}
|
|
2843
|
-
.sl-table-stepper button:disabled{opacity:.
|
|
2942
|
+
.sl-table-stepper button:disabled{opacity:.58;cursor:not-allowed}
|
|
2844
2943
|
.sl-table-stepper output{text-align:center;font-size:19px;font-weight:850;font-variant-numeric:tabular-nums}
|
|
2845
2944
|
.sl-table-range{margin-top:7px;color:var(--sl-muted);font-size:11px;text-align:center}
|
|
2846
2945
|
.sl-table-actions{display:flex;gap:9px;margin-top:17px}.sl-table-actions button{flex:1;min-height:46px;border-radius:10px;font-weight:800}
|
|
@@ -2882,22 +2981,25 @@ var CSS = (
|
|
|
2882
2981
|
.sl-ba::after{content:'\u2726';position:absolute;right:10px;top:3px;color:color-mix(in srgb,var(--sl-accent) 20%,transparent);font-size:42px;line-height:1}
|
|
2883
2982
|
.sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
|
|
2884
2983
|
.sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
|
|
2885
|
-
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
2984
|
+
.sl-ba-title .spark{color:var(--sl-accent-text);font-size:16px}
|
|
2886
2985
|
.sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
2887
2986
|
.sl-ba-copy .narrow{display:none}
|
|
2888
2987
|
/* "\u2605 Best seats" premium quick-pick \u2014 gold accent echoing the \u2605 Premium pill on
|
|
2889
2988
|
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
2890
2989
|
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
2891
2990
|
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
2892
|
-
color:var(--sl-premium-
|
|
2991
|
+
color:var(--sl-premium-text);background:color-mix(in srgb,var(--sl-premium) 10%,var(--sl-surface));
|
|
2893
2992
|
border:1px solid color-mix(in srgb,var(--sl-premium) 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
2894
2993
|
.sl-ba-premium .star{font-size:12px;line-height:1;color:var(--sl-premium)}
|
|
2895
2994
|
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
2896
2995
|
.sl-ba-premium.on{color:var(--sl-premium-ink);background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
|
|
2897
2996
|
box-shadow:0 6px 16px color-mix(in srgb,var(--sl-premium) 26%,transparent)}
|
|
2898
2997
|
.sl-ba-premium.on .star{color:#5a4410}
|
|
2899
|
-
.sl-ba select
|
|
2900
|
-
|
|
2998
|
+
.sl-ba select,.sl-price-select{appearance:none;-webkit-appearance:none;
|
|
2999
|
+
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23949ca9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
3000
|
+
background-repeat:no-repeat;background-position:right 10px center}
|
|
3001
|
+
.sl-ba select{background-color:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
3002
|
+
font:inherit;font-size:11px;padding:7px 26px 7px 8px;min-width:0;width:100%;max-width:none}
|
|
2901
3003
|
.sl-ba-qty{display:flex;align-items:center;gap:7px;padding:3px;border:1px solid var(--sl-line);border-radius:9px;background:var(--sl-surface)}
|
|
2902
3004
|
.sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
|
|
2903
3005
|
font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
|
|
@@ -2927,9 +3029,23 @@ var CSS = (
|
|
|
2927
3029
|
.sl-picker[data-layout="narrow"] .sl-ba-copy{display:none}
|
|
2928
3030
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
2929
3031
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
2930
|
-
.sl-picker[data-layout="narrow"] .sl-ba
|
|
3032
|
+
.sl-picker[data-layout="narrow"] .sl-ba{grid-template-columns:auto minmax(0,1fr)}
|
|
3033
|
+
.sl-picker[data-layout="narrow"] .sl-ba select{grid-column:1/-1;min-height:38px}
|
|
2931
3034
|
.sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
|
|
2932
|
-
|
|
3035
|
+
/* Quantity and Go share ONE row: a full-width row holding only "\u2212 2 +" was
|
|
3036
|
+
dead space, and the Find button still gets the lion's share. */
|
|
3037
|
+
.sl-picker[data-layout="narrow"] .sl-ba-qty{grid-column:1}
|
|
3038
|
+
.sl-picker[data-layout="narrow"] .sl-ba-go{grid-column:2;min-height:40px}
|
|
3039
|
+
/* Re-entry to best-available once the cart has seats (the card yields its
|
|
3040
|
+
space to chips, but the accelerator must stay reachable) + the quiet way
|
|
3041
|
+
back out of a reopened card. */
|
|
3042
|
+
.sl-ba-reopen{align-self:flex-start;display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:5px 12px;margin-top:2px;
|
|
3043
|
+
border:1px dashed color-mix(in srgb,var(--sl-accent) 40%,var(--sl-line));border-radius:999px;
|
|
3044
|
+
color:var(--sl-accent-text);font-size:11.5px;font-weight:750;transition:border-color .15s,background .15s}
|
|
3045
|
+
.sl-ba-reopen:hover,.sl-ba-reopen:focus-visible{background:color-mix(in srgb,var(--sl-accent) 8%,transparent)}
|
|
3046
|
+
.sl-ba-back{grid-column:1/-1;justify-self:start;min-height:30px;padding:3px 0;color:var(--sl-muted);
|
|
3047
|
+
font-size:11px;font-weight:700;text-decoration:underline;text-underline-offset:3px;transition:color .15s}
|
|
3048
|
+
.sl-ba-back:hover,.sl-ba-back:focus-visible{color:var(--sl-text)}
|
|
2933
3049
|
|
|
2934
3050
|
/* Sales-closed is a designed state, not a set of disables: one statement in
|
|
2935
3051
|
the tray where the buying flow was, carrying the event's own date so the
|
|
@@ -2948,9 +3064,9 @@ var CSS = (
|
|
|
2948
3064
|
(the cart chip's remove/view rail) instead get real height on narrow. */
|
|
2949
3065
|
@media (pointer:coarse){
|
|
2950
3066
|
.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}
|
|
3067
|
+
.sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle,.sl-ba-reopen{position:relative}
|
|
2952
3068
|
.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{
|
|
3069
|
+
.sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after,.sl-ba-reopen::after{
|
|
2954
3070
|
content:'';position:absolute;top:50%;left:50%;width:max(100%,44px);height:max(100%,44px);
|
|
2955
3071
|
transform:translate(-50%,-50%)}
|
|
2956
3072
|
/* The chip rail stacks remove over view in ~53px \u2014 pseudo-targets would
|
|
@@ -3047,18 +3163,12 @@ var CSS = (
|
|
|
3047
3163
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
3048
3164
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
3049
3165
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3050
|
-
.sl-view3d-nav button:disabled{opacity:.
|
|
3166
|
+
.sl-view3d-nav button:disabled{opacity:.6;cursor:not-allowed}
|
|
3051
3167
|
.sl-view3d-nav-toggle{display:none!important}
|
|
3052
3168
|
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
3053
3169
|
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
3054
3170
|
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
3055
3171
|
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
3056
|
-
.sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
|
|
3057
|
-
width:min(720px,100%);overflow:visible}
|
|
3058
|
-
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
3059
|
-
.sl-view3d-locator select{width:100%;min-width:0}
|
|
3060
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:100%;overflow-x:auto}
|
|
3061
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
3062
3172
|
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
3063
3173
|
Keep venue navigation in a separate top rail so those control families never
|
|
3064
3174
|
stack over one another. */
|
|
@@ -3068,9 +3178,6 @@ var CSS = (
|
|
|
3068
3178
|
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open{left:12px;top:68px;max-width:calc(100% - 24px);padding:8px;
|
|
3069
3179
|
border:1px solid rgba(150,165,205,.35);border-radius:14px;background:rgba(8,12,22,.9);backdrop-filter:blur(10px)}
|
|
3070
3180
|
.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
3181
|
/* Seat-eye is a decision state, not another venue-navigation state. Once the
|
|
3075
3182
|
buyer arrives, clear the floor/area/locator rails and the module's duplicate
|
|
3076
3183
|
Overview action. The picker-owned Back button becomes the one predictable
|
|
@@ -3104,7 +3211,7 @@ var CSS = (
|
|
|
3104
3211
|
.sl-confirm-confidence strong,.sl-confirm-confidence small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3105
3212
|
.sl-confirm-confidence strong{font-size:11px}.sl-confirm-confidence small{margin-top:2px;color:var(--sl-muted);font-size:9.5px}
|
|
3106
3213
|
.sl-confirm-confidence em{display:none;font-style:normal}
|
|
3107
|
-
.sl-confirm-confidence>b{flex:none;font-size:11px;color:var(--sl-accent)}
|
|
3214
|
+
.sl-confirm-confidence>b{flex:none;font-size:11px;color:var(--sl-accent-text)}
|
|
3108
3215
|
.sl-confirm-confidence:hover,.sl-confirm-confidence:focus-visible{border-color:var(--sl-accent)}
|
|
3109
3216
|
/* The production 3D decision dock is deliberately denser than the 2D popup:
|
|
3110
3217
|
the venue remains the main content and the two inspection actions share one
|
|
@@ -3186,7 +3293,7 @@ var CSS = (
|
|
|
3186
3293
|
.sl-view3d-compare-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:12px}
|
|
3187
3294
|
.sl-view3d-compare-actions button{min-height:44px;border-radius:9px;border:1px solid var(--sl-line);font-size:12px;font-weight:800}
|
|
3188
3295
|
.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:.
|
|
3296
|
+
.sl-view3d-compare-actions button:disabled{opacity:.62;cursor:not-allowed}
|
|
3190
3297
|
.sl-view3d-passport-shell{position:absolute;inset:0;z-index:40;display:grid;place-items:center;padding:16px}
|
|
3191
3298
|
.sl-view3d-passport-scrim{position:absolute;inset:0;background:rgba(3,6,12,.8);backdrop-filter:blur(6px)}
|
|
3192
3299
|
.sl-view3d-passport{position:relative;width:min(540px,100%);max-height:min(700px,calc(100% - 20px));overflow:auto;
|
|
@@ -3227,14 +3334,17 @@ var CSS = (
|
|
|
3227
3334
|
.sl-confirm-3d:hover,.sl-confirm-3d:focus-visible{border-color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 20%,transparent)}
|
|
3228
3335
|
.sl-confirm-3d svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3229
3336
|
|
|
3230
|
-
/* multi-floor
|
|
3231
|
-
.sl-floors{display:none;
|
|
3337
|
+
/* One compact multi-floor selector (flows within the left-rail region). */
|
|
3338
|
+
.sl-floors{display:none;align-items:center;gap:6px;max-width:min(260px,100%);padding:5px;border-radius:999px;
|
|
3339
|
+
background:color-mix(in srgb,var(--sl-surface) 92%,transparent);border:1px solid var(--sl-line);
|
|
3340
|
+
box-shadow:var(--sl-shadow);backdrop-filter:blur(8px)}
|
|
3232
3341
|
.sl-floors.on{display:flex}
|
|
3233
|
-
.sl-floors
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
.sl-
|
|
3237
|
-
|
|
3342
|
+
.sl-floors select{min-width:0;max-width:210px;min-height:34px;padding:6px 30px 6px 10px;border:0;border-radius:999px;
|
|
3343
|
+
font:700 12px/1 inherit;color:var(--sl-text);background:var(--sl-surface);cursor:pointer}
|
|
3344
|
+
.sl-floors select:focus-visible,.sl-floor-info:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
3345
|
+
.sl-floor-info{display:inline-grid;place-items:center;flex:0 0 30px;width:30px;height:30px;border-radius:999px;
|
|
3346
|
+
border:1px solid var(--sl-line);color:var(--sl-muted);background:transparent;font:800 13px/1 inherit;cursor:help}
|
|
3347
|
+
.sl-floor-info:hover{color:var(--sl-text);border-color:var(--sl-accent)}
|
|
3238
3348
|
|
|
3239
3349
|
/* tapped-section summary card \u2014 docks INSIDE the top-center anchor region on
|
|
3240
3350
|
wide (flows below the rung pills, never over them, never floating over the
|
|
@@ -3269,8 +3379,14 @@ var CSS = (
|
|
|
3269
3379
|
.sl-seccard-mix-dot{width:8px;height:8px;border-radius:50%;flex:none}
|
|
3270
3380
|
.sl-seccard-mix-price{font-weight:700;color:var(--sl-text)}
|
|
3271
3381
|
.sl-seccard-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:10px}
|
|
3272
|
-
.sl-seccard-overview{font-size:12px;font-weight:800;color:var(--sl-accent)}
|
|
3382
|
+
.sl-seccard-overview{font-size:12px;font-weight:800;color:var(--sl-accent-text)}
|
|
3273
3383
|
.sl-seccard-hint{font-size:10.5px;color:var(--sl-muted)}
|
|
3384
|
+
.sl-seccard-nav{display:inline-flex;align-items:center;gap:4px;margin-left:auto}
|
|
3385
|
+
.sl-seccard-nav button{width:28px;height:28px;border:1px solid var(--sl-line);border-radius:999px;color:var(--sl-text);
|
|
3386
|
+
background:color-mix(in srgb,var(--sl-surface) 88%,var(--sl-bg));font-size:14px;font-weight:850;line-height:1}
|
|
3387
|
+
.sl-seccard-nav button:hover,.sl-seccard-nav button:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
3388
|
+
.sl-seccard.mini .sl-seccard-nav,.sl-seccard.strip .sl-seccard-nav{margin-left:2px}
|
|
3389
|
+
.sl-seccard.mini .sl-seccard-nav button,.sl-seccard.strip .sl-seccard-nav button{width:24px;height:24px;font-size:12px}
|
|
3274
3390
|
|
|
3275
3391
|
/* view-from-seat button on the confirm popover */
|
|
3276
3392
|
/* Eager sightline preview inside the confirm card */
|
|
@@ -3323,10 +3439,29 @@ var CSS = (
|
|
|
3323
3439
|
white-space:nowrap;pointer-events:none;max-width:90%;overflow:hidden;text-overflow:ellipsis}
|
|
3324
3440
|
|
|
3325
3441
|
/* F3 minimap \u2014 venue overview + live viewport rect (flows in the bottom-left region) */
|
|
3326
|
-
.sl-minimap{border:1px solid var(--sl-line);border-radius:9px;
|
|
3327
|
-
|
|
3442
|
+
.sl-minimap{border:1px solid var(--sl-line);border-radius:9px;overflow:hidden;background:var(--sl-surface);
|
|
3443
|
+
box-shadow:0 12px 34px -14px rgba(0,0,0,.55);line-height:0;touch-action:none}
|
|
3444
|
+
.sl-minimap-bar{min-height:24px;padding:3px 5px 3px 8px;display:flex;align-items:center;gap:6px;border-bottom:1px solid var(--sl-line);
|
|
3445
|
+
color:var(--sl-muted);font-size:9px;font-weight:800;line-height:1.2;letter-spacing:.06em;text-transform:uppercase}
|
|
3446
|
+
.sl-minimap-label{min-width:0;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
3447
|
+
.sl-minimap-key{display:inline-flex;align-items:center;gap:3px;flex:none}
|
|
3448
|
+
.sl-minimap-key i{width:6px;height:6px;border-radius:999px;background:var(--sl-success)}
|
|
3449
|
+
.sl-minimap-key i.selected{background:var(--sl-accent)}
|
|
3450
|
+
.sl-minimap-key i.inactive{background:var(--sl-muted);opacity:.45}
|
|
3451
|
+
.sl-minimap-key i.focused{background:transparent;border:1.5px solid var(--sl-accent);width:7px;height:7px}
|
|
3452
|
+
.sl-minimap-close{display:none;width:24px;height:24px;border-radius:6px;color:var(--sl-muted);font-size:15px;line-height:1}
|
|
3328
3453
|
.sl-minimap canvas{display:block}
|
|
3454
|
+
.sl-minimap canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
3455
|
+
.sl-minimap canvas.sl-dragging{cursor:grabbing}
|
|
3456
|
+
.sl-minimap-toggle{display:none;width:42px;height:42px;border:1px solid var(--sl-line);border-radius:10px;background:var(--sl-surface);
|
|
3457
|
+
color:var(--sl-text);box-shadow:0 10px 28px -14px rgba(0,0,0,.65);align-items:center;justify-content:center}
|
|
3458
|
+
.sl-minimap-toggle svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linejoin:round}
|
|
3329
3459
|
.sl-picker[data-layout="narrow"] .sl-minimap{display:none}
|
|
3460
|
+
.sl-picker[data-layout="narrow"] .sl-minimap-toggle{display:flex}
|
|
3461
|
+
.sl-picker[data-layout="narrow"][data-minimap-open="true"] .sl-minimap{display:block}
|
|
3462
|
+
.sl-picker[data-layout="narrow"][data-minimap-open="true"] .sl-minimap-toggle{display:none}
|
|
3463
|
+
.sl-picker[data-layout="narrow"] .sl-minimap canvas{width:min(168px,42vw)!important;height:auto!important}
|
|
3464
|
+
.sl-picker[data-layout="narrow"] .sl-minimap-close{display:grid;place-items:center}
|
|
3330
3465
|
|
|
3331
3466
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
3332
3467
|
/* Out-of-band rows are exactly the rows a buyer reads while deciding whether
|
|
@@ -3404,17 +3539,88 @@ function formatWhen(startsAt, timezone, locale) {
|
|
|
3404
3539
|
}
|
|
3405
3540
|
return at.toLocaleString(locale, options);
|
|
3406
3541
|
}
|
|
3407
|
-
function
|
|
3542
|
+
function parseThemeColor(value) {
|
|
3543
|
+
if (!value) return null;
|
|
3544
|
+
const color = value.trim().toLowerCase();
|
|
3545
|
+
if (color === "white") return { r: 255, g: 255, b: 255 };
|
|
3546
|
+
if (color === "black") return { r: 0, g: 0, b: 0 };
|
|
3547
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(color)?.[1];
|
|
3548
|
+
if (hex) {
|
|
3549
|
+
const expanded = hex.length <= 4 ? [...hex].map((digit) => `${digit}${digit}`).join("") : hex;
|
|
3550
|
+
return {
|
|
3551
|
+
r: Number.parseInt(expanded.slice(0, 2), 16),
|
|
3552
|
+
g: Number.parseInt(expanded.slice(2, 4), 16),
|
|
3553
|
+
b: Number.parseInt(expanded.slice(4, 6), 16)
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
const rgb = /^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:\s*[,/]\s*[\d.]+%?)?\s*\)$/i.exec(color);
|
|
3557
|
+
if (!rgb) return null;
|
|
3558
|
+
return {
|
|
3559
|
+
r: Math.max(0, Math.min(255, Number(rgb[1]))),
|
|
3560
|
+
g: Math.max(0, Math.min(255, Number(rgb[2]))),
|
|
3561
|
+
b: Math.max(0, Math.min(255, Number(rgb[3])))
|
|
3562
|
+
};
|
|
3563
|
+
}
|
|
3564
|
+
function relativeLuminance(color) {
|
|
3565
|
+
const channel = (value) => {
|
|
3566
|
+
const normalized = value / 255;
|
|
3567
|
+
return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
3568
|
+
};
|
|
3569
|
+
return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
|
|
3570
|
+
}
|
|
3571
|
+
function themeContrast(foreground, background) {
|
|
3572
|
+
const fg = parseThemeColor(foreground);
|
|
3573
|
+
const bg = parseThemeColor(background);
|
|
3574
|
+
if (!fg || !bg) return null;
|
|
3575
|
+
const lighter = Math.max(relativeLuminance(fg), relativeLuminance(bg));
|
|
3576
|
+
const darker = Math.min(relativeLuminance(fg), relativeLuminance(bg));
|
|
3577
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
3578
|
+
}
|
|
3579
|
+
function highestContrastInk(background, dark = "#172033", light = "#eef1f8") {
|
|
3580
|
+
return (themeContrast(dark, background) ?? 0) >= (themeContrast(light, background) ?? 0) ? dark : light;
|
|
3581
|
+
}
|
|
3582
|
+
function rgbHex(color) {
|
|
3583
|
+
const channel = (value) => Math.round(value).toString(16).padStart(2, "0");
|
|
3584
|
+
return `#${channel(color.r)}${channel(color.g)}${channel(color.b)}`;
|
|
3585
|
+
}
|
|
3586
|
+
function mixThemeColors(first, second, firstWeight) {
|
|
3587
|
+
const a = parseThemeColor(first);
|
|
3588
|
+
const b = parseThemeColor(second);
|
|
3589
|
+
if (!a || !b) return null;
|
|
3590
|
+
const weight = Math.max(0, Math.min(1, firstWeight));
|
|
3591
|
+
return rgbHex({
|
|
3592
|
+
r: a.r * weight + b.r * (1 - weight),
|
|
3593
|
+
g: a.g * weight + b.g * (1 - weight),
|
|
3594
|
+
b: a.b * weight + b.b * (1 - weight)
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
function readableAccentText(accent, text, background, surface) {
|
|
3598
|
+
if ((themeContrast(accent, background) ?? 4.5) >= 4.5 && (themeContrast(accent, surface) ?? 4.5) >= 4.5) return accent;
|
|
3599
|
+
if (!parseThemeColor(accent) || !parseThemeColor(text)) return text;
|
|
3600
|
+
for (let textWeight = 0.15; textWeight <= 1; textWeight += 0.05) {
|
|
3601
|
+
const candidate = mixThemeColors(accent, text, 1 - textWeight);
|
|
3602
|
+
if (candidate && (themeContrast(candidate, background) ?? 0) >= 4.5 && (themeContrast(candidate, surface) ?? 0) >= 4.5) return candidate;
|
|
3603
|
+
}
|
|
3604
|
+
return text;
|
|
3605
|
+
}
|
|
3606
|
+
function resolvePickerTokens(chart, host) {
|
|
3607
|
+
const background = host?.background ?? chart?.background ?? "#0f1522";
|
|
3608
|
+
const isLight = parseThemeColor(background) ? highestContrastInk(background) === "#172033" : false;
|
|
3609
|
+
const surface = host?.surface ?? (isLight ? "#ffffff" : "#1a2234");
|
|
3408
3610
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
3409
|
-
const
|
|
3611
|
+
const chartAccentInk = chart?.accentInk;
|
|
3612
|
+
const accentInk = host?.accentInk ?? (chartAccentInk && (themeContrast(chartAccentInk, accent) ?? 4.5) >= 4.5 ? chartAccentInk : highestContrastInk(accent, "#1a1200", "#ffffff"));
|
|
3613
|
+
const chartText = chart?.textColor;
|
|
3614
|
+
const text = host?.text ?? (chartText && (themeContrast(chartText, background) ?? 4.5) >= 4.5 && (themeContrast(chartText, surface) ?? 4.5) >= 4.5 ? chartText : highestContrastInk(background));
|
|
3410
3615
|
return {
|
|
3411
3616
|
"--sl-accent": accent,
|
|
3412
3617
|
"--sl-accent-ink": accentInk,
|
|
3413
|
-
"--sl-
|
|
3414
|
-
"--sl-
|
|
3415
|
-
"--sl-
|
|
3416
|
-
"--sl-
|
|
3417
|
-
"--sl-
|
|
3618
|
+
"--sl-accent-text": readableAccentText(accent, text, background, surface),
|
|
3619
|
+
"--sl-bg": background,
|
|
3620
|
+
"--sl-surface": surface,
|
|
3621
|
+
"--sl-text": text,
|
|
3622
|
+
"--sl-muted": host?.muted ?? (isLight ? "#667085" : "#a5aec2"),
|
|
3623
|
+
"--sl-line": host?.line ?? (isLight ? "rgba(23,32,51,.16)" : "rgba(165,174,194,.24)"),
|
|
3418
3624
|
"--sl-font": host?.fontFamily ?? chart?.fontFamily ?? "-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,sans-serif",
|
|
3419
3625
|
"--sl-radius": `${host?.radius ?? 14}px`
|
|
3420
3626
|
};
|
|
@@ -3496,6 +3702,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
3496
3702
|
this.baZone = "";
|
|
3497
3703
|
/** "★ Best seats" premium quick-pick toggle — biases best-available to premium seats. */
|
|
3498
3704
|
this.baPremium = false;
|
|
3705
|
+
/**
|
|
3706
|
+
* The buyer re-opened the best-seats form with seats already in the cart
|
|
3707
|
+
* ("I picked one, actually find us three together"). The card normally
|
|
3708
|
+
* yields this space to the ticket chips the moment anything is selected —
|
|
3709
|
+
* which removed the accelerator at exactly the moment a one-handed phone
|
|
3710
|
+
* buyer wants it. Cleared when the cart empties, a hold lands, or the
|
|
3711
|
+
* buyer backs out.
|
|
3712
|
+
*/
|
|
3713
|
+
this.baReopen = false;
|
|
3499
3714
|
this.bestAvailableConfirm = false;
|
|
3500
3715
|
this.releasingHold = false;
|
|
3501
3716
|
/** Event sales window is closed (read-only load state / live close). */
|
|
@@ -3537,7 +3752,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
3537
3752
|
// F3 minimap
|
|
3538
3753
|
this.miniCanvas = null;
|
|
3539
3754
|
this.miniBase = null;
|
|
3755
|
+
this.miniWrap = null;
|
|
3756
|
+
this.miniToggle = null;
|
|
3757
|
+
this.miniLabel = null;
|
|
3540
3758
|
this.miniTf = null;
|
|
3759
|
+
this.miniDrag = null;
|
|
3760
|
+
this.miniSuppressClick = false;
|
|
3761
|
+
this.pickerLayoutSize = { width: 0, height: 0 };
|
|
3541
3762
|
// F4 price-band filter — active band's category keys (null = all prices)
|
|
3542
3763
|
this.priceBandKeys = null;
|
|
3543
3764
|
this.focusedCatKey = null;
|
|
@@ -3573,6 +3794,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3573
3794
|
/** Wide-layout ticket panel collapsed (map owns the full width). */
|
|
3574
3795
|
this.sideCollapsed = false;
|
|
3575
3796
|
this.sideToggleEl = null;
|
|
3797
|
+
this.pricesBtnEl = null;
|
|
3576
3798
|
/** True once we've asked the host page to pin us fullscreen (framed, no native). */
|
|
3577
3799
|
this.framedFs = false;
|
|
3578
3800
|
/** Last height (px) posted to a host frame; dedupes redundant reports. */
|
|
@@ -3637,6 +3859,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3637
3859
|
mapTheme: options.theme?.map ?? null,
|
|
3638
3860
|
onSelectionChange: () => {
|
|
3639
3861
|
this.syncTray();
|
|
3862
|
+
this.refreshMinimap();
|
|
3640
3863
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
3641
3864
|
this.syncSelectionTo3d();
|
|
3642
3865
|
},
|
|
@@ -3685,7 +3908,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3685
3908
|
if (this.tableDialog?.physicalSeatIds.includes(seat.id)) this.dismissTableDialog();
|
|
3686
3909
|
},
|
|
3687
3910
|
onSelectionLimit: () => {
|
|
3688
|
-
this.toast(
|
|
3911
|
+
this.toast(t2("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
|
|
3689
3912
|
},
|
|
3690
3913
|
onViewChange: () => {
|
|
3691
3914
|
this.reanchorConfirm();
|
|
@@ -3696,6 +3919,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
3696
3919
|
},
|
|
3697
3920
|
// Tapped-section glide-in → surface (or clear) the section-summary card.
|
|
3698
3921
|
onSectionFocus: (summary) => this.showSectionCard(summary),
|
|
3922
|
+
onDeckTap: () => {
|
|
3923
|
+
this.showSectionCard(null);
|
|
3924
|
+
this.syncFloors();
|
|
3925
|
+
this.syncRung();
|
|
3926
|
+
this.refreshMinimap();
|
|
3927
|
+
},
|
|
3699
3928
|
onFocusSeat: (seat) => this.announceSeat(seat),
|
|
3700
3929
|
onSeatHover: (d) => this.updateTooltip(d),
|
|
3701
3930
|
onHint: (m) => {
|
|
@@ -3801,8 +4030,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3801
4030
|
return `<span class="sl-cx-mark" role="img" aria-label="${title}" title="${title}">\u25D0</span>`;
|
|
3802
4031
|
}
|
|
3803
4032
|
wheelchairProvisionLabel(type) {
|
|
3804
|
-
if (type === "no-seat") return "Empty wheelchair space";
|
|
3805
|
-
if (type === "seat-present") return "Accessible physical seat";
|
|
4033
|
+
if (type === "no-seat") return this.tf("picker.emptyWheelchairSpace", "Empty wheelchair space");
|
|
4034
|
+
if (type === "seat-present") return this.tf("picker.accessiblePhysicalSeat", "Accessible physical seat");
|
|
3806
4035
|
return "";
|
|
3807
4036
|
}
|
|
3808
4037
|
wheelchairConfirmHtml(type) {
|
|
@@ -3883,9 +4112,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
3883
4112
|
const hideEventDetails = this.eventDetailsHidden && !active;
|
|
3884
4113
|
this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
|
|
3885
4114
|
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
3886
|
-
this.els.zfs?.setAttribute("title", active ? "Exit full screen" : "Full screen");
|
|
4115
|
+
this.els.zfs?.setAttribute("title", active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen"));
|
|
3887
4116
|
const zfsLabel = this.els.zfs?.querySelector(".sl-zfs-lbl");
|
|
3888
|
-
if (zfsLabel) zfsLabel.textContent = active ? "Exit full screen" : "Full screen";
|
|
4117
|
+
if (zfsLabel) zfsLabel.textContent = active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen");
|
|
3889
4118
|
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
3890
4119
|
}
|
|
3891
4120
|
/**
|
|
@@ -3913,7 +4142,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3913
4142
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
3914
4143
|
this.fsEscHandler = null;
|
|
3915
4144
|
}
|
|
3916
|
-
requestAnimationFrame(() => this.controller.
|
|
4145
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
3917
4146
|
}
|
|
3918
4147
|
setFsFallback(on) {
|
|
3919
4148
|
if (this.fsFallback === on) return;
|
|
@@ -3929,7 +4158,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3929
4158
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
3930
4159
|
this.fsEscHandler = null;
|
|
3931
4160
|
}
|
|
3932
|
-
requestAnimationFrame(() => this.controller.
|
|
4161
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
3933
4162
|
}
|
|
3934
4163
|
/**
|
|
3935
4164
|
* Close the picker. In modal mode (SeatPicker.open()) this dismisses the
|
|
@@ -3950,7 +4179,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3950
4179
|
frame.className = "sl-modal-frame";
|
|
3951
4180
|
frame.setAttribute("role", "dialog");
|
|
3952
4181
|
frame.setAttribute("aria-modal", "true");
|
|
3953
|
-
frame.setAttribute("aria-label", "Seat selection");
|
|
4182
|
+
frame.setAttribute("aria-label", tfStatic("picker.seatSelection", "Seat selection"));
|
|
3954
4183
|
frame.tabIndex = -1;
|
|
3955
4184
|
scrim.appendChild(frame);
|
|
3956
4185
|
document.body.appendChild(scrim);
|
|
@@ -4069,11 +4298,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
4069
4298
|
}
|
|
4070
4299
|
async render() {
|
|
4071
4300
|
if (this.rendered) return this;
|
|
4301
|
+
const renderStartedAt = performance.now();
|
|
4302
|
+
const renderProfile = {};
|
|
4303
|
+
const duration = (startedAt) => Math.round((performance.now() - startedAt) * 100) / 100;
|
|
4072
4304
|
this.rendered = true;
|
|
4073
4305
|
ensureStyle();
|
|
4306
|
+
const localeStartedAt = performance.now();
|
|
4074
4307
|
await loadLocale2(this.opts.locale);
|
|
4308
|
+
renderProfile.loadLocale = duration(localeStartedAt);
|
|
4075
4309
|
if (this.opts.messages) setStringOverrides2(this.opts.messages);
|
|
4076
4310
|
if (this.checkoutMode === "hosted") this.paymentOptions = this.pubApi.paymentOptions(this.opts.event);
|
|
4311
|
+
const shellStartedAt = performance.now();
|
|
4077
4312
|
const mount = resolveContainer3(this.opts.container);
|
|
4078
4313
|
const root = document.createElement("div");
|
|
4079
4314
|
root.className = "sl-picker";
|
|
@@ -4097,7 +4332,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4097
4332
|
this.syncTray();
|
|
4098
4333
|
}
|
|
4099
4334
|
});
|
|
4100
|
-
Object.entries(
|
|
4335
|
+
Object.entries(resolvePickerTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4101
4336
|
root.innerHTML = `
|
|
4102
4337
|
<div class="sl-head">
|
|
4103
4338
|
<div class="sl-logo" data-ref="logo"></div>
|
|
@@ -4110,7 +4345,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4110
4345
|
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>
|
|
4111
4346
|
<span data-ref="closedPillText"></span>
|
|
4112
4347
|
</span>
|
|
4113
|
-
<button type="button" class="sl-close" data-ref="close" aria-label="Close">
|
|
4348
|
+
<button type="button" class="sl-close" data-ref="close" aria-label="${this.tf("picker.close", "Close")}">
|
|
4114
4349
|
<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
|
4115
4350
|
</button>
|
|
4116
4351
|
</div>
|
|
@@ -4118,17 +4353,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
4118
4353
|
<div class="sl-map">
|
|
4119
4354
|
<div class="sl-map-host" data-ref="map"></div>
|
|
4120
4355
|
<div class="sl-zoom" data-ref="zoom">
|
|
4121
|
-
<button type="button" aria-label="Zoom in" title="Zoom in" data-ref="zin">+</button>
|
|
4122
|
-
<button type="button" aria-label="Zoom out" title="Zoom out" data-ref="zout">\u2212</button>
|
|
4123
|
-
<button type="button" aria-label="Fit to screen" title="Fit to screen" data-ref="zfit">
|
|
4356
|
+
<button type="button" aria-label="${this.tf("picker.zoomIn", "Zoom in")}" title="${this.tf("picker.zoomIn", "Zoom in")}" data-ref="zin">+</button>
|
|
4357
|
+
<button type="button" aria-label="${this.tf("picker.zoomOut", "Zoom out")}" title="${this.tf("picker.zoomOut", "Zoom out")}" data-ref="zout">\u2212</button>
|
|
4358
|
+
<button type="button" aria-label="${this.tf("picker.fitToScreen", "Fit to screen")}" title="${this.tf("picker.fitToScreen", "Fit to screen")}" data-ref="zfit">
|
|
4124
4359
|
<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>
|
|
4125
4360
|
</button>
|
|
4126
|
-
<button type="button" aria-label="Full screen" title="Full screen" aria-pressed="false" data-ref="zfs">
|
|
4361
|
+
<button type="button" aria-label="${this.tf("picker.fullScreen", "Full screen")}" title="${this.tf("picker.fullScreen", "Full screen")}" aria-pressed="false" data-ref="zfs">
|
|
4127
4362
|
<svg viewBox="0 0 24 24"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
|
4128
|
-
<span class="sl-zfs-lbl" aria-hidden="true"
|
|
4363
|
+
<span class="sl-zfs-lbl" aria-hidden="true">${this.tf("picker.fullScreen", "Full screen")}</span>
|
|
4129
4364
|
</button>
|
|
4130
4365
|
</div>
|
|
4131
|
-
<div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span
|
|
4366
|
+
<div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>${this.tf("picker.loadingSeatMap", "Loading seat map\u2026")}</div>
|
|
4132
4367
|
<div class="sl-toast" data-ref="toast" role="status" aria-live="polite"></div>
|
|
4133
4368
|
</div>
|
|
4134
4369
|
<div class="sl-side" data-ref="side">
|
|
@@ -4136,24 +4371,25 @@ var SeatPicker = class _SeatPicker {
|
|
|
4136
4371
|
<div class="sl-sheet-grab"></div>
|
|
4137
4372
|
<div class="sl-sheet-bar">
|
|
4138
4373
|
<div class="sl-sheet-peek" data-ref="peek"></div>
|
|
4139
|
-
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
|
|
4374
|
+
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="${this.tf("picker.openTicketPanel", "Open ticket panel")}" aria-expanded="false">
|
|
4140
4375
|
<svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
|
|
4141
4376
|
</button>
|
|
4142
4377
|
</div>
|
|
4143
4378
|
</div>
|
|
4144
|
-
<div class="sl-sec sl-filtersec" data-ref="filtersSec"
|
|
4379
|
+
<div class="sl-sec sl-filtersec" data-ref="filtersSec">${this.tf("picker.filters", "Filters")}</div>
|
|
4145
4380
|
<div class="sl-filters" data-ref="filters"></div>
|
|
4146
4381
|
<div class="sl-offer" data-ref="offer" role="status" aria-live="polite"></div>
|
|
4147
|
-
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span
|
|
4382
|
+
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>${this.tf("picker.ticketPrices", "Ticket prices")}</span></div>
|
|
4383
|
+
<div class="sl-prices-hint" data-ref="pricesHint"></div>
|
|
4148
4384
|
<div class="sl-prices" data-ref="prices"></div>
|
|
4149
|
-
<div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText"
|
|
4150
|
-
<div class="sl-sec sl-seats-sec"><span
|
|
4385
|
+
<div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText">${this.tf("picker.liveAvailability", "Live availability \u2014 seats update in real time")}</span></div>
|
|
4386
|
+
<div class="sl-sec sl-seats-sec"><span>${this.tf("picker.yourSeats", "Your seats")}</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
|
|
4151
4387
|
<div class="sl-tray" data-ref="tray"></div>
|
|
4152
4388
|
<div class="sl-foot" data-ref="foot">
|
|
4153
4389
|
<div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
|
|
4154
4390
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
4155
|
-
<span><b data-ref="holdTitle"
|
|
4156
|
-
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats"
|
|
4391
|
+
<span><b data-ref="holdTitle">${this.tf("picker.seatsSecured", "Seats secured")}</b><span class="sl-hold-copy" data-ref="holdCopy">${this.tf("picker.notChargedYet", "You won\u2019t be charged yet.")}</span></span>
|
|
4392
|
+
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="${this.tf("picker.releaseHeldTickets", "Release held tickets and choose different seats")}">${this.tf("picker.change", "Change")}</button>
|
|
4157
4393
|
</div>
|
|
4158
4394
|
<div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
|
|
4159
4395
|
<button type="button" class="sl-cta" data-ref="cta" disabled></button>
|
|
@@ -4175,14 +4411,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
4175
4411
|
const applyLayout = () => {
|
|
4176
4412
|
const w = root.clientWidth;
|
|
4177
4413
|
if (w <= 0) return;
|
|
4414
|
+
const h = root.clientHeight;
|
|
4415
|
+
const sizeChanged = w !== this.pickerLayoutSize.width || h !== this.pickerLayoutSize.height;
|
|
4416
|
+
this.pickerLayoutSize = { width: w, height: h };
|
|
4178
4417
|
this.reportFramedHeight();
|
|
4179
4418
|
const next = w < 640 ? "narrow" : "wide";
|
|
4180
|
-
const density =
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4419
|
+
const density = h < 560 ? "compact" : "comfortable";
|
|
4420
|
+
const layoutChanged = root.dataset.layout !== next || root.dataset.density !== density;
|
|
4421
|
+
if (layoutChanged) {
|
|
4422
|
+
root.dataset.layout = next;
|
|
4423
|
+
root.dataset.density = density;
|
|
4424
|
+
if (next === "narrow" && !root.dataset.sheet) root.dataset.sheet = "peek";
|
|
4425
|
+
this.dockLayoutChrome();
|
|
4426
|
+
}
|
|
4427
|
+
if (sizeChanged && this.miniBase) this.refreshMinimap();
|
|
4186
4428
|
};
|
|
4187
4429
|
this.ro = new ResizeObserver(applyLayout);
|
|
4188
4430
|
this.ro.observe(root);
|
|
@@ -4195,7 +4437,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4195
4437
|
this.fsChangeHandler = () => {
|
|
4196
4438
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
4197
4439
|
this.syncFullscreenButtons();
|
|
4198
|
-
requestAnimationFrame(() => this.controller.
|
|
4440
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4199
4441
|
};
|
|
4200
4442
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
4201
4443
|
const head = this.els.sheetHead;
|
|
@@ -4204,7 +4446,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4204
4446
|
const setSheet = (open) => {
|
|
4205
4447
|
root.dataset.sheet = open ? "open" : "peek";
|
|
4206
4448
|
toggle?.setAttribute("aria-expanded", String(open));
|
|
4207
|
-
toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
|
|
4449
|
+
toggle?.setAttribute("aria-label", open ? this.tf("picker.collapseTicketPanel", "Collapse ticket panel") : this.tf("picker.openTicketPanel", "Open ticket panel"));
|
|
4208
4450
|
};
|
|
4209
4451
|
setSheet(root.dataset.sheet === "open");
|
|
4210
4452
|
toggle?.addEventListener("click", (e) => {
|
|
@@ -4250,6 +4492,39 @@ var SeatPicker = class _SeatPicker {
|
|
|
4250
4492
|
head.releasePointerCapture?.(e.pointerId);
|
|
4251
4493
|
});
|
|
4252
4494
|
}
|
|
4495
|
+
const togglePrices = () => {
|
|
4496
|
+
if (this.root?.dataset.layout !== "narrow") return;
|
|
4497
|
+
const open = this.root.getAttribute("data-prices-open") === "true";
|
|
4498
|
+
this.root.setAttribute("data-prices-open", String(!open));
|
|
4499
|
+
this.els.pricesSec?.setAttribute("aria-expanded", String(!open));
|
|
4500
|
+
this.pricesBtnEl?.setAttribute("aria-pressed", String(!open));
|
|
4501
|
+
if (!open && this.root.dataset.sheet !== "open") {
|
|
4502
|
+
this.root.dataset.sheet = "open";
|
|
4503
|
+
const sheetToggle = this.els.sheetToggle;
|
|
4504
|
+
sheetToggle?.setAttribute("aria-expanded", "true");
|
|
4505
|
+
sheetToggle?.setAttribute("aria-label", "Collapse ticket panel");
|
|
4506
|
+
}
|
|
4507
|
+
};
|
|
4508
|
+
this.els.pricesSec?.addEventListener("click", (e) => {
|
|
4509
|
+
if (e.target.closest("select,button")) return;
|
|
4510
|
+
togglePrices();
|
|
4511
|
+
});
|
|
4512
|
+
this.els.pricesSec?.addEventListener("keydown", (e) => {
|
|
4513
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
4514
|
+
if (e.target.closest("select,button")) return;
|
|
4515
|
+
e.preventDefault();
|
|
4516
|
+
togglePrices();
|
|
4517
|
+
});
|
|
4518
|
+
const pricesBtn = document.createElement("button");
|
|
4519
|
+
pricesBtn.type = "button";
|
|
4520
|
+
pricesBtn.className = "sl-prices-btn";
|
|
4521
|
+
pricesBtn.setAttribute("aria-label", this.tf("picker.ticketPrices", "Ticket prices"));
|
|
4522
|
+
pricesBtn.setAttribute("title", this.tf("picker.ticketPrices", "Ticket prices"));
|
|
4523
|
+
pricesBtn.setAttribute("aria-pressed", "false");
|
|
4524
|
+
pricesBtn.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0L3 13V5a2 2 0 0 1 2-2h8l7.6 7.6a2 2 0 0 1 0 2.8z"/><circle cx="7.5" cy="7.5" r="1.3"/></svg>';
|
|
4525
|
+
pricesBtn.addEventListener("click", () => togglePrices());
|
|
4526
|
+
this.els.headInfo?.parentElement?.insertBefore(pricesBtn, this.els.hold ?? null);
|
|
4527
|
+
this.pricesBtnEl = pricesBtn;
|
|
4253
4528
|
this.tipEl = document.createElement("div");
|
|
4254
4529
|
this.tipEl.setAttribute("role", "tooltip");
|
|
4255
4530
|
this.tipEl.className = "sl-tip";
|
|
@@ -4264,10 +4539,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
4264
4539
|
const canvasHost = document.createElement("div");
|
|
4265
4540
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
4266
4541
|
this.mapHost.appendChild(canvasHost);
|
|
4542
|
+
renderProfile.mountBuyerShell = duration(shellStartedAt);
|
|
4543
|
+
const chartStartedAt = performance.now();
|
|
4267
4544
|
const info = await this.controller.render(canvasHost);
|
|
4545
|
+
renderProfile.loadChartAndStatuses = duration(chartStartedAt);
|
|
4268
4546
|
if (this.destroyed) return this;
|
|
4269
4547
|
if (!info) {
|
|
4270
|
-
this.els.boot.innerHTML =
|
|
4548
|
+
this.els.boot.innerHTML = `<div class="sl-boot-title">${this.tf("picker.mapDidNotLoad", "The seat map didn\u2019t load")}</div><div>${this.tf("picker.checkConnection", "Check your connection and try again.")}</div><button type="button" class="sl-boot-retry">${this.tf("picker.accessRetry", "Try again")}</button>`;
|
|
4271
4549
|
this.els.boot.querySelector("button").addEventListener("click", () => {
|
|
4272
4550
|
const container = this.opts.container;
|
|
4273
4551
|
const opts = this.opts;
|
|
@@ -4276,7 +4554,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4276
4554
|
});
|
|
4277
4555
|
return this;
|
|
4278
4556
|
}
|
|
4557
|
+
Object.assign(renderProfile, info.performanceProfile);
|
|
4279
4558
|
this.els.boot.remove();
|
|
4559
|
+
const controlsStartedAt = performance.now();
|
|
4280
4560
|
this.startRealtime();
|
|
4281
4561
|
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
4282
4562
|
root.dataset.eventMode = info.mode === "test" ? "test" : "live";
|
|
@@ -4292,7 +4572,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4292
4572
|
this.regions["top-left"].appendChild(badge);
|
|
4293
4573
|
}
|
|
4294
4574
|
const chartTheme = this.controller.doc?.theme;
|
|
4295
|
-
Object.entries(
|
|
4575
|
+
Object.entries(resolvePickerTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4296
4576
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
4297
4577
|
this.eventTimezone = info.timezone ?? null;
|
|
4298
4578
|
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl ?? info.posterUrl ?? null;
|
|
@@ -4302,6 +4582,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
4302
4582
|
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
4303
4583
|
this.eventWhenText = when || null;
|
|
4304
4584
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
4585
|
+
if (this.els.pricesHint) {
|
|
4586
|
+
this.els.pricesHint.textContent = this.tf(
|
|
4587
|
+
"picker.priceHint",
|
|
4588
|
+
"Colours on the map are ticket types \u2014 tap one below to show just those seats."
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4305
4591
|
this.buildBadge(chartTheme);
|
|
4306
4592
|
const present = /* @__PURE__ */ new Set();
|
|
4307
4593
|
let hasLimitedView = false;
|
|
@@ -4328,7 +4614,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4328
4614
|
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-a11y="1" data-f="${key}">${label}</button>`;
|
|
4329
4615
|
chips.insertAdjacentHTML(
|
|
4330
4616
|
"beforeend",
|
|
4331
|
-
mk("all", "All seats") + ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
|
|
4617
|
+
mk("all", this.tf("picker.allSeats", "All seats")) + ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
|
|
4332
4618
|
);
|
|
4333
4619
|
const active = /* @__PURE__ */ new Set();
|
|
4334
4620
|
const syncChips = () => {
|
|
@@ -4374,7 +4660,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4374
4660
|
cb.type = "button";
|
|
4375
4661
|
cb.className = "sl-cbbtn";
|
|
4376
4662
|
this.cbEl = cb;
|
|
4377
|
-
cb.setAttribute("aria-label", "Toggle colorblind-friendly colors");
|
|
4663
|
+
cb.setAttribute("aria-label", this.tf("picker.toggleColorblindColors", "Toggle colorblind-friendly colors"));
|
|
4378
4664
|
cb.setAttribute("aria-pressed", String(this.cbSafe));
|
|
4379
4665
|
cb.innerHTML = '<svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg>';
|
|
4380
4666
|
this.els.zfit.parentElement.appendChild(cb);
|
|
@@ -4394,12 +4680,34 @@ var SeatPicker = class _SeatPicker {
|
|
|
4394
4680
|
this.buildPanelToggle();
|
|
4395
4681
|
if (this.opts.panelCollapsed) this.setPanelCollapsed(true);
|
|
4396
4682
|
this.dockLayoutChrome();
|
|
4683
|
+
renderProfile.buildBuyerControls = duration(controlsStartedAt);
|
|
4684
|
+
const holdStartedAt = performance.now();
|
|
4397
4685
|
await this.restoreRememberedHold();
|
|
4686
|
+
renderProfile.restoreHold = duration(holdStartedAt);
|
|
4398
4687
|
if (this.destroyed) return this;
|
|
4399
4688
|
if (this.salesClosed) this.applySalesClosed();
|
|
4400
4689
|
this.syncPrices();
|
|
4401
4690
|
this.syncTray();
|
|
4402
4691
|
this.resumeHostedOrder();
|
|
4692
|
+
const doc = this.controller.doc;
|
|
4693
|
+
this.emitAnalytics("chart_rendered", {
|
|
4694
|
+
renderingTimeMillis: duration(renderStartedAt),
|
|
4695
|
+
performanceProfile: renderProfile,
|
|
4696
|
+
seatCount: this.allSeats().length,
|
|
4697
|
+
objectCount: doc?.objects.length ?? 0,
|
|
4698
|
+
sectionCount: doc?.objects.filter((object) => object.type === "section").length ?? 0,
|
|
4699
|
+
view: this.buyerView,
|
|
4700
|
+
// Dimensions a host needs to read the timings above as anything other
|
|
4701
|
+
// than one undifferentiated number: WHICH chart, HOW big, WHICH data
|
|
4702
|
+
// path, and whether the browser had already warmed for this chart.
|
|
4703
|
+
// The event key is the chart's identity here — an event owns exactly one
|
|
4704
|
+
// chart — and the name is the same string already drawn in the header.
|
|
4705
|
+
eventKey: this.opts.event,
|
|
4706
|
+
chartName: doc?.name,
|
|
4707
|
+
floorCount: this.controller.getFloors().length,
|
|
4708
|
+
transport: this.opts.transport ? "custom" : "pubapi",
|
|
4709
|
+
load: markChartLoaded(this.opts.event) ? "repeat" : "cold"
|
|
4710
|
+
});
|
|
4403
4711
|
return this;
|
|
4404
4712
|
}
|
|
4405
4713
|
/**
|
|
@@ -4460,8 +4768,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4460
4768
|
if (!btn) return;
|
|
4461
4769
|
const collapsed = this.sideCollapsed;
|
|
4462
4770
|
btn.setAttribute("aria-expanded", String(!collapsed));
|
|
4463
|
-
btn.setAttribute("aria-label", collapsed ? "Show the ticket panel" : "Hide the ticket panel");
|
|
4464
|
-
btn.
|
|
4771
|
+
btn.setAttribute("aria-label", collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel"));
|
|
4772
|
+
btn.title = collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel");
|
|
4773
|
+
btn.innerHTML = collapsed ? `<svg viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></svg><span>${this.tf("picker.tickets", "Tickets")}</span>` : `<svg viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg>`;
|
|
4465
4774
|
}
|
|
4466
4775
|
/**
|
|
4467
4776
|
* Collapse or reopen the wide-layout ticket panel in place (host-drivable —
|
|
@@ -4474,7 +4783,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4474
4783
|
if (this.destroyed) return;
|
|
4475
4784
|
this.sideCollapsed = collapsed;
|
|
4476
4785
|
this.applyPanelCollapsed();
|
|
4477
|
-
this.scheduleMotion(() => this.controller.
|
|
4786
|
+
this.scheduleMotion(() => this.controller.refitCurrentView(), 340);
|
|
4478
4787
|
}
|
|
4479
4788
|
/** Idempotent DOM apply, also run on every layout flip (inert must lift on narrow). */
|
|
4480
4789
|
applyPanelCollapsed() {
|
|
@@ -4487,13 +4796,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
4487
4796
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
4488
4797
|
this.applyPanelCollapsed();
|
|
4489
4798
|
const zfs = this.els.zfs;
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4799
|
+
const head = this.els.headInfo?.parentElement;
|
|
4800
|
+
if (zfs && head && zfs.parentElement !== head) {
|
|
4801
|
+
zfs.classList.add("sl-fs-pill");
|
|
4802
|
+
head.insertBefore(zfs, this.els.hold ?? null);
|
|
4803
|
+
}
|
|
4804
|
+
const pricesSec = this.els.pricesSec;
|
|
4805
|
+
if (pricesSec) {
|
|
4806
|
+
if (narrow) {
|
|
4807
|
+
pricesSec.setAttribute("role", "button");
|
|
4808
|
+
pricesSec.tabIndex = 0;
|
|
4809
|
+
pricesSec.setAttribute(
|
|
4810
|
+
"aria-expanded",
|
|
4811
|
+
String(this.root?.getAttribute("data-prices-open") === "true")
|
|
4812
|
+
);
|
|
4813
|
+
} else {
|
|
4814
|
+
pricesSec.removeAttribute("role");
|
|
4815
|
+
pricesSec.removeAttribute("aria-expanded");
|
|
4816
|
+
pricesSec.tabIndex = -1;
|
|
4497
4817
|
}
|
|
4498
4818
|
}
|
|
4499
4819
|
const filters = this.els.filters;
|
|
@@ -4520,7 +4840,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4520
4840
|
this.extendEl = el;
|
|
4521
4841
|
this.els.extendTxt = el.querySelector('[data-ref="extendTxt"]');
|
|
4522
4842
|
this.els.extendBtn = el.querySelector('[data-ref="extendBtn"]');
|
|
4523
|
-
this.els.extendBtn.textContent = "Add time";
|
|
4843
|
+
this.els.extendBtn.textContent = this.tf("picker.addTime", "Add time");
|
|
4524
4844
|
this.els.extendBtn.addEventListener("click", () => void this.handleExtend());
|
|
4525
4845
|
}
|
|
4526
4846
|
/** Success overlay + onBooked fire when the held seats settle to booked. */
|
|
@@ -4529,7 +4849,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4529
4849
|
el.className = "sl-booked";
|
|
4530
4850
|
el.setAttribute("role", "status");
|
|
4531
4851
|
el.setAttribute("aria-live", "polite");
|
|
4532
|
-
el.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title"
|
|
4852
|
+
el.innerHTML = `<div class="sl-booked-badge"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg></div><div class="sl-booked-title">${this.tf("picker.allSetTitle", "You're all set")}</div><div class="sl-booked-sub" data-ref="bookedSub"></div>`;
|
|
4533
4853
|
this.root.appendChild(el);
|
|
4534
4854
|
this.bookedEl = el;
|
|
4535
4855
|
this.els.bookedSub = el.querySelector('[data-ref="bookedSub"]');
|
|
@@ -4540,8 +4860,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4540
4860
|
* host `messages` override (which makes `t()` return the override, not the key).
|
|
4541
4861
|
*/
|
|
4542
4862
|
tf(key, fallback) {
|
|
4543
|
-
|
|
4544
|
-
return v === key ? fallback : v;
|
|
4863
|
+
return tfStatic(key, fallback);
|
|
4545
4864
|
}
|
|
4546
4865
|
/** Sold-out overlay — an informational state with no unavailable action. */
|
|
4547
4866
|
buildSoldoutOverlay() {
|
|
@@ -4726,7 +5045,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4726
5045
|
}
|
|
4727
5046
|
canAddTicket() {
|
|
4728
5047
|
if (this.totalTicketCount() < this.maxTickets) return true;
|
|
4729
|
-
this.toast(
|
|
5048
|
+
this.toast(t2("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
|
|
4730
5049
|
return false;
|
|
4731
5050
|
}
|
|
4732
5051
|
pendingGATotal(gaAreas) {
|
|
@@ -4749,21 +5068,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
4749
5068
|
}
|
|
4750
5069
|
if (this.confirmSeat || this.tableDialog && !this.tableDialogHeld) {
|
|
4751
5070
|
cta.disabled = true;
|
|
4752
|
-
cta.textContent = this.tableDialog ? "Confirm your table" : "Confirm or cancel this seat";
|
|
5071
|
+
cta.textContent = this.tableDialog ? this.tf("picker.confirmYourTable", "Confirm your table") : this.tf("picker.confirmOrCancelSeat", "Confirm or cancel this seat");
|
|
4753
5072
|
return;
|
|
4754
5073
|
}
|
|
4755
5074
|
if (this.ctaPhase === "holding") {
|
|
4756
5075
|
cta.disabled = true;
|
|
4757
|
-
cta.innerHTML =
|
|
5076
|
+
cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}`;
|
|
4758
5077
|
return;
|
|
4759
5078
|
}
|
|
4760
5079
|
if (this.ctaPhase === "checkout") {
|
|
4761
5080
|
cta.disabled = true;
|
|
4762
|
-
cta.innerHTML =
|
|
5081
|
+
cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.openingCheckout", "Opening secure checkout\u2026")}`;
|
|
4763
5082
|
return;
|
|
4764
5083
|
}
|
|
4765
5084
|
cta.disabled = count === 0;
|
|
4766
|
-
cta.textContent = this.hold ? pending ?
|
|
5085
|
+
cta.textContent = this.hold ? pending ? t2("picker.secureMoreAndCheckout", { count: pending }) : this.tf("picker.continueToCheckout", "Continue to checkout") : count ? this.tf("picker.holdSeatsAndCheckout", "Hold seats & checkout") : this.tf("picker.selectSeats", "Select seats");
|
|
4767
5086
|
}
|
|
4768
5087
|
setCtaPhase(phase) {
|
|
4769
5088
|
this.ctaPhase = phase;
|
|
@@ -4824,7 +5143,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4824
5143
|
this.syncTray();
|
|
4825
5144
|
this.emitHoldChange();
|
|
4826
5145
|
this.opts.onHoldRestored?.(restored, restored.seats ?? [], this.buildHandoff(restored));
|
|
4827
|
-
if (automatic) this.toast("Your held tickets have been restored.", "success");
|
|
5146
|
+
if (automatic) this.toast(this.tf("picker.heldTicketsRestored", "Your held tickets have been restored."), "success");
|
|
4828
5147
|
return restored;
|
|
4829
5148
|
} catch (error) {
|
|
4830
5149
|
const status = error?.status;
|
|
@@ -4859,10 +5178,77 @@ var SeatPicker = class _SeatPicker {
|
|
|
4859
5178
|
buildMinimap() {
|
|
4860
5179
|
const vp = this.controller.getViewport();
|
|
4861
5180
|
if (!vp || !this.els.map) return;
|
|
4862
|
-
const
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
5181
|
+
const host = this.regions["bottom-left"] ?? this.els.map;
|
|
5182
|
+
const toggle = document.createElement("button");
|
|
5183
|
+
toggle.type = "button";
|
|
5184
|
+
toggle.className = "sl-minimap-toggle";
|
|
5185
|
+
toggle.setAttribute("aria-label", this.tf("picker.openVenueMap", "Open venue overview map"));
|
|
5186
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
5187
|
+
toggle.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6l5-2 8 3 5-2v13l-5 2-8-3-5 2zM8 4v13M16 7v13"/></svg>';
|
|
5188
|
+
host.appendChild(toggle);
|
|
5189
|
+
this.miniToggle = toggle;
|
|
5190
|
+
const wrap = document.createElement("div");
|
|
5191
|
+
wrap.className = "sl-minimap";
|
|
5192
|
+
wrap.setAttribute("role", "group");
|
|
5193
|
+
const bar = document.createElement("div");
|
|
5194
|
+
bar.className = "sl-minimap-bar";
|
|
5195
|
+
const label = document.createElement("span");
|
|
5196
|
+
label.className = "sl-minimap-label";
|
|
5197
|
+
const key = document.createElement("span");
|
|
5198
|
+
key.className = "sl-minimap-key";
|
|
5199
|
+
key.setAttribute("aria-label", this.tf("picker.minimapStateKey", "Focused, selected, available, and inactive sections"));
|
|
5200
|
+
key.innerHTML = '<i class="focused" title="Focused"></i><i class="selected" title="Selected"></i><i title="Available"></i><i class="inactive" title="Inactive"></i>';
|
|
5201
|
+
const close = document.createElement("button");
|
|
5202
|
+
close.type = "button";
|
|
5203
|
+
close.className = "sl-minimap-close";
|
|
5204
|
+
close.setAttribute("aria-label", this.tf("picker.closeVenueMap", "Close venue overview map"));
|
|
5205
|
+
close.textContent = "\xD7";
|
|
5206
|
+
bar.append(label, key, close);
|
|
5207
|
+
const canvas = document.createElement("canvas");
|
|
5208
|
+
canvas.tabIndex = 0;
|
|
5209
|
+
canvas.setAttribute("role", "application");
|
|
5210
|
+
canvas.setAttribute("aria-label", this.tf("picker.venueMapInstructions", "Venue overview map. Drag the viewport, click a section, or use arrow keys to pan."));
|
|
5211
|
+
wrap.append(bar, canvas);
|
|
5212
|
+
host.appendChild(wrap);
|
|
5213
|
+
this.miniWrap = wrap;
|
|
5214
|
+
this.miniLabel = label;
|
|
5215
|
+
this.miniCanvas = canvas;
|
|
5216
|
+
const base = document.createElement("canvas");
|
|
5217
|
+
this.miniBase = base;
|
|
5218
|
+
toggle.addEventListener("click", () => this.setMinimapOpen(true));
|
|
5219
|
+
close.addEventListener("click", () => this.setMinimapOpen(false));
|
|
5220
|
+
canvas.addEventListener("click", (event) => {
|
|
5221
|
+
if (this.miniSuppressClick) {
|
|
5222
|
+
this.miniSuppressClick = false;
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
this.minimapJump(event);
|
|
5226
|
+
});
|
|
5227
|
+
canvas.addEventListener("pointerdown", (event) => this.minimapPointerDown(event));
|
|
5228
|
+
canvas.addEventListener("pointermove", (event) => this.minimapPointerMove(event));
|
|
5229
|
+
canvas.addEventListener("pointerup", (event) => this.minimapPointerUp(event));
|
|
5230
|
+
canvas.addEventListener("pointercancel", (event) => this.minimapPointerUp(event));
|
|
5231
|
+
canvas.addEventListener("keydown", (event) => this.minimapKeydown(event));
|
|
5232
|
+
this.layoutMinimap();
|
|
5233
|
+
this.syncMinimapLabel();
|
|
5234
|
+
this.drawMinimapStatic();
|
|
5235
|
+
this.drawMinimapRect();
|
|
5236
|
+
}
|
|
5237
|
+
setMinimapOpen(open) {
|
|
5238
|
+
this.root?.setAttribute("data-minimap-open", String(open));
|
|
5239
|
+
this.miniToggle?.setAttribute("aria-expanded", String(open));
|
|
5240
|
+
if (open) this.miniCanvas?.focus();
|
|
5241
|
+
else this.miniToggle?.focus();
|
|
5242
|
+
}
|
|
5243
|
+
/** Re-fit the thumbnail whenever the active floor or complete overview
|
|
5244
|
+
* changes bounds. This is presentation-only; the source chart is untouched. */
|
|
5245
|
+
layoutMinimap() {
|
|
5246
|
+
const canvas = this.miniCanvas;
|
|
5247
|
+
const base = this.miniBase;
|
|
5248
|
+
const b = this.controller.getViewport()?.bounds;
|
|
5249
|
+
if (!canvas || !base || !b || !(b.width > 0 && b.height > 0)) return;
|
|
5250
|
+
const MAXW = 196;
|
|
5251
|
+
const MAXH = 146;
|
|
4866
5252
|
const PAD = 6;
|
|
4867
5253
|
const aspect = b.width / Math.max(1, b.height);
|
|
4868
5254
|
let w = MAXW;
|
|
@@ -4874,32 +5260,40 @@ var SeatPicker = class _SeatPicker {
|
|
|
4874
5260
|
w = Math.max(64, w);
|
|
4875
5261
|
h = Math.max(48, h);
|
|
4876
5262
|
const dpr = Math.min(2, window.devicePixelRatio || 1);
|
|
4877
|
-
const wrap = document.createElement("div");
|
|
4878
|
-
wrap.className = "sl-minimap";
|
|
4879
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
4880
|
-
const canvas = document.createElement("canvas");
|
|
4881
5263
|
canvas.width = Math.round(w * dpr);
|
|
4882
5264
|
canvas.height = Math.round(h * dpr);
|
|
4883
5265
|
canvas.style.width = `${w}px`;
|
|
4884
5266
|
canvas.style.height = `${h}px`;
|
|
4885
|
-
wrap.appendChild(canvas);
|
|
4886
|
-
(this.regions["bottom-left"] ?? this.els.map).appendChild(wrap);
|
|
4887
|
-
this.miniCanvas = canvas;
|
|
4888
|
-
const scale = Math.min((w - PAD * 2) / Math.max(1, b.width), (h - PAD * 2) / Math.max(1, b.height)) * dpr;
|
|
4889
|
-
const offX = (w * dpr - b.width * scale) / 2 - b.x * scale;
|
|
4890
|
-
const offY = (h * dpr - b.height * scale) / 2 - b.y * scale;
|
|
4891
|
-
this.miniTf = { scale, offX, offY, dpr };
|
|
4892
|
-
const base = document.createElement("canvas");
|
|
4893
5267
|
base.width = canvas.width;
|
|
4894
5268
|
base.height = canvas.height;
|
|
4895
|
-
this.
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
this.
|
|
5269
|
+
this.miniTf = createMinimapTransform(b, canvas.width, canvas.height, dpr, PAD);
|
|
5270
|
+
}
|
|
5271
|
+
syncMinimapLabel() {
|
|
5272
|
+
if (!this.miniLabel || !this.miniWrap) return;
|
|
5273
|
+
const floors = this.controller.getFloors();
|
|
5274
|
+
const floor = this.controller.isFloorOverview() ? this.tf("picker.allFloors", "All floors") : floors.find((candidate) => candidate.id === this.controller.getActiveFloorId())?.name ?? this.tf("picker.venueOverview", "Venue overview");
|
|
5275
|
+
const sections = this.controller.getMinimapSnapshot().sections;
|
|
5276
|
+
const focused = sections.find((section) => section.active);
|
|
5277
|
+
const focusedLabel = focused?.label ?? this.lastSection?.label;
|
|
5278
|
+
const label = focusedLabel ? `${floor} \xB7 ${focusedLabel}` : floor;
|
|
5279
|
+
this.miniLabel.textContent = label;
|
|
5280
|
+
const selected = sections.filter((section) => section.state === "selected").length;
|
|
5281
|
+
const available = sections.filter((section) => section.state === "available").length;
|
|
5282
|
+
const inactive = sections.filter((section) => section.state === "inactive").length;
|
|
5283
|
+
this.miniWrap.setAttribute(
|
|
5284
|
+
"aria-label",
|
|
5285
|
+
sections.length ? `${label}. ${selected} selected, ${available} available, ${inactive} inactive sections.` : label
|
|
5286
|
+
);
|
|
5287
|
+
this.miniCanvas?.setAttribute(
|
|
5288
|
+
"aria-label",
|
|
5289
|
+
sections.length ? `${label}. ${selected} selected, ${available} available, ${inactive} inactive sections. ${this.tf("picker.venueMapInstructions", "The outlined region is the visible map area. Drag it, click a section, or use arrow keys to pan.")}` : `${label}. ${this.tf("picker.venueMapInstructions", "The outlined region is the visible map area. Drag it, click elsewhere, or use arrow keys to pan.")}`
|
|
5290
|
+
);
|
|
4899
5291
|
}
|
|
4900
5292
|
/** Repaint the static overview + rect (floor switch, live open/close). */
|
|
4901
5293
|
refreshMinimap() {
|
|
4902
5294
|
if (!this.miniBase) return;
|
|
5295
|
+
this.layoutMinimap();
|
|
5296
|
+
this.syncMinimapLabel();
|
|
4903
5297
|
this.drawMinimapStatic();
|
|
4904
5298
|
this.drawMinimapRect();
|
|
4905
5299
|
}
|
|
@@ -4907,8 +5301,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4907
5301
|
drawMinimapStatic() {
|
|
4908
5302
|
const base = this.miniBase;
|
|
4909
5303
|
const tf = this.miniTf;
|
|
4910
|
-
|
|
4911
|
-
if (!base || !tf || !doc) return;
|
|
5304
|
+
if (!base || !tf) return;
|
|
4912
5305
|
const ctx = base.getContext("2d");
|
|
4913
5306
|
if (!ctx) return;
|
|
4914
5307
|
ctx.clearRect(0, 0, base.width, base.height);
|
|
@@ -4917,34 +5310,42 @@ var SeatPicker = class _SeatPicker {
|
|
|
4917
5310
|
const line = this.cssVar("--sl-line") || "rgba(139,147,167,.5)";
|
|
4918
5311
|
const muted = this.cssVar("--sl-muted") || "#8b93a7";
|
|
4919
5312
|
const accent = this.cssVar("--sl-accent") || "#6e7bff";
|
|
4920
|
-
const
|
|
4921
|
-
|
|
4922
|
-
for (const
|
|
4923
|
-
if (
|
|
4924
|
-
drewSection = true;
|
|
4925
|
-
const closed = this.controller.isSectionClosed(o.id);
|
|
4926
|
-
const fill = closed ? muted : o.color ?? (o.zone && zoneColor.get(o.zone)) ?? accent;
|
|
5313
|
+
const available = this.cssVar("--sl-success") || "#22a06b";
|
|
5314
|
+
const snapshot = this.controller.getMinimapSnapshot();
|
|
5315
|
+
for (const section of snapshot.sections) {
|
|
5316
|
+
if (section.outline.length < 3) continue;
|
|
4927
5317
|
ctx.beginPath();
|
|
4928
|
-
|
|
5318
|
+
section.outline.forEach((point, index) => index === 0 ? ctx.moveTo(fx(point.x), fy(point.y)) : ctx.lineTo(fx(point.x), fy(point.y)));
|
|
4929
5319
|
ctx.closePath();
|
|
4930
|
-
ctx.globalAlpha =
|
|
4931
|
-
ctx.fillStyle =
|
|
5320
|
+
ctx.globalAlpha = section.state === "inactive" ? 0.18 : section.state === "selected" ? 0.72 : 0.34;
|
|
5321
|
+
ctx.fillStyle = section.state === "inactive" ? muted : section.state === "selected" ? accent : available;
|
|
4932
5322
|
ctx.fill();
|
|
4933
|
-
ctx.globalAlpha = 0.
|
|
5323
|
+
ctx.globalAlpha = 0.82;
|
|
4934
5324
|
ctx.lineWidth = Math.max(1, tf.dpr);
|
|
4935
5325
|
ctx.strokeStyle = line;
|
|
4936
5326
|
ctx.stroke();
|
|
5327
|
+
if (section.active) {
|
|
5328
|
+
ctx.globalAlpha = 1;
|
|
5329
|
+
ctx.lineJoin = "round";
|
|
5330
|
+
ctx.lineWidth = Math.max(4, tf.dpr * 3.5);
|
|
5331
|
+
ctx.strokeStyle = this.cssVar("--sl-surface") || "#fff";
|
|
5332
|
+
ctx.stroke();
|
|
5333
|
+
ctx.lineWidth = Math.max(2, tf.dpr * 1.75);
|
|
5334
|
+
ctx.strokeStyle = accent;
|
|
5335
|
+
ctx.stroke();
|
|
5336
|
+
}
|
|
4937
5337
|
}
|
|
4938
5338
|
ctx.globalAlpha = 1;
|
|
4939
|
-
if (!
|
|
5339
|
+
if (!snapshot.sections.length) {
|
|
4940
5340
|
const r = Math.max(1, tf.dpr);
|
|
4941
|
-
for (const seat of
|
|
4942
|
-
|
|
4943
|
-
ctx.fillStyle =
|
|
5341
|
+
for (const seat of snapshot.seats) {
|
|
5342
|
+
ctx.globalAlpha = seat.state === "inactive" ? 0.22 : 0.78;
|
|
5343
|
+
ctx.fillStyle = seat.state === "inactive" ? muted : seat.state === "selected" ? accent : available;
|
|
4944
5344
|
ctx.beginPath();
|
|
4945
5345
|
ctx.arc(fx(seat.x), fy(seat.y), r, 0, Math.PI * 2);
|
|
4946
5346
|
ctx.fill();
|
|
4947
5347
|
}
|
|
5348
|
+
ctx.globalAlpha = 1;
|
|
4948
5349
|
}
|
|
4949
5350
|
}
|
|
4950
5351
|
/** Blit the base overview, then stroke the current viewport rectangle on top. */
|
|
@@ -4959,41 +5360,106 @@ var SeatPicker = class _SeatPicker {
|
|
|
4959
5360
|
ctx.drawImage(base, 0, 0);
|
|
4960
5361
|
const vp = this.controller.getViewport();
|
|
4961
5362
|
if (!vp) return;
|
|
4962
|
-
const
|
|
4963
|
-
const
|
|
4964
|
-
const
|
|
4965
|
-
const w = v.width * tf.scale;
|
|
4966
|
-
const h = v.height * tf.scale;
|
|
4967
|
-
const accent = this.cssVar("--sl-accent") || "#f4b740";
|
|
5363
|
+
const plan = minimapViewportPlan(vp.visible, tf, canvas.width, canvas.height, Math.max(2, tf.dpr * 2));
|
|
5364
|
+
const ink = this.cssVar("--sl-text") || "#12151c";
|
|
5365
|
+
const surface = this.cssVar("--sl-surface") || "#fff";
|
|
4968
5366
|
ctx.save();
|
|
4969
|
-
ctx.globalAlpha = 0.
|
|
4970
|
-
ctx.fillStyle =
|
|
4971
|
-
ctx.fillRect(x, y,
|
|
5367
|
+
ctx.globalAlpha = 0.1;
|
|
5368
|
+
ctx.fillStyle = ink;
|
|
5369
|
+
ctx.fillRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
5370
|
+
ctx.globalAlpha = 0.9;
|
|
5371
|
+
ctx.lineJoin = "round";
|
|
5372
|
+
ctx.lineWidth = Math.max(4, tf.dpr * 3.25);
|
|
5373
|
+
ctx.strokeStyle = surface;
|
|
5374
|
+
ctx.strokeRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
4972
5375
|
ctx.globalAlpha = 1;
|
|
4973
|
-
ctx.lineWidth = Math.max(
|
|
4974
|
-
ctx.strokeStyle =
|
|
4975
|
-
ctx.strokeRect(x, y,
|
|
5376
|
+
ctx.lineWidth = Math.max(2, tf.dpr * 1.75);
|
|
5377
|
+
ctx.strokeStyle = ink;
|
|
5378
|
+
ctx.strokeRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
5379
|
+
if (plan.clipped && Object.values(plan.clippedEdges).some(Boolean)) {
|
|
5380
|
+
ctx.setLineDash([Math.max(3, tf.dpr * 3), Math.max(2, tf.dpr * 2)]);
|
|
5381
|
+
ctx.strokeRect(plan.clipped.x, plan.clipped.y, plan.clipped.width, plan.clipped.height);
|
|
5382
|
+
}
|
|
4976
5383
|
ctx.restore();
|
|
4977
5384
|
}
|
|
4978
|
-
|
|
4979
|
-
minimapJump(e) {
|
|
5385
|
+
minimapWorldPoint(event) {
|
|
4980
5386
|
const canvas = this.miniCanvas;
|
|
4981
5387
|
const tf = this.miniTf;
|
|
4982
|
-
if (!canvas || !tf) return;
|
|
4983
|
-
const
|
|
4984
|
-
const px = (
|
|
4985
|
-
const py = (
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
5388
|
+
if (!canvas || !tf) return null;
|
|
5389
|
+
const rect = canvas.getBoundingClientRect();
|
|
5390
|
+
const px = (event.clientX - rect.left) * (canvas.width / Math.max(1, rect.width));
|
|
5391
|
+
const py = (event.clientY - rect.top) * (canvas.height / Math.max(1, rect.height));
|
|
5392
|
+
return { ...minimapPointToWorld({ x: px, y: py }, tf), px, py };
|
|
5393
|
+
}
|
|
5394
|
+
minimapPointerDown(event) {
|
|
5395
|
+
const point = this.minimapWorldPoint(event);
|
|
5396
|
+
const visible = this.controller.getViewport()?.visible;
|
|
5397
|
+
if (!point || !visible) return;
|
|
5398
|
+
const inside = point.x >= visible.x && point.x <= visible.x + visible.width && point.y >= visible.y && point.y <= visible.y + visible.height;
|
|
5399
|
+
if (!inside) return;
|
|
5400
|
+
this.miniDrag = {
|
|
5401
|
+
pointerId: event.pointerId,
|
|
5402
|
+
offsetX: point.x - (visible.x + visible.width / 2),
|
|
5403
|
+
offsetY: point.y - (visible.y + visible.height / 2),
|
|
5404
|
+
startX: event.clientX,
|
|
5405
|
+
startY: event.clientY,
|
|
5406
|
+
moved: false
|
|
5407
|
+
};
|
|
5408
|
+
this.miniCanvas?.setPointerCapture(event.pointerId);
|
|
5409
|
+
this.miniCanvas?.classList.add("sl-dragging");
|
|
5410
|
+
event.preventDefault();
|
|
5411
|
+
}
|
|
5412
|
+
minimapPointerMove(event) {
|
|
5413
|
+
const drag = this.miniDrag;
|
|
5414
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
5415
|
+
const point = this.minimapWorldPoint(event);
|
|
5416
|
+
if (!point) return;
|
|
5417
|
+
if (!drag.moved && Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 2) {
|
|
5418
|
+
drag.moved = true;
|
|
5419
|
+
this.controller.clearSectionFocus();
|
|
5420
|
+
}
|
|
5421
|
+
this.controller.panToWorld({ x: point.x - drag.offsetX, y: point.y - drag.offsetY });
|
|
5422
|
+
event.preventDefault();
|
|
5423
|
+
}
|
|
5424
|
+
minimapPointerUp(event) {
|
|
5425
|
+
const drag = this.miniDrag;
|
|
5426
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
5427
|
+
this.miniSuppressClick = drag.moved;
|
|
5428
|
+
this.miniDrag = null;
|
|
5429
|
+
this.miniCanvas?.classList.remove("sl-dragging");
|
|
5430
|
+
if (this.miniCanvas?.hasPointerCapture(event.pointerId)) this.miniCanvas.releasePointerCapture(event.pointerId);
|
|
5431
|
+
}
|
|
5432
|
+
minimapKeydown(event) {
|
|
5433
|
+
const visible = this.controller.getViewport()?.visible;
|
|
5434
|
+
if (!visible) return;
|
|
5435
|
+
if (event.key === "Home") {
|
|
5436
|
+
this.controller.overview();
|
|
5437
|
+
event.preventDefault();
|
|
5438
|
+
return;
|
|
5439
|
+
}
|
|
5440
|
+
const dx = event.key === "ArrowLeft" ? -visible.width * 0.2 : event.key === "ArrowRight" ? visible.width * 0.2 : 0;
|
|
5441
|
+
const dy = event.key === "ArrowUp" ? -visible.height * 0.2 : event.key === "ArrowDown" ? visible.height * 0.2 : 0;
|
|
5442
|
+
if (!dx && !dy) return;
|
|
5443
|
+
this.controller.clearSectionFocus();
|
|
5444
|
+
this.controller.panToWorld({
|
|
5445
|
+
x: visible.x + visible.width / 2 + dx,
|
|
5446
|
+
y: visible.y + visible.height / 2 + dy
|
|
5447
|
+
});
|
|
5448
|
+
event.preventDefault();
|
|
5449
|
+
}
|
|
5450
|
+
/** Minimap click → focus an available section, otherwise pan there. */
|
|
5451
|
+
minimapJump(e) {
|
|
5452
|
+
const point = this.minimapWorldPoint(e);
|
|
5453
|
+
if (!point) return;
|
|
5454
|
+
for (const section of this.controller.getMinimapSnapshot().sections) {
|
|
5455
|
+
if (section.state === "inactive" || section.outline.length < 3) continue;
|
|
5456
|
+
if (pointInPolygon(point.x, point.y, section.outline)) {
|
|
5457
|
+
this.controller.focusSection(section.id);
|
|
4993
5458
|
return;
|
|
4994
5459
|
}
|
|
4995
5460
|
}
|
|
4996
|
-
this.controller.
|
|
5461
|
+
this.controller.clearSectionFocus();
|
|
5462
|
+
this.controller.panToWorld(point);
|
|
4997
5463
|
}
|
|
4998
5464
|
// ---- F4 price-band filter -------------------------------------------------
|
|
4999
5465
|
/** Effective display price of a category: host pricing override → first tier → base. */
|
|
@@ -5002,32 +5468,39 @@ var SeatPicker = class _SeatPicker {
|
|
|
5002
5468
|
if (chart === void 0 || !c.key) return chart;
|
|
5003
5469
|
return this.paidPrice(c.key, c.tiers?.[0]?.id ?? null, chart);
|
|
5004
5470
|
}
|
|
5005
|
-
/**
|
|
5471
|
+
/** Complete buyer-visible price range, including every ticket tier and
|
|
5472
|
+
* tier-specific host override. */
|
|
5473
|
+
catPriceRange(c) {
|
|
5474
|
+
const prices = c.tiers?.length ? c.tiers.map((tier) => this.paidPrice(c.key, tier.id ?? null, tier.price)) : c.price == null ? [] : [this.paidPrice(c.key, null, c.price)];
|
|
5475
|
+
return prices.length ? { min: Math.min(...prices), max: Math.max(...prices) } : void 0;
|
|
5476
|
+
}
|
|
5477
|
+
/** Derive category price bands without hiding the upper end of tier ranges. */
|
|
5006
5478
|
priceBands() {
|
|
5007
5479
|
const doc = this.controller.doc;
|
|
5008
5480
|
if (!doc) return [];
|
|
5009
|
-
const priced = doc.categories.map((c) => ({ key: c.key,
|
|
5481
|
+
const priced = doc.categories.map((c) => ({ key: c.key, range: this.catPriceRange(c) })).filter((x) => x.range != null);
|
|
5010
5482
|
if (!priced.length) return [];
|
|
5011
|
-
const distinct = [...new
|
|
5483
|
+
const distinct = [...new Map(priced.map((item) => [`${item.range.min}:${item.range.max}`, item.range])).values()].sort((a, b) => a.min - b.min || a.max - b.max);
|
|
5012
5484
|
if (distinct.length <= 5) {
|
|
5013
|
-
return distinct.map((
|
|
5014
|
-
id: `p${
|
|
5015
|
-
label: this.money(
|
|
5016
|
-
keys: priced.filter((
|
|
5017
|
-
min:
|
|
5018
|
-
max:
|
|
5485
|
+
return distinct.map((range) => ({
|
|
5486
|
+
id: `p${range.min}-${range.max}`,
|
|
5487
|
+
label: range.min === range.max ? this.money(range.min) : `${this.money(range.min)}\u2013${this.money(range.max)}`,
|
|
5488
|
+
keys: priced.filter((item) => item.range.min === range.min && item.range.max === range.max).map((item) => item.key),
|
|
5489
|
+
min: range.min,
|
|
5490
|
+
max: range.max
|
|
5019
5491
|
}));
|
|
5020
5492
|
}
|
|
5021
|
-
const
|
|
5493
|
+
const ordered = [...priced].sort((a, b) => a.range.min - b.range.min || a.range.max - b.range.max);
|
|
5494
|
+
const chunk = Math.ceil(ordered.length / 4);
|
|
5022
5495
|
const bands = [];
|
|
5023
|
-
for (let i = 0; i <
|
|
5024
|
-
const slice =
|
|
5025
|
-
const lo = slice
|
|
5026
|
-
const hi = slice
|
|
5496
|
+
for (let i = 0; i < ordered.length; i += chunk) {
|
|
5497
|
+
const slice = ordered.slice(i, i + chunk);
|
|
5498
|
+
const lo = Math.min(...slice.map((item) => item.range.min));
|
|
5499
|
+
const hi = Math.max(...slice.map((item) => item.range.max));
|
|
5027
5500
|
bands.push({
|
|
5028
5501
|
id: `b${i}`,
|
|
5029
5502
|
label: lo === hi ? this.money(lo) : `${this.money(lo)}\u2013${this.money(hi)}`,
|
|
5030
|
-
keys:
|
|
5503
|
+
keys: slice.map((item) => item.key),
|
|
5031
5504
|
min: lo,
|
|
5032
5505
|
max: hi
|
|
5033
5506
|
});
|
|
@@ -5042,8 +5515,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5042
5515
|
if (bands.length < 2) return;
|
|
5043
5516
|
const select = document.createElement("select");
|
|
5044
5517
|
select.className = "sl-price-select";
|
|
5045
|
-
select.setAttribute("aria-label", "Filter and focus seats by price");
|
|
5046
|
-
select.innerHTML = `<option value="all"
|
|
5518
|
+
select.setAttribute("aria-label", this.tf("picker.filterAndFocusByPrice", "Filter and focus seats by price"));
|
|
5519
|
+
select.innerHTML = `<option value="all">${this.tf("picker.allPrices", "All prices")}</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
|
|
5047
5520
|
this.els.pricesSec.appendChild(select);
|
|
5048
5521
|
select.addEventListener("change", () => {
|
|
5049
5522
|
const band = bands.find((candidate) => candidate.id === select.value);
|
|
@@ -5070,8 +5543,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5070
5543
|
const projection = document.createElement("div");
|
|
5071
5544
|
projection.className = "sl-projection";
|
|
5072
5545
|
projection.setAttribute("role", "group");
|
|
5073
|
-
projection.setAttribute("aria-label", "Venue view");
|
|
5074
|
-
projection.innerHTML =
|
|
5546
|
+
projection.setAttribute("aria-label", this.tf("picker.venueView", "Venue view"));
|
|
5547
|
+
projection.innerHTML = `<button type="button" data-view="map" aria-pressed="true" title="${this.tf("picker.flat2dMap", "Flat 2D map")}">${this.tf("picker.map", "Map")}</button><button type="button" data-view="venue3d" aria-pressed="false" title="${this.tf("picker.interactive3dVenueView", "Interactive 3D venue view")}">3D</button>`;
|
|
5075
5548
|
projection.querySelectorAll("button").forEach((button) => {
|
|
5076
5549
|
button.addEventListener("click", () => {
|
|
5077
5550
|
this.setBuyerView(button.dataset.view);
|
|
@@ -5082,7 +5555,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5082
5555
|
this.syncProjection();
|
|
5083
5556
|
}
|
|
5084
5557
|
if (hasSections) {
|
|
5085
|
-
const
|
|
5558
|
+
const allObjects = [
|
|
5559
|
+
...doc.objects,
|
|
5560
|
+
...(doc.floors ?? []).flatMap((floor) => floor.objects)
|
|
5561
|
+
];
|
|
5562
|
+
const authoredZoneIds = new Set((doc.zones ?? []).map((zone) => zone.id));
|
|
5563
|
+
const hasZones = allObjects.some((object) => object.type === "section" && typeof object.zone === "string" && authoredZoneIds.has(object.zone));
|
|
5564
|
+
const RUNGS = hasZones ? ["zones", "sections", "seats"] : ["sections", "seats"];
|
|
5086
5565
|
const pills = document.createElement("div");
|
|
5087
5566
|
pills.className = "sl-rungs on";
|
|
5088
5567
|
pills.setAttribute("role", "group");
|
|
@@ -5115,18 +5594,38 @@ var SeatPicker = class _SeatPicker {
|
|
|
5115
5594
|
const floors = this.controller.getFloors();
|
|
5116
5595
|
const rail = document.createElement("div");
|
|
5117
5596
|
rail.className = "sl-floors on";
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5597
|
+
const select = document.createElement("select");
|
|
5598
|
+
select.setAttribute("aria-label", this.tf("picker.chooseLevel", "Choose level"));
|
|
5599
|
+
const overview = document.createElement("option");
|
|
5600
|
+
overview.value = "";
|
|
5601
|
+
overview.textContent = this.tf("picker.allFloors", "All floors");
|
|
5602
|
+
select.appendChild(overview);
|
|
5603
|
+
for (const floor of floors) {
|
|
5604
|
+
const option = document.createElement("option");
|
|
5605
|
+
option.value = floor.id;
|
|
5606
|
+
option.textContent = floor.name;
|
|
5607
|
+
select.appendChild(option);
|
|
5608
|
+
}
|
|
5609
|
+
select.addEventListener("change", () => {
|
|
5610
|
+
if (select.value === "") this.controller.setFloorOverview(true);
|
|
5611
|
+
else this.controller.setFloor(select.value);
|
|
5612
|
+
this.showSectionCard(null);
|
|
5613
|
+
this.syncFloors();
|
|
5614
|
+
this.syncRung();
|
|
5615
|
+
this.refreshMinimap();
|
|
5129
5616
|
});
|
|
5617
|
+
rail.appendChild(select);
|
|
5618
|
+
const info = document.createElement("button");
|
|
5619
|
+
info.type = "button";
|
|
5620
|
+
info.className = "sl-floor-info";
|
|
5621
|
+
info.textContent = "i";
|
|
5622
|
+
const explanation = this.tf(
|
|
5623
|
+
"picker.floorHelp",
|
|
5624
|
+
"All floors shows the complete venue. Choose one level to inspect and select its seats."
|
|
5625
|
+
);
|
|
5626
|
+
info.setAttribute("aria-label", explanation);
|
|
5627
|
+
info.title = explanation;
|
|
5628
|
+
rail.appendChild(info);
|
|
5130
5629
|
this.regions["left-rail"].appendChild(rail);
|
|
5131
5630
|
this.floorsEl = rail;
|
|
5132
5631
|
this.syncFloors();
|
|
@@ -5172,19 +5671,59 @@ var SeatPicker = class _SeatPicker {
|
|
|
5172
5671
|
syncFloors() {
|
|
5173
5672
|
if (!this.floorsEl) return;
|
|
5174
5673
|
const active = this.controller.getActiveFloorId();
|
|
5175
|
-
this.
|
|
5176
|
-
|
|
5177
|
-
|
|
5674
|
+
const overview = this.controller.isFloorOverview();
|
|
5675
|
+
const select = this.floorsEl.querySelector("select");
|
|
5676
|
+
if (select) select.value = overview ? "" : active;
|
|
5178
5677
|
}
|
|
5179
5678
|
/** Show (or clear, on null) the tapped-section summary card. */
|
|
5180
5679
|
showSectionCard(summary) {
|
|
5181
5680
|
this.lastSection = summary;
|
|
5182
5681
|
this.secCardEl?.remove();
|
|
5183
5682
|
this.secCardEl = null;
|
|
5184
|
-
if (!summary)
|
|
5683
|
+
if (!summary) {
|
|
5684
|
+
this.refreshMinimap();
|
|
5685
|
+
return;
|
|
5686
|
+
}
|
|
5185
5687
|
this.secCardCollapsed = this.controller.getRung() === "seats";
|
|
5186
5688
|
this.secCardShownAt = Date.now();
|
|
5187
5689
|
this.renderSectionCard(summary);
|
|
5690
|
+
this.refreshMinimap();
|
|
5691
|
+
}
|
|
5692
|
+
/**
|
|
5693
|
+
* Open sections next to the focused one, in authored order.
|
|
5694
|
+
*
|
|
5695
|
+
* This is intentionally a circular ring: arena sections are usually authored
|
|
5696
|
+
* clockwise, so moving past the last section should reach the first without
|
|
5697
|
+
* forcing a buyer back through the overview level. Closed or empty sections
|
|
5698
|
+
* are skipped because focusing one cannot produce a useful buyer card.
|
|
5699
|
+
*/
|
|
5700
|
+
sectionNeighbours(sectionId) {
|
|
5701
|
+
const sections = this.activeFloorObjects().filter((object) => object.type === "section" && !this.controller.isSectionClosed(object.id) && this.controller.getSectionSeatCount(object.id) > 0);
|
|
5702
|
+
if (sections.length < 2) return null;
|
|
5703
|
+
const index = sections.findIndex((section) => section.id === sectionId);
|
|
5704
|
+
if (index < 0) return null;
|
|
5705
|
+
return {
|
|
5706
|
+
previous: sections[(index - 1 + sections.length) % sections.length],
|
|
5707
|
+
next: sections[(index + 1) % sections.length]
|
|
5708
|
+
};
|
|
5709
|
+
}
|
|
5710
|
+
/** Previous/next controls shared by expanded, mini, and phone section cards. */
|
|
5711
|
+
sectionNavigationHtml(summary) {
|
|
5712
|
+
const neighbours = this.sectionNeighbours(summary.id);
|
|
5713
|
+
if (!neighbours) return "";
|
|
5714
|
+
const previousLabel = neighbours.previous.label || neighbours.previous.id;
|
|
5715
|
+
const nextLabel = neighbours.next.label || neighbours.next.id;
|
|
5716
|
+
return `<span class="sl-seccard-nav" aria-label="${this.tf("picker.browseSections", "Browse sections")}"><button type="button" data-section-nav="${escapeOption(neighbours.previous.id)}" aria-label="${this.tf("picker.previousSection", "Previous section")}: ${escapeOption(previousLabel)}" title="${escapeOption(previousLabel)}">\u2190</button><button type="button" data-section-nav="${escapeOption(neighbours.next.id)}" aria-label="${this.tf("picker.nextSection", "Next section")}: ${escapeOption(nextLabel)}" title="${escapeOption(nextLabel)}">\u2192</button></span>`;
|
|
5717
|
+
}
|
|
5718
|
+
wireSectionNavigation(card) {
|
|
5719
|
+
card.querySelectorAll("[data-section-nav]").forEach((button) => {
|
|
5720
|
+
button.addEventListener("click", (event) => {
|
|
5721
|
+
event.preventDefault();
|
|
5722
|
+
event.stopPropagation();
|
|
5723
|
+
const sectionId = button.dataset.sectionNav;
|
|
5724
|
+
if (sectionId) this.controller.focusSection(sectionId);
|
|
5725
|
+
});
|
|
5726
|
+
});
|
|
5188
5727
|
}
|
|
5189
5728
|
/**
|
|
5190
5729
|
* Render the section card in the form the layout + state want: expanded card
|
|
@@ -5194,26 +5733,36 @@ var SeatPicker = class _SeatPicker {
|
|
|
5194
5733
|
renderSectionCard(summary) {
|
|
5195
5734
|
if (!this.els.map) return;
|
|
5196
5735
|
this.secCardEl?.remove();
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5736
|
+
if (this.root?.dataset.layout === "narrow" && (this.hold || this.committedSelection().length || this.pendingGACount())) {
|
|
5737
|
+
this.lastSection = null;
|
|
5738
|
+
return;
|
|
5739
|
+
}
|
|
5740
|
+
const paidRanges = summary.categories.length ? summary.categories.map((item) => {
|
|
5741
|
+
const category = this.controller.doc?.categories.find((candidate) => candidate.key === item.key);
|
|
5742
|
+
return category ? this.catPriceRange(category) : { min: item.priceMin, max: item.priceMax };
|
|
5743
|
+
}).filter((range) => range != null) : [{ min: summary.priceMin, max: summary.priceMax }];
|
|
5744
|
+
const paidMin = Math.min(...paidRanges.map((range) => range.min));
|
|
5745
|
+
const paidMax = Math.max(...paidRanges.map((range) => range.max));
|
|
5200
5746
|
const priceLabel = paidMin === paidMax ? this.money(paidMin) : `${this.money(paidMin)}\u2013${this.money(paidMax)}`;
|
|
5201
5747
|
const leftLabel = tCount("picker.seatsLeftInSection", summary.seatsLeft);
|
|
5202
5748
|
const xBtn = `<button type="button" class="sl-seccard-x" aria-label="${t2("picker.closeSectionSummary")}">\u2715</button>`;
|
|
5749
|
+
const sectionNav = this.sectionNavigationHtml(summary);
|
|
5203
5750
|
const card = document.createElement("div");
|
|
5204
5751
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
5205
5752
|
if (narrow) {
|
|
5206
5753
|
card.className = "sl-seccard strip on";
|
|
5207
5754
|
card.setAttribute("role", "status");
|
|
5208
5755
|
card.setAttribute("aria-label", t2("picker.sectionSummaryAria", { label: summary.label }));
|
|
5209
|
-
card.innerHTML = `<span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span><span class="sl-seccard-left">${leftLabel}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn;
|
|
5756
|
+
card.innerHTML = `<span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span><span class="sl-seccard-left">${leftLabel}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + sectionNav + xBtn;
|
|
5757
|
+
this.wireSectionNavigation(card);
|
|
5210
5758
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5211
5759
|
(this.els.sheetHead ?? this.els.side ?? this.els.map).appendChild(card);
|
|
5212
5760
|
} else if (this.secCardCollapsed) {
|
|
5213
5761
|
card.className = "sl-seccard mini on";
|
|
5214
5762
|
card.setAttribute("role", "button");
|
|
5215
5763
|
card.setAttribute("aria-label", t2("picker.sectionSummaryAria", { label: summary.label }));
|
|
5216
|
-
card.innerHTML = `<span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span><span class="sl-seccard-left">${leftLabel}</span>` + xBtn;
|
|
5764
|
+
card.innerHTML = `<span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span><span class="sl-seccard-left">${leftLabel}</span>` + sectionNav + xBtn;
|
|
5765
|
+
this.wireSectionNavigation(card);
|
|
5217
5766
|
card.addEventListener("click", (e) => {
|
|
5218
5767
|
if (e.target.closest(".sl-seccard-x")) return;
|
|
5219
5768
|
this.secCardCollapsed = false;
|
|
@@ -5228,9 +5777,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5228
5777
|
card.setAttribute("aria-label", t2("picker.sectionSummaryAria", { label: summary.label }));
|
|
5229
5778
|
const mix = summary.categories.map((c) => {
|
|
5230
5779
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
5231
|
-
|
|
5780
|
+
const category = this.controller.doc?.categories.find((candidate) => candidate.key === c.key);
|
|
5781
|
+
const range = category ? this.catPriceRange(category) : { min: c.priceMin, max: c.priceMax };
|
|
5782
|
+
const rangeLabel = range && range.min !== range.max ? `${this.money(range.min)}\u2013${this.money(range.max)}` : this.money(range?.min ?? c.price);
|
|
5783
|
+
return `<span class="sl-seccard-mix-item${dim ? " sl-dim" : ""}"><span class="sl-seccard-mix-dot" style="background:${c.color}"></span>${c.label} <span class="sl-seccard-mix-price">${rangeLabel}</span></span>`;
|
|
5232
5784
|
}).join("");
|
|
5233
|
-
card.innerHTML = `<div class="sl-seccard-head"><span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn + `</div><div class="sl-seccard-zone">${summary.zoneLabel ? `${summary.zoneLabel} \xB7 ` : ""}<span class="sl-seccard-left">${leftLabel}</span></div>` + (summary.entrance ? `<div class="sl-seccard-entrance">${t2("picker.entrance")} ${String(summary.entrance).replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch])}</div>` : "") + (mix ? `<div class="sl-seccard-mix">${mix}</div>` : "") + `<div class="sl-seccard-foot"><button type="button" class="sl-seccard-overview">\u2190 ${t2("picker.overview")}</button
|
|
5785
|
+
card.innerHTML = `<div class="sl-seccard-head"><span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn + `</div><div class="sl-seccard-zone">${summary.zoneLabel ? `${summary.zoneLabel} \xB7 ` : ""}<span class="sl-seccard-left">${leftLabel}</span></div>` + (summary.entrance ? `<div class="sl-seccard-entrance">${t2("picker.entrance")} ${String(summary.entrance).replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch])}</div>` : "") + (mix ? `<div class="sl-seccard-mix">${mix}</div>` : "") + `<div class="sl-seccard-foot"><button type="button" class="sl-seccard-overview">\u2190 ${t2("picker.overview")}</button>` + sectionNav + `<span class="sl-seccard-hint">${t2("picker.tapSeatHint")}</span></div>`;
|
|
5786
|
+
this.wireSectionNavigation(card);
|
|
5234
5787
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5235
5788
|
card.querySelector(".sl-seccard-overview").addEventListener("click", () => this.controller.overview());
|
|
5236
5789
|
(this.regions["top-center"] ?? this.els.map).appendChild(card);
|
|
@@ -5294,14 +5847,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
5294
5847
|
}
|
|
5295
5848
|
const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
|
|
5296
5849
|
const status = this.controller.getStatus(seat.id) ?? "free";
|
|
5297
|
-
const statusText = status === "free" ? "available" : status === "held" ? "on hold" : "taken";
|
|
5298
|
-
const
|
|
5850
|
+
const statusText = status === "free" ? this.tf("picker.statusAvailable", "available") : status === "held" ? this.tf("picker.statusOnHold", "on hold") : this.tf("picker.statusTaken2", "taken");
|
|
5851
|
+
const priceRange = cat ? this.catPriceRange(cat) : void 0;
|
|
5852
|
+
const priceText = priceRange ? priceRange.min === priceRange.max ? this.money(priceRange.min) : `${this.money(priceRange.min)}\u2013${this.money(priceRange.max)}` : void 0;
|
|
5299
5853
|
const table = this.controller.tableSelection(seat.id);
|
|
5300
5854
|
const details = table ?? this.controller.seatDetails(seat.id);
|
|
5301
5855
|
const typeWord = this.rowTypeWord(details);
|
|
5302
5856
|
const buyerName = details?.rowLabel ?? details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
5303
|
-
const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ?
|
|
5304
|
-
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${
|
|
5857
|
+
const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ? t2("picker.minToMaxGuests", { min: table.minOccupancy, max: table.maxOccupancy }) : t2("picker.capacityGuests", { count: table.capacity })}` : details?.objectType === "booth" ? `${typeWord} ${buyerName}` : details?.objectType === "table" ? `${typeWord} ${buyerName}, ${t2("picker.seatNumberLower", { label: details.seatNumber ?? seat.label })}` : t2("picker.seatLabel", { label: details?.displayLabel ?? seat.displayLabel ?? seat.label });
|
|
5858
|
+
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${priceText ? `, ${priceText}` : ""}, ${statusText}`;
|
|
5305
5859
|
}
|
|
5306
5860
|
// ---- atomic table confirmation / guest quantity ---------------------------
|
|
5307
5861
|
showTableDialog(table, held, returnFocus) {
|
|
@@ -5321,7 +5875,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5321
5875
|
const typeWord = this.rowTypeWord(table);
|
|
5322
5876
|
const el = document.createElement("div");
|
|
5323
5877
|
el.className = "sl-table-scrim";
|
|
5324
|
-
el.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc(variable ?
|
|
5878
|
+
el.innerHTML = `<section class="sl-table-dialog" role="dialog" aria-modal="true" aria-labelledby="sl-table-title" aria-describedby="sl-table-copy"><div class="sl-table-head"><div class="sl-table-eyebrow">${esc(variable ? t2("picker.flexiblePartyTypeWord", { typeWord }) : t2("picker.wholeTypeWord", { typeWord }))}</div><h2 class="sl-table-title" id="sl-table-title">${esc(table.displayLabel ?? table.label)}</h2><p class="sl-table-copy" id="sl-table-copy">${variable ? this.tf("picker.chooseGuestsCopy", "Choose how many guests will sit together. This table is held exclusively for your party.") : t2("picker.allPlacesBookedTogether", { count: table.capacity })}</p></div><div class="sl-table-body"><div class="sl-table-summary"><span>${esc(cat?.label ?? table.categoryKey)}</span><b data-table-unit>${t2("picker.perGuestPrice", { price: this.money(this.paidPrice(table.categoryKey, table.tierId ?? null, table.price)) })}</b><span class="muted">${this.tf("picker.tableCapacity", "Table capacity")}</span><span>${t2("picker.guestsCount", { count: table.capacity })}</span><span class="muted">${this.tf("picker.total", "Total")}</span><b data-table-total></b></div>` + (variable ? `<label class="sl-table-qtylabel" id="sl-table-qty-label">${this.tf("picker.numberOfGuests", "Number of guests")}</label><div class="sl-table-stepper" role="group" aria-labelledby="sl-table-qty-label"><button type="button" data-table-step="-1" aria-label="${this.tf("picker.fewerGuests", "Fewer guests")}">\u2212</button><output aria-live="polite" data-table-qty>${table.quantity}</output><button type="button" data-table-step="1" aria-label="${this.tf("picker.moreGuests", "More guests")}">+</button></div><div class="sl-table-range">${t2("picker.chooseMinMaxGuests", { min: table.minOccupancy, max: table.maxOccupancy })}</div>` : `<input type="hidden" data-table-qty value="${table.capacity}">`) + `<div class="sl-table-actions"><button type="button" class="sl-table-cancel">${t2("common.cancel")}</button><button type="button" class="sl-table-confirm">${held ? this.tf("picker.updateTable", "Update table") : variable ? this.tf("picker.selectTable", "Select table") : this.tf("picker.selectWholeTable", "Select whole table")}</button></div></div></section>`;
|
|
5325
5879
|
this.root.appendChild(el);
|
|
5326
5880
|
this.tableDialogEl = el;
|
|
5327
5881
|
this.renderTableDialogState();
|
|
@@ -5380,17 +5934,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
5380
5934
|
const button = this.tableDialogEl?.querySelector(".sl-table-confirm");
|
|
5381
5935
|
if (button) {
|
|
5382
5936
|
button.disabled = true;
|
|
5383
|
-
button.textContent = held ? "Updating\u2026" : "Selecting\u2026";
|
|
5937
|
+
button.textContent = held ? this.tf("picker.updatingEllipsis", "Updating\u2026") : this.tf("picker.selectingEllipsis", "Selecting\u2026");
|
|
5384
5938
|
}
|
|
5385
5939
|
if (held) {
|
|
5386
5940
|
try {
|
|
5387
5941
|
const updated = await this.controller.replaceTableQuantity(table.label, table.quantity, this.opts.holdTtlMs);
|
|
5388
5942
|
if (!updated) {
|
|
5389
|
-
this.toast("That guest count could not be secured. Your current table hold is unchanged.", "warning");
|
|
5943
|
+
this.toast(this.tf("picker.guestCountCouldNotBeSecured", "That guest count could not be secured. Your current table hold is unchanged."), "warning");
|
|
5390
5944
|
this.renderTableDialogState();
|
|
5391
5945
|
if (button) {
|
|
5392
5946
|
button.disabled = false;
|
|
5393
|
-
button.textContent = "Update table";
|
|
5947
|
+
button.textContent = this.tf("picker.updateTable", "Update table");
|
|
5394
5948
|
}
|
|
5395
5949
|
return;
|
|
5396
5950
|
}
|
|
@@ -5404,13 +5958,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5404
5958
|
this.dismissTableDialog();
|
|
5405
5959
|
this.syncTray();
|
|
5406
5960
|
this.emitHoldChange();
|
|
5407
|
-
this.toast(
|
|
5961
|
+
this.toast(t2("picker.tableUpdatedForGuests", { label: table.label, count: table.quantity }), "success");
|
|
5408
5962
|
} catch (error) {
|
|
5409
5963
|
this.opts.onError?.(error);
|
|
5410
|
-
this.toast("That guest count is no longer available. Your current hold is unchanged.", "error");
|
|
5964
|
+
this.toast(this.tf("picker.guestCountNoLongerAvailable", "That guest count is no longer available. Your current hold is unchanged."), "error");
|
|
5411
5965
|
if (button) {
|
|
5412
5966
|
button.disabled = false;
|
|
5413
|
-
button.textContent = "Update table";
|
|
5967
|
+
button.textContent = this.tf("picker.updateTable", "Update table");
|
|
5414
5968
|
}
|
|
5415
5969
|
}
|
|
5416
5970
|
return;
|
|
@@ -5418,7 +5972,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5418
5972
|
if (!this.controller.setTableQuantity(table.label, table.quantity)) {
|
|
5419
5973
|
if (button) {
|
|
5420
5974
|
button.disabled = false;
|
|
5421
|
-
button.textContent = table.bookingMode === "variable" ? "Select table" : "Select whole table";
|
|
5975
|
+
button.textContent = table.bookingMode === "variable" ? this.tf("picker.selectTable", "Select table") : this.tf("picker.selectWholeTable", "Select whole table");
|
|
5422
5976
|
}
|
|
5423
5977
|
return;
|
|
5424
5978
|
}
|
|
@@ -5464,16 +6018,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
5464
6018
|
'"': """
|
|
5465
6019
|
})[char]);
|
|
5466
6020
|
const identityFields = [
|
|
5467
|
-
details?.sectionLabel ? `<div class="sl-confirm-field"><span class="sl-confirm-key"
|
|
6021
|
+
details?.sectionLabel ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${this.tf("picker.section", "Section")}</span><span class="sl-confirm-value">${safe(details.sectionLabel)}</span></div>` : "",
|
|
5468
6022
|
details?.rowLabel || details?.objectType === "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${safe(this.rowTypeWord(details))}</span><span class="sl-confirm-value">${safe(this.rowShort(details) ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : "",
|
|
5469
|
-
details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key"
|
|
6023
|
+
details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-confirm-value">${safe(details?.seatNumber ?? details?.displayLabel ?? seat.displayLabel ?? seat.label)}</span></div>` : ""
|
|
5470
6024
|
].filter(Boolean).join("");
|
|
6025
|
+
const tiers = details?.tiers ?? cat?.tiers ?? [];
|
|
6026
|
+
const selectedTierId = details?.tierId ?? tiers[0]?.id;
|
|
6027
|
+
const tierGuidance = (tier) => tier.buyerMessage?.trim() || (tier.restriction === "companion" ? this.tf("picker.companionRequiresWheelchair", "Requires the adjacent wheelchair place") : void 0);
|
|
6028
|
+
const tierChoices = tiers.length > 1 ? `<fieldset class="sl-confirm-tiers"><legend>${this.tf("picker.ticketType", "Ticket type")}</legend>` + tiers.map((tier) => {
|
|
6029
|
+
const guidance = tierGuidance(tier);
|
|
6030
|
+
return `<button type="button" class="sl-confirm-tier" data-confirm-tier="${safe(tier.id)}" aria-pressed="${tier.id === selectedTierId}"><span class="sl-confirm-tier-copy"><span class="sl-confirm-tier-name">${safe(tier.name)}</span>` + (guidance ? `<small class="sl-confirm-tier-note">${safe(guidance)}</small>` : "") + `</span><span class="sl-confirm-tier-price">${this.money(this.paidPrice(seat.categoryKey, tier.id, tier.price))}</span></button>`;
|
|
6031
|
+
}).join("") + `</fieldset>` : tiers[0] && tierGuidance(tiers[0]) ? `<p class="sl-confirm-tier-note">${safe(tierGuidance(tiers[0]))}</p>` : "";
|
|
5471
6032
|
const el = document.createElement("div");
|
|
5472
6033
|
el.className = "sl-confirm";
|
|
5473
6034
|
el.setAttribute("role", "dialog");
|
|
5474
|
-
el.setAttribute("aria-label",
|
|
5475
|
-
|
|
5476
|
-
|
|
6035
|
+
el.setAttribute("aria-label", t2("picker.confirmSeatLabel", { label: seat.label }));
|
|
6036
|
+
const categoryColor = cat?.color ?? "#6e7bff";
|
|
6037
|
+
const categoryBand = mixThemeColors(categoryColor, this.cssVar("--sl-surface") || "#1a2234", 0.76) ?? categoryColor;
|
|
6038
|
+
el.style.setProperty("--sl-cat", categoryColor);
|
|
6039
|
+
el.style.setProperty("--sl-cat-ink", highestContrastInk(categoryBand, "#172033", "#ffffff"));
|
|
6040
|
+
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">` + tierChoices + 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">${t2("common.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>${this.tf("picker.select", "Select")}</button></div></div>`;
|
|
5477
6041
|
this.els.map.appendChild(el);
|
|
5478
6042
|
this.confirmEl = el;
|
|
5479
6043
|
const thumb = el.querySelector(".sl-confirm-thumb");
|
|
@@ -5489,6 +6053,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
5489
6053
|
this.openSeatConfidencePassport(seat, event.currentTarget instanceof HTMLElement ? event.currentTarget : null);
|
|
5490
6054
|
});
|
|
5491
6055
|
el.querySelector(".sl-confirm-compare")?.addEventListener("click", () => this.saveView3dComparisonSeat(seat));
|
|
6056
|
+
el.querySelectorAll("[data-confirm-tier]").forEach((button) => {
|
|
6057
|
+
button.addEventListener("click", () => {
|
|
6058
|
+
const tierId = button.dataset.confirmTier;
|
|
6059
|
+
const tier = tiers.find((candidate) => candidate.id === tierId);
|
|
6060
|
+
if (!tier) return;
|
|
6061
|
+
this.controller.setSeatTier(seat.id, tier.id);
|
|
6062
|
+
el.querySelectorAll("[data-confirm-tier]").forEach((candidate) => {
|
|
6063
|
+
candidate.setAttribute("aria-pressed", String(candidate === button));
|
|
6064
|
+
});
|
|
6065
|
+
const priceElement = el.querySelector(".sl-confirm-price");
|
|
6066
|
+
if (priceElement) {
|
|
6067
|
+
priceElement.textContent = this.money(this.paidPrice(seat.categoryKey, tier.id, tier.price));
|
|
6068
|
+
}
|
|
6069
|
+
this.reanchorConfirm();
|
|
6070
|
+
});
|
|
6071
|
+
});
|
|
5492
6072
|
el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
5493
6073
|
if (this.buyerView === "venue3d") {
|
|
5494
6074
|
this.dismissConfirm();
|
|
@@ -5626,7 +6206,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5626
6206
|
el.className = "sl-view";
|
|
5627
6207
|
el.setAttribute("role", "dialog");
|
|
5628
6208
|
el.setAttribute("aria-label", t2("picker.viewFromSeat", { label: seat.label }));
|
|
5629
|
-
el.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${t2("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="Close"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? t2("picker.real360") : t2("picker.preview")}</span><span class="sl-view-hint"
|
|
6209
|
+
el.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${t2("picker.viewFromSeat", { label: seat.label })}</span><span class="sl-view-cap">${caption}</span><button type="button" class="sl-view-x" aria-label="${this.tf("picker.close", "Close")}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div><div class="sl-view-pano"><span class="sl-view-badge">${real ? t2("picker.real360") : t2("picker.preview")}</span><span class="sl-view-hint">${this.tf("picker.dragToLookAround", "Drag to look around \xB7 scroll to zoom")}</span></div>`;
|
|
5630
6210
|
this.root.appendChild(el);
|
|
5631
6211
|
this.viewEl = el;
|
|
5632
6212
|
const pano = el.querySelector(".sl-view-pano");
|
|
@@ -5822,27 +6402,27 @@ var SeatPicker = class _SeatPicker {
|
|
|
5822
6402
|
copy.className = "sl-offer-copy";
|
|
5823
6403
|
const kicker = document.createElement("span");
|
|
5824
6404
|
kicker.className = "sl-offer-kicker";
|
|
5825
|
-
kicker.textContent = active ? "Current ticket offer" : "Upcoming ticket offer";
|
|
6405
|
+
kicker.textContent = active ? this.tf("picker.currentTicketOffer", "Current ticket offer") : this.tf("picker.upcomingTicketOffer", "Upcoming ticket offer");
|
|
5826
6406
|
const name = document.createElement("strong");
|
|
5827
6407
|
name.className = "sl-offer-name";
|
|
5828
|
-
name.textContent = offer.name || (active ? "Current offer" : "Scheduled offer");
|
|
6408
|
+
name.textContent = offer.name || (active ? this.tf("picker.currentOffer", "Current offer") : this.tf("picker.scheduledOffer", "Scheduled offer"));
|
|
5829
6409
|
const line = document.createElement("span");
|
|
5830
6410
|
line.className = "sl-offer-line";
|
|
5831
6411
|
const facts = [];
|
|
5832
6412
|
if (active && availability.fromPrice != null) facts.push(this.money(availability.fromPrice / 100));
|
|
5833
|
-
if (active && offer.remaining != null) facts.push(
|
|
5834
|
-
if (active && offer.endsAt != null) facts.push(
|
|
5835
|
-
if (upcoming?.startsAt != null) facts.push(
|
|
6413
|
+
if (active && offer.remaining != null) facts.push(t2("picker.offerRemainingAvailable", { count: offer.remaining }));
|
|
6414
|
+
if (active && offer.endsAt != null) facts.push(t2("picker.offerUntil", { time: formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale) }));
|
|
6415
|
+
if (upcoming?.startsAt != null) facts.push(t2("picker.offerStarts", { time: formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale) }));
|
|
5836
6416
|
line.textContent = facts.join(" \xB7 ");
|
|
5837
6417
|
copy.append(kicker, name, line);
|
|
5838
6418
|
const info = document.createElement("details");
|
|
5839
6419
|
info.className = "sl-offer-info";
|
|
5840
6420
|
const summary = document.createElement("summary");
|
|
5841
|
-
summary.setAttribute("aria-label",
|
|
6421
|
+
summary.setAttribute("aria-label", t2("picker.howOfferWorks", { name: name.textContent ?? "" }));
|
|
5842
6422
|
summary.textContent = "i";
|
|
5843
6423
|
const detail = document.createElement("div");
|
|
5844
6424
|
detail.className = "sl-offer-detail";
|
|
5845
|
-
detail.textContent = active ?
|
|
6425
|
+
detail.textContent = active ? this.tf("picker.offerDetailActive", "This price applies automatically to eligible seats. Tickets in active carts temporarily reduce the available quantity; released or expired holds return it. When the offer ends, the next matching offer or normal ticket price takes over.") : this.tf("picker.offerDetailUpcoming", "Tickets are available at their normal price now. This scheduled offer will apply automatically to eligible seats when it starts.");
|
|
5846
6426
|
info.append(summary, detail);
|
|
5847
6427
|
main.append(copy, info);
|
|
5848
6428
|
host.replaceChildren(main);
|
|
@@ -5865,21 +6445,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
5865
6445
|
const doc = this.controller.doc;
|
|
5866
6446
|
if (!doc || !this.els.prices) return;
|
|
5867
6447
|
const left = this.controller.categoryAvailability();
|
|
5868
|
-
|
|
5869
|
-
this.
|
|
6448
|
+
const sellable = doc.categories.filter((c) => !c.notForSale);
|
|
6449
|
+
this.narrateAvailability(sellable, left);
|
|
6450
|
+
this.syncSoldout(sellable, left);
|
|
5870
6451
|
const PRICE_LIMIT = 5;
|
|
5871
|
-
const overflow =
|
|
6452
|
+
const overflow = sellable.length - PRICE_LIMIT;
|
|
5872
6453
|
const collapsed = overflow > 1 && !this.pricesExpanded;
|
|
5873
|
-
const shown = collapsed ?
|
|
6454
|
+
const shown = collapsed ? sellable.slice(0, PRICE_LIMIT) : sellable;
|
|
5874
6455
|
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
5875
6456
|
this.els.prices.innerHTML = shown.map((c) => {
|
|
5876
6457
|
const price = this.catPrice(c);
|
|
6458
|
+
const range = this.catPriceRange(c);
|
|
5877
6459
|
const offer = this.offerPrice(c.key);
|
|
5878
6460
|
const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
|
|
5879
6461
|
const active = this.focusedCatKey === c.key;
|
|
5880
6462
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
5881
|
-
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? "Show all seats" :
|
|
5882
|
-
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ?
|
|
6463
|
+
return `<div class="sl-price-row${dim ? " sl-dim" : ""}${active ? " sl-active" : ""}" data-cat="${escapeOption(c.key)}" role="button" tabindex="0" aria-pressed="${active}" title="${escapeOption(active ? this.tf("picker.showAllSeats", "Show all seats") : t2("picker.showLabelSeatsOnMap", { label: c.label }))}"><span class="sl-dot" style="background:${escapeOption(c.color)}"></span><span class="sl-price-label">${escapeOption(c.label)}` + (offer?.offerName ? `<small class="sl-price-offer">${escapeOption(offer.offerName)}</small>` : "") + `</span><span class="sl-price-left">${tCount("picker.leftCount", left[c.key] ?? 0)}</span>` + (previous != null ? `<span class="sl-price-was">${escapeOption(this.money(previous))}</span>` : "") + (range != null ? `<span class="sl-price-amt">${range.min === range.max ? this.money(range.min) : `${this.money(range.min)}\u2013${this.money(range.max)}`}</span>` : price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
6464
|
+
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? t2("picker.showAllTicketTypes", { count: sellable.length }) : this.tf("picker.showFewer", "Show fewer")) + `</button>` : "") + `<div class="sl-status-key" aria-label="${this.tf("picker.seatStatusLegend", "Seat status legend")}"><span class="sl-status-item"><i class="sl-status-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></i>${this.tf("picker.temporarilyHeld", "Temporarily held")}</span><span class="sl-status-item"><i class="sl-status-icon sold" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M7 17L17 7"/></svg></i>${this.tf("picker.sold", "Sold")}</span></div>`;
|
|
5883
6465
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
5884
6466
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
5885
6467
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
@@ -5937,7 +6519,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5937
6519
|
const now = left[cat.key] ?? 0;
|
|
5938
6520
|
if (before === void 0 || now >= before) continue;
|
|
5939
6521
|
const taken = before - now;
|
|
5940
|
-
textEl.textContent =
|
|
6522
|
+
textEl.textContent = tCount("picker.seatsJustTakenInCategory", taken, { label: cat.label, left: now });
|
|
5941
6523
|
this.els.live?.classList.remove("on");
|
|
5942
6524
|
void this.els.live?.offsetWidth;
|
|
5943
6525
|
this.els.live?.classList.add("on");
|
|
@@ -5955,12 +6537,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
5955
6537
|
const gone = this.controller.getSelection().filter((s) => !ownLabels.has(s.label) && (this.controller.getStatus(s.id) ?? "free") !== "free");
|
|
5956
6538
|
if (!gone.length) return;
|
|
5957
6539
|
this.controller.deselect(gone.map((s) => s.id));
|
|
5958
|
-
this.toast(
|
|
6540
|
+
this.toast(t2("picker.seatJustTakenByAnother", { label: gone[0].label }), "error");
|
|
5959
6541
|
}
|
|
5960
6542
|
syncTray() {
|
|
5961
6543
|
if (!this.els.tray) return;
|
|
5962
6544
|
this.updateSelectionCapacity();
|
|
5963
6545
|
const seats = this.committedSelection();
|
|
6546
|
+
if (this.root?.dataset.layout === "narrow" && this.secCardEl && (this.hold || seats.length || this.pendingGACount())) {
|
|
6547
|
+
this.secCardEl.remove();
|
|
6548
|
+
this.secCardEl = null;
|
|
6549
|
+
this.lastSection = null;
|
|
6550
|
+
}
|
|
5964
6551
|
const gaAreas = this.controller.getGAAreas();
|
|
5965
6552
|
const heldItems = this.hold?.items ?? [];
|
|
5966
6553
|
const parts = [];
|
|
@@ -5971,18 +6558,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
5971
6558
|
`<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>`
|
|
5972
6559
|
);
|
|
5973
6560
|
} else if (!seats.length && !heldItems.length && !gaAreas.length) {
|
|
5974
|
-
parts.push(`<div class="sl-tray-hint"
|
|
6561
|
+
parts.push(`<div class="sl-tray-hint">${this.tf("picker.trayHintTapOrBest", "Tap a seat on the map, or let us pick the best available for you.")}</div>`);
|
|
5975
6562
|
} else if (!seats.length && !heldItems.length) {
|
|
5976
|
-
parts.push(`<div class="sl-tray-hint"
|
|
6563
|
+
parts.push(`<div class="sl-tray-hint">${this.tf("picker.trayHintTapOrStanding", "Tap a seat on the map \u2014 or grab standing tickets below.")}</div>`);
|
|
5977
6564
|
}
|
|
5978
6565
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
5979
|
-
if (
|
|
5980
|
-
|
|
6566
|
+
if (noPicks) this.baReopen = false;
|
|
6567
|
+
if (!this.salesClosed && !this.hold && (noPicks || this.baReopen || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
6568
|
+
const cats = (this.controller.doc?.categories ?? []).filter((c) => !c.notForSale);
|
|
5981
6569
|
const zones = this.controller.getBestAvailableZones();
|
|
5982
6570
|
if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
|
|
5983
|
-
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span
|
|
6571
|
+
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>${this.tf("picker.replaceCurrentChoices", "Replace your current choices?")}</div><div class="sl-ba-replace"><b>${t2("picker.willFindSeatsTogether", { count: this.baQty })}</b><span>${this.tf("picker.manualTicketsRemovedNote", "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>${this.tf("picker.keepMine", "Keep mine")}</button><button type="button" class="replace" data-ba-replace>${this.tf("picker.findNewSeats", "Find new seats")}</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>${this.tf("picker.findBestSeatsTogether", "Find the best seats together")}</div><div class="sl-ba-copy"><span class="wide">${this.tf("picker.willChooseClosestGroup", "We\u2019ll choose the closest available group for you.")}</span><span class="narrow">${this.tf("picker.closestGroupChosenInstantly", "Closest available group, chosen instantly.")}</span></div>` + // Premium quick-pick — present only when the chart actually has premium
|
|
5984
6572
|
// seats (same present-only philosophy as the a11y filter chips).
|
|
5985
|
-
(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=""
|
|
6573
|
+
(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="${this.tf("picker.preferredTicketType", "Preferred ticket type")}" data-ba-cat><option value="">${this.tf("picker.anyTicketType", "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="${this.tf("picker.preferredVenueZone", "Preferred venue zone")}" data-ba-zone><option value="">${this.tf("picker.anyVenueZone", "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="${this.tf("picker.fewerSeats", "Fewer seats")}">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="${this.tf("picker.moreSeats", "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>${this.tf("picker.findingBestSeats", "Finding the best seats\u2026")}` : tCount("picker.findBestSeatsCount", this.baQty)) + `</button>` + // Reopened over an existing selection: the buyer needs the way back
|
|
6574
|
+
// stated as plainly as the way forward (Go routes through the
|
|
6575
|
+
// "Replace your current choices?" confirm, so nothing is lost yet).
|
|
6576
|
+
(this.baReopen ? `<button type="button" class="sl-ba-back" data-ba-close>${this.tf("picker.keepMyPicks", "Keep my picks")}</button>` : "") + `</div>`);
|
|
5986
6577
|
}
|
|
5987
6578
|
const idGrid = (seatId, label, objectType, quantity = 1, objectId, identity) => {
|
|
5988
6579
|
const esc = (value) => String(value ?? "\u2014").replace(/[&<>"]/g, (char) => ({
|
|
@@ -5994,21 +6585,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
5994
6585
|
const d = seatId ? this.controller.seatDetails(seatId) : null;
|
|
5995
6586
|
const area = objectType === "ga" ? gaAreas.find((candidate) => candidate.id === objectId) : void 0;
|
|
5996
6587
|
const effectiveType = objectType === "ga" ? "ga" : d?.objectType ?? objectType;
|
|
5997
|
-
const typeWord = identity?.displayType?.trim() || d?.displayType?.trim() || area?.displayType?.trim() || (effectiveType === "table" ? "Table" : effectiveType === "booth" ? "Booth" : effectiveType === "ga" ? "General admission" : "Row");
|
|
6588
|
+
const typeWord = identity?.displayType?.trim() || d?.displayType?.trim() || area?.displayType?.trim() || (effectiveType === "table" ? this.tf("picker.table", "Table") : effectiveType === "booth" ? this.tf("picker.booth", "Booth") : effectiveType === "ga" ? this.tf("picker.generalAdmission", "General admission") : this.tf("picker.row", "Row"));
|
|
5998
6589
|
const buyerName = identity?.rowLabel ?? identity?.displayLabel ?? d?.rowLabel ?? d?.displayLabel ?? area?.displayLabel ?? area?.label ?? label;
|
|
5999
6590
|
if (effectiveType === "table" && identity?.bookingMode) {
|
|
6000
|
-
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span><span class="fld mid"><span class="sl-chip-eb"
|
|
6591
|
+
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span><span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.guests", "Guests")}</span><span class="val">${quantity}</span></span></div>`;
|
|
6001
6592
|
}
|
|
6002
6593
|
if (effectiveType === "ga") {
|
|
6003
|
-
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span>` + (quantity > 1 ? `<span class="fld mid"><span class="sl-chip-eb"
|
|
6594
|
+
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span>` + (quantity > 1 ? `<span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.tickets", "Tickets")}</span><span class="val">${quantity}</span></span>` : "") + `</div>`;
|
|
6004
6595
|
}
|
|
6005
6596
|
if (effectiveType === "booth") {
|
|
6006
|
-
return `<div class="sl-chip-id">` + (d?.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb"
|
|
6597
|
+
return `<div class="sl-chip-id">` + (d?.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.section", "Section")}</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(buyerName)}</span></span></div>`;
|
|
6007
6598
|
}
|
|
6008
6599
|
if (!d?.sectionLabel && !d?.rowLabel && !d?.seatNumber) {
|
|
6009
|
-
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb"
|
|
6600
|
+
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.seat", "Seat")}</span><span class="val">${esc(buyerName)}</span></span></div>`;
|
|
6010
6601
|
}
|
|
6011
|
-
return `<div class="sl-chip-id">` + (d.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb"
|
|
6602
|
+
return `<div class="sl-chip-id">` + (d.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb">${this.tf("picker.section", "Section")}</span><span class="val">${esc(d.sectionLabel)}</span></span>` : "") + (d.rowLabel ? `<span class="fld mid"><span class="sl-chip-eb">${esc(typeWord)}</span><span class="val">${esc(this.rowShort(d))}</span></span>` : "") + (d.seatNumber ? `<span class="fld mid"><span class="sl-chip-eb">${this.tf("picker.seat", "Seat")}</span><span class="val">${esc(d.seatNumber)}</span></span>` : "") + `</div>`;
|
|
6012
6603
|
};
|
|
6013
6604
|
const iconRail = (rmAria, viewLabel) => `<div class="sl-chip-rail"><button type="button" class="rm" aria-label="${rmAria}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>` + (viewLabel ? `<button type="button" class="view" data-view-label="${viewLabel}" aria-label="${t2("picker.viewFromSeat", { label: viewLabel })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "") + `</div>`;
|
|
6014
6605
|
for (const item of heldItems) {
|
|
@@ -6020,7 +6611,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6020
6611
|
const table = item.objectType === "table" ? this.controller.tableSelection(item.label) : null;
|
|
6021
6612
|
const canView2 = this.seatViewEnabled() && !!heldSeat && item.objectType !== "table";
|
|
6022
6613
|
parts.push(
|
|
6023
|
-
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label, item.objectType, item.quantity ?? 1, item.objectId, table ?? void 0) + `<div class="sl-chip-sub"><span class="sl-ticket-state held" aria-label="Held for you" title="Held for you"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>` + (table?.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(item.label)}">${item.quantity ?? 1}
|
|
6614
|
+
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"${heldSeat ? ` data-locate="${heldSeat.id}"` : ""}><div class="sl-chip-main">` + idGrid(heldSeat?.id ?? null, item.label, item.objectType, item.quantity ?? 1, item.objectId, table ?? void 0) + `<div class="sl-chip-sub"><span class="sl-ticket-state held" aria-label="${this.tf("picker.heldForYou", "Held for you")}" title="${this.tf("picker.heldForYou", "Held for you")}"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>` + (table?.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(item.label)}">${t2("picker.guestsCountEdit", { count: item.quantity ?? 1 })}</button>` : "") + this.wheelchairChipMarker(heldSeat?.wheelchairSpaceType) + this.commercialChipMarker(heldSeat?.commercial) + `<span class="amt">${this.money(this.paidPrice(item.categoryKey, item.tierId, item.unitPrice) * (item.quantity ?? 1))}</span></div></div>` + iconRail(t2("picker.removeHeldTicketLabel", { label: item.label }), canView2 ? item.label : null) + `</div>`
|
|
6024
6615
|
);
|
|
6025
6616
|
}
|
|
6026
6617
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -6030,14 +6621,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
6030
6621
|
nextTrayKeys.add(itemKey);
|
|
6031
6622
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
6032
6623
|
const tierSelect = s.tiers && s.tiers.length ? `<select class="tier" data-tier="${s.id}" aria-label="${t2("picker.ticketTierFor", { label: s.label })}">` + s.tiers.map((ti) => `<option value="${ti.id}"${ti.id === s.tierId ? " selected" : ""}>${ti.name} \xB7 ${this.money(this.paidPrice(s.categoryKey, ti.id, ti.price))}</option>`).join("") + `</select>` : "";
|
|
6624
|
+
const chosenTier = s.tiers?.find((tier) => tier.id === s.tierId) ?? s.tiers?.[0];
|
|
6625
|
+
const tierNote = chosenTier?.buyerMessage?.trim() || (chosenTier?.restriction === "companion" ? this.tf("picker.companionRequiresWheelchair", "Requires the adjacent wheelchair place") : "");
|
|
6033
6626
|
parts.push(
|
|
6034
|
-
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.label, s.objectType, s.quantity ?? 1, s.objectId, s) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>` + (s.objectType === "table" && s.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(s.label)}">${s.quantity ?? 1}
|
|
6627
|
+
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}" data-locate="${s.id}"><div class="sl-chip-main">` + idGrid(s.id, s.label, s.objectType, s.quantity ?? 1, s.objectId, s) + `<div class="sl-chip-sub"><span class="sl-ticket-state" aria-label="${this.tf("picker.selected", "Selected")}" title="${this.tf("picker.selected", "Selected")}"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><span class="cat">${cat?.label ?? s.categoryKey}</span>` + (s.objectType === "table" && s.bookingMode === "variable" ? `<button type="button" class="sl-table-edit" data-table-edit="${encodeURIComponent(s.label)}">${t2("picker.guestsCountEdit", { count: s.quantity ?? 1 })}</button>` : "") + `${this.wheelchairChipMarker(s.wheelchairSpaceType)}${this.commercialChipMarker(s.commercial)}${tierSelect}<span class="amt">${this.money(this.paidPrice(s.categoryKey, s.tierId ?? null, s.price) * (s.quantity ?? 1))}</span></div>` + (tierNote ? `<small class="sl-tier-note">${escapeOption(tierNote)}</small>` : "") + `</div>` + iconRail(t2("picker.removeSeatLabel", { label: s.label }), canView && s.objectType !== "table" ? s.label : null) + `</div>`
|
|
6035
6628
|
);
|
|
6036
6629
|
}
|
|
6037
6630
|
for (const area of gaAreas) {
|
|
6038
6631
|
const qty = this.gaQty.get(area.id) ?? 0;
|
|
6039
6632
|
parts.push(
|
|
6040
|
-
`<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.displayLabel ?? area.label}</div><div class="sl-ga-sub">${area.displayType ?? "General admission"} \xB7 ${this.money(this.paidPrice(area.categoryKey, null, area.price))} \xB7 ${area.available}
|
|
6633
|
+
`<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.displayLabel ?? area.label}</div><div class="sl-ga-sub">${area.displayType ?? this.tf("picker.generalAdmission", "General admission")} \xB7 ${this.money(this.paidPrice(area.categoryKey, null, area.price))} \xB7 ${tCount("picker.leftCount", area.available)}</div></div><div class="sl-ga-qty"><button type="button" data-d="-1" aria-label="${this.tf("picker.fewer", "Fewer")}">\u2212</button><span>${qty}</span><button type="button" data-d="1" aria-label="${this.tf("picker.more", "More")}">+</button></div></div>`
|
|
6634
|
+
);
|
|
6635
|
+
}
|
|
6636
|
+
if (!this.salesClosed && !this.hold && !noPicks && !this.baReopen && !this.bestAvailableBusy && !this.bestAvailableConfirm) {
|
|
6637
|
+
parts.push(
|
|
6638
|
+
`<button type="button" class="sl-ba-reopen" data-ba-reopen><span aria-hidden="true">\u2726</span>${this.tf("picker.findTogetherInstead", "Find seats together instead")}</button>`
|
|
6041
6639
|
);
|
|
6042
6640
|
}
|
|
6043
6641
|
this.els.tray.innerHTML = parts.join("");
|
|
@@ -6072,6 +6670,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
6072
6670
|
this.syncTray();
|
|
6073
6671
|
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
6074
6672
|
});
|
|
6673
|
+
this.els.tray.querySelector("[data-ba-reopen]")?.addEventListener("click", () => {
|
|
6674
|
+
this.baReopen = true;
|
|
6675
|
+
this.syncTray();
|
|
6676
|
+
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
6677
|
+
});
|
|
6678
|
+
this.els.tray.querySelector("[data-ba-close]")?.addEventListener("click", () => {
|
|
6679
|
+
this.baReopen = false;
|
|
6680
|
+
this.syncTray();
|
|
6681
|
+
this.els.tray.querySelector("[data-ba-reopen]")?.focus();
|
|
6682
|
+
});
|
|
6075
6683
|
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
6076
6684
|
this.bestAvailableConfirm = false;
|
|
6077
6685
|
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium, zoneId: this.baZone || void 0 });
|
|
@@ -6084,15 +6692,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
6084
6692
|
return;
|
|
6085
6693
|
}
|
|
6086
6694
|
const id = chip.dataset.seat;
|
|
6087
|
-
const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? "Seat";
|
|
6695
|
+
const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? this.tf("picker.seat", "Seat");
|
|
6088
6696
|
const remove = () => {
|
|
6089
6697
|
this.controller.deselect([id]);
|
|
6090
|
-
this.toast(
|
|
6091
|
-
label: "Undo",
|
|
6698
|
+
this.toast(t2("picker.labelRemoved", { label }), "neutral", {
|
|
6699
|
+
label: this.tf("picker.undo", "Undo"),
|
|
6092
6700
|
onClick: () => {
|
|
6093
6701
|
const restored = this.controller.select([id]);
|
|
6094
6702
|
this.toast(
|
|
6095
|
-
restored.length ?
|
|
6703
|
+
restored.length ? t2("picker.labelRestored", { label }) : t2("picker.labelNoLongerAvailable", { label }),
|
|
6096
6704
|
restored.length ? "success" : "warning"
|
|
6097
6705
|
);
|
|
6098
6706
|
}
|
|
@@ -6164,31 +6772,31 @@ var SeatPicker = class _SeatPicker {
|
|
|
6164
6772
|
const pendingCount = this.pendingSelectionCount();
|
|
6165
6773
|
const previousCount = this.lastTrayCount;
|
|
6166
6774
|
const previousTotal = this.lastTrayTotal;
|
|
6167
|
-
this.els.count.textContent = count ?
|
|
6775
|
+
this.els.count.textContent = count ? tCount("picker.ticketsCount", count) : this.tf("picker.noSeatsSelected", "No seats selected");
|
|
6168
6776
|
this.els.total.textContent = count ? this.money(total) : "";
|
|
6169
6777
|
this.root?.setAttribute("data-has-selection", String(count > 0));
|
|
6170
6778
|
this.root?.setAttribute(
|
|
6171
6779
|
"data-ba-active",
|
|
6172
|
-
String(this.bestAvailableConfirm || this.bestAvailableBusy)
|
|
6780
|
+
String(this.bestAvailableConfirm || this.bestAvailableBusy || this.baReopen)
|
|
6173
6781
|
);
|
|
6174
6782
|
this.els.foot?.classList.toggle("empty", count === 0);
|
|
6175
6783
|
if (this.els.seatSummary) {
|
|
6176
|
-
this.els.seatSummary.textContent = count ?
|
|
6784
|
+
this.els.seatSummary.textContent = count ? tCount("picker.seatsSelectedCount", count) : "";
|
|
6177
6785
|
}
|
|
6178
6786
|
this.syncCta(count, pendingCount);
|
|
6179
6787
|
if (this.hold) {
|
|
6180
6788
|
const securedCount = heldCount || this.hold.seats?.length || 0;
|
|
6181
6789
|
if (this.els.holdTitle) {
|
|
6182
|
-
this.els.holdTitle.textContent =
|
|
6790
|
+
this.els.holdTitle.textContent = t2("picker.securedCount", { count: securedCount });
|
|
6183
6791
|
}
|
|
6184
6792
|
if (this.els.holdCopy) {
|
|
6185
6793
|
this.holdCopyPending = pendingCount > 0;
|
|
6186
|
-
this.els.holdCopy.textContent = pendingCount ?
|
|
6794
|
+
this.els.holdCopy.textContent = pendingCount ? t2("picker.moreSelectedCount", { count: pendingCount }) : this.holdReassuranceText();
|
|
6187
6795
|
}
|
|
6188
6796
|
const change = this.els.holdChange;
|
|
6189
6797
|
if (change) {
|
|
6190
6798
|
change.disabled = this.releasingHold;
|
|
6191
|
-
change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
|
|
6799
|
+
change.textContent = this.releasingHold ? this.tf("picker.releasingEllipsis", "Releasing\u2026") : this.tf("picker.change", "Change");
|
|
6192
6800
|
}
|
|
6193
6801
|
}
|
|
6194
6802
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
@@ -6213,21 +6821,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
6213
6821
|
renderPeek(count, total, pendingCount) {
|
|
6214
6822
|
if (!this.els.peek) return;
|
|
6215
6823
|
if (count && this.ctaPhase === "holding") {
|
|
6216
|
-
this.els.peek.innerHTML = `<span
|
|
6824
|
+
this.els.peek.innerHTML = `<span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}</span>`;
|
|
6217
6825
|
return;
|
|
6218
6826
|
}
|
|
6219
6827
|
if (count && this.ctaPhase === "checkout") {
|
|
6220
|
-
this.els.peek.innerHTML = `<span
|
|
6828
|
+
this.els.peek.innerHTML = `<span>${t2("picker.peekSecured", { count, total: this.money(total) })}</span>`;
|
|
6221
6829
|
return;
|
|
6222
6830
|
}
|
|
6223
6831
|
if (count) {
|
|
6224
|
-
const
|
|
6225
|
-
this.els.peek.innerHTML = `<span>${count
|
|
6832
|
+
const toCheckout = !!this.hold;
|
|
6833
|
+
this.els.peek.innerHTML = `<span>${tCount("picker.peekTicketsTotal", count, { total: this.money(total) })}</span><button type="button" class="go sl-sheet-go" data-act="${toCheckout ? "checkout" : "open"}">${this.hold ? pendingCount ? this.tf("picker.secureMore", "Secure more") : this.tf("picker.continue", "Continue") : this.tf("picker.review", "Review")}</button>`;
|
|
6226
6834
|
} else if (this.salesClosed) {
|
|
6227
6835
|
this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
|
|
6228
6836
|
} else {
|
|
6229
6837
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
6230
|
-
this.els.peek.innerHTML = (prices.length ? `<span
|
|
6838
|
+
this.els.peek.innerHTML = (prices.length ? `<span>${t2("picker.peekFromPrice", { price: this.money(Math.min(...prices)) })}</span>` : `<span>${this.tf("picker.pickYourSeats", "Pick your seats")}</span>`) + `<button type="button" class="go sl-sheet-go" data-act="open">${this.tf("picker.bestSeatsStar", "\u2726 Best seats")}</button>`;
|
|
6231
6839
|
}
|
|
6232
6840
|
}
|
|
6233
6841
|
async removeHeldLabel(label, chip) {
|
|
@@ -6240,7 +6848,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6240
6848
|
const preserveAcrossNavigation = this.handedOff;
|
|
6241
6849
|
const released = await this.controller.releaseLabels([label]);
|
|
6242
6850
|
if (!released) {
|
|
6243
|
-
this.toast(
|
|
6851
|
+
this.toast(t2("picker.couldNotRemoveLabel", { label }), "error");
|
|
6244
6852
|
return false;
|
|
6245
6853
|
}
|
|
6246
6854
|
const remaining = this.controller.currentHold();
|
|
@@ -6256,7 +6864,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6256
6864
|
}
|
|
6257
6865
|
this.syncTray();
|
|
6258
6866
|
this.emitHoldChange();
|
|
6259
|
-
this.toast(
|
|
6867
|
+
this.toast(t2("picker.labelRemovedFromHold", { label }), "success");
|
|
6260
6868
|
return true;
|
|
6261
6869
|
} finally {
|
|
6262
6870
|
this.releasingLabels.delete(label);
|
|
@@ -6270,23 +6878,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
6270
6878
|
const button = this.els.holdChange;
|
|
6271
6879
|
if (button) {
|
|
6272
6880
|
button.disabled = true;
|
|
6273
|
-
button.textContent = "Releasing\u2026";
|
|
6881
|
+
button.textContent = this.tf("picker.releasingEllipsis", "Releasing\u2026");
|
|
6274
6882
|
}
|
|
6275
6883
|
try {
|
|
6276
6884
|
await this.release();
|
|
6277
|
-
if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
|
|
6885
|
+
if (!this.hold) this.toast(this.tf("picker.heldTicketsReleased", "Held tickets released. Choose your new seats."), "success");
|
|
6278
6886
|
} finally {
|
|
6279
6887
|
this.releasingHold = false;
|
|
6280
6888
|
if (button?.isConnected) {
|
|
6281
6889
|
button.disabled = false;
|
|
6282
|
-
button.textContent = "Change";
|
|
6890
|
+
button.textContent = this.tf("picker.change", "Change");
|
|
6283
6891
|
}
|
|
6284
6892
|
}
|
|
6285
6893
|
}
|
|
6286
6894
|
async handleCta() {
|
|
6287
6895
|
if (this.salesClosed) return;
|
|
6288
6896
|
if (this.totalTicketCount() > this.maxTickets) {
|
|
6289
|
-
this.toast(
|
|
6897
|
+
this.toast(t2("picker.removeTicketsUntilOrFewer", { count: this.maxTickets }), "warning");
|
|
6290
6898
|
return;
|
|
6291
6899
|
}
|
|
6292
6900
|
const committed = this.committedSelection();
|
|
@@ -6312,7 +6920,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6312
6920
|
hold = h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : hold;
|
|
6313
6921
|
}
|
|
6314
6922
|
if (!hold) {
|
|
6315
|
-
this.toast("One or more seats were just taken. Please pick again.", "error");
|
|
6923
|
+
this.toast(this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again."), "error");
|
|
6316
6924
|
this.setCtaPhase("idle");
|
|
6317
6925
|
this.syncTray();
|
|
6318
6926
|
return;
|
|
@@ -6329,7 +6937,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6329
6937
|
const problem = err;
|
|
6330
6938
|
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
6331
6939
|
if (problem.reason === "event_closed") this.setSalesClosed(true);
|
|
6332
|
-
const message = problem.reason === "event_closed" ? "Seat sales have closed for this event." :
|
|
6940
|
+
const message = problem.reason === "event_closed" ? this.tf("picker.seatSalesClosedForEvent", "Seat sales have closed for this event.") : problem.reason === "companion_pair_required" ? this.tf("picker.companionPairRequired", "Select the adjacent wheelchair place with each companion ticket.") : problem.reason === "ticket_option_not_applicable" ? this.tf("picker.ticketOptionNotApplicable", "That ticket choice is not available for the selected seat.") : labels.length ? labels.length === 1 ? t2("picker.labelsNoLongerAvailable.one", { labels: labels.join(", ") }) : t2("picker.labelsNoLongerAvailable.other", { labels: labels.join(", ") }) : this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again.");
|
|
6333
6941
|
this.toast(message, "error");
|
|
6334
6942
|
this.setCtaPhase("idle");
|
|
6335
6943
|
} finally {
|
|
@@ -6343,14 +6951,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
6343
6951
|
const ms = Math.max(0, this.holdExpiresAt - Date.now());
|
|
6344
6952
|
const m = Math.floor(ms / 6e4);
|
|
6345
6953
|
const s = String(Math.floor(ms % 6e4 / 1e3)).padStart(2, "0");
|
|
6346
|
-
return
|
|
6954
|
+
return t2("picker.holdReassurance", { time: `${m}:${s}` });
|
|
6347
6955
|
}
|
|
6348
6956
|
startHoldTimer(expiresAt) {
|
|
6349
6957
|
this.stopHoldTimer();
|
|
6350
6958
|
this.holdExpiresAt = expiresAt;
|
|
6351
6959
|
if (this.hold) this.rememberHold(this.hold);
|
|
6352
6960
|
const pill = this.els.hold;
|
|
6353
|
-
pill.innerHTML =
|
|
6961
|
+
pill.innerHTML = `<span class="sl-hold-dot" aria-hidden="true"></span><span>${this.tf("picker.held", "Held")}</span><span class="sl-hold-time" data-ref="holdTime"></span>`;
|
|
6354
6962
|
const time = pill.querySelector('[data-ref="holdTime"]');
|
|
6355
6963
|
this.els.holdNote?.classList.add("on");
|
|
6356
6964
|
const tick = () => {
|
|
@@ -6383,7 +6991,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6383
6991
|
const totalSecs = Math.ceil(ms / 1e3);
|
|
6384
6992
|
const m = Math.floor(totalSecs / 60);
|
|
6385
6993
|
const s = String(totalSecs % 60).padStart(2, "0");
|
|
6386
|
-
this.els.extendTxt.innerHTML =
|
|
6994
|
+
this.els.extendTxt.innerHTML = t2("picker.seatsHeldForNeedMoreTime", { time: `${m}:${s}` });
|
|
6387
6995
|
this.extendEl.classList.add("on");
|
|
6388
6996
|
} else {
|
|
6389
6997
|
this.extendEl.classList.remove("on");
|
|
@@ -6393,7 +7001,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6393
7001
|
const btn = this.els.extendBtn;
|
|
6394
7002
|
btn.disabled = true;
|
|
6395
7003
|
const prev = btn.textContent;
|
|
6396
|
-
btn.textContent = "Adding\u2026";
|
|
7004
|
+
btn.textContent = this.tf("picker.addingEllipsis", "Adding\u2026");
|
|
6397
7005
|
try {
|
|
6398
7006
|
const h = await this.controller.extendHold(this.opts.holdTtlMs);
|
|
6399
7007
|
if (h) {
|
|
@@ -6402,13 +7010,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6402
7010
|
this.extendEl?.classList.remove("on");
|
|
6403
7011
|
this.rememberHold(this.hold);
|
|
6404
7012
|
this.emitHoldChange();
|
|
6405
|
-
this.toast("More time added \u2014 your seats are still held.", "success");
|
|
7013
|
+
this.toast(this.tf("picker.moreTimeAdded", "More time added \u2014 your seats are still held."), "success");
|
|
6406
7014
|
} else {
|
|
6407
|
-
this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
|
|
7015
|
+
this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
|
|
6408
7016
|
}
|
|
6409
7017
|
} catch (err) {
|
|
6410
7018
|
this.opts.onError?.(err);
|
|
6411
|
-
this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
|
|
7019
|
+
this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
|
|
6412
7020
|
} finally {
|
|
6413
7021
|
btn.disabled = false;
|
|
6414
7022
|
btn.textContent = prev;
|
|
@@ -6435,7 +7043,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6435
7043
|
this.forgetHold();
|
|
6436
7044
|
const n = handoff.lineItems.reduce((sum, i) => sum + i.quantity, 0);
|
|
6437
7045
|
if (this.els.bookedSub) {
|
|
6438
|
-
this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${n} ${
|
|
7046
|
+
this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${tCount("picker.ticketsCount", n)}</span> ${this.tf("picker.confirmedAndOnWay", "confirmed. A confirmation is on its way.")}`;
|
|
6439
7047
|
}
|
|
6440
7048
|
this.bookedEl?.classList.add("on");
|
|
6441
7049
|
this.opts.onBooked?.(handoff);
|
|
@@ -6517,7 +7125,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6517
7125
|
({ mountCheckout } = await loadHostedCheckout());
|
|
6518
7126
|
} catch (err) {
|
|
6519
7127
|
this.opts.onError?.(err);
|
|
6520
|
-
this.toast("Checkout could not be opened. Your seats are still held \u2014 please try again.", "error");
|
|
7128
|
+
this.toast(this.tf("picker.checkoutCouldNotBeOpened", "Checkout could not be opened. Your seats are still held \u2014 please try again."), "error");
|
|
6521
7129
|
this.setCtaPhase("idle");
|
|
6522
7130
|
return;
|
|
6523
7131
|
}
|
|
@@ -6632,9 +7240,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
6632
7240
|
rowTypeWord(details) {
|
|
6633
7241
|
const authored = details?.displayType?.trim() || details?.rowType?.trim();
|
|
6634
7242
|
if (authored) return authored;
|
|
6635
|
-
if (details?.objectType === "table") return "Table";
|
|
6636
|
-
if (details?.objectType === "booth") return "Booth";
|
|
6637
|
-
return "Row";
|
|
7243
|
+
if (details?.objectType === "table") return this.tf("picker.table", "Table");
|
|
7244
|
+
if (details?.objectType === "booth") return this.tf("picker.booth", "Booth");
|
|
7245
|
+
return this.tf("picker.row", "Row");
|
|
6638
7246
|
}
|
|
6639
7247
|
rowShort(details) {
|
|
6640
7248
|
const row = details?.rowLabel;
|
|
@@ -6657,14 +7265,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
6657
7265
|
const isGroupedTable = details.objectType === "table" && !!details.bookingMode;
|
|
6658
7266
|
const isBooth = details.objectType === "booth";
|
|
6659
7267
|
const hasLoc = details.sectionLabel || details.rowLabel || details.seatNumber;
|
|
6660
|
-
const grid = isGroupedTable ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div><div class="sl-tip-field"><span class="sl-tip-key"
|
|
6661
|
-
const
|
|
7268
|
+
const grid = isGroupedTable ? `<div class="sl-tip-grid"><div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div><div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.guests", "Guests")}</span><span class="sl-tip-val">${details.bookingMode === "variable" ? `${details.minOccupancy}\u2013${details.maxOccupancy}` : details.capacity}</span></div></div>` : isBooth ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.section", "Section")}</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(details.rowLabel ?? details.displayLabel ?? details.label)}</span></div></div>` : hasLoc ? `<div class="sl-tip-grid">` + (details.sectionLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.section", "Section")}</span><span class="sl-tip-val">${esc(details.sectionLabel)}</span></div>` : "") + (details.rowLabel ? `<div class="sl-tip-field"><span class="sl-tip-key">${esc(this.rowTypeWord(details))}</span><span class="sl-tip-val">${esc(this.rowShort(details))}</span></div>` : "") + (details.seatNumber ? `<div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-tip-val">${esc(details.seatNumber)}</span></div>` : "") + `</div>` : `<div class="sl-tip-grid one"><div class="sl-tip-field"><span class="sl-tip-key">${this.tf("picker.seat", "Seat")}</span><span class="sl-tip-val">${esc(details.displayLabel ?? details.label)}</span></div></div>`;
|
|
7269
|
+
const notForSale = details.status === "not_for_sale";
|
|
7270
|
+
const statusLine = details.status === "free" ? "" : `<div class="sl-tip-status">${notForSale ? this.tf("picker.notAvailable", "Not available") : details.status === "held" ? t2("map.statusHeld") : t2("map.statusTaken")}</div>`;
|
|
6662
7271
|
const limited = this.limitedViewLabel(details.commercial);
|
|
6663
7272
|
const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc(limited)}</div>` : "";
|
|
6664
7273
|
const wheelchair = this.wheelchairProvisionLabel(details.wheelchairSpaceType);
|
|
6665
7274
|
const wheelchairLine = wheelchair ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u267F</span>${esc(wheelchair)}</div>` : "";
|
|
6666
7275
|
this.tipEl.style.setProperty("--sl-cat", details.categoryColor);
|
|
6667
|
-
this.tipEl.innerHTML = grid + `<div class="sl-tip-cat"><span class="sl-tip-dot" style="background:${details.categoryColor}"></span><span class="sl-tip-name">${esc(details.categoryLabel)}</span
|
|
7276
|
+
this.tipEl.innerHTML = grid + `<div class="sl-tip-cat"><span class="sl-tip-dot" style="background:${details.categoryColor}"></span><span class="sl-tip-name">${esc(details.categoryLabel)}</span>` + (notForSale ? "" : `<span class="sl-tip-amt">${price}</span>`) + `</div>` + wheelchairLine + cxLine + statusLine;
|
|
6668
7277
|
this.tipEl.style.display = "block";
|
|
6669
7278
|
this.placeTooltip();
|
|
6670
7279
|
}
|
|
@@ -6887,13 +7496,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
6887
7496
|
sourceLabel: this.tf("picker.chartDerivedModel", "Chart-derived model")
|
|
6888
7497
|
};
|
|
6889
7498
|
}
|
|
6890
|
-
/** Route
|
|
6891
|
-
|
|
7499
|
+
/** Route decoupled analytics into the host callback, tagged buyer. */
|
|
7500
|
+
emitAnalytics(event, props) {
|
|
6892
7501
|
try {
|
|
6893
7502
|
this.opts.onAnalytics?.(event, { ...props, surface: "buyer" });
|
|
6894
7503
|
} catch {
|
|
6895
7504
|
}
|
|
6896
7505
|
}
|
|
7506
|
+
emit3dAnalytics(event, props) {
|
|
7507
|
+
this.emitAnalytics(event, props);
|
|
7508
|
+
}
|
|
6897
7509
|
/** A 3D seat tap runs the SAME selection path as a 2D tap: toggle through the
|
|
6898
7510
|
* controller, then raise the shared confirm card (bottom-sheeted in 3D). */
|
|
6899
7511
|
onView3dSeatPick(seatId) {
|
|
@@ -6912,9 +7524,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
6912
7524
|
this.syncSelectionTo3d();
|
|
6913
7525
|
return;
|
|
6914
7526
|
}
|
|
6915
|
-
const
|
|
6916
|
-
if (
|
|
6917
|
-
this.showUnavailable3dSeat(seat,
|
|
7527
|
+
const status = this.controller.getStatus(seat.id);
|
|
7528
|
+
if (status === "held" || status === "booked" || status === "not_for_sale") {
|
|
7529
|
+
this.showUnavailable3dSeat(seat, this.seatState3dFor(seat), status);
|
|
6918
7530
|
this.syncSelectionTo3d();
|
|
6919
7531
|
return;
|
|
6920
7532
|
}
|
|
@@ -6936,31 +7548,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
6936
7548
|
else this.syncTray();
|
|
6937
7549
|
this.syncSelectionTo3d();
|
|
6938
7550
|
}
|
|
6939
|
-
/** Explain a
|
|
6940
|
-
*
|
|
6941
|
-
*
|
|
6942
|
-
|
|
7551
|
+
/** Explain a 3D seat that INVENTORY refuses — never one the buyer's own
|
|
7552
|
+
* filters merely dimmed, which stays buyable exactly as it is in 2D. Category
|
|
7553
|
+
* colour remains visible in the venue; this card names the availability state
|
|
7554
|
+
* explicitly so yellow never has to carry both meanings. */
|
|
7555
|
+
showUnavailable3dSeat(seat, visualState, status) {
|
|
6943
7556
|
const overlay = this.view3dEl;
|
|
6944
7557
|
if (!overlay) return;
|
|
6945
7558
|
this.dismissUnavailable3dSeat();
|
|
6946
|
-
const status = this.controller.getStatus(seat.id);
|
|
6947
7559
|
const details = this.controller.seatDetails(seat.id);
|
|
6948
7560
|
const identity = details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
6949
7561
|
const section = details?.sectionLabel;
|
|
6950
7562
|
const row = this.rowShort(details);
|
|
6951
|
-
const location2 = [section, row ?
|
|
7563
|
+
const location2 = [section, row ? t2("picker.rowLabel", { label: row }) : null, identity].filter(Boolean).join(" \xB7 ");
|
|
6952
7564
|
const copy = status === "held" ? {
|
|
6953
7565
|
title: this.tf("picker.temporarilyHeld", "Temporarily held"),
|
|
6954
7566
|
message: this.tf("picker.heldSeatExplanation", "Another buyer is holding this seat. It may become available again.")
|
|
6955
7567
|
} : status === "booked" ? {
|
|
6956
7568
|
title: this.tf("picker.sold", "Sold"),
|
|
6957
7569
|
message: this.tf("picker.soldSeatExplanation", "This seat has already been booked.")
|
|
6958
|
-
} :
|
|
7570
|
+
} : {
|
|
6959
7571
|
title: this.tf("picker.notForSale", "Not for sale"),
|
|
6960
7572
|
message: this.tf("picker.notForSaleExplanation", "This seat is not included in the current sale.")
|
|
6961
|
-
} : {
|
|
6962
|
-
title: this.tf("picker.filteredSeat", "Unavailable with current filters"),
|
|
6963
|
-
message: this.tf("picker.filteredSeatExplanation", "Change the active price or view filters to make this seat selectable.")
|
|
6964
7573
|
};
|
|
6965
7574
|
const card = document.createElement("div");
|
|
6966
7575
|
card.className = "sl-view3d-unavailable";
|
|
@@ -7044,7 +7653,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7044
7653
|
chip = document.createElement("div");
|
|
7045
7654
|
chip.className = "sl-view3d-compare-saved";
|
|
7046
7655
|
chip.setAttribute("role", "group");
|
|
7047
|
-
chip.setAttribute("aria-label", "Saved seat comparison");
|
|
7656
|
+
chip.setAttribute("aria-label", this.tf("picker.savedSeatComparison", "Saved seat comparison"));
|
|
7048
7657
|
const main2 = document.createElement("button");
|
|
7049
7658
|
main2.type = "button";
|
|
7050
7659
|
main2.className = "main";
|
|
@@ -7056,7 +7665,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7056
7665
|
clear.type = "button";
|
|
7057
7666
|
clear.className = "clear";
|
|
7058
7667
|
clear.textContent = "\xD7";
|
|
7059
|
-
clear.setAttribute("aria-label", "Clear saved seat comparison");
|
|
7668
|
+
clear.setAttribute("aria-label", this.tf("picker.clearSavedSeatComparison", "Clear saved seat comparison"));
|
|
7060
7669
|
clear.addEventListener("click", () => this.clearView3dComparison());
|
|
7061
7670
|
chip.append(main2, clear);
|
|
7062
7671
|
overlay.appendChild(chip);
|
|
@@ -7065,8 +7674,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
7065
7674
|
const count = this.view3dCompareSeatIds.length;
|
|
7066
7675
|
const main = chip.querySelector(".main");
|
|
7067
7676
|
if (main) {
|
|
7068
|
-
main.textContent = count > 1 ?
|
|
7069
|
-
main.setAttribute("aria-label", count > 1 ?
|
|
7677
|
+
main.textContent = count > 1 ? t2("picker.compareCount", { count }) : this.tf("picker.oneSeatSaved", "1 seat saved");
|
|
7678
|
+
main.setAttribute("aria-label", count > 1 ? t2("picker.openComparisonOfSeats", { count }) : this.tf("picker.oneSeatSavedChooseAnother", "One seat saved; choose another to compare"));
|
|
7070
7679
|
}
|
|
7071
7680
|
}
|
|
7072
7681
|
view3dComparisonSnapshot(seatId) {
|
|
@@ -7338,13 +7947,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
7338
7947
|
const floors = handle.floors();
|
|
7339
7948
|
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
7340
7949
|
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
7341
|
-
const allSections = handle.sections().filter((
|
|
7950
|
+
const allSections = handle.sections().filter((section) => section.seatCount > 0 && !floorLabels.has(section.label.trim().toLocaleLowerCase()));
|
|
7342
7951
|
const sections = zones.length > 1 ? [] : allSections;
|
|
7343
7952
|
const wantFloors = floors.length > 1;
|
|
7344
7953
|
const wantZones = zones.length > 1;
|
|
7345
7954
|
const wantSections = sections.length > 1;
|
|
7346
|
-
|
|
7347
|
-
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
7955
|
+
if (!wantFloors && !wantZones && !wantSections) return;
|
|
7348
7956
|
const nav = document.createElement("div");
|
|
7349
7957
|
nav.className = "sl-view3d-nav";
|
|
7350
7958
|
const finderToggle = document.createElement("button");
|
|
@@ -7354,7 +7962,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7354
7962
|
const setFinderOpen = (open) => {
|
|
7355
7963
|
nav.classList.toggle("is-open", open);
|
|
7356
7964
|
finderToggle.setAttribute("aria-expanded", String(open));
|
|
7357
|
-
finderToggle.textContent = open ? "Close
|
|
7965
|
+
finderToggle.textContent = open ? this.tf("picker.closeVenueNav", "Close") : wantFloors && (wantZones || wantSections) ? this.tf("picker.levelsAndAreas", "Levels & areas") : wantFloors ? this.tf("picker.levels", "Levels") : this.tf("picker.areas", "Areas");
|
|
7358
7966
|
};
|
|
7359
7967
|
finderToggle.addEventListener("click", () => setFinderOpen(!nav.classList.contains("is-open")));
|
|
7360
7968
|
nav.addEventListener("keydown", (event) => {
|
|
@@ -7366,16 +7974,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
7366
7974
|
});
|
|
7367
7975
|
setFinderOpen(false);
|
|
7368
7976
|
nav.appendChild(finderToggle);
|
|
7977
|
+
let syncAreasForFloor = (_index) => void 0;
|
|
7978
|
+
let syncPressedFloor = (_index) => void 0;
|
|
7369
7979
|
if (wantFloors) {
|
|
7370
7980
|
const row = document.createElement("div");
|
|
7371
7981
|
row.setAttribute("role", "group");
|
|
7372
7982
|
row.setAttribute("aria-label", this.tf("picker.levels", "Levels"));
|
|
7373
7983
|
const pills = [];
|
|
7984
|
+
syncPressedFloor = (index) => {
|
|
7985
|
+
pills.forEach((pill) => {
|
|
7986
|
+
pill.setAttribute("aria-pressed", String(
|
|
7987
|
+
(pill.dataset.floor === "" ? null : Number(pill.dataset.floor)) === index
|
|
7988
|
+
));
|
|
7989
|
+
});
|
|
7990
|
+
};
|
|
7374
7991
|
const select = (index) => {
|
|
7375
7992
|
if (!handle.focusFloor(index)) return;
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
});
|
|
7993
|
+
syncPressedFloor(index);
|
|
7994
|
+
syncAreasForFloor(index);
|
|
7379
7995
|
};
|
|
7380
7996
|
const add = (label, index) => {
|
|
7381
7997
|
const b = document.createElement("button");
|
|
@@ -7391,7 +8007,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7391
8007
|
row.appendChild(b);
|
|
7392
8008
|
};
|
|
7393
8009
|
add(this.tf("picker.allLevels", "All levels"), null);
|
|
7394
|
-
for (const f of floors) add(f.label ||
|
|
8010
|
+
for (const f of floors) add(f.label || t2("picker.levelNumber", { number: f.index + 1 }), f.index);
|
|
7395
8011
|
nav.appendChild(row);
|
|
7396
8012
|
}
|
|
7397
8013
|
if (wantZones || wantSections) {
|
|
@@ -7400,119 +8016,55 @@ var SeatPicker = class _SeatPicker {
|
|
|
7400
8016
|
row.setAttribute("aria-label", this.tf("picker.areas", "Areas"));
|
|
7401
8017
|
const entries = wantZones ? zones.map((z) => ({ id: z.id, label: z.label || z.id, go: () => {
|
|
7402
8018
|
handle.focusZone(z.id);
|
|
7403
|
-
} })) : sections.map((s) => ({
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
placeholder.value = "";
|
|
7411
|
-
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
7412
|
-
select.appendChild(placeholder);
|
|
7413
|
-
for (const entry of entries) {
|
|
7414
|
-
const option = document.createElement("option");
|
|
7415
|
-
option.value = entry.id;
|
|
7416
|
-
option.textContent = entry.label;
|
|
7417
|
-
select.appendChild(option);
|
|
7418
|
-
}
|
|
7419
|
-
select.addEventListener("change", () => {
|
|
7420
|
-
entries.find((entry) => entry.id === select.value)?.go();
|
|
7421
|
-
});
|
|
7422
|
-
row.appendChild(select);
|
|
7423
|
-
} else {
|
|
7424
|
-
for (const e of entries) {
|
|
7425
|
-
const b = document.createElement("button");
|
|
7426
|
-
b.type = "button";
|
|
7427
|
-
b.textContent = e.label;
|
|
7428
|
-
b.addEventListener("click", () => {
|
|
7429
|
-
e.go();
|
|
7430
|
-
setFinderOpen(false);
|
|
7431
|
-
});
|
|
7432
|
-
row.appendChild(b);
|
|
7433
|
-
}
|
|
7434
|
-
}
|
|
7435
|
-
nav.appendChild(row);
|
|
7436
|
-
}
|
|
7437
|
-
if (wantLocator) {
|
|
7438
|
-
const locator = document.createElement("div");
|
|
7439
|
-
locator.className = "sl-view3d-locator";
|
|
7440
|
-
locator.setAttribute("role", "group");
|
|
7441
|
-
locator.setAttribute("aria-label", "Find an exact seat in 3D");
|
|
7442
|
-
const sectionSelect = document.createElement("select");
|
|
7443
|
-
sectionSelect.setAttribute("aria-label", "Choose section in 3D");
|
|
7444
|
-
const rowSelect = document.createElement("select");
|
|
7445
|
-
rowSelect.setAttribute("aria-label", "Choose row in 3D");
|
|
7446
|
-
const seatSelect = document.createElement("select");
|
|
7447
|
-
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
7448
|
-
const view = document.createElement("button");
|
|
7449
|
-
view.type = "button";
|
|
7450
|
-
view.textContent = "Inspect seat";
|
|
7451
|
-
view.disabled = true;
|
|
7452
|
-
const fill = (select, placeholder, entries) => {
|
|
7453
|
-
select.replaceChildren();
|
|
7454
|
-
const first = document.createElement("option");
|
|
7455
|
-
first.value = "";
|
|
7456
|
-
first.textContent = placeholder;
|
|
7457
|
-
select.appendChild(first);
|
|
7458
|
-
for (const entry of entries) {
|
|
7459
|
-
const option = document.createElement("option");
|
|
7460
|
-
option.value = entry.id;
|
|
7461
|
-
option.textContent = entry.label;
|
|
7462
|
-
select.appendChild(option);
|
|
8019
|
+
} })) : sections.map((s) => ({
|
|
8020
|
+
id: s.id,
|
|
8021
|
+
label: s.label || s.id,
|
|
8022
|
+
floorIndex: s.floorIndex,
|
|
8023
|
+
go: () => {
|
|
8024
|
+
handle.focusSection(s.id);
|
|
8025
|
+
if (Number.isFinite(s.floorIndex)) syncPressedFloor(s.floorIndex);
|
|
7463
8026
|
}
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
fill(rowSelect, "2. Row", []);
|
|
7471
|
-
fill(seatSelect, "3. Seat", []);
|
|
7472
|
-
rowSelect.disabled = true;
|
|
7473
|
-
seatSelect.disabled = true;
|
|
7474
|
-
sectionSelect.addEventListener("change", () => {
|
|
7475
|
-
const sectionId = sectionSelect.value;
|
|
7476
|
-
view.disabled = true;
|
|
7477
|
-
fill(seatSelect, "3. Seat", []);
|
|
7478
|
-
seatSelect.disabled = true;
|
|
7479
|
-
if (!sectionId || !handle.focusSection(sectionId)) {
|
|
7480
|
-
fill(rowSelect, "2. Row", []);
|
|
7481
|
-
rowSelect.disabled = true;
|
|
8027
|
+
}));
|
|
8028
|
+
const renderEntries = (floorIndex) => {
|
|
8029
|
+
const visible = wantZones || floorIndex === null ? entries : entries.filter((entry) => entry.floorIndex === void 0 || entry.floorIndex === floorIndex);
|
|
8030
|
+
row.replaceChildren();
|
|
8031
|
+
if (!visible.length) {
|
|
8032
|
+
row.remove();
|
|
7482
8033
|
return;
|
|
7483
8034
|
}
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
label
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
8035
|
+
if (!row.isConnected) nav.appendChild(row);
|
|
8036
|
+
if (!wantZones && visible.length > MAX_3D_SECTION_PILLS) {
|
|
8037
|
+
const select = document.createElement("select");
|
|
8038
|
+
select.setAttribute("aria-label", this.tf("picker.jumpToSection", "Jump to section"));
|
|
8039
|
+
const placeholder = document.createElement("option");
|
|
8040
|
+
placeholder.value = "";
|
|
8041
|
+
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
8042
|
+
select.appendChild(placeholder);
|
|
8043
|
+
for (const entry of visible) {
|
|
8044
|
+
const option = document.createElement("option");
|
|
8045
|
+
option.value = entry.id;
|
|
8046
|
+
option.textContent = entry.label;
|
|
8047
|
+
select.appendChild(option);
|
|
8048
|
+
}
|
|
8049
|
+
select.addEventListener("change", () => {
|
|
8050
|
+
visible.find((entry) => entry.id === select.value)?.go();
|
|
8051
|
+
});
|
|
8052
|
+
row.appendChild(select);
|
|
7497
8053
|
return;
|
|
7498
8054
|
}
|
|
7499
|
-
const
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
const seatId = seatSelect.value;
|
|
7509
|
-
if (seatId) {
|
|
7510
|
-
setFinderOpen(false);
|
|
7511
|
-
this.onView3dSeatPick(seatId);
|
|
8055
|
+
for (const entry of visible) {
|
|
8056
|
+
const button = document.createElement("button");
|
|
8057
|
+
button.type = "button";
|
|
8058
|
+
button.textContent = entry.label;
|
|
8059
|
+
button.addEventListener("click", () => {
|
|
8060
|
+
entry.go();
|
|
8061
|
+
setFinderOpen(false);
|
|
8062
|
+
});
|
|
8063
|
+
row.appendChild(button);
|
|
7512
8064
|
}
|
|
7513
|
-
}
|
|
7514
|
-
|
|
7515
|
-
|
|
8065
|
+
};
|
|
8066
|
+
syncAreasForFloor = renderEntries;
|
|
8067
|
+
renderEntries(null);
|
|
7516
8068
|
}
|
|
7517
8069
|
overlay.appendChild(nav);
|
|
7518
8070
|
}
|
|
@@ -7529,7 +8081,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7529
8081
|
const overlay = document.createElement("div");
|
|
7530
8082
|
overlay.className = "sl-view3d";
|
|
7531
8083
|
overlay.setAttribute("role", "group");
|
|
7532
|
-
overlay.setAttribute("aria-label", "Interactive 3D venue view");
|
|
8084
|
+
overlay.setAttribute("aria-label", this.tf("picker.interactive3dVenueView", "Interactive 3D venue view"));
|
|
7533
8085
|
const back = document.createElement("button");
|
|
7534
8086
|
back.type = "button";
|
|
7535
8087
|
back.className = "sl-view3d-back";
|
|
@@ -7555,7 +8107,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7555
8107
|
fullscreen.type = "button";
|
|
7556
8108
|
fullscreen.className = "sl-view3d-fs";
|
|
7557
8109
|
fullscreen.textContent = "\u26F6";
|
|
7558
|
-
fullscreen.setAttribute("aria-label", "Full screen");
|
|
8110
|
+
fullscreen.setAttribute("aria-label", this.tf("picker.fullScreen", "Full screen"));
|
|
7559
8111
|
fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
|
|
7560
8112
|
fullscreen.addEventListener("click", () => this.toggleFullscreen());
|
|
7561
8113
|
overlay.appendChild(fullscreen);
|
|
@@ -7592,7 +8144,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7592
8144
|
onSeatInspect: (id) => this.onView3dSeatPick(id),
|
|
7593
8145
|
onSectionFocusChange: (sectionId) => {
|
|
7594
8146
|
const select = overlay.querySelector(
|
|
7595
|
-
'select[
|
|
8147
|
+
'select[data-locator="section"]'
|
|
7596
8148
|
);
|
|
7597
8149
|
const next = sectionId ?? "";
|
|
7598
8150
|
if (!select || select.value === next) return;
|
|
@@ -7689,7 +8241,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7689
8241
|
if (button) {
|
|
7690
8242
|
button.disabled = true;
|
|
7691
8243
|
button.classList.add("sl-busy");
|
|
7692
|
-
button.innerHTML =
|
|
8244
|
+
button.innerHTML = `<span class="sl-ba-spin" aria-hidden="true"></span>${this.tf("picker.findingEllipsis", "Finding\u2026")}`;
|
|
7693
8245
|
}
|
|
7694
8246
|
try {
|
|
7695
8247
|
const h = await this.controller.bestAvailable(qty, categoryKey, { ...opts, ttlMs: this.opts.holdTtlMs });
|
|
@@ -7711,7 +8263,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7711
8263
|
} catch (err) {
|
|
7712
8264
|
this.opts.onError?.(err);
|
|
7713
8265
|
const reason = err?.reason;
|
|
7714
|
-
const message = reason === "not_enough_together" ?
|
|
8266
|
+
const message = reason === "not_enough_together" ? t2("picker.couldNotFindSeatsTogether", { count: qty }) : reason === "companion_pair_required" ? this.tf("picker.companionPairRequired", "Select the adjacent wheelchair place with each companion ticket.") : reason === "ticket_option_not_applicable" ? this.tf("picker.ticketOptionNotApplicable", "That ticket choice is not available for the selected seat.") : reason === "sold_out" ? this.tf("picker.ticketTypeSoldOut", "That ticket type is sold out. Try another ticket type.") : reason === "event_closed" ? this.tf("picker.seatSalesClosedForEvent", "Seat sales have closed for this event.") : this.tf("picker.seatsNoLongerAvailableTryAnother", "Those seats are no longer available. Try another quantity or ticket type.");
|
|
7715
8267
|
this.toast(message, "error");
|
|
7716
8268
|
return null;
|
|
7717
8269
|
} finally {
|
|
@@ -7740,7 +8292,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7740
8292
|
}
|
|
7741
8293
|
}
|
|
7742
8294
|
if (!released) {
|
|
7743
|
-
this.toast("Couldn't release your tickets. Your hold is unchanged.", "error");
|
|
8295
|
+
this.toast(this.tf("picker.couldNotReleaseTickets", "Couldn't release your tickets. Your hold is unchanged."), "error");
|
|
7744
8296
|
return;
|
|
7745
8297
|
}
|
|
7746
8298
|
this.hold = null;
|