@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.cjs
CHANGED
|
@@ -2524,6 +2524,9 @@ var BuyerAssetObjectUrls = class {
|
|
|
2524
2524
|
}
|
|
2525
2525
|
};
|
|
2526
2526
|
|
|
2527
|
+
// src/SeatPicker.ts
|
|
2528
|
+
var import_minimapGeometry = require("@seatlayer/core/picker/minimapGeometry");
|
|
2529
|
+
|
|
2527
2530
|
// src/offerAvailability.ts
|
|
2528
2531
|
var SALE_STATES = [
|
|
2529
2532
|
"on-sale",
|
|
@@ -2659,6 +2662,16 @@ var DEFAULT_MAX_SELECTION2 = 10;
|
|
|
2659
2662
|
var EXTEND_PROMPT_MS = 6e4;
|
|
2660
2663
|
var MAX_3D_SEATS_DEFAULT = 6e4;
|
|
2661
2664
|
var MAX_3D_SECTION_PILLS = 12;
|
|
2665
|
+
function tfStatic(key, fallback) {
|
|
2666
|
+
const v = (0, import_core2.t)(key);
|
|
2667
|
+
return v === key ? fallback : v;
|
|
2668
|
+
}
|
|
2669
|
+
var chartsLoadedThisPage = /* @__PURE__ */ new Set();
|
|
2670
|
+
function markChartLoaded(eventKey) {
|
|
2671
|
+
const seen = chartsLoadedThisPage.has(eventKey);
|
|
2672
|
+
chartsLoadedThisPage.add(eventKey);
|
|
2673
|
+
return seen;
|
|
2674
|
+
}
|
|
2662
2675
|
function pointInPolygon(x, y, poly) {
|
|
2663
2676
|
let inside = false;
|
|
2664
2677
|
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
@@ -2761,9 +2774,13 @@ var CSS2 = (
|
|
|
2761
2774
|
the WIDGET's meanings, not an org's brand: premium gold, environment
|
|
2762
2775
|
warning, danger, success. Hosts can still override them with CSS. */
|
|
2763
2776
|
--sl-premium:#e8c15a;--sl-premium-deep:#c9a24b;--sl-premium-ink:#1c1608;
|
|
2764
|
-
--sl-
|
|
2777
|
+
--sl-premium-text:color-mix(in srgb,var(--sl-premium-deep) 48%,var(--sl-text));
|
|
2778
|
+
--sl-warn:#d8a425;--sl-warn-text:color-mix(in srgb,var(--sl-warn) 48%,var(--sl-text));
|
|
2779
|
+
--sl-danger:#e5484d;--sl-success:#22a06b}
|
|
2765
2780
|
.sl-picker *{box-sizing:border-box;margin:0;padding:0}
|
|
2766
|
-
.
|
|
2781
|
+
/* Keep the reset intentionally specificity-free. Standalone map and panel
|
|
2782
|
+
controls must be able to establish their own token surfaces in either theme. */
|
|
2783
|
+
.sl-picker :where(button){font:inherit;color:inherit;background:none;border:0;cursor:pointer}
|
|
2767
2784
|
|
|
2768
2785
|
/* header */
|
|
2769
2786
|
.sl-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--sl-line);flex:none}
|
|
@@ -2833,11 +2850,30 @@ var CSS2 = (
|
|
|
2833
2850
|
.sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
|
|
2834
2851
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
2835
2852
|
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
2836
|
-
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(
|
|
2853
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(380px,64%)}
|
|
2854
|
+
/* With the price list expanded the sheet takes the with-selection height so
|
|
2855
|
+
the tray (cart + best-seats) is never crushed under the fixed-size list. */
|
|
2856
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-side{height:min(480px,74%)}
|
|
2837
2857
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:76px;overflow:hidden}
|
|
2838
2858
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
2839
|
-
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:
|
|
2859
|
+
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:96px;overflow-y:auto;overscroll-behavior:contain}
|
|
2860
|
+
.sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-tray{min-height:48px}
|
|
2861
|
+
.sl-picker[data-layout="narrow"] .sl-prices{max-height:150px;overflow-y:auto;overscroll-behavior:contain}
|
|
2840
2862
|
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
2863
|
+
/* NARROW FOOT SAYS EVERYTHING ONCE (owner report: with a hold the footer ate
|
|
2864
|
+
~190px and the ticket list got a sliver). The sheet head already carries
|
|
2865
|
+
"N tickets \xB7 $total" and the header pill already ticks the clock \u2014 so the
|
|
2866
|
+
total row hides, the secured note collapses to one compact line, and the
|
|
2867
|
+
badge tucks in. The tray gets the pixels; a thin scrollbar shows there is
|
|
2868
|
+
more to scroll. */
|
|
2869
|
+
.sl-picker[data-layout="narrow"] .sl-total{display:none}
|
|
2870
|
+
.sl-picker[data-layout="narrow"] .sl-foot{padding:8px 12px 8px}
|
|
2871
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note{margin-bottom:6px;padding:5px 9px}
|
|
2872
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note svg{display:none}
|
|
2873
|
+
.sl-picker[data-layout="narrow"] .sl-hold-note b{display:inline;margin-right:6px;font-size:11px}
|
|
2874
|
+
.sl-picker[data-layout="narrow"] .sl-hold-copy{display:inline;white-space:normal;font-size:10.5px}
|
|
2875
|
+
.sl-picker[data-layout="narrow"] .sl-powered{margin-top:4px;padding:2px 8px;font-size:11px}
|
|
2876
|
+
.sl-picker[data-layout="narrow"] .sl-tray{scrollbar-width:thin}
|
|
2841
2877
|
.sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
|
|
2842
2878
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
|
|
2843
2879
|
.sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
|
|
@@ -2845,6 +2881,7 @@ var CSS2 = (
|
|
|
2845
2881
|
.sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
|
|
2846
2882
|
.sl-picker[data-layout="narrow"] .sl-filters{order:4}
|
|
2847
2883
|
.sl-picker[data-layout="narrow"] .sl-prices-sec{order:5}
|
|
2884
|
+
.sl-picker[data-layout="narrow"] .sl-prices-hint{order:6}
|
|
2848
2885
|
.sl-picker[data-layout="narrow"] .sl-pricef{order:6}
|
|
2849
2886
|
.sl-picker[data-layout="narrow"] .sl-prices{order:7}
|
|
2850
2887
|
.sl-picker[data-layout="narrow"] .sl-foot{order:8}
|
|
@@ -2852,6 +2889,7 @@ var CSS2 = (
|
|
|
2852
2889
|
.sl-picker[data-layout="narrow"] .sl-filtersec,
|
|
2853
2890
|
.sl-picker[data-layout="narrow"] .sl-filters,
|
|
2854
2891
|
.sl-picker[data-layout="narrow"] .sl-prices-sec,
|
|
2892
|
+
.sl-picker[data-layout="narrow"] .sl-prices-hint,
|
|
2855
2893
|
.sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
|
|
2856
2894
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
|
|
2857
2895
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
|
|
@@ -2869,7 +2907,13 @@ var CSS2 = (
|
|
|
2869
2907
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
2870
2908
|
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
2871
2909
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
2872
|
-
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:
|
|
2910
|
+
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:52px;padding:3px 10px 5px}
|
|
2911
|
+
/* OPEN sheet: the head is context, not controls \u2014 the pill it carries at peek
|
|
2912
|
+
("Best seats"/"Continue") duplicates the card and CTA right below it, and
|
|
2913
|
+
every saved pixel is tray room (owner call 2026-08-14). */
|
|
2914
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-head{min-height:42px}
|
|
2915
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-bar{min-height:32px}
|
|
2916
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-sheet-peek .go{display:none}
|
|
2873
2917
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-sheet-head{height:100%}
|
|
2874
2918
|
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:1px auto 5px}
|
|
2875
2919
|
.sl-sheet-bar{display:flex;align-items:center;gap:8px;min-height:44px}
|
|
@@ -2910,9 +2954,19 @@ var CSS2 = (
|
|
|
2910
2954
|
sheet brings it back, exactly like the filters row above. The tray hint comes
|
|
2911
2955
|
back with it: the first-time buyer on a phone needs the instruction more than
|
|
2912
2956
|
the desktop buyer, not less. */
|
|
2913
|
-
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-prices-sec{display:flex!important}
|
|
2914
|
-
|
|
2957
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-sec{display:flex!important}
|
|
2958
|
+
/* MOBILE SPACE BELONGS TO THE CART (owner call 2026-08-14): the price list is
|
|
2959
|
+
one tap away behind the "Ticket prices" row, not a permanent tenant. Zone
|
|
2960
|
+
flags and section taps already answer "what does it cost" in context. */
|
|
2961
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices-hint{display:block!important}
|
|
2962
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-prices-open="true"] .sl-prices{display:flex!important}
|
|
2915
2963
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-tray-hint{display:block!important}
|
|
2964
|
+
/* The row reads as the toggle it is on narrow: chevron + pointer. */
|
|
2965
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec{cursor:pointer}
|
|
2966
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec::after{content:'';width:8px;height:8px;flex:none;margin-left:auto;
|
|
2967
|
+
border-right:2px solid var(--sl-muted);border-bottom:2px solid var(--sl-muted);
|
|
2968
|
+
transform:rotate(45deg);transition:transform .2s cubic-bezier(.2,.8,.2,1)}
|
|
2969
|
+
.sl-picker[data-layout="narrow"][data-prices-open="true"] .sl-prices-sec::after{transform:rotate(-135deg)}
|
|
2916
2970
|
.sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
2917
2971
|
color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
2918
2972
|
.sl-cbbtn:hover{border-color:var(--sl-muted)}
|
|
@@ -2922,24 +2976,30 @@ var CSS2 = (
|
|
|
2922
2976
|
.sl-offer{display:none;margin:12px 14px 2px;padding:12px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));
|
|
2923
2977
|
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
2924
2978
|
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
2925
|
-
.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)}
|
|
2979
|
+
.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)}
|
|
2926
2980
|
.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)}
|
|
2927
2981
|
.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;
|
|
2928
2982
|
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
2929
|
-
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
2983
|
+
.sl-offer-info>summary::-webkit-details-marker{display:none}.sl-offer-info[open]>summary{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
2930
2984
|
.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)}
|
|
2931
2985
|
.sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
2932
2986
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
2933
2987
|
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
2934
|
-
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
2988
|
+
background-color:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
2989
|
+
/* One sentence that teaches the whole colour code \u2014 a first-time buyer sees
|
|
2990
|
+
"Head Tables \xB7 $165" and coloured dots with nothing connecting them. Gone
|
|
2991
|
+
the moment they have seats (they've learned it) and while sales are closed. */
|
|
2992
|
+
.sl-prices-hint{padding:0 14px 7px;font-size:11px;line-height:1.45;color:var(--sl-muted)}
|
|
2993
|
+
.sl-picker[data-has-selection="true"] .sl-prices-hint,
|
|
2994
|
+
.sl-picker[data-sales-closed="true"] .sl-prices-hint{display:none}
|
|
2935
2995
|
.sl-prices{display:flex;flex-direction:column;padding:4px 14px 8px;border-bottom:1px solid var(--sl-line)}
|
|
2936
2996
|
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
2937
2997
|
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px;
|
|
2938
2998
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
2939
2999
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
2940
3000
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
2941
|
-
.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}
|
|
2942
|
-
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent)}
|
|
3001
|
+
.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}
|
|
3002
|
+
.sl-price-row.sl-active .sl-price-label{color:var(--sl-accent-text)}
|
|
2943
3003
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
2944
3004
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
2945
3005
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
@@ -2974,7 +3034,7 @@ var CSS2 = (
|
|
|
2974
3034
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
|
|
2975
3035
|
.sl-ticket-state{width:17px;height:17px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
2976
3036
|
background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
2977
|
-
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent)}
|
|
3037
|
+
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent-text)}
|
|
2978
3038
|
.sl-ticket-state svg{width:10px;height:10px;stroke:currentColor;stroke-width:2.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
2979
3039
|
.sl-chip-main{min-width:0;padding:8px 10px 8px 11px;display:flex;flex-direction:column;justify-content:center;gap:5px}
|
|
2980
3040
|
.sl-chip-id{display:flex;gap:12px;min-width:0}
|
|
@@ -3017,7 +3077,7 @@ var CSS2 = (
|
|
|
3017
3077
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
3018
3078
|
|
|
3019
3079
|
/* footer */
|
|
3020
|
-
.sl-foot{position:relative;z-index:2;padding:
|
|
3080
|
+
.sl-foot{position:relative;z-index:2;padding:10px 16px 8px;border-top:1px solid var(--sl-line);flex:none;
|
|
3021
3081
|
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
3022
3082
|
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
3023
3083
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
@@ -3031,14 +3091,13 @@ var CSS2 = (
|
|
|
3031
3091
|
.sl-hold-note>span{flex:1;min-width:0}
|
|
3032
3092
|
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
3033
3093
|
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
3034
|
-
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
3094
|
+
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
3035
3095
|
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
3036
3096
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
3037
3097
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
3038
3098
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
3039
|
-
/* Primary checkout CTA.
|
|
3040
|
-
|
|
3041
|
-
and the button renders as plain text with no accent fill. */
|
|
3099
|
+
/* Primary checkout CTA. The extra root scope documents that this is widget
|
|
3100
|
+
chrome and keeps the contract resilient when embedded beside host styles. */
|
|
3042
3101
|
.sl-picker .sl-cta{display:flex;align-items:center;justify-content:center;width:100%;min-height:44px;
|
|
3043
3102
|
padding:12px 16px;border-radius:var(--sl-r-sm);font-weight:800;font-size:14px;line-height:1.1;
|
|
3044
3103
|
background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
@@ -3049,8 +3108,8 @@ var CSS2 = (
|
|
|
3049
3108
|
.sl-cta-spin,.sl-ba-spin{width:14px;height:14px;border-radius:50%;border:2px solid currentColor;border-right-color:transparent;
|
|
3050
3109
|
animation:slspin .7s linear infinite;flex:none}
|
|
3051
3110
|
/* Disabled ("Select seats"): quieter, but still a full-width button shape. */
|
|
3052
|
-
.sl-picker .sl-cta:disabled{background:var(--sl-surface);color:var(--sl-muted);
|
|
3053
|
-
cursor:not-allowed;filter:none;transform:none}
|
|
3111
|
+
.sl-picker .sl-cta:disabled{background:color-mix(in srgb,var(--sl-bg) 35%,var(--sl-surface));color:var(--sl-muted);
|
|
3112
|
+
box-shadow:inset 0 0 0 1px var(--sl-line);opacity:1;cursor:not-allowed;filter:none;transform:none}
|
|
3054
3113
|
|
|
3055
3114
|
/* Chrome anchor regions (Feature 6) \u2014 every interactive map overlay is APPENDED
|
|
3056
3115
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
@@ -3080,7 +3139,7 @@ var CSS2 = (
|
|
|
3080
3139
|
in the same region, so no displacement hack is needed. */
|
|
3081
3140
|
.sl-testbadge{display:inline-flex;align-items:center;padding:5px 10px;border-radius:7px;pointer-events:none;
|
|
3082
3141
|
font-size:10px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;white-space:nowrap;
|
|
3083
|
-
color:var(--sl-warn);background:color-mix(in srgb,var(--sl-warn) 13%,var(--sl-surface));
|
|
3142
|
+
color:var(--sl-warn-text);background:color-mix(in srgb,var(--sl-warn) 13%,var(--sl-surface));
|
|
3084
3143
|
border:1px dashed color-mix(in srgb,var(--sl-warn) 55%,transparent)}
|
|
3085
3144
|
|
|
3086
3145
|
/* zoom column (flows within the bottom-right region) */
|
|
@@ -3091,11 +3150,11 @@ var CSS2 = (
|
|
|
3091
3150
|
color:var(--sl-text);font-size:17px;font-weight:700;display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
3092
3151
|
.sl-zoom button:hover{border-color:var(--sl-muted)}
|
|
3093
3152
|
.sl-zoom svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3094
|
-
/* Full-screen promotion
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
region beside Map|3D
|
|
3098
|
-
|
|
3153
|
+
/* Full-screen promotion: the capability existed (native + iOS overlay +
|
|
3154
|
+
framed-host paths) but hid as the 4th unlabelled glyph in a corner stack \u2014
|
|
3155
|
+
even the owner could not pick it out. dockLayoutChrome docks the SAME
|
|
3156
|
+
button into the top-right region beside Map|3D on EVERY layout and this
|
|
3157
|
+
class dresses it as a labelled pill. */
|
|
3099
3158
|
.sl-zfs-lbl{display:none}
|
|
3100
3159
|
.sl-fs-pill.sl-fs-pill{display:inline-flex;align-items:center;justify-content:center;width:auto;min-height:40px;height:auto;
|
|
3101
3160
|
padding:0 13px;gap:6px;border-radius:999px;font-size:11px;font-weight:800;
|
|
@@ -3103,6 +3162,16 @@ var CSS2 = (
|
|
|
3103
3162
|
.sl-fs-pill.sl-fs-pill:hover{border-color:var(--sl-muted)}
|
|
3104
3163
|
.sl-fs-pill svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3105
3164
|
.sl-fs-pill .sl-zfs-lbl{display:inline;letter-spacing:.02em;white-space:nowrap}
|
|
3165
|
+
/* In the header, icons carry it (owner call): square Full screen glyph, and
|
|
3166
|
+
on narrow a price-tag toggle that opens the sheet with the list expanded. */
|
|
3167
|
+
.sl-head .sl-fs-pill{min-height:34px;min-width:34px;width:34px;padding:0;flex:none}
|
|
3168
|
+
.sl-head .sl-fs-pill .sl-zfs-lbl{display:none}
|
|
3169
|
+
.sl-prices-btn{display:none;align-items:center;justify-content:center;width:34px;min-height:34px;flex:none;
|
|
3170
|
+
border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);color:var(--sl-text);transition:border-color .15s}
|
|
3171
|
+
.sl-prices-btn:hover{border-color:var(--sl-muted)}
|
|
3172
|
+
.sl-prices-btn svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3173
|
+
.sl-prices-btn[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3174
|
+
.sl-picker[data-layout="narrow"] .sl-prices-btn{display:inline-flex}
|
|
3106
3175
|
|
|
3107
3176
|
/* toast + boot states (toast flows in the bottom-center region) */
|
|
3108
3177
|
.sl-toast{transform:translateY(6px) scale(.98);max-width:100%;
|
|
@@ -3137,7 +3206,7 @@ var CSS2 = (
|
|
|
3137
3206
|
.sl-extend-btn{flex:none;padding:8px 14px;border-radius:999px;font-weight:800;font-size:12.5px;
|
|
3138
3207
|
background:var(--sl-accent);color:var(--sl-accent-ink);transition:filter .15s,opacity .15s}
|
|
3139
3208
|
.sl-extend-btn:hover{filter:brightness(1.08)}
|
|
3140
|
-
.sl-extend-btn:disabled{opacity:.
|
|
3209
|
+
.sl-extend-btn:disabled{opacity:.62;cursor:not-allowed}
|
|
3141
3210
|
|
|
3142
3211
|
/* booked confirmation overlay (covers the widget once the held seats are sold) */
|
|
3143
3212
|
.sl-booked{position:absolute;inset:0;z-index:11;display:flex;flex-direction:column;align-items:center;
|
|
@@ -3164,7 +3233,7 @@ var CSS2 = (
|
|
|
3164
3233
|
justify-content:center;text-align:center;gap:8px;padding:24px;
|
|
3165
3234
|
background:color-mix(in srgb,var(--sl-bg) 82%,transparent);backdrop-filter:blur(4px)}
|
|
3166
3235
|
.sl-soldout.on{display:flex}
|
|
3167
|
-
.sl-soldout-eyebrow{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sl-accent);font-weight:800}
|
|
3236
|
+
.sl-soldout-eyebrow{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--sl-accent-text);font-weight:800}
|
|
3168
3237
|
.sl-soldout-title{font-size:32px;font-weight:800;color:var(--sl-text);line-height:1.05}
|
|
3169
3238
|
.sl-soldout-copy{max-width:360px;font-size:13px;color:var(--sl-muted);line-height:1.5}
|
|
3170
3239
|
|
|
@@ -3180,9 +3249,9 @@ var CSS2 = (
|
|
|
3180
3249
|
/* "Powered by SeatLayer" attribution badge (side-panel foot) \u2014 the canonical
|
|
3181
3250
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
3182
3251
|
theme sets hideBadge. */
|
|
3183
|
-
.sl-powered{display:flex;align-items:center;justify-content:center;gap:
|
|
3184
|
-
font-size:
|
|
3185
|
-
text-decoration:none;padding:
|
|
3252
|
+
.sl-powered{display:flex;align-items:center;justify-content:center;gap:5px;margin-top:2px;
|
|
3253
|
+
font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--sl-text);opacity:.72;
|
|
3254
|
+
text-decoration:none;padding:2px 8px;border-radius:999px;width:fit-content;margin-inline:auto;
|
|
3186
3255
|
transition:opacity .15s ease,background-color .15s ease}
|
|
3187
3256
|
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
3188
3257
|
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
@@ -3220,18 +3289,44 @@ var CSS2 = (
|
|
|
3220
3289
|
.sl-confirm-field:first-child .sl-confirm-value{font-size:13.5px;line-height:1.25;white-space:normal;
|
|
3221
3290
|
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
3222
3291
|
.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))}
|
|
3223
|
-
.sl-confirm-cat .sl-dot{border:2px solid
|
|
3224
|
-
.sl-confirm-cat-name{font-size:13.5px;font-weight:800;color
|
|
3225
|
-
.sl-confirm-price{font-size:17px;font-weight:850;color
|
|
3292
|
+
.sl-confirm-cat .sl-dot{border:2px solid color-mix(in srgb,var(--sl-cat-ink) 78%,transparent);width:11px;height:11px}
|
|
3293
|
+
.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}
|
|
3294
|
+
.sl-confirm-price{font-size:17px;font-weight:850;color:var(--sl-cat-ink);font-variant-numeric:tabular-nums}
|
|
3226
3295
|
.sl-confirm-body{padding:11px 12px 12px}
|
|
3296
|
+
.sl-confirm-tiers{display:grid;gap:6px;margin:0 0 9px;padding:0;border:0}
|
|
3297
|
+
.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}
|
|
3298
|
+
.sl-confirm-tier{display:flex;align-items:center;gap:9px;width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--sl-line);
|
|
3299
|
+
border-radius:9px;color:var(--sl-text);background:color-mix(in srgb,var(--sl-bg) 72%,var(--sl-surface));text-align:left}
|
|
3300
|
+
.sl-confirm-tier:hover{border-color:color-mix(in srgb,var(--sl-accent) 58%,var(--sl-line))}
|
|
3301
|
+
.sl-confirm-tier[aria-pressed="true"]{border-color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 13%,var(--sl-surface));
|
|
3302
|
+
box-shadow:inset 3px 0 0 var(--sl-accent)}
|
|
3303
|
+
.sl-confirm-tier-copy{min-width:0;flex:1;display:grid;gap:2px}
|
|
3304
|
+
.sl-confirm-tier-name{min-width:0;font-size:12.5px;font-weight:800}
|
|
3305
|
+
.sl-confirm-tier-note,.sl-tier-note{color:var(--sl-muted);font-size:10.5px;line-height:1.3}
|
|
3306
|
+
.sl-confirm-tier[aria-pressed="true"] .sl-confirm-tier-note{color:var(--sl-text)}
|
|
3307
|
+
.sl-confirm-tier-price{font-size:13px;font-weight:850;font-variant-numeric:tabular-nums}
|
|
3227
3308
|
.sl-confirm-row{display:flex;gap:8px;margin-top:10px}
|
|
3228
3309
|
.sl-confirm-row button{flex:1;min-height:44px;padding:9px 12px;border-radius:9px;font-weight:800;font-size:13px}
|
|
3229
3310
|
.sl-confirm-add{background:var(--sl-accent)!important;color:var(--sl-accent-ink)!important;display:flex;align-items:center;justify-content:center;gap:7px}
|
|
3230
3311
|
.sl-confirm-add svg{width:16px;height:16px;stroke:currentColor;stroke-width:2.8;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3231
3312
|
.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}
|
|
3232
3313
|
.sl-confirm-cancel:hover{color:var(--sl-text)}
|
|
3233
|
-
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(
|
|
3314
|
+
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(310px,calc(100% - 24px));
|
|
3234
3315
|
transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
3316
|
+
/* Phone-size confirm card (owner call 2026-08-14): same content \u2014 identity,
|
|
3317
|
+
price band, view photo, 3D link \u2014 on a tighter grid. The values stay large
|
|
3318
|
+
enough to double-check against the map; buttons keep tap-size. */
|
|
3319
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-field{padding:9px 9px 8px}
|
|
3320
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-value{font-size:15px}
|
|
3321
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-cat{padding:8px 10px}
|
|
3322
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-cat-name{font-size:12.5px}
|
|
3323
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-price{font-size:15px}
|
|
3324
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-body{padding:9px 10px 10px}
|
|
3325
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-tier{min-height:38px;padding:7px 9px}
|
|
3326
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-thumbwrap{height:62px;margin-bottom:6px}
|
|
3327
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-3d{margin-top:7px;padding:6px}
|
|
3328
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-row{margin-top:8px}
|
|
3329
|
+
.sl-picker[data-layout="narrow"] .sl-confirm-row button{min-height:42px}
|
|
3235
3330
|
|
|
3236
3331
|
/* Atomic whole/variable-table chooser. Lives inside the widget so the same
|
|
3237
3332
|
accessible dialog works in inline, modal, desktop and 390px mobile hosts. */
|
|
@@ -3251,7 +3346,7 @@ var CSS2 = (
|
|
|
3251
3346
|
.sl-table-stepper{display:grid;grid-template-columns:48px 1fr 48px;align-items:center;border:1px solid var(--sl-line);
|
|
3252
3347
|
border-radius:12px;overflow:hidden;background:var(--sl-bg)}
|
|
3253
3348
|
.sl-table-stepper button{height:48px;font-size:24px;font-weight:700;background:color-mix(in srgb,var(--sl-line) 34%,transparent)!important}
|
|
3254
|
-
.sl-table-stepper button:disabled{opacity:.
|
|
3349
|
+
.sl-table-stepper button:disabled{opacity:.58;cursor:not-allowed}
|
|
3255
3350
|
.sl-table-stepper output{text-align:center;font-size:19px;font-weight:850;font-variant-numeric:tabular-nums}
|
|
3256
3351
|
.sl-table-range{margin-top:7px;color:var(--sl-muted);font-size:11px;text-align:center}
|
|
3257
3352
|
.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}
|
|
@@ -3293,22 +3388,25 @@ var CSS2 = (
|
|
|
3293
3388
|
.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}
|
|
3294
3389
|
.sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
|
|
3295
3390
|
.sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
|
|
3296
|
-
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
3391
|
+
.sl-ba-title .spark{color:var(--sl-accent-text);font-size:16px}
|
|
3297
3392
|
.sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
3298
3393
|
.sl-ba-copy .narrow{display:none}
|
|
3299
3394
|
/* "\u2605 Best seats" premium quick-pick \u2014 gold accent echoing the \u2605 Premium pill on
|
|
3300
3395
|
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
3301
3396
|
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
3302
3397
|
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
3303
|
-
color:var(--sl-premium-
|
|
3398
|
+
color:var(--sl-premium-text);background:color-mix(in srgb,var(--sl-premium) 10%,var(--sl-surface));
|
|
3304
3399
|
border:1px solid color-mix(in srgb,var(--sl-premium) 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
3305
3400
|
.sl-ba-premium .star{font-size:12px;line-height:1;color:var(--sl-premium)}
|
|
3306
3401
|
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
3307
3402
|
.sl-ba-premium.on{color:var(--sl-premium-ink);background:linear-gradient(135deg,#f0cf6b,#e0b23f);border-color:transparent;
|
|
3308
3403
|
box-shadow:0 6px 16px color-mix(in srgb,var(--sl-premium) 26%,transparent)}
|
|
3309
3404
|
.sl-ba-premium.on .star{color:#5a4410}
|
|
3310
|
-
.sl-ba select
|
|
3311
|
-
|
|
3405
|
+
.sl-ba select,.sl-price-select{appearance:none;-webkit-appearance:none;
|
|
3406
|
+
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");
|
|
3407
|
+
background-repeat:no-repeat;background-position:right 10px center}
|
|
3408
|
+
.sl-ba select{background-color:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
3409
|
+
font:inherit;font-size:11px;padding:7px 26px 7px 8px;min-width:0;width:100%;max-width:none}
|
|
3312
3410
|
.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)}
|
|
3313
3411
|
.sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
|
|
3314
3412
|
font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
|
|
@@ -3338,9 +3436,23 @@ var CSS2 = (
|
|
|
3338
3436
|
.sl-picker[data-layout="narrow"] .sl-ba-copy{display:none}
|
|
3339
3437
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
3340
3438
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
3341
|
-
.sl-picker[data-layout="narrow"] .sl-ba
|
|
3439
|
+
.sl-picker[data-layout="narrow"] .sl-ba{grid-template-columns:auto minmax(0,1fr)}
|
|
3440
|
+
.sl-picker[data-layout="narrow"] .sl-ba select{grid-column:1/-1;min-height:38px}
|
|
3342
3441
|
.sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
|
|
3343
|
-
|
|
3442
|
+
/* Quantity and Go share ONE row: a full-width row holding only "\u2212 2 +" was
|
|
3443
|
+
dead space, and the Find button still gets the lion's share. */
|
|
3444
|
+
.sl-picker[data-layout="narrow"] .sl-ba-qty{grid-column:1}
|
|
3445
|
+
.sl-picker[data-layout="narrow"] .sl-ba-go{grid-column:2;min-height:40px}
|
|
3446
|
+
/* Re-entry to best-available once the cart has seats (the card yields its
|
|
3447
|
+
space to chips, but the accelerator must stay reachable) + the quiet way
|
|
3448
|
+
back out of a reopened card. */
|
|
3449
|
+
.sl-ba-reopen{align-self:flex-start;display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:5px 12px;margin-top:2px;
|
|
3450
|
+
border:1px dashed color-mix(in srgb,var(--sl-accent) 40%,var(--sl-line));border-radius:999px;
|
|
3451
|
+
color:var(--sl-accent-text);font-size:11.5px;font-weight:750;transition:border-color .15s,background .15s}
|
|
3452
|
+
.sl-ba-reopen:hover,.sl-ba-reopen:focus-visible{background:color-mix(in srgb,var(--sl-accent) 8%,transparent)}
|
|
3453
|
+
.sl-ba-back{grid-column:1/-1;justify-self:start;min-height:30px;padding:3px 0;color:var(--sl-muted);
|
|
3454
|
+
font-size:11px;font-weight:700;text-decoration:underline;text-underline-offset:3px;transition:color .15s}
|
|
3455
|
+
.sl-ba-back:hover,.sl-ba-back:focus-visible{color:var(--sl-text)}
|
|
3344
3456
|
|
|
3345
3457
|
/* Sales-closed is a designed state, not a set of disables: one statement in
|
|
3346
3458
|
the tray where the buying flow was, carrying the event's own date so the
|
|
@@ -3359,9 +3471,9 @@ var CSS2 = (
|
|
|
3359
3471
|
(the cart chip's remove/view rail) instead get real height on narrow. */
|
|
3360
3472
|
@media (pointer:coarse){
|
|
3361
3473
|
.sl-zoom button,.sl-cbbtn,.sl-close,.sl-ga-qty button,.sl-ba-qty button,.sl-price-more,
|
|
3362
|
-
.sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle{position:relative}
|
|
3474
|
+
.sl-hold-change,.sl-seccard-x,.sl-fs-pill,.sl-side-toggle,.sl-ba-reopen{position:relative}
|
|
3363
3475
|
.sl-zoom button::after,.sl-cbbtn::after,.sl-close::after,.sl-ga-qty button::after,.sl-ba-qty button::after,
|
|
3364
|
-
.sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after{
|
|
3476
|
+
.sl-price-more::after,.sl-hold-change::after,.sl-seccard-x::after,.sl-fs-pill::after,.sl-side-toggle::after,.sl-ba-reopen::after{
|
|
3365
3477
|
content:'';position:absolute;top:50%;left:50%;width:max(100%,44px);height:max(100%,44px);
|
|
3366
3478
|
transform:translate(-50%,-50%)}
|
|
3367
3479
|
/* The chip rail stacks remove over view in ~53px \u2014 pseudo-targets would
|
|
@@ -3458,18 +3570,12 @@ var CSS2 = (
|
|
|
3458
3570
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
3459
3571
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
3460
3572
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3461
|
-
.sl-view3d-nav button:disabled{opacity:.
|
|
3573
|
+
.sl-view3d-nav button:disabled{opacity:.6;cursor:not-allowed}
|
|
3462
3574
|
.sl-view3d-nav-toggle{display:none!important}
|
|
3463
3575
|
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
3464
3576
|
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
3465
3577
|
border:1px solid rgba(150,165,205,.45);backdrop-filter:blur(6px);cursor:pointer}
|
|
3466
3578
|
.sl-view3d-nav select:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
3467
|
-
.sl-view3d-nav .sl-view3d-locator{display:grid;grid-template-columns:minmax(150px,1.25fr) minmax(110px,.8fr) minmax(105px,.7fr) auto;
|
|
3468
|
-
width:min(720px,100%);overflow:visible}
|
|
3469
|
-
.sl-view3d-nav.is-seat-focused .sl-view3d-locator{display:none}
|
|
3470
|
-
.sl-view3d-locator select{width:100%;min-width:0}
|
|
3471
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav .sl-view3d-locator{display:flex;width:100%;overflow-x:auto}
|
|
3472
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-locator select{flex:0 0 155px}
|
|
3473
3579
|
/* On phones the library owns the bottom edge for seat/panorama/overview actions.
|
|
3474
3580
|
Keep venue navigation in a separate top rail so those control families never
|
|
3475
3581
|
stack over one another. */
|
|
@@ -3479,9 +3585,6 @@ var CSS2 = (
|
|
|
3479
3585
|
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open{left:12px;top:68px;max-width:calc(100% - 24px);padding:8px;
|
|
3480
3586
|
border:1px solid rgba(150,165,205,.35);border-radius:14px;background:rgba(8,12,22,.9);backdrop-filter:blur(10px)}
|
|
3481
3587
|
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open>div{display:flex}
|
|
3482
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator{display:grid;grid-template-columns:1fr 1fr;width:100%;overflow:visible}
|
|
3483
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator select{width:100%;min-width:0;flex:auto}
|
|
3484
|
-
.sl-picker[data-layout="narrow"] .sl-view3d-nav.is-open .sl-view3d-locator button{width:100%}
|
|
3485
3588
|
/* Seat-eye is a decision state, not another venue-navigation state. Once the
|
|
3486
3589
|
buyer arrives, clear the floor/area/locator rails and the module's duplicate
|
|
3487
3590
|
Overview action. The picker-owned Back button becomes the one predictable
|
|
@@ -3515,7 +3618,7 @@ var CSS2 = (
|
|
|
3515
3618
|
.sl-confirm-confidence strong,.sl-confirm-confidence small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3516
3619
|
.sl-confirm-confidence strong{font-size:11px}.sl-confirm-confidence small{margin-top:2px;color:var(--sl-muted);font-size:9.5px}
|
|
3517
3620
|
.sl-confirm-confidence em{display:none;font-style:normal}
|
|
3518
|
-
.sl-confirm-confidence>b{flex:none;font-size:11px;color:var(--sl-accent)}
|
|
3621
|
+
.sl-confirm-confidence>b{flex:none;font-size:11px;color:var(--sl-accent-text)}
|
|
3519
3622
|
.sl-confirm-confidence:hover,.sl-confirm-confidence:focus-visible{border-color:var(--sl-accent)}
|
|
3520
3623
|
/* The production 3D decision dock is deliberately denser than the 2D popup:
|
|
3521
3624
|
the venue remains the main content and the two inspection actions share one
|
|
@@ -3597,7 +3700,7 @@ var CSS2 = (
|
|
|
3597
3700
|
.sl-view3d-compare-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:12px}
|
|
3598
3701
|
.sl-view3d-compare-actions button{min-height:44px;border-radius:9px;border:1px solid var(--sl-line);font-size:12px;font-weight:800}
|
|
3599
3702
|
.sl-view3d-compare-actions .select{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3600
|
-
.sl-view3d-compare-actions button:disabled{opacity:.
|
|
3703
|
+
.sl-view3d-compare-actions button:disabled{opacity:.62;cursor:not-allowed}
|
|
3601
3704
|
.sl-view3d-passport-shell{position:absolute;inset:0;z-index:40;display:grid;place-items:center;padding:16px}
|
|
3602
3705
|
.sl-view3d-passport-scrim{position:absolute;inset:0;background:rgba(3,6,12,.8);backdrop-filter:blur(6px)}
|
|
3603
3706
|
.sl-view3d-passport{position:relative;width:min(540px,100%);max-height:min(700px,calc(100% - 20px));overflow:auto;
|
|
@@ -3638,14 +3741,17 @@ var CSS2 = (
|
|
|
3638
3741
|
.sl-confirm-3d:hover,.sl-confirm-3d:focus-visible{border-color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 20%,transparent)}
|
|
3639
3742
|
.sl-confirm-3d svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3640
3743
|
|
|
3641
|
-
/* multi-floor
|
|
3642
|
-
.sl-floors{display:none;
|
|
3744
|
+
/* One compact multi-floor selector (flows within the left-rail region). */
|
|
3745
|
+
.sl-floors{display:none;align-items:center;gap:6px;max-width:min(260px,100%);padding:5px;border-radius:999px;
|
|
3746
|
+
background:color-mix(in srgb,var(--sl-surface) 92%,transparent);border:1px solid var(--sl-line);
|
|
3747
|
+
box-shadow:var(--sl-shadow);backdrop-filter:blur(8px)}
|
|
3643
3748
|
.sl-floors.on{display:flex}
|
|
3644
|
-
.sl-floors
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
.sl-
|
|
3648
|
-
|
|
3749
|
+
.sl-floors select{min-width:0;max-width:210px;min-height:34px;padding:6px 30px 6px 10px;border:0;border-radius:999px;
|
|
3750
|
+
font:700 12px/1 inherit;color:var(--sl-text);background:var(--sl-surface);cursor:pointer}
|
|
3751
|
+
.sl-floors select:focus-visible,.sl-floor-info:focus-visible{outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
3752
|
+
.sl-floor-info{display:inline-grid;place-items:center;flex:0 0 30px;width:30px;height:30px;border-radius:999px;
|
|
3753
|
+
border:1px solid var(--sl-line);color:var(--sl-muted);background:transparent;font:800 13px/1 inherit;cursor:help}
|
|
3754
|
+
.sl-floor-info:hover{color:var(--sl-text);border-color:var(--sl-accent)}
|
|
3649
3755
|
|
|
3650
3756
|
/* tapped-section summary card \u2014 docks INSIDE the top-center anchor region on
|
|
3651
3757
|
wide (flows below the rung pills, never over them, never floating over the
|
|
@@ -3680,8 +3786,14 @@ var CSS2 = (
|
|
|
3680
3786
|
.sl-seccard-mix-dot{width:8px;height:8px;border-radius:50%;flex:none}
|
|
3681
3787
|
.sl-seccard-mix-price{font-weight:700;color:var(--sl-text)}
|
|
3682
3788
|
.sl-seccard-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:10px}
|
|
3683
|
-
.sl-seccard-overview{font-size:12px;font-weight:800;color:var(--sl-accent)}
|
|
3789
|
+
.sl-seccard-overview{font-size:12px;font-weight:800;color:var(--sl-accent-text)}
|
|
3684
3790
|
.sl-seccard-hint{font-size:10.5px;color:var(--sl-muted)}
|
|
3791
|
+
.sl-seccard-nav{display:inline-flex;align-items:center;gap:4px;margin-left:auto}
|
|
3792
|
+
.sl-seccard-nav button{width:28px;height:28px;border:1px solid var(--sl-line);border-radius:999px;color:var(--sl-text);
|
|
3793
|
+
background:color-mix(in srgb,var(--sl-surface) 88%,var(--sl-bg));font-size:14px;font-weight:850;line-height:1}
|
|
3794
|
+
.sl-seccard-nav button:hover,.sl-seccard-nav button:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent-text)}
|
|
3795
|
+
.sl-seccard.mini .sl-seccard-nav,.sl-seccard.strip .sl-seccard-nav{margin-left:2px}
|
|
3796
|
+
.sl-seccard.mini .sl-seccard-nav button,.sl-seccard.strip .sl-seccard-nav button{width:24px;height:24px;font-size:12px}
|
|
3685
3797
|
|
|
3686
3798
|
/* view-from-seat button on the confirm popover */
|
|
3687
3799
|
/* Eager sightline preview inside the confirm card */
|
|
@@ -3734,10 +3846,29 @@ var CSS2 = (
|
|
|
3734
3846
|
white-space:nowrap;pointer-events:none;max-width:90%;overflow:hidden;text-overflow:ellipsis}
|
|
3735
3847
|
|
|
3736
3848
|
/* F3 minimap \u2014 venue overview + live viewport rect (flows in the bottom-left region) */
|
|
3737
|
-
.sl-minimap{border:1px solid var(--sl-line);border-radius:9px;
|
|
3738
|
-
|
|
3849
|
+
.sl-minimap{border:1px solid var(--sl-line);border-radius:9px;overflow:hidden;background:var(--sl-surface);
|
|
3850
|
+
box-shadow:0 12px 34px -14px rgba(0,0,0,.55);line-height:0;touch-action:none}
|
|
3851
|
+
.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);
|
|
3852
|
+
color:var(--sl-muted);font-size:9px;font-weight:800;line-height:1.2;letter-spacing:.06em;text-transform:uppercase}
|
|
3853
|
+
.sl-minimap-label{min-width:0;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
3854
|
+
.sl-minimap-key{display:inline-flex;align-items:center;gap:3px;flex:none}
|
|
3855
|
+
.sl-minimap-key i{width:6px;height:6px;border-radius:999px;background:var(--sl-success)}
|
|
3856
|
+
.sl-minimap-key i.selected{background:var(--sl-accent)}
|
|
3857
|
+
.sl-minimap-key i.inactive{background:var(--sl-muted);opacity:.45}
|
|
3858
|
+
.sl-minimap-key i.focused{background:transparent;border:1.5px solid var(--sl-accent);width:7px;height:7px}
|
|
3859
|
+
.sl-minimap-close{display:none;width:24px;height:24px;border-radius:6px;color:var(--sl-muted);font-size:15px;line-height:1}
|
|
3739
3860
|
.sl-minimap canvas{display:block}
|
|
3861
|
+
.sl-minimap canvas:focus-visible{outline:2px solid var(--sl-accent);outline-offset:-3px}
|
|
3862
|
+
.sl-minimap canvas.sl-dragging{cursor:grabbing}
|
|
3863
|
+
.sl-minimap-toggle{display:none;width:42px;height:42px;border:1px solid var(--sl-line);border-radius:10px;background:var(--sl-surface);
|
|
3864
|
+
color:var(--sl-text);box-shadow:0 10px 28px -14px rgba(0,0,0,.65);align-items:center;justify-content:center}
|
|
3865
|
+
.sl-minimap-toggle svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linejoin:round}
|
|
3740
3866
|
.sl-picker[data-layout="narrow"] .sl-minimap{display:none}
|
|
3867
|
+
.sl-picker[data-layout="narrow"] .sl-minimap-toggle{display:flex}
|
|
3868
|
+
.sl-picker[data-layout="narrow"][data-minimap-open="true"] .sl-minimap{display:block}
|
|
3869
|
+
.sl-picker[data-layout="narrow"][data-minimap-open="true"] .sl-minimap-toggle{display:none}
|
|
3870
|
+
.sl-picker[data-layout="narrow"] .sl-minimap canvas{width:min(168px,42vw)!important;height:auto!important}
|
|
3871
|
+
.sl-picker[data-layout="narrow"] .sl-minimap-close{display:grid;place-items:center}
|
|
3741
3872
|
|
|
3742
3873
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
3743
3874
|
/* Out-of-band rows are exactly the rows a buyer reads while deciding whether
|
|
@@ -3815,17 +3946,88 @@ function formatWhen(startsAt, timezone, locale) {
|
|
|
3815
3946
|
}
|
|
3816
3947
|
return at.toLocaleString(locale, options);
|
|
3817
3948
|
}
|
|
3818
|
-
function
|
|
3949
|
+
function parseThemeColor(value) {
|
|
3950
|
+
if (!value) return null;
|
|
3951
|
+
const color = value.trim().toLowerCase();
|
|
3952
|
+
if (color === "white") return { r: 255, g: 255, b: 255 };
|
|
3953
|
+
if (color === "black") return { r: 0, g: 0, b: 0 };
|
|
3954
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(color)?.[1];
|
|
3955
|
+
if (hex) {
|
|
3956
|
+
const expanded = hex.length <= 4 ? [...hex].map((digit) => `${digit}${digit}`).join("") : hex;
|
|
3957
|
+
return {
|
|
3958
|
+
r: Number.parseInt(expanded.slice(0, 2), 16),
|
|
3959
|
+
g: Number.parseInt(expanded.slice(2, 4), 16),
|
|
3960
|
+
b: Number.parseInt(expanded.slice(4, 6), 16)
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
const rgb = /^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:\s*[,/]\s*[\d.]+%?)?\s*\)$/i.exec(color);
|
|
3964
|
+
if (!rgb) return null;
|
|
3965
|
+
return {
|
|
3966
|
+
r: Math.max(0, Math.min(255, Number(rgb[1]))),
|
|
3967
|
+
g: Math.max(0, Math.min(255, Number(rgb[2]))),
|
|
3968
|
+
b: Math.max(0, Math.min(255, Number(rgb[3])))
|
|
3969
|
+
};
|
|
3970
|
+
}
|
|
3971
|
+
function relativeLuminance(color) {
|
|
3972
|
+
const channel = (value) => {
|
|
3973
|
+
const normalized = value / 255;
|
|
3974
|
+
return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
3975
|
+
};
|
|
3976
|
+
return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
|
|
3977
|
+
}
|
|
3978
|
+
function themeContrast(foreground, background) {
|
|
3979
|
+
const fg = parseThemeColor(foreground);
|
|
3980
|
+
const bg = parseThemeColor(background);
|
|
3981
|
+
if (!fg || !bg) return null;
|
|
3982
|
+
const lighter = Math.max(relativeLuminance(fg), relativeLuminance(bg));
|
|
3983
|
+
const darker = Math.min(relativeLuminance(fg), relativeLuminance(bg));
|
|
3984
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
3985
|
+
}
|
|
3986
|
+
function highestContrastInk(background, dark = "#172033", light = "#eef1f8") {
|
|
3987
|
+
return (themeContrast(dark, background) ?? 0) >= (themeContrast(light, background) ?? 0) ? dark : light;
|
|
3988
|
+
}
|
|
3989
|
+
function rgbHex(color) {
|
|
3990
|
+
const channel = (value) => Math.round(value).toString(16).padStart(2, "0");
|
|
3991
|
+
return `#${channel(color.r)}${channel(color.g)}${channel(color.b)}`;
|
|
3992
|
+
}
|
|
3993
|
+
function mixThemeColors(first, second, firstWeight) {
|
|
3994
|
+
const a = parseThemeColor(first);
|
|
3995
|
+
const b = parseThemeColor(second);
|
|
3996
|
+
if (!a || !b) return null;
|
|
3997
|
+
const weight = Math.max(0, Math.min(1, firstWeight));
|
|
3998
|
+
return rgbHex({
|
|
3999
|
+
r: a.r * weight + b.r * (1 - weight),
|
|
4000
|
+
g: a.g * weight + b.g * (1 - weight),
|
|
4001
|
+
b: a.b * weight + b.b * (1 - weight)
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
function readableAccentText(accent, text, background, surface) {
|
|
4005
|
+
if ((themeContrast(accent, background) ?? 4.5) >= 4.5 && (themeContrast(accent, surface) ?? 4.5) >= 4.5) return accent;
|
|
4006
|
+
if (!parseThemeColor(accent) || !parseThemeColor(text)) return text;
|
|
4007
|
+
for (let textWeight = 0.15; textWeight <= 1; textWeight += 0.05) {
|
|
4008
|
+
const candidate = mixThemeColors(accent, text, 1 - textWeight);
|
|
4009
|
+
if (candidate && (themeContrast(candidate, background) ?? 0) >= 4.5 && (themeContrast(candidate, surface) ?? 0) >= 4.5) return candidate;
|
|
4010
|
+
}
|
|
4011
|
+
return text;
|
|
4012
|
+
}
|
|
4013
|
+
function resolvePickerTokens(chart, host) {
|
|
4014
|
+
const background = host?.background ?? chart?.background ?? "#0f1522";
|
|
4015
|
+
const isLight = parseThemeColor(background) ? highestContrastInk(background) === "#172033" : false;
|
|
4016
|
+
const surface = host?.surface ?? (isLight ? "#ffffff" : "#1a2234");
|
|
3819
4017
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
3820
|
-
const
|
|
4018
|
+
const chartAccentInk = chart?.accentInk;
|
|
4019
|
+
const accentInk = host?.accentInk ?? (chartAccentInk && (themeContrast(chartAccentInk, accent) ?? 4.5) >= 4.5 ? chartAccentInk : highestContrastInk(accent, "#1a1200", "#ffffff"));
|
|
4020
|
+
const chartText = chart?.textColor;
|
|
4021
|
+
const text = host?.text ?? (chartText && (themeContrast(chartText, background) ?? 4.5) >= 4.5 && (themeContrast(chartText, surface) ?? 4.5) >= 4.5 ? chartText : highestContrastInk(background));
|
|
3821
4022
|
return {
|
|
3822
4023
|
"--sl-accent": accent,
|
|
3823
4024
|
"--sl-accent-ink": accentInk,
|
|
3824
|
-
"--sl-
|
|
3825
|
-
"--sl-
|
|
3826
|
-
"--sl-
|
|
3827
|
-
"--sl-
|
|
3828
|
-
"--sl-
|
|
4025
|
+
"--sl-accent-text": readableAccentText(accent, text, background, surface),
|
|
4026
|
+
"--sl-bg": background,
|
|
4027
|
+
"--sl-surface": surface,
|
|
4028
|
+
"--sl-text": text,
|
|
4029
|
+
"--sl-muted": host?.muted ?? (isLight ? "#667085" : "#a5aec2"),
|
|
4030
|
+
"--sl-line": host?.line ?? (isLight ? "rgba(23,32,51,.16)" : "rgba(165,174,194,.24)"),
|
|
3829
4031
|
"--sl-font": host?.fontFamily ?? chart?.fontFamily ?? "-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,sans-serif",
|
|
3830
4032
|
"--sl-radius": `${host?.radius ?? 14}px`
|
|
3831
4033
|
};
|
|
@@ -3907,6 +4109,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
3907
4109
|
this.baZone = "";
|
|
3908
4110
|
/** "★ Best seats" premium quick-pick toggle — biases best-available to premium seats. */
|
|
3909
4111
|
this.baPremium = false;
|
|
4112
|
+
/**
|
|
4113
|
+
* The buyer re-opened the best-seats form with seats already in the cart
|
|
4114
|
+
* ("I picked one, actually find us three together"). The card normally
|
|
4115
|
+
* yields this space to the ticket chips the moment anything is selected —
|
|
4116
|
+
* which removed the accelerator at exactly the moment a one-handed phone
|
|
4117
|
+
* buyer wants it. Cleared when the cart empties, a hold lands, or the
|
|
4118
|
+
* buyer backs out.
|
|
4119
|
+
*/
|
|
4120
|
+
this.baReopen = false;
|
|
3910
4121
|
this.bestAvailableConfirm = false;
|
|
3911
4122
|
this.releasingHold = false;
|
|
3912
4123
|
/** Event sales window is closed (read-only load state / live close). */
|
|
@@ -3948,7 +4159,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
3948
4159
|
// F3 minimap
|
|
3949
4160
|
this.miniCanvas = null;
|
|
3950
4161
|
this.miniBase = null;
|
|
4162
|
+
this.miniWrap = null;
|
|
4163
|
+
this.miniToggle = null;
|
|
4164
|
+
this.miniLabel = null;
|
|
3951
4165
|
this.miniTf = null;
|
|
4166
|
+
this.miniDrag = null;
|
|
4167
|
+
this.miniSuppressClick = false;
|
|
4168
|
+
this.pickerLayoutSize = { width: 0, height: 0 };
|
|
3952
4169
|
// F4 price-band filter — active band's category keys (null = all prices)
|
|
3953
4170
|
this.priceBandKeys = null;
|
|
3954
4171
|
this.focusedCatKey = null;
|
|
@@ -3984,6 +4201,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3984
4201
|
/** Wide-layout ticket panel collapsed (map owns the full width). */
|
|
3985
4202
|
this.sideCollapsed = false;
|
|
3986
4203
|
this.sideToggleEl = null;
|
|
4204
|
+
this.pricesBtnEl = null;
|
|
3987
4205
|
/** True once we've asked the host page to pin us fullscreen (framed, no native). */
|
|
3988
4206
|
this.framedFs = false;
|
|
3989
4207
|
/** Last height (px) posted to a host frame; dedupes redundant reports. */
|
|
@@ -4048,6 +4266,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4048
4266
|
mapTheme: options.theme?.map ?? null,
|
|
4049
4267
|
onSelectionChange: () => {
|
|
4050
4268
|
this.syncTray();
|
|
4269
|
+
this.refreshMinimap();
|
|
4051
4270
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
4052
4271
|
this.syncSelectionTo3d();
|
|
4053
4272
|
},
|
|
@@ -4096,7 +4315,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4096
4315
|
if (this.tableDialog?.physicalSeatIds.includes(seat.id)) this.dismissTableDialog();
|
|
4097
4316
|
},
|
|
4098
4317
|
onSelectionLimit: () => {
|
|
4099
|
-
this.toast(
|
|
4318
|
+
this.toast((0, import_core2.t)("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
|
|
4100
4319
|
},
|
|
4101
4320
|
onViewChange: () => {
|
|
4102
4321
|
this.reanchorConfirm();
|
|
@@ -4107,6 +4326,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
4107
4326
|
},
|
|
4108
4327
|
// Tapped-section glide-in → surface (or clear) the section-summary card.
|
|
4109
4328
|
onSectionFocus: (summary) => this.showSectionCard(summary),
|
|
4329
|
+
onDeckTap: () => {
|
|
4330
|
+
this.showSectionCard(null);
|
|
4331
|
+
this.syncFloors();
|
|
4332
|
+
this.syncRung();
|
|
4333
|
+
this.refreshMinimap();
|
|
4334
|
+
},
|
|
4110
4335
|
onFocusSeat: (seat) => this.announceSeat(seat),
|
|
4111
4336
|
onSeatHover: (d) => this.updateTooltip(d),
|
|
4112
4337
|
onHint: (m) => {
|
|
@@ -4212,8 +4437,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
4212
4437
|
return `<span class="sl-cx-mark" role="img" aria-label="${title}" title="${title}">\u25D0</span>`;
|
|
4213
4438
|
}
|
|
4214
4439
|
wheelchairProvisionLabel(type) {
|
|
4215
|
-
if (type === "no-seat") return "Empty wheelchair space";
|
|
4216
|
-
if (type === "seat-present") return "Accessible physical seat";
|
|
4440
|
+
if (type === "no-seat") return this.tf("picker.emptyWheelchairSpace", "Empty wheelchair space");
|
|
4441
|
+
if (type === "seat-present") return this.tf("picker.accessiblePhysicalSeat", "Accessible physical seat");
|
|
4217
4442
|
return "";
|
|
4218
4443
|
}
|
|
4219
4444
|
wheelchairConfirmHtml(type) {
|
|
@@ -4294,9 +4519,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4294
4519
|
const hideEventDetails = this.eventDetailsHidden && !active;
|
|
4295
4520
|
this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
|
|
4296
4521
|
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
4297
|
-
this.els.zfs?.setAttribute("title", active ? "Exit full screen" : "Full screen");
|
|
4522
|
+
this.els.zfs?.setAttribute("title", active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen"));
|
|
4298
4523
|
const zfsLabel = this.els.zfs?.querySelector(".sl-zfs-lbl");
|
|
4299
|
-
if (zfsLabel) zfsLabel.textContent = active ? "Exit full screen" : "Full screen";
|
|
4524
|
+
if (zfsLabel) zfsLabel.textContent = active ? this.tf("picker.exitFullScreen", "Exit full screen") : this.tf("picker.fullScreen", "Full screen");
|
|
4300
4525
|
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
4301
4526
|
}
|
|
4302
4527
|
/**
|
|
@@ -4324,7 +4549,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4324
4549
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
4325
4550
|
this.fsEscHandler = null;
|
|
4326
4551
|
}
|
|
4327
|
-
requestAnimationFrame(() => this.controller.
|
|
4552
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4328
4553
|
}
|
|
4329
4554
|
setFsFallback(on) {
|
|
4330
4555
|
if (this.fsFallback === on) return;
|
|
@@ -4340,7 +4565,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4340
4565
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
4341
4566
|
this.fsEscHandler = null;
|
|
4342
4567
|
}
|
|
4343
|
-
requestAnimationFrame(() => this.controller.
|
|
4568
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4344
4569
|
}
|
|
4345
4570
|
/**
|
|
4346
4571
|
* Close the picker. In modal mode (SeatPicker.open()) this dismisses the
|
|
@@ -4361,7 +4586,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4361
4586
|
frame.className = "sl-modal-frame";
|
|
4362
4587
|
frame.setAttribute("role", "dialog");
|
|
4363
4588
|
frame.setAttribute("aria-modal", "true");
|
|
4364
|
-
frame.setAttribute("aria-label", "Seat selection");
|
|
4589
|
+
frame.setAttribute("aria-label", tfStatic("picker.seatSelection", "Seat selection"));
|
|
4365
4590
|
frame.tabIndex = -1;
|
|
4366
4591
|
scrim.appendChild(frame);
|
|
4367
4592
|
document.body.appendChild(scrim);
|
|
@@ -4480,11 +4705,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
4480
4705
|
}
|
|
4481
4706
|
async render() {
|
|
4482
4707
|
if (this.rendered) return this;
|
|
4708
|
+
const renderStartedAt = performance.now();
|
|
4709
|
+
const renderProfile = {};
|
|
4710
|
+
const duration = (startedAt) => Math.round((performance.now() - startedAt) * 100) / 100;
|
|
4483
4711
|
this.rendered = true;
|
|
4484
4712
|
ensureStyle2();
|
|
4713
|
+
const localeStartedAt = performance.now();
|
|
4485
4714
|
await (0, import_core2.loadLocale)(this.opts.locale);
|
|
4715
|
+
renderProfile.loadLocale = duration(localeStartedAt);
|
|
4486
4716
|
if (this.opts.messages) (0, import_core2.setStringOverrides)(this.opts.messages);
|
|
4487
4717
|
if (this.checkoutMode === "hosted") this.paymentOptions = this.pubApi.paymentOptions(this.opts.event);
|
|
4718
|
+
const shellStartedAt = performance.now();
|
|
4488
4719
|
const mount = resolveContainer3(this.opts.container);
|
|
4489
4720
|
const root = document.createElement("div");
|
|
4490
4721
|
root.className = "sl-picker";
|
|
@@ -4508,7 +4739,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4508
4739
|
this.syncTray();
|
|
4509
4740
|
}
|
|
4510
4741
|
});
|
|
4511
|
-
Object.entries(
|
|
4742
|
+
Object.entries(resolvePickerTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4512
4743
|
root.innerHTML = `
|
|
4513
4744
|
<div class="sl-head">
|
|
4514
4745
|
<div class="sl-logo" data-ref="logo"></div>
|
|
@@ -4521,7 +4752,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4521
4752
|
<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>
|
|
4522
4753
|
<span data-ref="closedPillText"></span>
|
|
4523
4754
|
</span>
|
|
4524
|
-
<button type="button" class="sl-close" data-ref="close" aria-label="Close">
|
|
4755
|
+
<button type="button" class="sl-close" data-ref="close" aria-label="${this.tf("picker.close", "Close")}">
|
|
4525
4756
|
<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>
|
|
4526
4757
|
</button>
|
|
4527
4758
|
</div>
|
|
@@ -4529,17 +4760,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
4529
4760
|
<div class="sl-map">
|
|
4530
4761
|
<div class="sl-map-host" data-ref="map"></div>
|
|
4531
4762
|
<div class="sl-zoom" data-ref="zoom">
|
|
4532
|
-
<button type="button" aria-label="Zoom in" title="Zoom in" data-ref="zin">+</button>
|
|
4533
|
-
<button type="button" aria-label="Zoom out" title="Zoom out" data-ref="zout">\u2212</button>
|
|
4534
|
-
<button type="button" aria-label="Fit to screen" title="Fit to screen" data-ref="zfit">
|
|
4763
|
+
<button type="button" aria-label="${this.tf("picker.zoomIn", "Zoom in")}" title="${this.tf("picker.zoomIn", "Zoom in")}" data-ref="zin">+</button>
|
|
4764
|
+
<button type="button" aria-label="${this.tf("picker.zoomOut", "Zoom out")}" title="${this.tf("picker.zoomOut", "Zoom out")}" data-ref="zout">\u2212</button>
|
|
4765
|
+
<button type="button" aria-label="${this.tf("picker.fitToScreen", "Fit to screen")}" title="${this.tf("picker.fitToScreen", "Fit to screen")}" data-ref="zfit">
|
|
4535
4766
|
<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>
|
|
4536
4767
|
</button>
|
|
4537
|
-
<button type="button" aria-label="Full screen" title="Full screen" aria-pressed="false" data-ref="zfs">
|
|
4768
|
+
<button type="button" aria-label="${this.tf("picker.fullScreen", "Full screen")}" title="${this.tf("picker.fullScreen", "Full screen")}" aria-pressed="false" data-ref="zfs">
|
|
4538
4769
|
<svg viewBox="0 0 24 24"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
|
4539
|
-
<span class="sl-zfs-lbl" aria-hidden="true"
|
|
4770
|
+
<span class="sl-zfs-lbl" aria-hidden="true">${this.tf("picker.fullScreen", "Full screen")}</span>
|
|
4540
4771
|
</button>
|
|
4541
4772
|
</div>
|
|
4542
|
-
<div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span
|
|
4773
|
+
<div class="sl-boot" data-ref="boot"><span class="sl-boot-spin"></span>${this.tf("picker.loadingSeatMap", "Loading seat map\u2026")}</div>
|
|
4543
4774
|
<div class="sl-toast" data-ref="toast" role="status" aria-live="polite"></div>
|
|
4544
4775
|
</div>
|
|
4545
4776
|
<div class="sl-side" data-ref="side">
|
|
@@ -4547,24 +4778,25 @@ var SeatPicker = class _SeatPicker {
|
|
|
4547
4778
|
<div class="sl-sheet-grab"></div>
|
|
4548
4779
|
<div class="sl-sheet-bar">
|
|
4549
4780
|
<div class="sl-sheet-peek" data-ref="peek"></div>
|
|
4550
|
-
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
|
|
4781
|
+
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="${this.tf("picker.openTicketPanel", "Open ticket panel")}" aria-expanded="false">
|
|
4551
4782
|
<svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
|
|
4552
4783
|
</button>
|
|
4553
4784
|
</div>
|
|
4554
4785
|
</div>
|
|
4555
|
-
<div class="sl-sec sl-filtersec" data-ref="filtersSec"
|
|
4786
|
+
<div class="sl-sec sl-filtersec" data-ref="filtersSec">${this.tf("picker.filters", "Filters")}</div>
|
|
4556
4787
|
<div class="sl-filters" data-ref="filters"></div>
|
|
4557
4788
|
<div class="sl-offer" data-ref="offer" role="status" aria-live="polite"></div>
|
|
4558
|
-
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span
|
|
4789
|
+
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>${this.tf("picker.ticketPrices", "Ticket prices")}</span></div>
|
|
4790
|
+
<div class="sl-prices-hint" data-ref="pricesHint"></div>
|
|
4559
4791
|
<div class="sl-prices" data-ref="prices"></div>
|
|
4560
|
-
<div class="sl-live" data-ref="live" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span><span data-ref="liveText"
|
|
4561
|
-
<div class="sl-sec sl-seats-sec"><span
|
|
4792
|
+
<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>
|
|
4793
|
+
<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>
|
|
4562
4794
|
<div class="sl-tray" data-ref="tray"></div>
|
|
4563
4795
|
<div class="sl-foot" data-ref="foot">
|
|
4564
4796
|
<div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
|
|
4565
4797
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
4566
|
-
<span><b data-ref="holdTitle"
|
|
4567
|
-
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats"
|
|
4798
|
+
<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>
|
|
4799
|
+
<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>
|
|
4568
4800
|
</div>
|
|
4569
4801
|
<div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
|
|
4570
4802
|
<button type="button" class="sl-cta" data-ref="cta" disabled></button>
|
|
@@ -4586,14 +4818,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
4586
4818
|
const applyLayout = () => {
|
|
4587
4819
|
const w = root.clientWidth;
|
|
4588
4820
|
if (w <= 0) return;
|
|
4821
|
+
const h = root.clientHeight;
|
|
4822
|
+
const sizeChanged = w !== this.pickerLayoutSize.width || h !== this.pickerLayoutSize.height;
|
|
4823
|
+
this.pickerLayoutSize = { width: w, height: h };
|
|
4589
4824
|
this.reportFramedHeight();
|
|
4590
4825
|
const next = w < 640 ? "narrow" : "wide";
|
|
4591
|
-
const density =
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4826
|
+
const density = h < 560 ? "compact" : "comfortable";
|
|
4827
|
+
const layoutChanged = root.dataset.layout !== next || root.dataset.density !== density;
|
|
4828
|
+
if (layoutChanged) {
|
|
4829
|
+
root.dataset.layout = next;
|
|
4830
|
+
root.dataset.density = density;
|
|
4831
|
+
if (next === "narrow" && !root.dataset.sheet) root.dataset.sheet = "peek";
|
|
4832
|
+
this.dockLayoutChrome();
|
|
4833
|
+
}
|
|
4834
|
+
if (sizeChanged && this.miniBase) this.refreshMinimap();
|
|
4597
4835
|
};
|
|
4598
4836
|
this.ro = new ResizeObserver(applyLayout);
|
|
4599
4837
|
this.ro.observe(root);
|
|
@@ -4606,7 +4844,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4606
4844
|
this.fsChangeHandler = () => {
|
|
4607
4845
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
4608
4846
|
this.syncFullscreenButtons();
|
|
4609
|
-
requestAnimationFrame(() => this.controller.
|
|
4847
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4610
4848
|
};
|
|
4611
4849
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
4612
4850
|
const head = this.els.sheetHead;
|
|
@@ -4615,7 +4853,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4615
4853
|
const setSheet = (open) => {
|
|
4616
4854
|
root.dataset.sheet = open ? "open" : "peek";
|
|
4617
4855
|
toggle?.setAttribute("aria-expanded", String(open));
|
|
4618
|
-
toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
|
|
4856
|
+
toggle?.setAttribute("aria-label", open ? this.tf("picker.collapseTicketPanel", "Collapse ticket panel") : this.tf("picker.openTicketPanel", "Open ticket panel"));
|
|
4619
4857
|
};
|
|
4620
4858
|
setSheet(root.dataset.sheet === "open");
|
|
4621
4859
|
toggle?.addEventListener("click", (e) => {
|
|
@@ -4661,6 +4899,39 @@ var SeatPicker = class _SeatPicker {
|
|
|
4661
4899
|
head.releasePointerCapture?.(e.pointerId);
|
|
4662
4900
|
});
|
|
4663
4901
|
}
|
|
4902
|
+
const togglePrices = () => {
|
|
4903
|
+
if (this.root?.dataset.layout !== "narrow") return;
|
|
4904
|
+
const open = this.root.getAttribute("data-prices-open") === "true";
|
|
4905
|
+
this.root.setAttribute("data-prices-open", String(!open));
|
|
4906
|
+
this.els.pricesSec?.setAttribute("aria-expanded", String(!open));
|
|
4907
|
+
this.pricesBtnEl?.setAttribute("aria-pressed", String(!open));
|
|
4908
|
+
if (!open && this.root.dataset.sheet !== "open") {
|
|
4909
|
+
this.root.dataset.sheet = "open";
|
|
4910
|
+
const sheetToggle = this.els.sheetToggle;
|
|
4911
|
+
sheetToggle?.setAttribute("aria-expanded", "true");
|
|
4912
|
+
sheetToggle?.setAttribute("aria-label", "Collapse ticket panel");
|
|
4913
|
+
}
|
|
4914
|
+
};
|
|
4915
|
+
this.els.pricesSec?.addEventListener("click", (e) => {
|
|
4916
|
+
if (e.target.closest("select,button")) return;
|
|
4917
|
+
togglePrices();
|
|
4918
|
+
});
|
|
4919
|
+
this.els.pricesSec?.addEventListener("keydown", (e) => {
|
|
4920
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
4921
|
+
if (e.target.closest("select,button")) return;
|
|
4922
|
+
e.preventDefault();
|
|
4923
|
+
togglePrices();
|
|
4924
|
+
});
|
|
4925
|
+
const pricesBtn = document.createElement("button");
|
|
4926
|
+
pricesBtn.type = "button";
|
|
4927
|
+
pricesBtn.className = "sl-prices-btn";
|
|
4928
|
+
pricesBtn.setAttribute("aria-label", this.tf("picker.ticketPrices", "Ticket prices"));
|
|
4929
|
+
pricesBtn.setAttribute("title", this.tf("picker.ticketPrices", "Ticket prices"));
|
|
4930
|
+
pricesBtn.setAttribute("aria-pressed", "false");
|
|
4931
|
+
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>';
|
|
4932
|
+
pricesBtn.addEventListener("click", () => togglePrices());
|
|
4933
|
+
this.els.headInfo?.parentElement?.insertBefore(pricesBtn, this.els.hold ?? null);
|
|
4934
|
+
this.pricesBtnEl = pricesBtn;
|
|
4664
4935
|
this.tipEl = document.createElement("div");
|
|
4665
4936
|
this.tipEl.setAttribute("role", "tooltip");
|
|
4666
4937
|
this.tipEl.className = "sl-tip";
|
|
@@ -4675,10 +4946,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
4675
4946
|
const canvasHost = document.createElement("div");
|
|
4676
4947
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
4677
4948
|
this.mapHost.appendChild(canvasHost);
|
|
4949
|
+
renderProfile.mountBuyerShell = duration(shellStartedAt);
|
|
4950
|
+
const chartStartedAt = performance.now();
|
|
4678
4951
|
const info = await this.controller.render(canvasHost);
|
|
4952
|
+
renderProfile.loadChartAndStatuses = duration(chartStartedAt);
|
|
4679
4953
|
if (this.destroyed) return this;
|
|
4680
4954
|
if (!info) {
|
|
4681
|
-
this.els.boot.innerHTML =
|
|
4955
|
+
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>`;
|
|
4682
4956
|
this.els.boot.querySelector("button").addEventListener("click", () => {
|
|
4683
4957
|
const container = this.opts.container;
|
|
4684
4958
|
const opts = this.opts;
|
|
@@ -4687,7 +4961,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4687
4961
|
});
|
|
4688
4962
|
return this;
|
|
4689
4963
|
}
|
|
4964
|
+
Object.assign(renderProfile, info.performanceProfile);
|
|
4690
4965
|
this.els.boot.remove();
|
|
4966
|
+
const controlsStartedAt = performance.now();
|
|
4691
4967
|
this.startRealtime();
|
|
4692
4968
|
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
4693
4969
|
root.dataset.eventMode = info.mode === "test" ? "test" : "live";
|
|
@@ -4703,7 +4979,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4703
4979
|
this.regions["top-left"].appendChild(badge);
|
|
4704
4980
|
}
|
|
4705
4981
|
const chartTheme = this.controller.doc?.theme;
|
|
4706
|
-
Object.entries(
|
|
4982
|
+
Object.entries(resolvePickerTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4707
4983
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
4708
4984
|
this.eventTimezone = info.timezone ?? null;
|
|
4709
4985
|
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl ?? info.posterUrl ?? null;
|
|
@@ -4713,6 +4989,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
4713
4989
|
const when = info.startsAt ? formatWhen(info.startsAt, info.timezone ?? null, this.opts.locale) : "";
|
|
4714
4990
|
this.eventWhenText = when || null;
|
|
4715
4991
|
this.els.meta.textContent = [info.venue, when].filter(Boolean).join(" \xB7 ");
|
|
4992
|
+
if (this.els.pricesHint) {
|
|
4993
|
+
this.els.pricesHint.textContent = this.tf(
|
|
4994
|
+
"picker.priceHint",
|
|
4995
|
+
"Colours on the map are ticket types \u2014 tap one below to show just those seats."
|
|
4996
|
+
);
|
|
4997
|
+
}
|
|
4716
4998
|
this.buildBadge(chartTheme);
|
|
4717
4999
|
const present = /* @__PURE__ */ new Set();
|
|
4718
5000
|
let hasLimitedView = false;
|
|
@@ -4739,7 +5021,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4739
5021
|
const mk = (key, label) => `<button type="button" class="sl-chip-f${key === "all" ? " on" : ""}" data-a11y="1" data-f="${key}">${label}</button>`;
|
|
4740
5022
|
chips.insertAdjacentHTML(
|
|
4741
5023
|
"beforeend",
|
|
4742
|
-
mk("all", "All seats") + import_core2.ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
|
|
5024
|
+
mk("all", this.tf("picker.allSeats", "All seats")) + import_core2.ACCESSIBILITY_TYPES.filter(({ key }) => present.has(key)).map(({ key, short, icon }) => mk(key, `${icon} ${short}`)).join("")
|
|
4743
5025
|
);
|
|
4744
5026
|
const active = /* @__PURE__ */ new Set();
|
|
4745
5027
|
const syncChips = () => {
|
|
@@ -4785,7 +5067,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4785
5067
|
cb.type = "button";
|
|
4786
5068
|
cb.className = "sl-cbbtn";
|
|
4787
5069
|
this.cbEl = cb;
|
|
4788
|
-
cb.setAttribute("aria-label", "Toggle colorblind-friendly colors");
|
|
5070
|
+
cb.setAttribute("aria-label", this.tf("picker.toggleColorblindColors", "Toggle colorblind-friendly colors"));
|
|
4789
5071
|
cb.setAttribute("aria-pressed", String(this.cbSafe));
|
|
4790
5072
|
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>';
|
|
4791
5073
|
this.els.zfit.parentElement.appendChild(cb);
|
|
@@ -4805,12 +5087,34 @@ var SeatPicker = class _SeatPicker {
|
|
|
4805
5087
|
this.buildPanelToggle();
|
|
4806
5088
|
if (this.opts.panelCollapsed) this.setPanelCollapsed(true);
|
|
4807
5089
|
this.dockLayoutChrome();
|
|
5090
|
+
renderProfile.buildBuyerControls = duration(controlsStartedAt);
|
|
5091
|
+
const holdStartedAt = performance.now();
|
|
4808
5092
|
await this.restoreRememberedHold();
|
|
5093
|
+
renderProfile.restoreHold = duration(holdStartedAt);
|
|
4809
5094
|
if (this.destroyed) return this;
|
|
4810
5095
|
if (this.salesClosed) this.applySalesClosed();
|
|
4811
5096
|
this.syncPrices();
|
|
4812
5097
|
this.syncTray();
|
|
4813
5098
|
this.resumeHostedOrder();
|
|
5099
|
+
const doc = this.controller.doc;
|
|
5100
|
+
this.emitAnalytics("chart_rendered", {
|
|
5101
|
+
renderingTimeMillis: duration(renderStartedAt),
|
|
5102
|
+
performanceProfile: renderProfile,
|
|
5103
|
+
seatCount: this.allSeats().length,
|
|
5104
|
+
objectCount: doc?.objects.length ?? 0,
|
|
5105
|
+
sectionCount: doc?.objects.filter((object) => object.type === "section").length ?? 0,
|
|
5106
|
+
view: this.buyerView,
|
|
5107
|
+
// Dimensions a host needs to read the timings above as anything other
|
|
5108
|
+
// than one undifferentiated number: WHICH chart, HOW big, WHICH data
|
|
5109
|
+
// path, and whether the browser had already warmed for this chart.
|
|
5110
|
+
// The event key is the chart's identity here — an event owns exactly one
|
|
5111
|
+
// chart — and the name is the same string already drawn in the header.
|
|
5112
|
+
eventKey: this.opts.event,
|
|
5113
|
+
chartName: doc?.name,
|
|
5114
|
+
floorCount: this.controller.getFloors().length,
|
|
5115
|
+
transport: this.opts.transport ? "custom" : "pubapi",
|
|
5116
|
+
load: markChartLoaded(this.opts.event) ? "repeat" : "cold"
|
|
5117
|
+
});
|
|
4814
5118
|
return this;
|
|
4815
5119
|
}
|
|
4816
5120
|
/**
|
|
@@ -4871,8 +5175,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4871
5175
|
if (!btn) return;
|
|
4872
5176
|
const collapsed = this.sideCollapsed;
|
|
4873
5177
|
btn.setAttribute("aria-expanded", String(!collapsed));
|
|
4874
|
-
btn.setAttribute("aria-label", collapsed ? "Show the ticket panel" : "Hide the ticket panel");
|
|
4875
|
-
btn.
|
|
5178
|
+
btn.setAttribute("aria-label", collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel"));
|
|
5179
|
+
btn.title = collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel");
|
|
5180
|
+
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>`;
|
|
4876
5181
|
}
|
|
4877
5182
|
/**
|
|
4878
5183
|
* Collapse or reopen the wide-layout ticket panel in place (host-drivable —
|
|
@@ -4885,7 +5190,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4885
5190
|
if (this.destroyed) return;
|
|
4886
5191
|
this.sideCollapsed = collapsed;
|
|
4887
5192
|
this.applyPanelCollapsed();
|
|
4888
|
-
this.scheduleMotion(() => this.controller.
|
|
5193
|
+
this.scheduleMotion(() => this.controller.refitCurrentView(), 340);
|
|
4889
5194
|
}
|
|
4890
5195
|
/** Idempotent DOM apply, also run on every layout flip (inert must lift on narrow). */
|
|
4891
5196
|
applyPanelCollapsed() {
|
|
@@ -4898,13 +5203,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
4898
5203
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
4899
5204
|
this.applyPanelCollapsed();
|
|
4900
5205
|
const zfs = this.els.zfs;
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
5206
|
+
const head = this.els.headInfo?.parentElement;
|
|
5207
|
+
if (zfs && head && zfs.parentElement !== head) {
|
|
5208
|
+
zfs.classList.add("sl-fs-pill");
|
|
5209
|
+
head.insertBefore(zfs, this.els.hold ?? null);
|
|
5210
|
+
}
|
|
5211
|
+
const pricesSec = this.els.pricesSec;
|
|
5212
|
+
if (pricesSec) {
|
|
5213
|
+
if (narrow) {
|
|
5214
|
+
pricesSec.setAttribute("role", "button");
|
|
5215
|
+
pricesSec.tabIndex = 0;
|
|
5216
|
+
pricesSec.setAttribute(
|
|
5217
|
+
"aria-expanded",
|
|
5218
|
+
String(this.root?.getAttribute("data-prices-open") === "true")
|
|
5219
|
+
);
|
|
5220
|
+
} else {
|
|
5221
|
+
pricesSec.removeAttribute("role");
|
|
5222
|
+
pricesSec.removeAttribute("aria-expanded");
|
|
5223
|
+
pricesSec.tabIndex = -1;
|
|
4908
5224
|
}
|
|
4909
5225
|
}
|
|
4910
5226
|
const filters = this.els.filters;
|
|
@@ -4931,7 +5247,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4931
5247
|
this.extendEl = el2;
|
|
4932
5248
|
this.els.extendTxt = el2.querySelector('[data-ref="extendTxt"]');
|
|
4933
5249
|
this.els.extendBtn = el2.querySelector('[data-ref="extendBtn"]');
|
|
4934
|
-
this.els.extendBtn.textContent = "Add time";
|
|
5250
|
+
this.els.extendBtn.textContent = this.tf("picker.addTime", "Add time");
|
|
4935
5251
|
this.els.extendBtn.addEventListener("click", () => void this.handleExtend());
|
|
4936
5252
|
}
|
|
4937
5253
|
/** Success overlay + onBooked fire when the held seats settle to booked. */
|
|
@@ -4940,7 +5256,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4940
5256
|
el2.className = "sl-booked";
|
|
4941
5257
|
el2.setAttribute("role", "status");
|
|
4942
5258
|
el2.setAttribute("aria-live", "polite");
|
|
4943
|
-
el2.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"
|
|
5259
|
+
el2.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>`;
|
|
4944
5260
|
this.root.appendChild(el2);
|
|
4945
5261
|
this.bookedEl = el2;
|
|
4946
5262
|
this.els.bookedSub = el2.querySelector('[data-ref="bookedSub"]');
|
|
@@ -4951,8 +5267,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4951
5267
|
* host `messages` override (which makes `t()` return the override, not the key).
|
|
4952
5268
|
*/
|
|
4953
5269
|
tf(key, fallback) {
|
|
4954
|
-
|
|
4955
|
-
return v === key ? fallback : v;
|
|
5270
|
+
return tfStatic(key, fallback);
|
|
4956
5271
|
}
|
|
4957
5272
|
/** Sold-out overlay — an informational state with no unavailable action. */
|
|
4958
5273
|
buildSoldoutOverlay() {
|
|
@@ -5137,7 +5452,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5137
5452
|
}
|
|
5138
5453
|
canAddTicket() {
|
|
5139
5454
|
if (this.totalTicketCount() < this.maxTickets) return true;
|
|
5140
|
-
this.toast(
|
|
5455
|
+
this.toast((0, import_core2.t)("picker.maxTicketsForOrder", { count: this.maxTickets }), "warning");
|
|
5141
5456
|
return false;
|
|
5142
5457
|
}
|
|
5143
5458
|
pendingGATotal(gaAreas) {
|
|
@@ -5160,21 +5475,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
5160
5475
|
}
|
|
5161
5476
|
if (this.confirmSeat || this.tableDialog && !this.tableDialogHeld) {
|
|
5162
5477
|
cta.disabled = true;
|
|
5163
|
-
cta.textContent = this.tableDialog ? "Confirm your table" : "Confirm or cancel this seat";
|
|
5478
|
+
cta.textContent = this.tableDialog ? this.tf("picker.confirmYourTable", "Confirm your table") : this.tf("picker.confirmOrCancelSeat", "Confirm or cancel this seat");
|
|
5164
5479
|
return;
|
|
5165
5480
|
}
|
|
5166
5481
|
if (this.ctaPhase === "holding") {
|
|
5167
5482
|
cta.disabled = true;
|
|
5168
|
-
cta.innerHTML =
|
|
5483
|
+
cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}`;
|
|
5169
5484
|
return;
|
|
5170
5485
|
}
|
|
5171
5486
|
if (this.ctaPhase === "checkout") {
|
|
5172
5487
|
cta.disabled = true;
|
|
5173
|
-
cta.innerHTML =
|
|
5488
|
+
cta.innerHTML = `<span class="sl-cta-spin" aria-hidden="true"></span>${this.tf("picker.openingCheckout", "Opening secure checkout\u2026")}`;
|
|
5174
5489
|
return;
|
|
5175
5490
|
}
|
|
5176
5491
|
cta.disabled = count === 0;
|
|
5177
|
-
cta.textContent = this.hold ? pending ?
|
|
5492
|
+
cta.textContent = this.hold ? pending ? (0, import_core2.t)("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");
|
|
5178
5493
|
}
|
|
5179
5494
|
setCtaPhase(phase) {
|
|
5180
5495
|
this.ctaPhase = phase;
|
|
@@ -5235,7 +5550,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5235
5550
|
this.syncTray();
|
|
5236
5551
|
this.emitHoldChange();
|
|
5237
5552
|
this.opts.onHoldRestored?.(restored, restored.seats ?? [], this.buildHandoff(restored));
|
|
5238
|
-
if (automatic) this.toast("Your held tickets have been restored.", "success");
|
|
5553
|
+
if (automatic) this.toast(this.tf("picker.heldTicketsRestored", "Your held tickets have been restored."), "success");
|
|
5239
5554
|
return restored;
|
|
5240
5555
|
} catch (error) {
|
|
5241
5556
|
const status = error?.status;
|
|
@@ -5270,10 +5585,77 @@ var SeatPicker = class _SeatPicker {
|
|
|
5270
5585
|
buildMinimap() {
|
|
5271
5586
|
const vp = this.controller.getViewport();
|
|
5272
5587
|
if (!vp || !this.els.map) return;
|
|
5273
|
-
const
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5588
|
+
const host = this.regions["bottom-left"] ?? this.els.map;
|
|
5589
|
+
const toggle = document.createElement("button");
|
|
5590
|
+
toggle.type = "button";
|
|
5591
|
+
toggle.className = "sl-minimap-toggle";
|
|
5592
|
+
toggle.setAttribute("aria-label", this.tf("picker.openVenueMap", "Open venue overview map"));
|
|
5593
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
5594
|
+
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>';
|
|
5595
|
+
host.appendChild(toggle);
|
|
5596
|
+
this.miniToggle = toggle;
|
|
5597
|
+
const wrap = document.createElement("div");
|
|
5598
|
+
wrap.className = "sl-minimap";
|
|
5599
|
+
wrap.setAttribute("role", "group");
|
|
5600
|
+
const bar = document.createElement("div");
|
|
5601
|
+
bar.className = "sl-minimap-bar";
|
|
5602
|
+
const label = document.createElement("span");
|
|
5603
|
+
label.className = "sl-minimap-label";
|
|
5604
|
+
const key = document.createElement("span");
|
|
5605
|
+
key.className = "sl-minimap-key";
|
|
5606
|
+
key.setAttribute("aria-label", this.tf("picker.minimapStateKey", "Focused, selected, available, and inactive sections"));
|
|
5607
|
+
key.innerHTML = '<i class="focused" title="Focused"></i><i class="selected" title="Selected"></i><i title="Available"></i><i class="inactive" title="Inactive"></i>';
|
|
5608
|
+
const close = document.createElement("button");
|
|
5609
|
+
close.type = "button";
|
|
5610
|
+
close.className = "sl-minimap-close";
|
|
5611
|
+
close.setAttribute("aria-label", this.tf("picker.closeVenueMap", "Close venue overview map"));
|
|
5612
|
+
close.textContent = "\xD7";
|
|
5613
|
+
bar.append(label, key, close);
|
|
5614
|
+
const canvas = document.createElement("canvas");
|
|
5615
|
+
canvas.tabIndex = 0;
|
|
5616
|
+
canvas.setAttribute("role", "application");
|
|
5617
|
+
canvas.setAttribute("aria-label", this.tf("picker.venueMapInstructions", "Venue overview map. Drag the viewport, click a section, or use arrow keys to pan."));
|
|
5618
|
+
wrap.append(bar, canvas);
|
|
5619
|
+
host.appendChild(wrap);
|
|
5620
|
+
this.miniWrap = wrap;
|
|
5621
|
+
this.miniLabel = label;
|
|
5622
|
+
this.miniCanvas = canvas;
|
|
5623
|
+
const base = document.createElement("canvas");
|
|
5624
|
+
this.miniBase = base;
|
|
5625
|
+
toggle.addEventListener("click", () => this.setMinimapOpen(true));
|
|
5626
|
+
close.addEventListener("click", () => this.setMinimapOpen(false));
|
|
5627
|
+
canvas.addEventListener("click", (event) => {
|
|
5628
|
+
if (this.miniSuppressClick) {
|
|
5629
|
+
this.miniSuppressClick = false;
|
|
5630
|
+
return;
|
|
5631
|
+
}
|
|
5632
|
+
this.minimapJump(event);
|
|
5633
|
+
});
|
|
5634
|
+
canvas.addEventListener("pointerdown", (event) => this.minimapPointerDown(event));
|
|
5635
|
+
canvas.addEventListener("pointermove", (event) => this.minimapPointerMove(event));
|
|
5636
|
+
canvas.addEventListener("pointerup", (event) => this.minimapPointerUp(event));
|
|
5637
|
+
canvas.addEventListener("pointercancel", (event) => this.minimapPointerUp(event));
|
|
5638
|
+
canvas.addEventListener("keydown", (event) => this.minimapKeydown(event));
|
|
5639
|
+
this.layoutMinimap();
|
|
5640
|
+
this.syncMinimapLabel();
|
|
5641
|
+
this.drawMinimapStatic();
|
|
5642
|
+
this.drawMinimapRect();
|
|
5643
|
+
}
|
|
5644
|
+
setMinimapOpen(open) {
|
|
5645
|
+
this.root?.setAttribute("data-minimap-open", String(open));
|
|
5646
|
+
this.miniToggle?.setAttribute("aria-expanded", String(open));
|
|
5647
|
+
if (open) this.miniCanvas?.focus();
|
|
5648
|
+
else this.miniToggle?.focus();
|
|
5649
|
+
}
|
|
5650
|
+
/** Re-fit the thumbnail whenever the active floor or complete overview
|
|
5651
|
+
* changes bounds. This is presentation-only; the source chart is untouched. */
|
|
5652
|
+
layoutMinimap() {
|
|
5653
|
+
const canvas = this.miniCanvas;
|
|
5654
|
+
const base = this.miniBase;
|
|
5655
|
+
const b = this.controller.getViewport()?.bounds;
|
|
5656
|
+
if (!canvas || !base || !b || !(b.width > 0 && b.height > 0)) return;
|
|
5657
|
+
const MAXW = 196;
|
|
5658
|
+
const MAXH = 146;
|
|
5277
5659
|
const PAD = 6;
|
|
5278
5660
|
const aspect = b.width / Math.max(1, b.height);
|
|
5279
5661
|
let w = MAXW;
|
|
@@ -5285,32 +5667,40 @@ var SeatPicker = class _SeatPicker {
|
|
|
5285
5667
|
w = Math.max(64, w);
|
|
5286
5668
|
h = Math.max(48, h);
|
|
5287
5669
|
const dpr = Math.min(2, window.devicePixelRatio || 1);
|
|
5288
|
-
const wrap = document.createElement("div");
|
|
5289
|
-
wrap.className = "sl-minimap";
|
|
5290
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
5291
|
-
const canvas = document.createElement("canvas");
|
|
5292
5670
|
canvas.width = Math.round(w * dpr);
|
|
5293
5671
|
canvas.height = Math.round(h * dpr);
|
|
5294
5672
|
canvas.style.width = `${w}px`;
|
|
5295
5673
|
canvas.style.height = `${h}px`;
|
|
5296
|
-
wrap.appendChild(canvas);
|
|
5297
|
-
(this.regions["bottom-left"] ?? this.els.map).appendChild(wrap);
|
|
5298
|
-
this.miniCanvas = canvas;
|
|
5299
|
-
const scale = Math.min((w - PAD * 2) / Math.max(1, b.width), (h - PAD * 2) / Math.max(1, b.height)) * dpr;
|
|
5300
|
-
const offX = (w * dpr - b.width * scale) / 2 - b.x * scale;
|
|
5301
|
-
const offY = (h * dpr - b.height * scale) / 2 - b.y * scale;
|
|
5302
|
-
this.miniTf = { scale, offX, offY, dpr };
|
|
5303
|
-
const base = document.createElement("canvas");
|
|
5304
5674
|
base.width = canvas.width;
|
|
5305
5675
|
base.height = canvas.height;
|
|
5306
|
-
this.
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
this.
|
|
5676
|
+
this.miniTf = (0, import_minimapGeometry.createMinimapTransform)(b, canvas.width, canvas.height, dpr, PAD);
|
|
5677
|
+
}
|
|
5678
|
+
syncMinimapLabel() {
|
|
5679
|
+
if (!this.miniLabel || !this.miniWrap) return;
|
|
5680
|
+
const floors = this.controller.getFloors();
|
|
5681
|
+
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");
|
|
5682
|
+
const sections = this.controller.getMinimapSnapshot().sections;
|
|
5683
|
+
const focused = sections.find((section) => section.active);
|
|
5684
|
+
const focusedLabel = focused?.label ?? this.lastSection?.label;
|
|
5685
|
+
const label = focusedLabel ? `${floor} \xB7 ${focusedLabel}` : floor;
|
|
5686
|
+
this.miniLabel.textContent = label;
|
|
5687
|
+
const selected = sections.filter((section) => section.state === "selected").length;
|
|
5688
|
+
const available = sections.filter((section) => section.state === "available").length;
|
|
5689
|
+
const inactive = sections.filter((section) => section.state === "inactive").length;
|
|
5690
|
+
this.miniWrap.setAttribute(
|
|
5691
|
+
"aria-label",
|
|
5692
|
+
sections.length ? `${label}. ${selected} selected, ${available} available, ${inactive} inactive sections.` : label
|
|
5693
|
+
);
|
|
5694
|
+
this.miniCanvas?.setAttribute(
|
|
5695
|
+
"aria-label",
|
|
5696
|
+
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.")}`
|
|
5697
|
+
);
|
|
5310
5698
|
}
|
|
5311
5699
|
/** Repaint the static overview + rect (floor switch, live open/close). */
|
|
5312
5700
|
refreshMinimap() {
|
|
5313
5701
|
if (!this.miniBase) return;
|
|
5702
|
+
this.layoutMinimap();
|
|
5703
|
+
this.syncMinimapLabel();
|
|
5314
5704
|
this.drawMinimapStatic();
|
|
5315
5705
|
this.drawMinimapRect();
|
|
5316
5706
|
}
|
|
@@ -5318,8 +5708,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5318
5708
|
drawMinimapStatic() {
|
|
5319
5709
|
const base = this.miniBase;
|
|
5320
5710
|
const tf = this.miniTf;
|
|
5321
|
-
|
|
5322
|
-
if (!base || !tf || !doc) return;
|
|
5711
|
+
if (!base || !tf) return;
|
|
5323
5712
|
const ctx = base.getContext("2d");
|
|
5324
5713
|
if (!ctx) return;
|
|
5325
5714
|
ctx.clearRect(0, 0, base.width, base.height);
|
|
@@ -5328,34 +5717,42 @@ var SeatPicker = class _SeatPicker {
|
|
|
5328
5717
|
const line = this.cssVar("--sl-line") || "rgba(139,147,167,.5)";
|
|
5329
5718
|
const muted = this.cssVar("--sl-muted") || "#8b93a7";
|
|
5330
5719
|
const accent = this.cssVar("--sl-accent") || "#6e7bff";
|
|
5331
|
-
const
|
|
5332
|
-
|
|
5333
|
-
for (const
|
|
5334
|
-
if (
|
|
5335
|
-
drewSection = true;
|
|
5336
|
-
const closed = this.controller.isSectionClosed(o.id);
|
|
5337
|
-
const fill = closed ? muted : o.color ?? (o.zone && zoneColor.get(o.zone)) ?? accent;
|
|
5720
|
+
const available = this.cssVar("--sl-success") || "#22a06b";
|
|
5721
|
+
const snapshot = this.controller.getMinimapSnapshot();
|
|
5722
|
+
for (const section of snapshot.sections) {
|
|
5723
|
+
if (section.outline.length < 3) continue;
|
|
5338
5724
|
ctx.beginPath();
|
|
5339
|
-
|
|
5725
|
+
section.outline.forEach((point, index) => index === 0 ? ctx.moveTo(fx(point.x), fy(point.y)) : ctx.lineTo(fx(point.x), fy(point.y)));
|
|
5340
5726
|
ctx.closePath();
|
|
5341
|
-
ctx.globalAlpha =
|
|
5342
|
-
ctx.fillStyle =
|
|
5727
|
+
ctx.globalAlpha = section.state === "inactive" ? 0.18 : section.state === "selected" ? 0.72 : 0.34;
|
|
5728
|
+
ctx.fillStyle = section.state === "inactive" ? muted : section.state === "selected" ? accent : available;
|
|
5343
5729
|
ctx.fill();
|
|
5344
|
-
ctx.globalAlpha = 0.
|
|
5730
|
+
ctx.globalAlpha = 0.82;
|
|
5345
5731
|
ctx.lineWidth = Math.max(1, tf.dpr);
|
|
5346
5732
|
ctx.strokeStyle = line;
|
|
5347
5733
|
ctx.stroke();
|
|
5734
|
+
if (section.active) {
|
|
5735
|
+
ctx.globalAlpha = 1;
|
|
5736
|
+
ctx.lineJoin = "round";
|
|
5737
|
+
ctx.lineWidth = Math.max(4, tf.dpr * 3.5);
|
|
5738
|
+
ctx.strokeStyle = this.cssVar("--sl-surface") || "#fff";
|
|
5739
|
+
ctx.stroke();
|
|
5740
|
+
ctx.lineWidth = Math.max(2, tf.dpr * 1.75);
|
|
5741
|
+
ctx.strokeStyle = accent;
|
|
5742
|
+
ctx.stroke();
|
|
5743
|
+
}
|
|
5348
5744
|
}
|
|
5349
5745
|
ctx.globalAlpha = 1;
|
|
5350
|
-
if (!
|
|
5746
|
+
if (!snapshot.sections.length) {
|
|
5351
5747
|
const r = Math.max(1, tf.dpr);
|
|
5352
|
-
for (const seat of
|
|
5353
|
-
|
|
5354
|
-
ctx.fillStyle =
|
|
5748
|
+
for (const seat of snapshot.seats) {
|
|
5749
|
+
ctx.globalAlpha = seat.state === "inactive" ? 0.22 : 0.78;
|
|
5750
|
+
ctx.fillStyle = seat.state === "inactive" ? muted : seat.state === "selected" ? accent : available;
|
|
5355
5751
|
ctx.beginPath();
|
|
5356
5752
|
ctx.arc(fx(seat.x), fy(seat.y), r, 0, Math.PI * 2);
|
|
5357
5753
|
ctx.fill();
|
|
5358
5754
|
}
|
|
5755
|
+
ctx.globalAlpha = 1;
|
|
5359
5756
|
}
|
|
5360
5757
|
}
|
|
5361
5758
|
/** Blit the base overview, then stroke the current viewport rectangle on top. */
|
|
@@ -5370,41 +5767,106 @@ var SeatPicker = class _SeatPicker {
|
|
|
5370
5767
|
ctx.drawImage(base, 0, 0);
|
|
5371
5768
|
const vp = this.controller.getViewport();
|
|
5372
5769
|
if (!vp) return;
|
|
5373
|
-
const
|
|
5374
|
-
const
|
|
5375
|
-
const
|
|
5376
|
-
const w = v.width * tf.scale;
|
|
5377
|
-
const h = v.height * tf.scale;
|
|
5378
|
-
const accent = this.cssVar("--sl-accent") || "#f4b740";
|
|
5770
|
+
const plan = (0, import_minimapGeometry.minimapViewportPlan)(vp.visible, tf, canvas.width, canvas.height, Math.max(2, tf.dpr * 2));
|
|
5771
|
+
const ink = this.cssVar("--sl-text") || "#12151c";
|
|
5772
|
+
const surface = this.cssVar("--sl-surface") || "#fff";
|
|
5379
5773
|
ctx.save();
|
|
5380
|
-
ctx.globalAlpha = 0.
|
|
5381
|
-
ctx.fillStyle =
|
|
5382
|
-
ctx.fillRect(x, y,
|
|
5774
|
+
ctx.globalAlpha = 0.1;
|
|
5775
|
+
ctx.fillStyle = ink;
|
|
5776
|
+
ctx.fillRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
5777
|
+
ctx.globalAlpha = 0.9;
|
|
5778
|
+
ctx.lineJoin = "round";
|
|
5779
|
+
ctx.lineWidth = Math.max(4, tf.dpr * 3.25);
|
|
5780
|
+
ctx.strokeStyle = surface;
|
|
5781
|
+
ctx.strokeRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
5383
5782
|
ctx.globalAlpha = 1;
|
|
5384
|
-
ctx.lineWidth = Math.max(
|
|
5385
|
-
ctx.strokeStyle =
|
|
5386
|
-
ctx.strokeRect(x, y,
|
|
5783
|
+
ctx.lineWidth = Math.max(2, tf.dpr * 1.75);
|
|
5784
|
+
ctx.strokeStyle = ink;
|
|
5785
|
+
ctx.strokeRect(plan.raw.x, plan.raw.y, plan.raw.width, plan.raw.height);
|
|
5786
|
+
if (plan.clipped && Object.values(plan.clippedEdges).some(Boolean)) {
|
|
5787
|
+
ctx.setLineDash([Math.max(3, tf.dpr * 3), Math.max(2, tf.dpr * 2)]);
|
|
5788
|
+
ctx.strokeRect(plan.clipped.x, plan.clipped.y, plan.clipped.width, plan.clipped.height);
|
|
5789
|
+
}
|
|
5387
5790
|
ctx.restore();
|
|
5388
5791
|
}
|
|
5389
|
-
|
|
5390
|
-
minimapJump(e) {
|
|
5792
|
+
minimapWorldPoint(event) {
|
|
5391
5793
|
const canvas = this.miniCanvas;
|
|
5392
5794
|
const tf = this.miniTf;
|
|
5393
|
-
if (!canvas || !tf) return;
|
|
5394
|
-
const
|
|
5395
|
-
const px = (
|
|
5396
|
-
const py = (
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5795
|
+
if (!canvas || !tf) return null;
|
|
5796
|
+
const rect = canvas.getBoundingClientRect();
|
|
5797
|
+
const px = (event.clientX - rect.left) * (canvas.width / Math.max(1, rect.width));
|
|
5798
|
+
const py = (event.clientY - rect.top) * (canvas.height / Math.max(1, rect.height));
|
|
5799
|
+
return { ...(0, import_minimapGeometry.minimapPointToWorld)({ x: px, y: py }, tf), px, py };
|
|
5800
|
+
}
|
|
5801
|
+
minimapPointerDown(event) {
|
|
5802
|
+
const point = this.minimapWorldPoint(event);
|
|
5803
|
+
const visible = this.controller.getViewport()?.visible;
|
|
5804
|
+
if (!point || !visible) return;
|
|
5805
|
+
const inside = point.x >= visible.x && point.x <= visible.x + visible.width && point.y >= visible.y && point.y <= visible.y + visible.height;
|
|
5806
|
+
if (!inside) return;
|
|
5807
|
+
this.miniDrag = {
|
|
5808
|
+
pointerId: event.pointerId,
|
|
5809
|
+
offsetX: point.x - (visible.x + visible.width / 2),
|
|
5810
|
+
offsetY: point.y - (visible.y + visible.height / 2),
|
|
5811
|
+
startX: event.clientX,
|
|
5812
|
+
startY: event.clientY,
|
|
5813
|
+
moved: false
|
|
5814
|
+
};
|
|
5815
|
+
this.miniCanvas?.setPointerCapture(event.pointerId);
|
|
5816
|
+
this.miniCanvas?.classList.add("sl-dragging");
|
|
5817
|
+
event.preventDefault();
|
|
5818
|
+
}
|
|
5819
|
+
minimapPointerMove(event) {
|
|
5820
|
+
const drag = this.miniDrag;
|
|
5821
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
5822
|
+
const point = this.minimapWorldPoint(event);
|
|
5823
|
+
if (!point) return;
|
|
5824
|
+
if (!drag.moved && Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 2) {
|
|
5825
|
+
drag.moved = true;
|
|
5826
|
+
this.controller.clearSectionFocus();
|
|
5827
|
+
}
|
|
5828
|
+
this.controller.panToWorld({ x: point.x - drag.offsetX, y: point.y - drag.offsetY });
|
|
5829
|
+
event.preventDefault();
|
|
5830
|
+
}
|
|
5831
|
+
minimapPointerUp(event) {
|
|
5832
|
+
const drag = this.miniDrag;
|
|
5833
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
5834
|
+
this.miniSuppressClick = drag.moved;
|
|
5835
|
+
this.miniDrag = null;
|
|
5836
|
+
this.miniCanvas?.classList.remove("sl-dragging");
|
|
5837
|
+
if (this.miniCanvas?.hasPointerCapture(event.pointerId)) this.miniCanvas.releasePointerCapture(event.pointerId);
|
|
5838
|
+
}
|
|
5839
|
+
minimapKeydown(event) {
|
|
5840
|
+
const visible = this.controller.getViewport()?.visible;
|
|
5841
|
+
if (!visible) return;
|
|
5842
|
+
if (event.key === "Home") {
|
|
5843
|
+
this.controller.overview();
|
|
5844
|
+
event.preventDefault();
|
|
5845
|
+
return;
|
|
5846
|
+
}
|
|
5847
|
+
const dx = event.key === "ArrowLeft" ? -visible.width * 0.2 : event.key === "ArrowRight" ? visible.width * 0.2 : 0;
|
|
5848
|
+
const dy = event.key === "ArrowUp" ? -visible.height * 0.2 : event.key === "ArrowDown" ? visible.height * 0.2 : 0;
|
|
5849
|
+
if (!dx && !dy) return;
|
|
5850
|
+
this.controller.clearSectionFocus();
|
|
5851
|
+
this.controller.panToWorld({
|
|
5852
|
+
x: visible.x + visible.width / 2 + dx,
|
|
5853
|
+
y: visible.y + visible.height / 2 + dy
|
|
5854
|
+
});
|
|
5855
|
+
event.preventDefault();
|
|
5856
|
+
}
|
|
5857
|
+
/** Minimap click → focus an available section, otherwise pan there. */
|
|
5858
|
+
minimapJump(e) {
|
|
5859
|
+
const point = this.minimapWorldPoint(e);
|
|
5860
|
+
if (!point) return;
|
|
5861
|
+
for (const section of this.controller.getMinimapSnapshot().sections) {
|
|
5862
|
+
if (section.state === "inactive" || section.outline.length < 3) continue;
|
|
5863
|
+
if (pointInPolygon(point.x, point.y, section.outline)) {
|
|
5864
|
+
this.controller.focusSection(section.id);
|
|
5404
5865
|
return;
|
|
5405
5866
|
}
|
|
5406
5867
|
}
|
|
5407
|
-
this.controller.
|
|
5868
|
+
this.controller.clearSectionFocus();
|
|
5869
|
+
this.controller.panToWorld(point);
|
|
5408
5870
|
}
|
|
5409
5871
|
// ---- F4 price-band filter -------------------------------------------------
|
|
5410
5872
|
/** Effective display price of a category: host pricing override → first tier → base. */
|
|
@@ -5413,32 +5875,39 @@ var SeatPicker = class _SeatPicker {
|
|
|
5413
5875
|
if (chart === void 0 || !c.key) return chart;
|
|
5414
5876
|
return this.paidPrice(c.key, c.tiers?.[0]?.id ?? null, chart);
|
|
5415
5877
|
}
|
|
5416
|
-
/**
|
|
5878
|
+
/** Complete buyer-visible price range, including every ticket tier and
|
|
5879
|
+
* tier-specific host override. */
|
|
5880
|
+
catPriceRange(c) {
|
|
5881
|
+
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)];
|
|
5882
|
+
return prices.length ? { min: Math.min(...prices), max: Math.max(...prices) } : void 0;
|
|
5883
|
+
}
|
|
5884
|
+
/** Derive category price bands without hiding the upper end of tier ranges. */
|
|
5417
5885
|
priceBands() {
|
|
5418
5886
|
const doc = this.controller.doc;
|
|
5419
5887
|
if (!doc) return [];
|
|
5420
|
-
const priced = doc.categories.map((c) => ({ key: c.key,
|
|
5888
|
+
const priced = doc.categories.map((c) => ({ key: c.key, range: this.catPriceRange(c) })).filter((x) => x.range != null);
|
|
5421
5889
|
if (!priced.length) return [];
|
|
5422
|
-
const distinct = [...new
|
|
5890
|
+
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);
|
|
5423
5891
|
if (distinct.length <= 5) {
|
|
5424
|
-
return distinct.map((
|
|
5425
|
-
id: `p${
|
|
5426
|
-
label: this.money(
|
|
5427
|
-
keys: priced.filter((
|
|
5428
|
-
min:
|
|
5429
|
-
max:
|
|
5892
|
+
return distinct.map((range) => ({
|
|
5893
|
+
id: `p${range.min}-${range.max}`,
|
|
5894
|
+
label: range.min === range.max ? this.money(range.min) : `${this.money(range.min)}\u2013${this.money(range.max)}`,
|
|
5895
|
+
keys: priced.filter((item) => item.range.min === range.min && item.range.max === range.max).map((item) => item.key),
|
|
5896
|
+
min: range.min,
|
|
5897
|
+
max: range.max
|
|
5430
5898
|
}));
|
|
5431
5899
|
}
|
|
5432
|
-
const
|
|
5900
|
+
const ordered = [...priced].sort((a, b) => a.range.min - b.range.min || a.range.max - b.range.max);
|
|
5901
|
+
const chunk = Math.ceil(ordered.length / 4);
|
|
5433
5902
|
const bands = [];
|
|
5434
|
-
for (let i = 0; i <
|
|
5435
|
-
const slice =
|
|
5436
|
-
const lo = slice
|
|
5437
|
-
const hi = slice
|
|
5903
|
+
for (let i = 0; i < ordered.length; i += chunk) {
|
|
5904
|
+
const slice = ordered.slice(i, i + chunk);
|
|
5905
|
+
const lo = Math.min(...slice.map((item) => item.range.min));
|
|
5906
|
+
const hi = Math.max(...slice.map((item) => item.range.max));
|
|
5438
5907
|
bands.push({
|
|
5439
5908
|
id: `b${i}`,
|
|
5440
5909
|
label: lo === hi ? this.money(lo) : `${this.money(lo)}\u2013${this.money(hi)}`,
|
|
5441
|
-
keys:
|
|
5910
|
+
keys: slice.map((item) => item.key),
|
|
5442
5911
|
min: lo,
|
|
5443
5912
|
max: hi
|
|
5444
5913
|
});
|
|
@@ -5453,8 +5922,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5453
5922
|
if (bands.length < 2) return;
|
|
5454
5923
|
const select = document.createElement("select");
|
|
5455
5924
|
select.className = "sl-price-select";
|
|
5456
|
-
select.setAttribute("aria-label", "Filter and focus seats by price");
|
|
5457
|
-
select.innerHTML = `<option value="all"
|
|
5925
|
+
select.setAttribute("aria-label", this.tf("picker.filterAndFocusByPrice", "Filter and focus seats by price"));
|
|
5926
|
+
select.innerHTML = `<option value="all">${this.tf("picker.allPrices", "All prices")}</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
|
|
5458
5927
|
this.els.pricesSec.appendChild(select);
|
|
5459
5928
|
select.addEventListener("change", () => {
|
|
5460
5929
|
const band = bands.find((candidate) => candidate.id === select.value);
|
|
@@ -5481,8 +5950,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5481
5950
|
const projection = document.createElement("div");
|
|
5482
5951
|
projection.className = "sl-projection";
|
|
5483
5952
|
projection.setAttribute("role", "group");
|
|
5484
|
-
projection.setAttribute("aria-label", "Venue view");
|
|
5485
|
-
projection.innerHTML =
|
|
5953
|
+
projection.setAttribute("aria-label", this.tf("picker.venueView", "Venue view"));
|
|
5954
|
+
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>`;
|
|
5486
5955
|
projection.querySelectorAll("button").forEach((button) => {
|
|
5487
5956
|
button.addEventListener("click", () => {
|
|
5488
5957
|
this.setBuyerView(button.dataset.view);
|
|
@@ -5493,7 +5962,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5493
5962
|
this.syncProjection();
|
|
5494
5963
|
}
|
|
5495
5964
|
if (hasSections) {
|
|
5496
|
-
const
|
|
5965
|
+
const allObjects = [
|
|
5966
|
+
...doc.objects,
|
|
5967
|
+
...(doc.floors ?? []).flatMap((floor) => floor.objects)
|
|
5968
|
+
];
|
|
5969
|
+
const authoredZoneIds = new Set((doc.zones ?? []).map((zone) => zone.id));
|
|
5970
|
+
const hasZones = allObjects.some((object) => object.type === "section" && typeof object.zone === "string" && authoredZoneIds.has(object.zone));
|
|
5971
|
+
const RUNGS = hasZones ? ["zones", "sections", "seats"] : ["sections", "seats"];
|
|
5497
5972
|
const pills = document.createElement("div");
|
|
5498
5973
|
pills.className = "sl-rungs on";
|
|
5499
5974
|
pills.setAttribute("role", "group");
|
|
@@ -5526,18 +6001,38 @@ var SeatPicker = class _SeatPicker {
|
|
|
5526
6001
|
const floors = this.controller.getFloors();
|
|
5527
6002
|
const rail = document.createElement("div");
|
|
5528
6003
|
rail.className = "sl-floors on";
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
6004
|
+
const select = document.createElement("select");
|
|
6005
|
+
select.setAttribute("aria-label", this.tf("picker.chooseLevel", "Choose level"));
|
|
6006
|
+
const overview = document.createElement("option");
|
|
6007
|
+
overview.value = "";
|
|
6008
|
+
overview.textContent = this.tf("picker.allFloors", "All floors");
|
|
6009
|
+
select.appendChild(overview);
|
|
6010
|
+
for (const floor of floors) {
|
|
6011
|
+
const option = document.createElement("option");
|
|
6012
|
+
option.value = floor.id;
|
|
6013
|
+
option.textContent = floor.name;
|
|
6014
|
+
select.appendChild(option);
|
|
6015
|
+
}
|
|
6016
|
+
select.addEventListener("change", () => {
|
|
6017
|
+
if (select.value === "") this.controller.setFloorOverview(true);
|
|
6018
|
+
else this.controller.setFloor(select.value);
|
|
6019
|
+
this.showSectionCard(null);
|
|
6020
|
+
this.syncFloors();
|
|
6021
|
+
this.syncRung();
|
|
6022
|
+
this.refreshMinimap();
|
|
5540
6023
|
});
|
|
6024
|
+
rail.appendChild(select);
|
|
6025
|
+
const info = document.createElement("button");
|
|
6026
|
+
info.type = "button";
|
|
6027
|
+
info.className = "sl-floor-info";
|
|
6028
|
+
info.textContent = "i";
|
|
6029
|
+
const explanation = this.tf(
|
|
6030
|
+
"picker.floorHelp",
|
|
6031
|
+
"All floors shows the complete venue. Choose one level to inspect and select its seats."
|
|
6032
|
+
);
|
|
6033
|
+
info.setAttribute("aria-label", explanation);
|
|
6034
|
+
info.title = explanation;
|
|
6035
|
+
rail.appendChild(info);
|
|
5541
6036
|
this.regions["left-rail"].appendChild(rail);
|
|
5542
6037
|
this.floorsEl = rail;
|
|
5543
6038
|
this.syncFloors();
|
|
@@ -5583,19 +6078,59 @@ var SeatPicker = class _SeatPicker {
|
|
|
5583
6078
|
syncFloors() {
|
|
5584
6079
|
if (!this.floorsEl) return;
|
|
5585
6080
|
const active = this.controller.getActiveFloorId();
|
|
5586
|
-
this.
|
|
5587
|
-
|
|
5588
|
-
|
|
6081
|
+
const overview = this.controller.isFloorOverview();
|
|
6082
|
+
const select = this.floorsEl.querySelector("select");
|
|
6083
|
+
if (select) select.value = overview ? "" : active;
|
|
5589
6084
|
}
|
|
5590
6085
|
/** Show (or clear, on null) the tapped-section summary card. */
|
|
5591
6086
|
showSectionCard(summary) {
|
|
5592
6087
|
this.lastSection = summary;
|
|
5593
6088
|
this.secCardEl?.remove();
|
|
5594
6089
|
this.secCardEl = null;
|
|
5595
|
-
if (!summary)
|
|
6090
|
+
if (!summary) {
|
|
6091
|
+
this.refreshMinimap();
|
|
6092
|
+
return;
|
|
6093
|
+
}
|
|
5596
6094
|
this.secCardCollapsed = this.controller.getRung() === "seats";
|
|
5597
6095
|
this.secCardShownAt = Date.now();
|
|
5598
6096
|
this.renderSectionCard(summary);
|
|
6097
|
+
this.refreshMinimap();
|
|
6098
|
+
}
|
|
6099
|
+
/**
|
|
6100
|
+
* Open sections next to the focused one, in authored order.
|
|
6101
|
+
*
|
|
6102
|
+
* This is intentionally a circular ring: arena sections are usually authored
|
|
6103
|
+
* clockwise, so moving past the last section should reach the first without
|
|
6104
|
+
* forcing a buyer back through the overview level. Closed or empty sections
|
|
6105
|
+
* are skipped because focusing one cannot produce a useful buyer card.
|
|
6106
|
+
*/
|
|
6107
|
+
sectionNeighbours(sectionId) {
|
|
6108
|
+
const sections = this.activeFloorObjects().filter((object) => object.type === "section" && !this.controller.isSectionClosed(object.id) && this.controller.getSectionSeatCount(object.id) > 0);
|
|
6109
|
+
if (sections.length < 2) return null;
|
|
6110
|
+
const index = sections.findIndex((section) => section.id === sectionId);
|
|
6111
|
+
if (index < 0) return null;
|
|
6112
|
+
return {
|
|
6113
|
+
previous: sections[(index - 1 + sections.length) % sections.length],
|
|
6114
|
+
next: sections[(index + 1) % sections.length]
|
|
6115
|
+
};
|
|
6116
|
+
}
|
|
6117
|
+
/** Previous/next controls shared by expanded, mini, and phone section cards. */
|
|
6118
|
+
sectionNavigationHtml(summary) {
|
|
6119
|
+
const neighbours = this.sectionNeighbours(summary.id);
|
|
6120
|
+
if (!neighbours) return "";
|
|
6121
|
+
const previousLabel = neighbours.previous.label || neighbours.previous.id;
|
|
6122
|
+
const nextLabel = neighbours.next.label || neighbours.next.id;
|
|
6123
|
+
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>`;
|
|
6124
|
+
}
|
|
6125
|
+
wireSectionNavigation(card) {
|
|
6126
|
+
card.querySelectorAll("[data-section-nav]").forEach((button) => {
|
|
6127
|
+
button.addEventListener("click", (event) => {
|
|
6128
|
+
event.preventDefault();
|
|
6129
|
+
event.stopPropagation();
|
|
6130
|
+
const sectionId = button.dataset.sectionNav;
|
|
6131
|
+
if (sectionId) this.controller.focusSection(sectionId);
|
|
6132
|
+
});
|
|
6133
|
+
});
|
|
5599
6134
|
}
|
|
5600
6135
|
/**
|
|
5601
6136
|
* Render the section card in the form the layout + state want: expanded card
|
|
@@ -5605,26 +6140,36 @@ var SeatPicker = class _SeatPicker {
|
|
|
5605
6140
|
renderSectionCard(summary) {
|
|
5606
6141
|
if (!this.els.map) return;
|
|
5607
6142
|
this.secCardEl?.remove();
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
6143
|
+
if (this.root?.dataset.layout === "narrow" && (this.hold || this.committedSelection().length || this.pendingGACount())) {
|
|
6144
|
+
this.lastSection = null;
|
|
6145
|
+
return;
|
|
6146
|
+
}
|
|
6147
|
+
const paidRanges = summary.categories.length ? summary.categories.map((item) => {
|
|
6148
|
+
const category = this.controller.doc?.categories.find((candidate) => candidate.key === item.key);
|
|
6149
|
+
return category ? this.catPriceRange(category) : { min: item.priceMin, max: item.priceMax };
|
|
6150
|
+
}).filter((range) => range != null) : [{ min: summary.priceMin, max: summary.priceMax }];
|
|
6151
|
+
const paidMin = Math.min(...paidRanges.map((range) => range.min));
|
|
6152
|
+
const paidMax = Math.max(...paidRanges.map((range) => range.max));
|
|
5611
6153
|
const priceLabel = paidMin === paidMax ? this.money(paidMin) : `${this.money(paidMin)}\u2013${this.money(paidMax)}`;
|
|
5612
6154
|
const leftLabel = (0, import_core2.tCount)("picker.seatsLeftInSection", summary.seatsLeft);
|
|
5613
6155
|
const xBtn = `<button type="button" class="sl-seccard-x" aria-label="${(0, import_core2.t)("picker.closeSectionSummary")}">\u2715</button>`;
|
|
6156
|
+
const sectionNav = this.sectionNavigationHtml(summary);
|
|
5614
6157
|
const card = document.createElement("div");
|
|
5615
6158
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
5616
6159
|
if (narrow) {
|
|
5617
6160
|
card.className = "sl-seccard strip on";
|
|
5618
6161
|
card.setAttribute("role", "status");
|
|
5619
6162
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5620
|
-
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;
|
|
6163
|
+
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;
|
|
6164
|
+
this.wireSectionNavigation(card);
|
|
5621
6165
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5622
6166
|
(this.els.sheetHead ?? this.els.side ?? this.els.map).appendChild(card);
|
|
5623
6167
|
} else if (this.secCardCollapsed) {
|
|
5624
6168
|
card.className = "sl-seccard mini on";
|
|
5625
6169
|
card.setAttribute("role", "button");
|
|
5626
6170
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5627
|
-
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;
|
|
6171
|
+
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;
|
|
6172
|
+
this.wireSectionNavigation(card);
|
|
5628
6173
|
card.addEventListener("click", (e) => {
|
|
5629
6174
|
if (e.target.closest(".sl-seccard-x")) return;
|
|
5630
6175
|
this.secCardCollapsed = false;
|
|
@@ -5639,9 +6184,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5639
6184
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5640
6185
|
const mix = summary.categories.map((c) => {
|
|
5641
6186
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
5642
|
-
|
|
6187
|
+
const category = this.controller.doc?.categories.find((candidate) => candidate.key === c.key);
|
|
6188
|
+
const range = category ? this.catPriceRange(category) : { min: c.priceMin, max: c.priceMax };
|
|
6189
|
+
const rangeLabel = range && range.min !== range.max ? `${this.money(range.min)}\u2013${this.money(range.max)}` : this.money(range?.min ?? c.price);
|
|
6190
|
+
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>`;
|
|
5643
6191
|
}).join("");
|
|
5644
|
-
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">${(0, import_core2.t)("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 ${(0, import_core2.t)("picker.overview")}</button
|
|
6192
|
+
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">${(0, import_core2.t)("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 ${(0, import_core2.t)("picker.overview")}</button>` + sectionNav + `<span class="sl-seccard-hint">${(0, import_core2.t)("picker.tapSeatHint")}</span></div>`;
|
|
6193
|
+
this.wireSectionNavigation(card);
|
|
5645
6194
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5646
6195
|
card.querySelector(".sl-seccard-overview").addEventListener("click", () => this.controller.overview());
|
|
5647
6196
|
(this.regions["top-center"] ?? this.els.map).appendChild(card);
|
|
@@ -5705,14 +6254,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
5705
6254
|
}
|
|
5706
6255
|
const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
|
|
5707
6256
|
const status = this.controller.getStatus(seat.id) ?? "free";
|
|
5708
|
-
const statusText = status === "free" ? "available" : status === "held" ? "on hold" : "taken";
|
|
5709
|
-
const
|
|
6257
|
+
const statusText = status === "free" ? this.tf("picker.statusAvailable", "available") : status === "held" ? this.tf("picker.statusOnHold", "on hold") : this.tf("picker.statusTaken2", "taken");
|
|
6258
|
+
const priceRange = cat ? this.catPriceRange(cat) : void 0;
|
|
6259
|
+
const priceText = priceRange ? priceRange.min === priceRange.max ? this.money(priceRange.min) : `${this.money(priceRange.min)}\u2013${this.money(priceRange.max)}` : void 0;
|
|
5710
6260
|
const table = this.controller.tableSelection(seat.id);
|
|
5711
6261
|
const details = table ?? this.controller.seatDetails(seat.id);
|
|
5712
6262
|
const typeWord = this.rowTypeWord(details);
|
|
5713
6263
|
const buyerName = details?.rowLabel ?? details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
5714
|
-
const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ?
|
|
5715
|
-
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${
|
|
6264
|
+
const identity = table ? `${typeWord} ${buyerName}, ${table.bookingMode === "variable" ? (0, import_core2.t)("picker.minToMaxGuests", { min: table.minOccupancy, max: table.maxOccupancy }) : (0, import_core2.t)("picker.capacityGuests", { count: table.capacity })}` : details?.objectType === "booth" ? `${typeWord} ${buyerName}` : details?.objectType === "table" ? `${typeWord} ${buyerName}, ${(0, import_core2.t)("picker.seatNumberLower", { label: details.seatNumber ?? seat.label })}` : (0, import_core2.t)("picker.seatLabel", { label: details?.displayLabel ?? seat.displayLabel ?? seat.label });
|
|
6265
|
+
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${priceText ? `, ${priceText}` : ""}, ${statusText}`;
|
|
5716
6266
|
}
|
|
5717
6267
|
// ---- atomic table confirmation / guest quantity ---------------------------
|
|
5718
6268
|
showTableDialog(table, held, returnFocus) {
|
|
@@ -5732,7 +6282,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5732
6282
|
const typeWord = this.rowTypeWord(table);
|
|
5733
6283
|
const el2 = document.createElement("div");
|
|
5734
6284
|
el2.className = "sl-table-scrim";
|
|
5735
|
-
el2.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 ?
|
|
6285
|
+
el2.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 ? (0, import_core2.t)("picker.flexiblePartyTypeWord", { typeWord }) : (0, import_core2.t)("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.") : (0, import_core2.t)("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>${(0, import_core2.t)("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>${(0, import_core2.t)("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">${(0, import_core2.t)("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">${(0, import_core2.t)("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>`;
|
|
5736
6286
|
this.root.appendChild(el2);
|
|
5737
6287
|
this.tableDialogEl = el2;
|
|
5738
6288
|
this.renderTableDialogState();
|
|
@@ -5791,17 +6341,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
5791
6341
|
const button = this.tableDialogEl?.querySelector(".sl-table-confirm");
|
|
5792
6342
|
if (button) {
|
|
5793
6343
|
button.disabled = true;
|
|
5794
|
-
button.textContent = held ? "Updating\u2026" : "Selecting\u2026";
|
|
6344
|
+
button.textContent = held ? this.tf("picker.updatingEllipsis", "Updating\u2026") : this.tf("picker.selectingEllipsis", "Selecting\u2026");
|
|
5795
6345
|
}
|
|
5796
6346
|
if (held) {
|
|
5797
6347
|
try {
|
|
5798
6348
|
const updated = await this.controller.replaceTableQuantity(table.label, table.quantity, this.opts.holdTtlMs);
|
|
5799
6349
|
if (!updated) {
|
|
5800
|
-
this.toast("That guest count could not be secured. Your current table hold is unchanged.", "warning");
|
|
6350
|
+
this.toast(this.tf("picker.guestCountCouldNotBeSecured", "That guest count could not be secured. Your current table hold is unchanged."), "warning");
|
|
5801
6351
|
this.renderTableDialogState();
|
|
5802
6352
|
if (button) {
|
|
5803
6353
|
button.disabled = false;
|
|
5804
|
-
button.textContent = "Update table";
|
|
6354
|
+
button.textContent = this.tf("picker.updateTable", "Update table");
|
|
5805
6355
|
}
|
|
5806
6356
|
return;
|
|
5807
6357
|
}
|
|
@@ -5815,13 +6365,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5815
6365
|
this.dismissTableDialog();
|
|
5816
6366
|
this.syncTray();
|
|
5817
6367
|
this.emitHoldChange();
|
|
5818
|
-
this.toast(
|
|
6368
|
+
this.toast((0, import_core2.t)("picker.tableUpdatedForGuests", { label: table.label, count: table.quantity }), "success");
|
|
5819
6369
|
} catch (error) {
|
|
5820
6370
|
this.opts.onError?.(error);
|
|
5821
|
-
this.toast("That guest count is no longer available. Your current hold is unchanged.", "error");
|
|
6371
|
+
this.toast(this.tf("picker.guestCountNoLongerAvailable", "That guest count is no longer available. Your current hold is unchanged."), "error");
|
|
5822
6372
|
if (button) {
|
|
5823
6373
|
button.disabled = false;
|
|
5824
|
-
button.textContent = "Update table";
|
|
6374
|
+
button.textContent = this.tf("picker.updateTable", "Update table");
|
|
5825
6375
|
}
|
|
5826
6376
|
}
|
|
5827
6377
|
return;
|
|
@@ -5829,7 +6379,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5829
6379
|
if (!this.controller.setTableQuantity(table.label, table.quantity)) {
|
|
5830
6380
|
if (button) {
|
|
5831
6381
|
button.disabled = false;
|
|
5832
|
-
button.textContent = table.bookingMode === "variable" ? "Select table" : "Select whole table";
|
|
6382
|
+
button.textContent = table.bookingMode === "variable" ? this.tf("picker.selectTable", "Select table") : this.tf("picker.selectWholeTable", "Select whole table");
|
|
5833
6383
|
}
|
|
5834
6384
|
return;
|
|
5835
6385
|
}
|
|
@@ -5875,16 +6425,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
5875
6425
|
'"': """
|
|
5876
6426
|
})[char]);
|
|
5877
6427
|
const identityFields = [
|
|
5878
|
-
details?.sectionLabel ? `<div class="sl-confirm-field"><span class="sl-confirm-key"
|
|
6428
|
+
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>` : "",
|
|
5879
6429
|
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>` : "",
|
|
5880
|
-
details?.objectType !== "booth" ? `<div class="sl-confirm-field"><span class="sl-confirm-key"
|
|
6430
|
+
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>` : ""
|
|
5881
6431
|
].filter(Boolean).join("");
|
|
6432
|
+
const tiers = details?.tiers ?? cat?.tiers ?? [];
|
|
6433
|
+
const selectedTierId = details?.tierId ?? tiers[0]?.id;
|
|
6434
|
+
const tierGuidance = (tier) => tier.buyerMessage?.trim() || (tier.restriction === "companion" ? this.tf("picker.companionRequiresWheelchair", "Requires the adjacent wheelchair place") : void 0);
|
|
6435
|
+
const tierChoices = tiers.length > 1 ? `<fieldset class="sl-confirm-tiers"><legend>${this.tf("picker.ticketType", "Ticket type")}</legend>` + tiers.map((tier) => {
|
|
6436
|
+
const guidance = tierGuidance(tier);
|
|
6437
|
+
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>`;
|
|
6438
|
+
}).join("") + `</fieldset>` : tiers[0] && tierGuidance(tiers[0]) ? `<p class="sl-confirm-tier-note">${safe(tierGuidance(tiers[0]))}</p>` : "";
|
|
5882
6439
|
const el2 = document.createElement("div");
|
|
5883
6440
|
el2.className = "sl-confirm";
|
|
5884
6441
|
el2.setAttribute("role", "dialog");
|
|
5885
|
-
el2.setAttribute("aria-label",
|
|
5886
|
-
|
|
5887
|
-
|
|
6442
|
+
el2.setAttribute("aria-label", (0, import_core2.t)("picker.confirmSeatLabel", { label: seat.label }));
|
|
6443
|
+
const categoryColor = cat?.color ?? "#6e7bff";
|
|
6444
|
+
const categoryBand = mixThemeColors(categoryColor, this.cssVar("--sl-surface") || "#1a2234", 0.76) ?? categoryColor;
|
|
6445
|
+
el2.style.setProperty("--sl-cat", categoryColor);
|
|
6446
|
+
el2.style.setProperty("--sl-cat-ink", highestContrastInk(categoryBand, "#172033", "#ffffff"));
|
|
6447
|
+
el2.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">${(0, import_core2.t)("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>`;
|
|
5888
6448
|
this.els.map.appendChild(el2);
|
|
5889
6449
|
this.confirmEl = el2;
|
|
5890
6450
|
const thumb = el2.querySelector(".sl-confirm-thumb");
|
|
@@ -5900,6 +6460,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
5900
6460
|
this.openSeatConfidencePassport(seat, event.currentTarget instanceof HTMLElement ? event.currentTarget : null);
|
|
5901
6461
|
});
|
|
5902
6462
|
el2.querySelector(".sl-confirm-compare")?.addEventListener("click", () => this.saveView3dComparisonSeat(seat));
|
|
6463
|
+
el2.querySelectorAll("[data-confirm-tier]").forEach((button) => {
|
|
6464
|
+
button.addEventListener("click", () => {
|
|
6465
|
+
const tierId = button.dataset.confirmTier;
|
|
6466
|
+
const tier = tiers.find((candidate) => candidate.id === tierId);
|
|
6467
|
+
if (!tier) return;
|
|
6468
|
+
this.controller.setSeatTier(seat.id, tier.id);
|
|
6469
|
+
el2.querySelectorAll("[data-confirm-tier]").forEach((candidate) => {
|
|
6470
|
+
candidate.setAttribute("aria-pressed", String(candidate === button));
|
|
6471
|
+
});
|
|
6472
|
+
const priceElement = el2.querySelector(".sl-confirm-price");
|
|
6473
|
+
if (priceElement) {
|
|
6474
|
+
priceElement.textContent = this.money(this.paidPrice(seat.categoryKey, tier.id, tier.price));
|
|
6475
|
+
}
|
|
6476
|
+
this.reanchorConfirm();
|
|
6477
|
+
});
|
|
6478
|
+
});
|
|
5903
6479
|
el2.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
5904
6480
|
if (this.buyerView === "venue3d") {
|
|
5905
6481
|
this.dismissConfirm();
|
|
@@ -6037,7 +6613,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6037
6613
|
el2.className = "sl-view";
|
|
6038
6614
|
el2.setAttribute("role", "dialog");
|
|
6039
6615
|
el2.setAttribute("aria-label", (0, import_core2.t)("picker.viewFromSeat", { label: seat.label }));
|
|
6040
|
-
el2.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("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 ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint"
|
|
6616
|
+
el2.innerHTML = `<div class="sl-view-head"><span class="sl-view-title">${(0, import_core2.t)("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 ? (0, import_core2.t)("picker.real360") : (0, import_core2.t)("picker.preview")}</span><span class="sl-view-hint">${this.tf("picker.dragToLookAround", "Drag to look around \xB7 scroll to zoom")}</span></div>`;
|
|
6041
6617
|
this.root.appendChild(el2);
|
|
6042
6618
|
this.viewEl = el2;
|
|
6043
6619
|
const pano = el2.querySelector(".sl-view-pano");
|
|
@@ -6233,27 +6809,27 @@ var SeatPicker = class _SeatPicker {
|
|
|
6233
6809
|
copy.className = "sl-offer-copy";
|
|
6234
6810
|
const kicker = document.createElement("span");
|
|
6235
6811
|
kicker.className = "sl-offer-kicker";
|
|
6236
|
-
kicker.textContent = active ? "Current ticket offer" : "Upcoming ticket offer";
|
|
6812
|
+
kicker.textContent = active ? this.tf("picker.currentTicketOffer", "Current ticket offer") : this.tf("picker.upcomingTicketOffer", "Upcoming ticket offer");
|
|
6237
6813
|
const name = document.createElement("strong");
|
|
6238
6814
|
name.className = "sl-offer-name";
|
|
6239
|
-
name.textContent = offer.name || (active ? "Current offer" : "Scheduled offer");
|
|
6815
|
+
name.textContent = offer.name || (active ? this.tf("picker.currentOffer", "Current offer") : this.tf("picker.scheduledOffer", "Scheduled offer"));
|
|
6240
6816
|
const line = document.createElement("span");
|
|
6241
6817
|
line.className = "sl-offer-line";
|
|
6242
6818
|
const facts = [];
|
|
6243
6819
|
if (active && availability.fromPrice != null) facts.push(this.money(availability.fromPrice / 100));
|
|
6244
|
-
if (active && offer.remaining != null) facts.push(
|
|
6245
|
-
if (active && offer.endsAt != null) facts.push(
|
|
6246
|
-
if (upcoming?.startsAt != null) facts.push(
|
|
6820
|
+
if (active && offer.remaining != null) facts.push((0, import_core2.t)("picker.offerRemainingAvailable", { count: offer.remaining }));
|
|
6821
|
+
if (active && offer.endsAt != null) facts.push((0, import_core2.t)("picker.offerUntil", { time: formatWhen(offer.endsAt, this.eventTimezone, this.opts.locale) }));
|
|
6822
|
+
if (upcoming?.startsAt != null) facts.push((0, import_core2.t)("picker.offerStarts", { time: formatWhen(upcoming.startsAt, this.eventTimezone, this.opts.locale) }));
|
|
6247
6823
|
line.textContent = facts.join(" \xB7 ");
|
|
6248
6824
|
copy.append(kicker, name, line);
|
|
6249
6825
|
const info = document.createElement("details");
|
|
6250
6826
|
info.className = "sl-offer-info";
|
|
6251
6827
|
const summary = document.createElement("summary");
|
|
6252
|
-
summary.setAttribute("aria-label",
|
|
6828
|
+
summary.setAttribute("aria-label", (0, import_core2.t)("picker.howOfferWorks", { name: name.textContent ?? "" }));
|
|
6253
6829
|
summary.textContent = "i";
|
|
6254
6830
|
const detail = document.createElement("div");
|
|
6255
6831
|
detail.className = "sl-offer-detail";
|
|
6256
|
-
detail.textContent = active ?
|
|
6832
|
+
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.");
|
|
6257
6833
|
info.append(summary, detail);
|
|
6258
6834
|
main.append(copy, info);
|
|
6259
6835
|
host.replaceChildren(main);
|
|
@@ -6276,21 +6852,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
6276
6852
|
const doc = this.controller.doc;
|
|
6277
6853
|
if (!doc || !this.els.prices) return;
|
|
6278
6854
|
const left = this.controller.categoryAvailability();
|
|
6279
|
-
|
|
6280
|
-
this.
|
|
6855
|
+
const sellable = doc.categories.filter((c) => !c.notForSale);
|
|
6856
|
+
this.narrateAvailability(sellable, left);
|
|
6857
|
+
this.syncSoldout(sellable, left);
|
|
6281
6858
|
const PRICE_LIMIT = 5;
|
|
6282
|
-
const overflow =
|
|
6859
|
+
const overflow = sellable.length - PRICE_LIMIT;
|
|
6283
6860
|
const collapsed = overflow > 1 && !this.pricesExpanded;
|
|
6284
|
-
const shown = collapsed ?
|
|
6861
|
+
const shown = collapsed ? sellable.slice(0, PRICE_LIMIT) : sellable;
|
|
6285
6862
|
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
6286
6863
|
this.els.prices.innerHTML = shown.map((c) => {
|
|
6287
6864
|
const price = this.catPrice(c);
|
|
6865
|
+
const range = this.catPriceRange(c);
|
|
6288
6866
|
const offer = this.offerPrice(c.key);
|
|
6289
6867
|
const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
|
|
6290
6868
|
const active = this.focusedCatKey === c.key;
|
|
6291
6869
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
6292
|
-
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" :
|
|
6293
|
-
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ?
|
|
6870
|
+
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") : (0, import_core2.t)("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">${(0, import_core2.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>`;
|
|
6871
|
+
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? (0, import_core2.t)("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>`;
|
|
6294
6872
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
6295
6873
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
6296
6874
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
@@ -6348,7 +6926,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6348
6926
|
const now = left[cat.key] ?? 0;
|
|
6349
6927
|
if (before === void 0 || now >= before) continue;
|
|
6350
6928
|
const taken = before - now;
|
|
6351
|
-
textEl.textContent =
|
|
6929
|
+
textEl.textContent = (0, import_core2.tCount)("picker.seatsJustTakenInCategory", taken, { label: cat.label, left: now });
|
|
6352
6930
|
this.els.live?.classList.remove("on");
|
|
6353
6931
|
void this.els.live?.offsetWidth;
|
|
6354
6932
|
this.els.live?.classList.add("on");
|
|
@@ -6366,12 +6944,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
6366
6944
|
const gone = this.controller.getSelection().filter((s) => !ownLabels.has(s.label) && (this.controller.getStatus(s.id) ?? "free") !== "free");
|
|
6367
6945
|
if (!gone.length) return;
|
|
6368
6946
|
this.controller.deselect(gone.map((s) => s.id));
|
|
6369
|
-
this.toast(
|
|
6947
|
+
this.toast((0, import_core2.t)("picker.seatJustTakenByAnother", { label: gone[0].label }), "error");
|
|
6370
6948
|
}
|
|
6371
6949
|
syncTray() {
|
|
6372
6950
|
if (!this.els.tray) return;
|
|
6373
6951
|
this.updateSelectionCapacity();
|
|
6374
6952
|
const seats = this.committedSelection();
|
|
6953
|
+
if (this.root?.dataset.layout === "narrow" && this.secCardEl && (this.hold || seats.length || this.pendingGACount())) {
|
|
6954
|
+
this.secCardEl.remove();
|
|
6955
|
+
this.secCardEl = null;
|
|
6956
|
+
this.lastSection = null;
|
|
6957
|
+
}
|
|
6375
6958
|
const gaAreas = this.controller.getGAAreas();
|
|
6376
6959
|
const heldItems = this.hold?.items ?? [];
|
|
6377
6960
|
const parts = [];
|
|
@@ -6382,18 +6965,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
6382
6965
|
`<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>`
|
|
6383
6966
|
);
|
|
6384
6967
|
} else if (!seats.length && !heldItems.length && !gaAreas.length) {
|
|
6385
|
-
parts.push(`<div class="sl-tray-hint"
|
|
6968
|
+
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>`);
|
|
6386
6969
|
} else if (!seats.length && !heldItems.length) {
|
|
6387
|
-
parts.push(`<div class="sl-tray-hint"
|
|
6970
|
+
parts.push(`<div class="sl-tray-hint">${this.tf("picker.trayHintTapOrStanding", "Tap a seat on the map \u2014 or grab standing tickets below.")}</div>`);
|
|
6388
6971
|
}
|
|
6389
6972
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
6390
|
-
if (
|
|
6391
|
-
|
|
6973
|
+
if (noPicks) this.baReopen = false;
|
|
6974
|
+
if (!this.salesClosed && !this.hold && (noPicks || this.baReopen || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
6975
|
+
const cats = (this.controller.doc?.categories ?? []).filter((c) => !c.notForSale);
|
|
6392
6976
|
const zones = this.controller.getBestAvailableZones();
|
|
6393
6977
|
if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
|
|
6394
|
-
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span
|
|
6978
|
+
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>${(0, import_core2.t)("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
|
|
6395
6979
|
// seats (same present-only philosophy as the a11y filter chips).
|
|
6396
|
-
(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=""
|
|
6980
|
+
(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")}` : (0, import_core2.tCount)("picker.findBestSeatsCount", this.baQty)) + `</button>` + // Reopened over an existing selection: the buyer needs the way back
|
|
6981
|
+
// stated as plainly as the way forward (Go routes through the
|
|
6982
|
+
// "Replace your current choices?" confirm, so nothing is lost yet).
|
|
6983
|
+
(this.baReopen ? `<button type="button" class="sl-ba-back" data-ba-close>${this.tf("picker.keepMyPicks", "Keep my picks")}</button>` : "") + `</div>`);
|
|
6397
6984
|
}
|
|
6398
6985
|
const idGrid = (seatId, label, objectType, quantity = 1, objectId, identity) => {
|
|
6399
6986
|
const esc = (value) => String(value ?? "\u2014").replace(/[&<>"]/g, (char) => ({
|
|
@@ -6405,21 +6992,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
6405
6992
|
const d = seatId ? this.controller.seatDetails(seatId) : null;
|
|
6406
6993
|
const area = objectType === "ga" ? gaAreas.find((candidate) => candidate.id === objectId) : void 0;
|
|
6407
6994
|
const effectiveType = objectType === "ga" ? "ga" : d?.objectType ?? objectType;
|
|
6408
|
-
const typeWord = identity?.displayType?.trim() || d?.displayType?.trim() || area?.displayType?.trim() || (effectiveType === "table" ? "Table" : effectiveType === "booth" ? "Booth" : effectiveType === "ga" ? "General admission" : "Row");
|
|
6995
|
+
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"));
|
|
6409
6996
|
const buyerName = identity?.rowLabel ?? identity?.displayLabel ?? d?.rowLabel ?? d?.displayLabel ?? area?.displayLabel ?? area?.label ?? label;
|
|
6410
6997
|
if (effectiveType === "table" && identity?.bookingMode) {
|
|
6411
|
-
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"
|
|
6998
|
+
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>`;
|
|
6412
6999
|
}
|
|
6413
7000
|
if (effectiveType === "ga") {
|
|
6414
|
-
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"
|
|
7001
|
+
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>`;
|
|
6415
7002
|
}
|
|
6416
7003
|
if (effectiveType === "booth") {
|
|
6417
|
-
return `<div class="sl-chip-id">` + (d?.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb"
|
|
7004
|
+
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>`;
|
|
6418
7005
|
}
|
|
6419
7006
|
if (!d?.sectionLabel && !d?.rowLabel && !d?.seatNumber) {
|
|
6420
|
-
return `<div class="sl-chip-id"><span class="fld sec"><span class="sl-chip-eb"
|
|
7007
|
+
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>`;
|
|
6421
7008
|
}
|
|
6422
|
-
return `<div class="sl-chip-id">` + (d.sectionLabel ? `<span class="fld sec"><span class="sl-chip-eb"
|
|
7009
|
+
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>`;
|
|
6423
7010
|
};
|
|
6424
7011
|
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="${(0, import_core2.t)("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>`;
|
|
6425
7012
|
for (const item of heldItems) {
|
|
@@ -6431,7 +7018,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6431
7018
|
const table = item.objectType === "table" ? this.controller.tableSelection(item.label) : null;
|
|
6432
7019
|
const canView2 = this.seatViewEnabled() && !!heldSeat && item.objectType !== "table";
|
|
6433
7020
|
parts.push(
|
|
6434
|
-
`<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}
|
|
7021
|
+
`<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)}">${(0, import_core2.t)("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((0, import_core2.t)("picker.removeHeldTicketLabel", { label: item.label }), canView2 ? item.label : null) + `</div>`
|
|
6435
7022
|
);
|
|
6436
7023
|
}
|
|
6437
7024
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -6441,14 +7028,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
6441
7028
|
nextTrayKeys.add(itemKey);
|
|
6442
7029
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
6443
7030
|
const tierSelect = s.tiers && s.tiers.length ? `<select class="tier" data-tier="${s.id}" aria-label="${(0, import_core2.t)("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>` : "";
|
|
7031
|
+
const chosenTier = s.tiers?.find((tier) => tier.id === s.tierId) ?? s.tiers?.[0];
|
|
7032
|
+
const tierNote = chosenTier?.buyerMessage?.trim() || (chosenTier?.restriction === "companion" ? this.tf("picker.companionRequiresWheelchair", "Requires the adjacent wheelchair place") : "");
|
|
6444
7033
|
parts.push(
|
|
6445
|
-
`<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}
|
|
7034
|
+
`<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)}">${(0, import_core2.t)("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((0, import_core2.t)("picker.removeSeatLabel", { label: s.label }), canView && s.objectType !== "table" ? s.label : null) + `</div>`
|
|
6446
7035
|
);
|
|
6447
7036
|
}
|
|
6448
7037
|
for (const area of gaAreas) {
|
|
6449
7038
|
const qty = this.gaQty.get(area.id) ?? 0;
|
|
6450
7039
|
parts.push(
|
|
6451
|
-
`<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}
|
|
7040
|
+
`<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 ${(0, import_core2.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>`
|
|
7041
|
+
);
|
|
7042
|
+
}
|
|
7043
|
+
if (!this.salesClosed && !this.hold && !noPicks && !this.baReopen && !this.bestAvailableBusy && !this.bestAvailableConfirm) {
|
|
7044
|
+
parts.push(
|
|
7045
|
+
`<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>`
|
|
6452
7046
|
);
|
|
6453
7047
|
}
|
|
6454
7048
|
this.els.tray.innerHTML = parts.join("");
|
|
@@ -6483,6 +7077,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
6483
7077
|
this.syncTray();
|
|
6484
7078
|
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
6485
7079
|
});
|
|
7080
|
+
this.els.tray.querySelector("[data-ba-reopen]")?.addEventListener("click", () => {
|
|
7081
|
+
this.baReopen = true;
|
|
7082
|
+
this.syncTray();
|
|
7083
|
+
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
7084
|
+
});
|
|
7085
|
+
this.els.tray.querySelector("[data-ba-close]")?.addEventListener("click", () => {
|
|
7086
|
+
this.baReopen = false;
|
|
7087
|
+
this.syncTray();
|
|
7088
|
+
this.els.tray.querySelector("[data-ba-reopen]")?.focus();
|
|
7089
|
+
});
|
|
6486
7090
|
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
6487
7091
|
this.bestAvailableConfirm = false;
|
|
6488
7092
|
void this.bestAvailable(this.baQty, this.baCat || void 0, { preferPremium: this.baPremium, zoneId: this.baZone || void 0 });
|
|
@@ -6495,15 +7099,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
6495
7099
|
return;
|
|
6496
7100
|
}
|
|
6497
7101
|
const id = chip.dataset.seat;
|
|
6498
|
-
const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? "Seat";
|
|
7102
|
+
const label = this.controller.getSelection().find((sel) => sel.id === id)?.label ?? this.tf("picker.seat", "Seat");
|
|
6499
7103
|
const remove = () => {
|
|
6500
7104
|
this.controller.deselect([id]);
|
|
6501
|
-
this.toast(
|
|
6502
|
-
label: "Undo",
|
|
7105
|
+
this.toast((0, import_core2.t)("picker.labelRemoved", { label }), "neutral", {
|
|
7106
|
+
label: this.tf("picker.undo", "Undo"),
|
|
6503
7107
|
onClick: () => {
|
|
6504
7108
|
const restored = this.controller.select([id]);
|
|
6505
7109
|
this.toast(
|
|
6506
|
-
restored.length ?
|
|
7110
|
+
restored.length ? (0, import_core2.t)("picker.labelRestored", { label }) : (0, import_core2.t)("picker.labelNoLongerAvailable", { label }),
|
|
6507
7111
|
restored.length ? "success" : "warning"
|
|
6508
7112
|
);
|
|
6509
7113
|
}
|
|
@@ -6575,31 +7179,31 @@ var SeatPicker = class _SeatPicker {
|
|
|
6575
7179
|
const pendingCount = this.pendingSelectionCount();
|
|
6576
7180
|
const previousCount = this.lastTrayCount;
|
|
6577
7181
|
const previousTotal = this.lastTrayTotal;
|
|
6578
|
-
this.els.count.textContent = count ?
|
|
7182
|
+
this.els.count.textContent = count ? (0, import_core2.tCount)("picker.ticketsCount", count) : this.tf("picker.noSeatsSelected", "No seats selected");
|
|
6579
7183
|
this.els.total.textContent = count ? this.money(total) : "";
|
|
6580
7184
|
this.root?.setAttribute("data-has-selection", String(count > 0));
|
|
6581
7185
|
this.root?.setAttribute(
|
|
6582
7186
|
"data-ba-active",
|
|
6583
|
-
String(this.bestAvailableConfirm || this.bestAvailableBusy)
|
|
7187
|
+
String(this.bestAvailableConfirm || this.bestAvailableBusy || this.baReopen)
|
|
6584
7188
|
);
|
|
6585
7189
|
this.els.foot?.classList.toggle("empty", count === 0);
|
|
6586
7190
|
if (this.els.seatSummary) {
|
|
6587
|
-
this.els.seatSummary.textContent = count ?
|
|
7191
|
+
this.els.seatSummary.textContent = count ? (0, import_core2.tCount)("picker.seatsSelectedCount", count) : "";
|
|
6588
7192
|
}
|
|
6589
7193
|
this.syncCta(count, pendingCount);
|
|
6590
7194
|
if (this.hold) {
|
|
6591
7195
|
const securedCount = heldCount || this.hold.seats?.length || 0;
|
|
6592
7196
|
if (this.els.holdTitle) {
|
|
6593
|
-
this.els.holdTitle.textContent =
|
|
7197
|
+
this.els.holdTitle.textContent = (0, import_core2.t)("picker.securedCount", { count: securedCount });
|
|
6594
7198
|
}
|
|
6595
7199
|
if (this.els.holdCopy) {
|
|
6596
7200
|
this.holdCopyPending = pendingCount > 0;
|
|
6597
|
-
this.els.holdCopy.textContent = pendingCount ?
|
|
7201
|
+
this.els.holdCopy.textContent = pendingCount ? (0, import_core2.t)("picker.moreSelectedCount", { count: pendingCount }) : this.holdReassuranceText();
|
|
6598
7202
|
}
|
|
6599
7203
|
const change = this.els.holdChange;
|
|
6600
7204
|
if (change) {
|
|
6601
7205
|
change.disabled = this.releasingHold;
|
|
6602
|
-
change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
|
|
7206
|
+
change.textContent = this.releasingHold ? this.tf("picker.releasingEllipsis", "Releasing\u2026") : this.tf("picker.change", "Change");
|
|
6603
7207
|
}
|
|
6604
7208
|
}
|
|
6605
7209
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
@@ -6624,21 +7228,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
6624
7228
|
renderPeek(count, total, pendingCount) {
|
|
6625
7229
|
if (!this.els.peek) return;
|
|
6626
7230
|
if (count && this.ctaPhase === "holding") {
|
|
6627
|
-
this.els.peek.innerHTML = `<span
|
|
7231
|
+
this.els.peek.innerHTML = `<span>${this.tf("picker.securingSeats", "Securing your seats\u2026")}</span>`;
|
|
6628
7232
|
return;
|
|
6629
7233
|
}
|
|
6630
7234
|
if (count && this.ctaPhase === "checkout") {
|
|
6631
|
-
this.els.peek.innerHTML = `<span
|
|
7235
|
+
this.els.peek.innerHTML = `<span>${(0, import_core2.t)("picker.peekSecured", { count, total: this.money(total) })}</span>`;
|
|
6632
7236
|
return;
|
|
6633
7237
|
}
|
|
6634
7238
|
if (count) {
|
|
6635
|
-
const
|
|
6636
|
-
this.els.peek.innerHTML = `<span>${
|
|
7239
|
+
const toCheckout = !!this.hold;
|
|
7240
|
+
this.els.peek.innerHTML = `<span>${(0, import_core2.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>`;
|
|
6637
7241
|
} else if (this.salesClosed) {
|
|
6638
7242
|
this.els.peek.innerHTML = `<span>${this.tf("picker.salesClosedPill", "Sales are closed")}</span>`;
|
|
6639
7243
|
} else {
|
|
6640
7244
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
6641
|
-
this.els.peek.innerHTML = (prices.length ? `<span
|
|
7245
|
+
this.els.peek.innerHTML = (prices.length ? `<span>${(0, import_core2.t)("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>`;
|
|
6642
7246
|
}
|
|
6643
7247
|
}
|
|
6644
7248
|
async removeHeldLabel(label, chip) {
|
|
@@ -6651,7 +7255,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6651
7255
|
const preserveAcrossNavigation = this.handedOff;
|
|
6652
7256
|
const released = await this.controller.releaseLabels([label]);
|
|
6653
7257
|
if (!released) {
|
|
6654
|
-
this.toast(
|
|
7258
|
+
this.toast((0, import_core2.t)("picker.couldNotRemoveLabel", { label }), "error");
|
|
6655
7259
|
return false;
|
|
6656
7260
|
}
|
|
6657
7261
|
const remaining = this.controller.currentHold();
|
|
@@ -6667,7 +7271,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6667
7271
|
}
|
|
6668
7272
|
this.syncTray();
|
|
6669
7273
|
this.emitHoldChange();
|
|
6670
|
-
this.toast(
|
|
7274
|
+
this.toast((0, import_core2.t)("picker.labelRemovedFromHold", { label }), "success");
|
|
6671
7275
|
return true;
|
|
6672
7276
|
} finally {
|
|
6673
7277
|
this.releasingLabels.delete(label);
|
|
@@ -6681,23 +7285,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
6681
7285
|
const button = this.els.holdChange;
|
|
6682
7286
|
if (button) {
|
|
6683
7287
|
button.disabled = true;
|
|
6684
|
-
button.textContent = "Releasing\u2026";
|
|
7288
|
+
button.textContent = this.tf("picker.releasingEllipsis", "Releasing\u2026");
|
|
6685
7289
|
}
|
|
6686
7290
|
try {
|
|
6687
7291
|
await this.release();
|
|
6688
|
-
if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
|
|
7292
|
+
if (!this.hold) this.toast(this.tf("picker.heldTicketsReleased", "Held tickets released. Choose your new seats."), "success");
|
|
6689
7293
|
} finally {
|
|
6690
7294
|
this.releasingHold = false;
|
|
6691
7295
|
if (button?.isConnected) {
|
|
6692
7296
|
button.disabled = false;
|
|
6693
|
-
button.textContent = "Change";
|
|
7297
|
+
button.textContent = this.tf("picker.change", "Change");
|
|
6694
7298
|
}
|
|
6695
7299
|
}
|
|
6696
7300
|
}
|
|
6697
7301
|
async handleCta() {
|
|
6698
7302
|
if (this.salesClosed) return;
|
|
6699
7303
|
if (this.totalTicketCount() > this.maxTickets) {
|
|
6700
|
-
this.toast(
|
|
7304
|
+
this.toast((0, import_core2.t)("picker.removeTicketsUntilOrFewer", { count: this.maxTickets }), "warning");
|
|
6701
7305
|
return;
|
|
6702
7306
|
}
|
|
6703
7307
|
const committed = this.committedSelection();
|
|
@@ -6723,7 +7327,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6723
7327
|
hold = h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : hold;
|
|
6724
7328
|
}
|
|
6725
7329
|
if (!hold) {
|
|
6726
|
-
this.toast("One or more seats were just taken. Please pick again.", "error");
|
|
7330
|
+
this.toast(this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again."), "error");
|
|
6727
7331
|
this.setCtaPhase("idle");
|
|
6728
7332
|
this.syncTray();
|
|
6729
7333
|
return;
|
|
@@ -6740,7 +7344,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6740
7344
|
const problem = err;
|
|
6741
7345
|
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
6742
7346
|
if (problem.reason === "event_closed") this.setSalesClosed(true);
|
|
6743
|
-
const message = problem.reason === "event_closed" ? "Seat sales have closed for this event." :
|
|
7347
|
+
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 ? (0, import_core2.t)("picker.labelsNoLongerAvailable.one", { labels: labels.join(", ") }) : (0, import_core2.t)("picker.labelsNoLongerAvailable.other", { labels: labels.join(", ") }) : this.tf("picker.seatsJustTaken", "One or more seats were just taken. Please pick again.");
|
|
6744
7348
|
this.toast(message, "error");
|
|
6745
7349
|
this.setCtaPhase("idle");
|
|
6746
7350
|
} finally {
|
|
@@ -6754,14 +7358,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
6754
7358
|
const ms = Math.max(0, this.holdExpiresAt - Date.now());
|
|
6755
7359
|
const m = Math.floor(ms / 6e4);
|
|
6756
7360
|
const s = String(Math.floor(ms % 6e4 / 1e3)).padStart(2, "0");
|
|
6757
|
-
return
|
|
7361
|
+
return (0, import_core2.t)("picker.holdReassurance", { time: `${m}:${s}` });
|
|
6758
7362
|
}
|
|
6759
7363
|
startHoldTimer(expiresAt) {
|
|
6760
7364
|
this.stopHoldTimer();
|
|
6761
7365
|
this.holdExpiresAt = expiresAt;
|
|
6762
7366
|
if (this.hold) this.rememberHold(this.hold);
|
|
6763
7367
|
const pill = this.els.hold;
|
|
6764
|
-
pill.innerHTML =
|
|
7368
|
+
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>`;
|
|
6765
7369
|
const time = pill.querySelector('[data-ref="holdTime"]');
|
|
6766
7370
|
this.els.holdNote?.classList.add("on");
|
|
6767
7371
|
const tick = () => {
|
|
@@ -6794,7 +7398,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6794
7398
|
const totalSecs = Math.ceil(ms / 1e3);
|
|
6795
7399
|
const m = Math.floor(totalSecs / 60);
|
|
6796
7400
|
const s = String(totalSecs % 60).padStart(2, "0");
|
|
6797
|
-
this.els.extendTxt.innerHTML =
|
|
7401
|
+
this.els.extendTxt.innerHTML = (0, import_core2.t)("picker.seatsHeldForNeedMoreTime", { time: `${m}:${s}` });
|
|
6798
7402
|
this.extendEl.classList.add("on");
|
|
6799
7403
|
} else {
|
|
6800
7404
|
this.extendEl.classList.remove("on");
|
|
@@ -6804,7 +7408,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6804
7408
|
const btn = this.els.extendBtn;
|
|
6805
7409
|
btn.disabled = true;
|
|
6806
7410
|
const prev = btn.textContent;
|
|
6807
|
-
btn.textContent = "Adding\u2026";
|
|
7411
|
+
btn.textContent = this.tf("picker.addingEllipsis", "Adding\u2026");
|
|
6808
7412
|
try {
|
|
6809
7413
|
const h = await this.controller.extendHold(this.opts.holdTtlMs);
|
|
6810
7414
|
if (h) {
|
|
@@ -6813,13 +7417,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
6813
7417
|
this.extendEl?.classList.remove("on");
|
|
6814
7418
|
this.rememberHold(this.hold);
|
|
6815
7419
|
this.emitHoldChange();
|
|
6816
|
-
this.toast("More time added \u2014 your seats are still held.", "success");
|
|
7420
|
+
this.toast(this.tf("picker.moreTimeAdded", "More time added \u2014 your seats are still held."), "success");
|
|
6817
7421
|
} else {
|
|
6818
|
-
this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
|
|
7422
|
+
this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
|
|
6819
7423
|
}
|
|
6820
7424
|
} catch (err) {
|
|
6821
7425
|
this.opts.onError?.(err);
|
|
6822
|
-
this.toast("Couldn't add more time \u2014 please head to checkout now.", "warning");
|
|
7426
|
+
this.toast(this.tf("picker.couldNotAddMoreTime", "Couldn't add more time \u2014 please head to checkout now."), "warning");
|
|
6823
7427
|
} finally {
|
|
6824
7428
|
btn.disabled = false;
|
|
6825
7429
|
btn.textContent = prev;
|
|
@@ -6846,7 +7450,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6846
7450
|
this.forgetHold();
|
|
6847
7451
|
const n = handoff.lineItems.reduce((sum, i) => sum + i.quantity, 0);
|
|
6848
7452
|
if (this.els.bookedSub) {
|
|
6849
|
-
this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${n} ${
|
|
7453
|
+
this.els.bookedSub.innerHTML = `<span class="sl-booked-seats">${(0, import_core2.tCount)("picker.ticketsCount", n)}</span> ${this.tf("picker.confirmedAndOnWay", "confirmed. A confirmation is on its way.")}`;
|
|
6850
7454
|
}
|
|
6851
7455
|
this.bookedEl?.classList.add("on");
|
|
6852
7456
|
this.opts.onBooked?.(handoff);
|
|
@@ -6928,7 +7532,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6928
7532
|
({ mountCheckout: mountCheckout2 } = await loadHostedCheckout());
|
|
6929
7533
|
} catch (err) {
|
|
6930
7534
|
this.opts.onError?.(err);
|
|
6931
|
-
this.toast("Checkout could not be opened. Your seats are still held \u2014 please try again.", "error");
|
|
7535
|
+
this.toast(this.tf("picker.checkoutCouldNotBeOpened", "Checkout could not be opened. Your seats are still held \u2014 please try again."), "error");
|
|
6932
7536
|
this.setCtaPhase("idle");
|
|
6933
7537
|
return;
|
|
6934
7538
|
}
|
|
@@ -7043,9 +7647,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
7043
7647
|
rowTypeWord(details) {
|
|
7044
7648
|
const authored = details?.displayType?.trim() || details?.rowType?.trim();
|
|
7045
7649
|
if (authored) return authored;
|
|
7046
|
-
if (details?.objectType === "table") return "Table";
|
|
7047
|
-
if (details?.objectType === "booth") return "Booth";
|
|
7048
|
-
return "Row";
|
|
7650
|
+
if (details?.objectType === "table") return this.tf("picker.table", "Table");
|
|
7651
|
+
if (details?.objectType === "booth") return this.tf("picker.booth", "Booth");
|
|
7652
|
+
return this.tf("picker.row", "Row");
|
|
7049
7653
|
}
|
|
7050
7654
|
rowShort(details) {
|
|
7051
7655
|
const row = details?.rowLabel;
|
|
@@ -7068,14 +7672,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
7068
7672
|
const isGroupedTable = details.objectType === "table" && !!details.bookingMode;
|
|
7069
7673
|
const isBooth = details.objectType === "booth";
|
|
7070
7674
|
const hasLoc = details.sectionLabel || details.rowLabel || details.seatNumber;
|
|
7071
|
-
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"
|
|
7072
|
-
const
|
|
7675
|
+
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>`;
|
|
7676
|
+
const notForSale = details.status === "not_for_sale";
|
|
7677
|
+
const statusLine = details.status === "free" ? "" : `<div class="sl-tip-status">${notForSale ? this.tf("picker.notAvailable", "Not available") : details.status === "held" ? (0, import_core2.t)("map.statusHeld") : (0, import_core2.t)("map.statusTaken")}</div>`;
|
|
7073
7678
|
const limited = this.limitedViewLabel(details.commercial);
|
|
7074
7679
|
const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc(limited)}</div>` : "";
|
|
7075
7680
|
const wheelchair = this.wheelchairProvisionLabel(details.wheelchairSpaceType);
|
|
7076
7681
|
const wheelchairLine = wheelchair ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u267F</span>${esc(wheelchair)}</div>` : "";
|
|
7077
7682
|
this.tipEl.style.setProperty("--sl-cat", details.categoryColor);
|
|
7078
|
-
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
|
|
7683
|
+
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;
|
|
7079
7684
|
this.tipEl.style.display = "block";
|
|
7080
7685
|
this.placeTooltip();
|
|
7081
7686
|
}
|
|
@@ -7298,13 +7903,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
7298
7903
|
sourceLabel: this.tf("picker.chartDerivedModel", "Chart-derived model")
|
|
7299
7904
|
};
|
|
7300
7905
|
}
|
|
7301
|
-
/** Route
|
|
7302
|
-
|
|
7906
|
+
/** Route decoupled analytics into the host callback, tagged buyer. */
|
|
7907
|
+
emitAnalytics(event, props) {
|
|
7303
7908
|
try {
|
|
7304
7909
|
this.opts.onAnalytics?.(event, { ...props, surface: "buyer" });
|
|
7305
7910
|
} catch {
|
|
7306
7911
|
}
|
|
7307
7912
|
}
|
|
7913
|
+
emit3dAnalytics(event, props) {
|
|
7914
|
+
this.emitAnalytics(event, props);
|
|
7915
|
+
}
|
|
7308
7916
|
/** A 3D seat tap runs the SAME selection path as a 2D tap: toggle through the
|
|
7309
7917
|
* controller, then raise the shared confirm card (bottom-sheeted in 3D). */
|
|
7310
7918
|
onView3dSeatPick(seatId) {
|
|
@@ -7323,9 +7931,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
7323
7931
|
this.syncSelectionTo3d();
|
|
7324
7932
|
return;
|
|
7325
7933
|
}
|
|
7326
|
-
const
|
|
7327
|
-
if (
|
|
7328
|
-
this.showUnavailable3dSeat(seat,
|
|
7934
|
+
const status = this.controller.getStatus(seat.id);
|
|
7935
|
+
if (status === "held" || status === "booked" || status === "not_for_sale") {
|
|
7936
|
+
this.showUnavailable3dSeat(seat, this.seatState3dFor(seat), status);
|
|
7329
7937
|
this.syncSelectionTo3d();
|
|
7330
7938
|
return;
|
|
7331
7939
|
}
|
|
@@ -7347,31 +7955,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
7347
7955
|
else this.syncTray();
|
|
7348
7956
|
this.syncSelectionTo3d();
|
|
7349
7957
|
}
|
|
7350
|
-
/** Explain a
|
|
7351
|
-
*
|
|
7352
|
-
*
|
|
7353
|
-
|
|
7958
|
+
/** Explain a 3D seat that INVENTORY refuses — never one the buyer's own
|
|
7959
|
+
* filters merely dimmed, which stays buyable exactly as it is in 2D. Category
|
|
7960
|
+
* colour remains visible in the venue; this card names the availability state
|
|
7961
|
+
* explicitly so yellow never has to carry both meanings. */
|
|
7962
|
+
showUnavailable3dSeat(seat, visualState, status) {
|
|
7354
7963
|
const overlay = this.view3dEl;
|
|
7355
7964
|
if (!overlay) return;
|
|
7356
7965
|
this.dismissUnavailable3dSeat();
|
|
7357
|
-
const status = this.controller.getStatus(seat.id);
|
|
7358
7966
|
const details = this.controller.seatDetails(seat.id);
|
|
7359
7967
|
const identity = details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
7360
7968
|
const section = details?.sectionLabel;
|
|
7361
7969
|
const row = this.rowShort(details);
|
|
7362
|
-
const location2 = [section, row ?
|
|
7970
|
+
const location2 = [section, row ? (0, import_core2.t)("picker.rowLabel", { label: row }) : null, identity].filter(Boolean).join(" \xB7 ");
|
|
7363
7971
|
const copy = status === "held" ? {
|
|
7364
7972
|
title: this.tf("picker.temporarilyHeld", "Temporarily held"),
|
|
7365
7973
|
message: this.tf("picker.heldSeatExplanation", "Another buyer is holding this seat. It may become available again.")
|
|
7366
7974
|
} : status === "booked" ? {
|
|
7367
7975
|
title: this.tf("picker.sold", "Sold"),
|
|
7368
7976
|
message: this.tf("picker.soldSeatExplanation", "This seat has already been booked.")
|
|
7369
|
-
} :
|
|
7977
|
+
} : {
|
|
7370
7978
|
title: this.tf("picker.notForSale", "Not for sale"),
|
|
7371
7979
|
message: this.tf("picker.notForSaleExplanation", "This seat is not included in the current sale.")
|
|
7372
|
-
} : {
|
|
7373
|
-
title: this.tf("picker.filteredSeat", "Unavailable with current filters"),
|
|
7374
|
-
message: this.tf("picker.filteredSeatExplanation", "Change the active price or view filters to make this seat selectable.")
|
|
7375
7980
|
};
|
|
7376
7981
|
const card = document.createElement("div");
|
|
7377
7982
|
card.className = "sl-view3d-unavailable";
|
|
@@ -7455,7 +8060,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7455
8060
|
chip = document.createElement("div");
|
|
7456
8061
|
chip.className = "sl-view3d-compare-saved";
|
|
7457
8062
|
chip.setAttribute("role", "group");
|
|
7458
|
-
chip.setAttribute("aria-label", "Saved seat comparison");
|
|
8063
|
+
chip.setAttribute("aria-label", this.tf("picker.savedSeatComparison", "Saved seat comparison"));
|
|
7459
8064
|
const main2 = document.createElement("button");
|
|
7460
8065
|
main2.type = "button";
|
|
7461
8066
|
main2.className = "main";
|
|
@@ -7467,7 +8072,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7467
8072
|
clear.type = "button";
|
|
7468
8073
|
clear.className = "clear";
|
|
7469
8074
|
clear.textContent = "\xD7";
|
|
7470
|
-
clear.setAttribute("aria-label", "Clear saved seat comparison");
|
|
8075
|
+
clear.setAttribute("aria-label", this.tf("picker.clearSavedSeatComparison", "Clear saved seat comparison"));
|
|
7471
8076
|
clear.addEventListener("click", () => this.clearView3dComparison());
|
|
7472
8077
|
chip.append(main2, clear);
|
|
7473
8078
|
overlay.appendChild(chip);
|
|
@@ -7476,8 +8081,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
7476
8081
|
const count = this.view3dCompareSeatIds.length;
|
|
7477
8082
|
const main = chip.querySelector(".main");
|
|
7478
8083
|
if (main) {
|
|
7479
|
-
main.textContent = count > 1 ?
|
|
7480
|
-
main.setAttribute("aria-label", count > 1 ?
|
|
8084
|
+
main.textContent = count > 1 ? (0, import_core2.t)("picker.compareCount", { count }) : this.tf("picker.oneSeatSaved", "1 seat saved");
|
|
8085
|
+
main.setAttribute("aria-label", count > 1 ? (0, import_core2.t)("picker.openComparisonOfSeats", { count }) : this.tf("picker.oneSeatSavedChooseAnother", "One seat saved; choose another to compare"));
|
|
7481
8086
|
}
|
|
7482
8087
|
}
|
|
7483
8088
|
view3dComparisonSnapshot(seatId) {
|
|
@@ -7749,13 +8354,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
7749
8354
|
const floors = handle.floors();
|
|
7750
8355
|
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
7751
8356
|
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
7752
|
-
const allSections = handle.sections().filter((
|
|
8357
|
+
const allSections = handle.sections().filter((section) => section.seatCount > 0 && !floorLabels.has(section.label.trim().toLocaleLowerCase()));
|
|
7753
8358
|
const sections = zones.length > 1 ? [] : allSections;
|
|
7754
8359
|
const wantFloors = floors.length > 1;
|
|
7755
8360
|
const wantZones = zones.length > 1;
|
|
7756
8361
|
const wantSections = sections.length > 1;
|
|
7757
|
-
|
|
7758
|
-
if (!wantFloors && !wantZones && !wantSections && !wantLocator) return;
|
|
8362
|
+
if (!wantFloors && !wantZones && !wantSections) return;
|
|
7759
8363
|
const nav = document.createElement("div");
|
|
7760
8364
|
nav.className = "sl-view3d-nav";
|
|
7761
8365
|
const finderToggle = document.createElement("button");
|
|
@@ -7765,7 +8369,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7765
8369
|
const setFinderOpen = (open) => {
|
|
7766
8370
|
nav.classList.toggle("is-open", open);
|
|
7767
8371
|
finderToggle.setAttribute("aria-expanded", String(open));
|
|
7768
|
-
finderToggle.textContent = open ? "Close
|
|
8372
|
+
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");
|
|
7769
8373
|
};
|
|
7770
8374
|
finderToggle.addEventListener("click", () => setFinderOpen(!nav.classList.contains("is-open")));
|
|
7771
8375
|
nav.addEventListener("keydown", (event) => {
|
|
@@ -7777,16 +8381,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
7777
8381
|
});
|
|
7778
8382
|
setFinderOpen(false);
|
|
7779
8383
|
nav.appendChild(finderToggle);
|
|
8384
|
+
let syncAreasForFloor = (_index) => void 0;
|
|
8385
|
+
let syncPressedFloor = (_index) => void 0;
|
|
7780
8386
|
if (wantFloors) {
|
|
7781
8387
|
const row = document.createElement("div");
|
|
7782
8388
|
row.setAttribute("role", "group");
|
|
7783
8389
|
row.setAttribute("aria-label", this.tf("picker.levels", "Levels"));
|
|
7784
8390
|
const pills = [];
|
|
8391
|
+
syncPressedFloor = (index) => {
|
|
8392
|
+
pills.forEach((pill) => {
|
|
8393
|
+
pill.setAttribute("aria-pressed", String(
|
|
8394
|
+
(pill.dataset.floor === "" ? null : Number(pill.dataset.floor)) === index
|
|
8395
|
+
));
|
|
8396
|
+
});
|
|
8397
|
+
};
|
|
7785
8398
|
const select = (index) => {
|
|
7786
8399
|
if (!handle.focusFloor(index)) return;
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
});
|
|
8400
|
+
syncPressedFloor(index);
|
|
8401
|
+
syncAreasForFloor(index);
|
|
7790
8402
|
};
|
|
7791
8403
|
const add = (label, index) => {
|
|
7792
8404
|
const b = document.createElement("button");
|
|
@@ -7802,7 +8414,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7802
8414
|
row.appendChild(b);
|
|
7803
8415
|
};
|
|
7804
8416
|
add(this.tf("picker.allLevels", "All levels"), null);
|
|
7805
|
-
for (const f of floors) add(f.label ||
|
|
8417
|
+
for (const f of floors) add(f.label || (0, import_core2.t)("picker.levelNumber", { number: f.index + 1 }), f.index);
|
|
7806
8418
|
nav.appendChild(row);
|
|
7807
8419
|
}
|
|
7808
8420
|
if (wantZones || wantSections) {
|
|
@@ -7811,119 +8423,55 @@ var SeatPicker = class _SeatPicker {
|
|
|
7811
8423
|
row.setAttribute("aria-label", this.tf("picker.areas", "Areas"));
|
|
7812
8424
|
const entries = wantZones ? zones.map((z) => ({ id: z.id, label: z.label || z.id, go: () => {
|
|
7813
8425
|
handle.focusZone(z.id);
|
|
7814
|
-
} })) : sections.map((s) => ({
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
placeholder.value = "";
|
|
7822
|
-
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
7823
|
-
select.appendChild(placeholder);
|
|
7824
|
-
for (const entry of entries) {
|
|
7825
|
-
const option = document.createElement("option");
|
|
7826
|
-
option.value = entry.id;
|
|
7827
|
-
option.textContent = entry.label;
|
|
7828
|
-
select.appendChild(option);
|
|
8426
|
+
} })) : sections.map((s) => ({
|
|
8427
|
+
id: s.id,
|
|
8428
|
+
label: s.label || s.id,
|
|
8429
|
+
floorIndex: s.floorIndex,
|
|
8430
|
+
go: () => {
|
|
8431
|
+
handle.focusSection(s.id);
|
|
8432
|
+
if (Number.isFinite(s.floorIndex)) syncPressedFloor(s.floorIndex);
|
|
7829
8433
|
}
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
row.
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
const b = document.createElement("button");
|
|
7837
|
-
b.type = "button";
|
|
7838
|
-
b.textContent = e.label;
|
|
7839
|
-
b.addEventListener("click", () => {
|
|
7840
|
-
e.go();
|
|
7841
|
-
setFinderOpen(false);
|
|
7842
|
-
});
|
|
7843
|
-
row.appendChild(b);
|
|
7844
|
-
}
|
|
7845
|
-
}
|
|
7846
|
-
nav.appendChild(row);
|
|
7847
|
-
}
|
|
7848
|
-
if (wantLocator) {
|
|
7849
|
-
const locator = document.createElement("div");
|
|
7850
|
-
locator.className = "sl-view3d-locator";
|
|
7851
|
-
locator.setAttribute("role", "group");
|
|
7852
|
-
locator.setAttribute("aria-label", "Find an exact seat in 3D");
|
|
7853
|
-
const sectionSelect = document.createElement("select");
|
|
7854
|
-
sectionSelect.setAttribute("aria-label", "Choose section in 3D");
|
|
7855
|
-
const rowSelect = document.createElement("select");
|
|
7856
|
-
rowSelect.setAttribute("aria-label", "Choose row in 3D");
|
|
7857
|
-
const seatSelect = document.createElement("select");
|
|
7858
|
-
seatSelect.setAttribute("aria-label", "Choose seat in 3D");
|
|
7859
|
-
const view = document.createElement("button");
|
|
7860
|
-
view.type = "button";
|
|
7861
|
-
view.textContent = "Inspect seat";
|
|
7862
|
-
view.disabled = true;
|
|
7863
|
-
const fill = (select, placeholder, entries) => {
|
|
7864
|
-
select.replaceChildren();
|
|
7865
|
-
const first = document.createElement("option");
|
|
7866
|
-
first.value = "";
|
|
7867
|
-
first.textContent = placeholder;
|
|
7868
|
-
select.appendChild(first);
|
|
7869
|
-
for (const entry of entries) {
|
|
7870
|
-
const option = document.createElement("option");
|
|
7871
|
-
option.value = entry.id;
|
|
7872
|
-
option.textContent = entry.label;
|
|
7873
|
-
select.appendChild(option);
|
|
7874
|
-
}
|
|
7875
|
-
select.value = "";
|
|
7876
|
-
};
|
|
7877
|
-
fill(sectionSelect, "1. Section", allSections.map((section) => ({
|
|
7878
|
-
id: section.id,
|
|
7879
|
-
label: `${section.label} \xB7 ${section.seatCount.toLocaleString()} seats`
|
|
7880
|
-
})));
|
|
7881
|
-
fill(rowSelect, "2. Row", []);
|
|
7882
|
-
fill(seatSelect, "3. Seat", []);
|
|
7883
|
-
rowSelect.disabled = true;
|
|
7884
|
-
seatSelect.disabled = true;
|
|
7885
|
-
sectionSelect.addEventListener("change", () => {
|
|
7886
|
-
const sectionId = sectionSelect.value;
|
|
7887
|
-
view.disabled = true;
|
|
7888
|
-
fill(seatSelect, "3. Seat", []);
|
|
7889
|
-
seatSelect.disabled = true;
|
|
7890
|
-
if (!sectionId || !handle.focusSection(sectionId)) {
|
|
7891
|
-
fill(rowSelect, "2. Row", []);
|
|
7892
|
-
rowSelect.disabled = true;
|
|
8434
|
+
}));
|
|
8435
|
+
const renderEntries = (floorIndex) => {
|
|
8436
|
+
const visible = wantZones || floorIndex === null ? entries : entries.filter((entry) => entry.floorIndex === void 0 || entry.floorIndex === floorIndex);
|
|
8437
|
+
row.replaceChildren();
|
|
8438
|
+
if (!visible.length) {
|
|
8439
|
+
row.remove();
|
|
7893
8440
|
return;
|
|
7894
8441
|
}
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
label
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
8442
|
+
if (!row.isConnected) nav.appendChild(row);
|
|
8443
|
+
if (!wantZones && visible.length > MAX_3D_SECTION_PILLS) {
|
|
8444
|
+
const select = document.createElement("select");
|
|
8445
|
+
select.setAttribute("aria-label", this.tf("picker.jumpToSection", "Jump to section"));
|
|
8446
|
+
const placeholder = document.createElement("option");
|
|
8447
|
+
placeholder.value = "";
|
|
8448
|
+
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
8449
|
+
select.appendChild(placeholder);
|
|
8450
|
+
for (const entry of visible) {
|
|
8451
|
+
const option = document.createElement("option");
|
|
8452
|
+
option.value = entry.id;
|
|
8453
|
+
option.textContent = entry.label;
|
|
8454
|
+
select.appendChild(option);
|
|
8455
|
+
}
|
|
8456
|
+
select.addEventListener("change", () => {
|
|
8457
|
+
visible.find((entry) => entry.id === select.value)?.go();
|
|
8458
|
+
});
|
|
8459
|
+
row.appendChild(select);
|
|
7908
8460
|
return;
|
|
7909
8461
|
}
|
|
7910
|
-
const
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
const seatId = seatSelect.value;
|
|
7920
|
-
if (seatId) {
|
|
7921
|
-
setFinderOpen(false);
|
|
7922
|
-
this.onView3dSeatPick(seatId);
|
|
8462
|
+
for (const entry of visible) {
|
|
8463
|
+
const button = document.createElement("button");
|
|
8464
|
+
button.type = "button";
|
|
8465
|
+
button.textContent = entry.label;
|
|
8466
|
+
button.addEventListener("click", () => {
|
|
8467
|
+
entry.go();
|
|
8468
|
+
setFinderOpen(false);
|
|
8469
|
+
});
|
|
8470
|
+
row.appendChild(button);
|
|
7923
8471
|
}
|
|
7924
|
-
}
|
|
7925
|
-
|
|
7926
|
-
|
|
8472
|
+
};
|
|
8473
|
+
syncAreasForFloor = renderEntries;
|
|
8474
|
+
renderEntries(null);
|
|
7927
8475
|
}
|
|
7928
8476
|
overlay.appendChild(nav);
|
|
7929
8477
|
}
|
|
@@ -7940,7 +8488,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7940
8488
|
const overlay = document.createElement("div");
|
|
7941
8489
|
overlay.className = "sl-view3d";
|
|
7942
8490
|
overlay.setAttribute("role", "group");
|
|
7943
|
-
overlay.setAttribute("aria-label", "Interactive 3D venue view");
|
|
8491
|
+
overlay.setAttribute("aria-label", this.tf("picker.interactive3dVenueView", "Interactive 3D venue view"));
|
|
7944
8492
|
const back = document.createElement("button");
|
|
7945
8493
|
back.type = "button";
|
|
7946
8494
|
back.className = "sl-view3d-back";
|
|
@@ -7966,7 +8514,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7966
8514
|
fullscreen.type = "button";
|
|
7967
8515
|
fullscreen.className = "sl-view3d-fs";
|
|
7968
8516
|
fullscreen.textContent = "\u26F6";
|
|
7969
|
-
fullscreen.setAttribute("aria-label", "Full screen");
|
|
8517
|
+
fullscreen.setAttribute("aria-label", this.tf("picker.fullScreen", "Full screen"));
|
|
7970
8518
|
fullscreen.setAttribute("aria-pressed", String(!!document.fullscreenElement || this.fsFallback || this.framedFs));
|
|
7971
8519
|
fullscreen.addEventListener("click", () => this.toggleFullscreen());
|
|
7972
8520
|
overlay.appendChild(fullscreen);
|
|
@@ -8003,7 +8551,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8003
8551
|
onSeatInspect: (id) => this.onView3dSeatPick(id),
|
|
8004
8552
|
onSectionFocusChange: (sectionId) => {
|
|
8005
8553
|
const select = overlay.querySelector(
|
|
8006
|
-
'select[
|
|
8554
|
+
'select[data-locator="section"]'
|
|
8007
8555
|
);
|
|
8008
8556
|
const next = sectionId ?? "";
|
|
8009
8557
|
if (!select || select.value === next) return;
|
|
@@ -8100,7 +8648,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8100
8648
|
if (button) {
|
|
8101
8649
|
button.disabled = true;
|
|
8102
8650
|
button.classList.add("sl-busy");
|
|
8103
|
-
button.innerHTML =
|
|
8651
|
+
button.innerHTML = `<span class="sl-ba-spin" aria-hidden="true"></span>${this.tf("picker.findingEllipsis", "Finding\u2026")}`;
|
|
8104
8652
|
}
|
|
8105
8653
|
try {
|
|
8106
8654
|
const h = await this.controller.bestAvailable(qty, categoryKey, { ...opts, ttlMs: this.opts.holdTtlMs });
|
|
@@ -8122,7 +8670,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8122
8670
|
} catch (err) {
|
|
8123
8671
|
this.opts.onError?.(err);
|
|
8124
8672
|
const reason = err?.reason;
|
|
8125
|
-
const message = reason === "not_enough_together" ?
|
|
8673
|
+
const message = reason === "not_enough_together" ? (0, import_core2.t)("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.");
|
|
8126
8674
|
this.toast(message, "error");
|
|
8127
8675
|
return null;
|
|
8128
8676
|
} finally {
|
|
@@ -8151,7 +8699,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8151
8699
|
}
|
|
8152
8700
|
}
|
|
8153
8701
|
if (!released) {
|
|
8154
|
-
this.toast("Couldn't release your tickets. Your hold is unchanged.", "error");
|
|
8702
|
+
this.toast(this.tf("picker.couldNotReleaseTickets", "Couldn't release your tickets. Your hold is unchanged."), "error");
|
|
8155
8703
|
return;
|
|
8156
8704
|
}
|
|
8157
8705
|
this.hold = null;
|