@seatlayer/js 0.51.0 → 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 +687 -224
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -8
- package/dist/index.d.ts +43 -8
- package/dist/index.js +691 -224
- 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",
|
|
@@ -2663,6 +2666,12 @@ function tfStatic(key, fallback) {
|
|
|
2663
2666
|
const v = (0, import_core2.t)(key);
|
|
2664
2667
|
return v === key ? fallback : v;
|
|
2665
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
|
+
}
|
|
2666
2675
|
function pointInPolygon(x, y, poly) {
|
|
2667
2676
|
let inside = false;
|
|
2668
2677
|
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
@@ -2765,9 +2774,13 @@ var CSS2 = (
|
|
|
2765
2774
|
the WIDGET's meanings, not an org's brand: premium gold, environment
|
|
2766
2775
|
warning, danger, success. Hosts can still override them with CSS. */
|
|
2767
2776
|
--sl-premium:#e8c15a;--sl-premium-deep:#c9a24b;--sl-premium-ink:#1c1608;
|
|
2768
|
-
--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}
|
|
2769
2780
|
.sl-picker *{box-sizing:border-box;margin:0;padding:0}
|
|
2770
|
-
.
|
|
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}
|
|
2771
2784
|
|
|
2772
2785
|
/* header */
|
|
2773
2786
|
.sl-head{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--sl-line);flex:none}
|
|
@@ -2963,11 +2976,11 @@ var CSS2 = (
|
|
|
2963
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));
|
|
2964
2977
|
border-radius:12px;background:color-mix(in srgb,var(--sl-accent) 8%,var(--sl-surface));color:var(--sl-text)}
|
|
2965
2978
|
.sl-offer.has{display:block}.sl-offer-main{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
|
|
2966
|
-
.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)}
|
|
2967
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)}
|
|
2968
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;
|
|
2969
2982
|
display:grid;place-items:center;cursor:pointer;font-size:12px;font-weight:850;color:var(--sl-text);background:var(--sl-surface)}
|
|
2970
|
-
.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)}
|
|
2971
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)}
|
|
2972
2985
|
.sl-sec{padding:14px 14px 4px;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
2973
2986
|
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
@@ -2985,8 +2998,8 @@ var CSS2 = (
|
|
|
2985
2998
|
padding:0 6px;margin:0 -6px;border-radius:8px;cursor:pointer;transition:background .15s}
|
|
2986
2999
|
.sl-price-row:hover,.sl-price-row:focus-visible{background:color-mix(in srgb,var(--sl-line) 40%,transparent)}
|
|
2987
3000
|
.sl-price-row.sl-active{background:color-mix(in srgb,var(--sl-accent) 9%,transparent)}
|
|
2988
|
-
.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}
|
|
2989
|
-
.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)}
|
|
2990
3003
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
2991
3004
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
2992
3005
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
@@ -3021,7 +3034,7 @@ var CSS2 = (
|
|
|
3021
3034
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
|
|
3022
3035
|
.sl-ticket-state{width:17px;height:17px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
3023
3036
|
background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
3024
|
-
.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)}
|
|
3025
3038
|
.sl-ticket-state svg{width:10px;height:10px;stroke:currentColor;stroke-width:2.6;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3026
3039
|
.sl-chip-main{min-width:0;padding:8px 10px 8px 11px;display:flex;flex-direction:column;justify-content:center;gap:5px}
|
|
3027
3040
|
.sl-chip-id{display:flex;gap:12px;min-width:0}
|
|
@@ -3064,7 +3077,7 @@ var CSS2 = (
|
|
|
3064
3077
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
3065
3078
|
|
|
3066
3079
|
/* footer */
|
|
3067
|
-
.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;
|
|
3068
3081
|
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
3069
3082
|
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
3070
3083
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
@@ -3078,14 +3091,13 @@ var CSS2 = (
|
|
|
3078
3091
|
.sl-hold-note>span{flex:1;min-width:0}
|
|
3079
3092
|
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
3080
3093
|
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
3081
|
-
.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)}
|
|
3082
3095
|
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
3083
3096
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
3084
3097
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
3085
3098
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
3086
|
-
/* Primary checkout CTA.
|
|
3087
|
-
|
|
3088
|
-
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. */
|
|
3089
3101
|
.sl-picker .sl-cta{display:flex;align-items:center;justify-content:center;width:100%;min-height:44px;
|
|
3090
3102
|
padding:12px 16px;border-radius:var(--sl-r-sm);font-weight:800;font-size:14px;line-height:1.1;
|
|
3091
3103
|
background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
@@ -3096,8 +3108,8 @@ var CSS2 = (
|
|
|
3096
3108
|
.sl-cta-spin,.sl-ba-spin{width:14px;height:14px;border-radius:50%;border:2px solid currentColor;border-right-color:transparent;
|
|
3097
3109
|
animation:slspin .7s linear infinite;flex:none}
|
|
3098
3110
|
/* Disabled ("Select seats"): quieter, but still a full-width button shape. */
|
|
3099
|
-
.sl-picker .sl-cta:disabled{background:var(--sl-surface);color:var(--sl-muted);
|
|
3100
|
-
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}
|
|
3101
3113
|
|
|
3102
3114
|
/* Chrome anchor regions (Feature 6) \u2014 every interactive map overlay is APPENDED
|
|
3103
3115
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
@@ -3127,7 +3139,7 @@ var CSS2 = (
|
|
|
3127
3139
|
in the same region, so no displacement hack is needed. */
|
|
3128
3140
|
.sl-testbadge{display:inline-flex;align-items:center;padding:5px 10px;border-radius:7px;pointer-events:none;
|
|
3129
3141
|
font-size:10px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;white-space:nowrap;
|
|
3130
|
-
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));
|
|
3131
3143
|
border:1px dashed color-mix(in srgb,var(--sl-warn) 55%,transparent)}
|
|
3132
3144
|
|
|
3133
3145
|
/* zoom column (flows within the bottom-right region) */
|
|
@@ -3194,7 +3206,7 @@ var CSS2 = (
|
|
|
3194
3206
|
.sl-extend-btn{flex:none;padding:8px 14px;border-radius:999px;font-weight:800;font-size:12.5px;
|
|
3195
3207
|
background:var(--sl-accent);color:var(--sl-accent-ink);transition:filter .15s,opacity .15s}
|
|
3196
3208
|
.sl-extend-btn:hover{filter:brightness(1.08)}
|
|
3197
|
-
.sl-extend-btn:disabled{opacity:.
|
|
3209
|
+
.sl-extend-btn:disabled{opacity:.62;cursor:not-allowed}
|
|
3198
3210
|
|
|
3199
3211
|
/* booked confirmation overlay (covers the widget once the held seats are sold) */
|
|
3200
3212
|
.sl-booked{position:absolute;inset:0;z-index:11;display:flex;flex-direction:column;align-items:center;
|
|
@@ -3221,7 +3233,7 @@ var CSS2 = (
|
|
|
3221
3233
|
justify-content:center;text-align:center;gap:8px;padding:24px;
|
|
3222
3234
|
background:color-mix(in srgb,var(--sl-bg) 82%,transparent);backdrop-filter:blur(4px)}
|
|
3223
3235
|
.sl-soldout.on{display:flex}
|
|
3224
|
-
.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}
|
|
3225
3237
|
.sl-soldout-title{font-size:32px;font-weight:800;color:var(--sl-text);line-height:1.05}
|
|
3226
3238
|
.sl-soldout-copy{max-width:360px;font-size:13px;color:var(--sl-muted);line-height:1.5}
|
|
3227
3239
|
|
|
@@ -3237,9 +3249,9 @@ var CSS2 = (
|
|
|
3237
3249
|
/* "Powered by SeatLayer" attribution badge (side-panel foot) \u2014 the canonical
|
|
3238
3250
|
Layered Rows mark + wordmark. Hidden when the host opts out or the org's paid
|
|
3239
3251
|
theme sets hideBadge. */
|
|
3240
|
-
.sl-powered{display:flex;align-items:center;justify-content:center;gap:
|
|
3241
|
-
font-size:
|
|
3242
|
-
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;
|
|
3243
3255
|
transition:opacity .15s ease,background-color .15s ease}
|
|
3244
3256
|
.sl-powered:hover{opacity:1;background:color-mix(in srgb,var(--sl-text) 8%,transparent)}
|
|
3245
3257
|
.sl-powered:focus-visible{opacity:1;outline:2px solid var(--sl-accent);outline-offset:2px}
|
|
@@ -3277,10 +3289,22 @@ var CSS2 = (
|
|
|
3277
3289
|
.sl-confirm-field:first-child .sl-confirm-value{font-size:13.5px;line-height:1.25;white-space:normal;
|
|
3278
3290
|
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
3279
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))}
|
|
3280
|
-
.sl-confirm-cat .sl-dot{border:2px solid
|
|
3281
|
-
.sl-confirm-cat-name{font-size:13.5px;font-weight:800;color
|
|
3282
|
-
.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}
|
|
3283
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}
|
|
3284
3308
|
.sl-confirm-row{display:flex;gap:8px;margin-top:10px}
|
|
3285
3309
|
.sl-confirm-row button{flex:1;min-height:44px;padding:9px 12px;border-radius:9px;font-weight:800;font-size:13px}
|
|
3286
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}
|
|
@@ -3298,6 +3322,7 @@ var CSS2 = (
|
|
|
3298
3322
|
.sl-picker[data-layout="narrow"] .sl-confirm-cat-name{font-size:12.5px}
|
|
3299
3323
|
.sl-picker[data-layout="narrow"] .sl-confirm-price{font-size:15px}
|
|
3300
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}
|
|
3301
3326
|
.sl-picker[data-layout="narrow"] .sl-confirm-thumbwrap{height:62px;margin-bottom:6px}
|
|
3302
3327
|
.sl-picker[data-layout="narrow"] .sl-confirm-3d{margin-top:7px;padding:6px}
|
|
3303
3328
|
.sl-picker[data-layout="narrow"] .sl-confirm-row{margin-top:8px}
|
|
@@ -3321,7 +3346,7 @@ var CSS2 = (
|
|
|
3321
3346
|
.sl-table-stepper{display:grid;grid-template-columns:48px 1fr 48px;align-items:center;border:1px solid var(--sl-line);
|
|
3322
3347
|
border-radius:12px;overflow:hidden;background:var(--sl-bg)}
|
|
3323
3348
|
.sl-table-stepper button{height:48px;font-size:24px;font-weight:700;background:color-mix(in srgb,var(--sl-line) 34%,transparent)!important}
|
|
3324
|
-
.sl-table-stepper button:disabled{opacity:.
|
|
3349
|
+
.sl-table-stepper button:disabled{opacity:.58;cursor:not-allowed}
|
|
3325
3350
|
.sl-table-stepper output{text-align:center;font-size:19px;font-weight:850;font-variant-numeric:tabular-nums}
|
|
3326
3351
|
.sl-table-range{margin-top:7px;color:var(--sl-muted);font-size:11px;text-align:center}
|
|
3327
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}
|
|
@@ -3363,14 +3388,14 @@ var CSS2 = (
|
|
|
3363
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}
|
|
3364
3389
|
.sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
|
|
3365
3390
|
.sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
|
|
3366
|
-
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
3391
|
+
.sl-ba-title .spark{color:var(--sl-accent-text);font-size:16px}
|
|
3367
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}
|
|
3368
3393
|
.sl-ba-copy .narrow{display:none}
|
|
3369
3394
|
/* "\u2605 Best seats" premium quick-pick \u2014 gold accent echoing the \u2605 Premium pill on
|
|
3370
3395
|
the confirm popover; deliberately distinct from the accent-toned qty/go. */
|
|
3371
3396
|
.sl-ba-premium{position:relative;z-index:1;grid-column:1/-1;justify-self:start;display:inline-flex;align-items:center;gap:6px;
|
|
3372
3397
|
padding:6px 12px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer;
|
|
3373
|
-
color:var(--sl-premium-
|
|
3398
|
+
color:var(--sl-premium-text);background:color-mix(in srgb,var(--sl-premium) 10%,var(--sl-surface));
|
|
3374
3399
|
border:1px solid color-mix(in srgb,var(--sl-premium) 34%,var(--sl-line));transition:filter .15s,background .15s,color .15s}
|
|
3375
3400
|
.sl-ba-premium .star{font-size:12px;line-height:1;color:var(--sl-premium)}
|
|
3376
3401
|
.sl-ba-premium:hover{filter:brightness(1.05)}
|
|
@@ -3423,7 +3448,7 @@ var CSS2 = (
|
|
|
3423
3448
|
back out of a reopened card. */
|
|
3424
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;
|
|
3425
3450
|
border:1px dashed color-mix(in srgb,var(--sl-accent) 40%,var(--sl-line));border-radius:999px;
|
|
3426
|
-
color:var(--sl-accent);font-size:11.5px;font-weight:750;transition:border-color .15s,background .15s}
|
|
3451
|
+
color:var(--sl-accent-text);font-size:11.5px;font-weight:750;transition:border-color .15s,background .15s}
|
|
3427
3452
|
.sl-ba-reopen:hover,.sl-ba-reopen:focus-visible{background:color-mix(in srgb,var(--sl-accent) 8%,transparent)}
|
|
3428
3453
|
.sl-ba-back{grid-column:1/-1;justify-self:start;min-height:30px;padding:3px 0;color:var(--sl-muted);
|
|
3429
3454
|
font-size:11px;font-weight:700;text-decoration:underline;text-underline-offset:3px;transition:color .15s}
|
|
@@ -3545,7 +3570,7 @@ var CSS2 = (
|
|
|
3545
3570
|
border:1px solid rgba(150,165,205,.35);backdrop-filter:blur(6px);cursor:pointer}
|
|
3546
3571
|
.sl-view3d-nav button:hover,.sl-view3d-nav button:focus-visible{color:#eef1f8;border-color:rgba(190,205,240,.6)}
|
|
3547
3572
|
.sl-view3d-nav button[aria-pressed="true"]{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3548
|
-
.sl-view3d-nav button:disabled{opacity:.
|
|
3573
|
+
.sl-view3d-nav button:disabled{opacity:.6;cursor:not-allowed}
|
|
3549
3574
|
.sl-view3d-nav-toggle{display:none!important}
|
|
3550
3575
|
.sl-view3d-nav select{min-height:38px;max-width:100%;padding:7px 34px 7px 12px;
|
|
3551
3576
|
border-radius:999px;font:700 11.5px/1 inherit;color:#eef1f8;background:rgba(12,18,32,.86);
|
|
@@ -3593,7 +3618,7 @@ var CSS2 = (
|
|
|
3593
3618
|
.sl-confirm-confidence strong,.sl-confirm-confidence small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3594
3619
|
.sl-confirm-confidence strong{font-size:11px}.sl-confirm-confidence small{margin-top:2px;color:var(--sl-muted);font-size:9.5px}
|
|
3595
3620
|
.sl-confirm-confidence em{display:none;font-style:normal}
|
|
3596
|
-
.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)}
|
|
3597
3622
|
.sl-confirm-confidence:hover,.sl-confirm-confidence:focus-visible{border-color:var(--sl-accent)}
|
|
3598
3623
|
/* The production 3D decision dock is deliberately denser than the 2D popup:
|
|
3599
3624
|
the venue remains the main content and the two inspection actions share one
|
|
@@ -3675,7 +3700,7 @@ var CSS2 = (
|
|
|
3675
3700
|
.sl-view3d-compare-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:12px}
|
|
3676
3701
|
.sl-view3d-compare-actions button{min-height:44px;border-radius:9px;border:1px solid var(--sl-line);font-size:12px;font-weight:800}
|
|
3677
3702
|
.sl-view3d-compare-actions .select{background:var(--sl-accent);color:var(--sl-accent-ink);border-color:transparent}
|
|
3678
|
-
.sl-view3d-compare-actions button:disabled{opacity:.
|
|
3703
|
+
.sl-view3d-compare-actions button:disabled{opacity:.62;cursor:not-allowed}
|
|
3679
3704
|
.sl-view3d-passport-shell{position:absolute;inset:0;z-index:40;display:grid;place-items:center;padding:16px}
|
|
3680
3705
|
.sl-view3d-passport-scrim{position:absolute;inset:0;background:rgba(3,6,12,.8);backdrop-filter:blur(6px)}
|
|
3681
3706
|
.sl-view3d-passport{position:relative;width:min(540px,100%);max-height:min(700px,calc(100% - 20px));overflow:auto;
|
|
@@ -3716,14 +3741,17 @@ var CSS2 = (
|
|
|
3716
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)}
|
|
3717
3742
|
.sl-confirm-3d svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
3718
3743
|
|
|
3719
|
-
/* multi-floor
|
|
3720
|
-
.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)}
|
|
3721
3748
|
.sl-floors.on{display:flex}
|
|
3722
|
-
.sl-floors
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
.sl-
|
|
3726
|
-
|
|
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)}
|
|
3727
3755
|
|
|
3728
3756
|
/* tapped-section summary card \u2014 docks INSIDE the top-center anchor region on
|
|
3729
3757
|
wide (flows below the rung pills, never over them, never floating over the
|
|
@@ -3758,8 +3786,14 @@ var CSS2 = (
|
|
|
3758
3786
|
.sl-seccard-mix-dot{width:8px;height:8px;border-radius:50%;flex:none}
|
|
3759
3787
|
.sl-seccard-mix-price{font-weight:700;color:var(--sl-text)}
|
|
3760
3788
|
.sl-seccard-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:10px}
|
|
3761
|
-
.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)}
|
|
3762
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}
|
|
3763
3797
|
|
|
3764
3798
|
/* view-from-seat button on the confirm popover */
|
|
3765
3799
|
/* Eager sightline preview inside the confirm card */
|
|
@@ -3812,10 +3846,29 @@ var CSS2 = (
|
|
|
3812
3846
|
white-space:nowrap;pointer-events:none;max-width:90%;overflow:hidden;text-overflow:ellipsis}
|
|
3813
3847
|
|
|
3814
3848
|
/* F3 minimap \u2014 venue overview + live viewport rect (flows in the bottom-left region) */
|
|
3815
|
-
.sl-minimap{border:1px solid var(--sl-line);border-radius:9px;
|
|
3816
|
-
|
|
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}
|
|
3817
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}
|
|
3818
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}
|
|
3819
3872
|
|
|
3820
3873
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
3821
3874
|
/* Out-of-band rows are exactly the rows a buyer reads while deciding whether
|
|
@@ -3893,17 +3946,88 @@ function formatWhen(startsAt, timezone, locale) {
|
|
|
3893
3946
|
}
|
|
3894
3947
|
return at.toLocaleString(locale, options);
|
|
3895
3948
|
}
|
|
3896
|
-
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");
|
|
3897
4017
|
const accent = host?.accent ?? chart?.accent ?? "#f4b740";
|
|
3898
|
-
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));
|
|
3899
4022
|
return {
|
|
3900
4023
|
"--sl-accent": accent,
|
|
3901
4024
|
"--sl-accent-ink": accentInk,
|
|
3902
|
-
"--sl-
|
|
3903
|
-
"--sl-
|
|
3904
|
-
"--sl-
|
|
3905
|
-
"--sl-
|
|
3906
|
-
"--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)"),
|
|
3907
4031
|
"--sl-font": host?.fontFamily ?? chart?.fontFamily ?? "-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,sans-serif",
|
|
3908
4032
|
"--sl-radius": `${host?.radius ?? 14}px`
|
|
3909
4033
|
};
|
|
@@ -4035,7 +4159,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
4035
4159
|
// F3 minimap
|
|
4036
4160
|
this.miniCanvas = null;
|
|
4037
4161
|
this.miniBase = null;
|
|
4162
|
+
this.miniWrap = null;
|
|
4163
|
+
this.miniToggle = null;
|
|
4164
|
+
this.miniLabel = null;
|
|
4038
4165
|
this.miniTf = null;
|
|
4166
|
+
this.miniDrag = null;
|
|
4167
|
+
this.miniSuppressClick = false;
|
|
4168
|
+
this.pickerLayoutSize = { width: 0, height: 0 };
|
|
4039
4169
|
// F4 price-band filter — active band's category keys (null = all prices)
|
|
4040
4170
|
this.priceBandKeys = null;
|
|
4041
4171
|
this.focusedCatKey = null;
|
|
@@ -4136,6 +4266,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4136
4266
|
mapTheme: options.theme?.map ?? null,
|
|
4137
4267
|
onSelectionChange: () => {
|
|
4138
4268
|
this.syncTray();
|
|
4269
|
+
this.refreshMinimap();
|
|
4139
4270
|
if (this.committedSelection().length) this.collapseSectionCard();
|
|
4140
4271
|
this.syncSelectionTo3d();
|
|
4141
4272
|
},
|
|
@@ -4195,6 +4326,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
4195
4326
|
},
|
|
4196
4327
|
// Tapped-section glide-in → surface (or clear) the section-summary card.
|
|
4197
4328
|
onSectionFocus: (summary) => this.showSectionCard(summary),
|
|
4329
|
+
onDeckTap: () => {
|
|
4330
|
+
this.showSectionCard(null);
|
|
4331
|
+
this.syncFloors();
|
|
4332
|
+
this.syncRung();
|
|
4333
|
+
this.refreshMinimap();
|
|
4334
|
+
},
|
|
4198
4335
|
onFocusSeat: (seat) => this.announceSeat(seat),
|
|
4199
4336
|
onSeatHover: (d) => this.updateTooltip(d),
|
|
4200
4337
|
onHint: (m) => {
|
|
@@ -4412,7 +4549,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4412
4549
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
4413
4550
|
this.fsEscHandler = null;
|
|
4414
4551
|
}
|
|
4415
|
-
requestAnimationFrame(() => this.controller.
|
|
4552
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4416
4553
|
}
|
|
4417
4554
|
setFsFallback(on) {
|
|
4418
4555
|
if (this.fsFallback === on) return;
|
|
@@ -4428,7 +4565,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4428
4565
|
window.removeEventListener("keydown", this.fsEscHandler);
|
|
4429
4566
|
this.fsEscHandler = null;
|
|
4430
4567
|
}
|
|
4431
|
-
requestAnimationFrame(() => this.controller.
|
|
4568
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4432
4569
|
}
|
|
4433
4570
|
/**
|
|
4434
4571
|
* Close the picker. In modal mode (SeatPicker.open()) this dismisses the
|
|
@@ -4568,11 +4705,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
4568
4705
|
}
|
|
4569
4706
|
async render() {
|
|
4570
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;
|
|
4571
4711
|
this.rendered = true;
|
|
4572
4712
|
ensureStyle2();
|
|
4713
|
+
const localeStartedAt = performance.now();
|
|
4573
4714
|
await (0, import_core2.loadLocale)(this.opts.locale);
|
|
4715
|
+
renderProfile.loadLocale = duration(localeStartedAt);
|
|
4574
4716
|
if (this.opts.messages) (0, import_core2.setStringOverrides)(this.opts.messages);
|
|
4575
4717
|
if (this.checkoutMode === "hosted") this.paymentOptions = this.pubApi.paymentOptions(this.opts.event);
|
|
4718
|
+
const shellStartedAt = performance.now();
|
|
4576
4719
|
const mount = resolveContainer3(this.opts.container);
|
|
4577
4720
|
const root = document.createElement("div");
|
|
4578
4721
|
root.className = "sl-picker";
|
|
@@ -4596,7 +4739,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4596
4739
|
this.syncTray();
|
|
4597
4740
|
}
|
|
4598
4741
|
});
|
|
4599
|
-
Object.entries(
|
|
4742
|
+
Object.entries(resolvePickerTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4600
4743
|
root.innerHTML = `
|
|
4601
4744
|
<div class="sl-head">
|
|
4602
4745
|
<div class="sl-logo" data-ref="logo"></div>
|
|
@@ -4675,14 +4818,20 @@ var SeatPicker = class _SeatPicker {
|
|
|
4675
4818
|
const applyLayout = () => {
|
|
4676
4819
|
const w = root.clientWidth;
|
|
4677
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 };
|
|
4678
4824
|
this.reportFramedHeight();
|
|
4679
4825
|
const next = w < 640 ? "narrow" : "wide";
|
|
4680
|
-
const density =
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
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();
|
|
4686
4835
|
};
|
|
4687
4836
|
this.ro = new ResizeObserver(applyLayout);
|
|
4688
4837
|
this.ro.observe(root);
|
|
@@ -4695,7 +4844,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4695
4844
|
this.fsChangeHandler = () => {
|
|
4696
4845
|
if (!document.fullscreenElement) this.setFsFallback(false);
|
|
4697
4846
|
this.syncFullscreenButtons();
|
|
4698
|
-
requestAnimationFrame(() => this.controller.
|
|
4847
|
+
requestAnimationFrame(() => this.controller.refitCurrentView());
|
|
4699
4848
|
};
|
|
4700
4849
|
document.addEventListener("fullscreenchange", this.fsChangeHandler);
|
|
4701
4850
|
const head = this.els.sheetHead;
|
|
@@ -4797,7 +4946,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
4797
4946
|
const canvasHost = document.createElement("div");
|
|
4798
4947
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
4799
4948
|
this.mapHost.appendChild(canvasHost);
|
|
4949
|
+
renderProfile.mountBuyerShell = duration(shellStartedAt);
|
|
4950
|
+
const chartStartedAt = performance.now();
|
|
4800
4951
|
const info = await this.controller.render(canvasHost);
|
|
4952
|
+
renderProfile.loadChartAndStatuses = duration(chartStartedAt);
|
|
4801
4953
|
if (this.destroyed) return this;
|
|
4802
4954
|
if (!info) {
|
|
4803
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>`;
|
|
@@ -4809,7 +4961,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4809
4961
|
});
|
|
4810
4962
|
return this;
|
|
4811
4963
|
}
|
|
4964
|
+
Object.assign(renderProfile, info.performanceProfile);
|
|
4812
4965
|
this.els.boot.remove();
|
|
4966
|
+
const controlsStartedAt = performance.now();
|
|
4813
4967
|
this.startRealtime();
|
|
4814
4968
|
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
4815
4969
|
root.dataset.eventMode = info.mode === "test" ? "test" : "live";
|
|
@@ -4825,7 +4979,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
4825
4979
|
this.regions["top-left"].appendChild(badge);
|
|
4826
4980
|
}
|
|
4827
4981
|
const chartTheme = this.controller.doc?.theme;
|
|
4828
|
-
Object.entries(
|
|
4982
|
+
Object.entries(resolvePickerTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
4829
4983
|
this.currency = info.currency ?? this.opts.currency ?? "USD";
|
|
4830
4984
|
this.eventTimezone = info.timezone ?? null;
|
|
4831
4985
|
const logoUrl = this.opts.theme?.logoUrl ?? chartTheme?.logoUrl ?? info.posterUrl ?? null;
|
|
@@ -4933,12 +5087,34 @@ var SeatPicker = class _SeatPicker {
|
|
|
4933
5087
|
this.buildPanelToggle();
|
|
4934
5088
|
if (this.opts.panelCollapsed) this.setPanelCollapsed(true);
|
|
4935
5089
|
this.dockLayoutChrome();
|
|
5090
|
+
renderProfile.buildBuyerControls = duration(controlsStartedAt);
|
|
5091
|
+
const holdStartedAt = performance.now();
|
|
4936
5092
|
await this.restoreRememberedHold();
|
|
5093
|
+
renderProfile.restoreHold = duration(holdStartedAt);
|
|
4937
5094
|
if (this.destroyed) return this;
|
|
4938
5095
|
if (this.salesClosed) this.applySalesClosed();
|
|
4939
5096
|
this.syncPrices();
|
|
4940
5097
|
this.syncTray();
|
|
4941
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
|
+
});
|
|
4942
5118
|
return this;
|
|
4943
5119
|
}
|
|
4944
5120
|
/**
|
|
@@ -5000,7 +5176,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5000
5176
|
const collapsed = this.sideCollapsed;
|
|
5001
5177
|
btn.setAttribute("aria-expanded", String(!collapsed));
|
|
5002
5178
|
btn.setAttribute("aria-label", collapsed ? this.tf("picker.showTicketPanel", "Show the ticket panel") : this.tf("picker.hideTicketPanel", "Hide the ticket panel"));
|
|
5003
|
-
btn.
|
|
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>`;
|
|
5004
5181
|
}
|
|
5005
5182
|
/**
|
|
5006
5183
|
* Collapse or reopen the wide-layout ticket panel in place (host-drivable —
|
|
@@ -5013,7 +5190,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5013
5190
|
if (this.destroyed) return;
|
|
5014
5191
|
this.sideCollapsed = collapsed;
|
|
5015
5192
|
this.applyPanelCollapsed();
|
|
5016
|
-
this.scheduleMotion(() => this.controller.
|
|
5193
|
+
this.scheduleMotion(() => this.controller.refitCurrentView(), 340);
|
|
5017
5194
|
}
|
|
5018
5195
|
/** Idempotent DOM apply, also run on every layout flip (inert must lift on narrow). */
|
|
5019
5196
|
applyPanelCollapsed() {
|
|
@@ -5408,10 +5585,77 @@ var SeatPicker = class _SeatPicker {
|
|
|
5408
5585
|
buildMinimap() {
|
|
5409
5586
|
const vp = this.controller.getViewport();
|
|
5410
5587
|
if (!vp || !this.els.map) return;
|
|
5411
|
-
const
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
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;
|
|
5415
5659
|
const PAD = 6;
|
|
5416
5660
|
const aspect = b.width / Math.max(1, b.height);
|
|
5417
5661
|
let w = MAXW;
|
|
@@ -5423,32 +5667,40 @@ var SeatPicker = class _SeatPicker {
|
|
|
5423
5667
|
w = Math.max(64, w);
|
|
5424
5668
|
h = Math.max(48, h);
|
|
5425
5669
|
const dpr = Math.min(2, window.devicePixelRatio || 1);
|
|
5426
|
-
const wrap = document.createElement("div");
|
|
5427
|
-
wrap.className = "sl-minimap";
|
|
5428
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
5429
|
-
const canvas = document.createElement("canvas");
|
|
5430
5670
|
canvas.width = Math.round(w * dpr);
|
|
5431
5671
|
canvas.height = Math.round(h * dpr);
|
|
5432
5672
|
canvas.style.width = `${w}px`;
|
|
5433
5673
|
canvas.style.height = `${h}px`;
|
|
5434
|
-
wrap.appendChild(canvas);
|
|
5435
|
-
(this.regions["bottom-left"] ?? this.els.map).appendChild(wrap);
|
|
5436
|
-
this.miniCanvas = canvas;
|
|
5437
|
-
const scale = Math.min((w - PAD * 2) / Math.max(1, b.width), (h - PAD * 2) / Math.max(1, b.height)) * dpr;
|
|
5438
|
-
const offX = (w * dpr - b.width * scale) / 2 - b.x * scale;
|
|
5439
|
-
const offY = (h * dpr - b.height * scale) / 2 - b.y * scale;
|
|
5440
|
-
this.miniTf = { scale, offX, offY, dpr };
|
|
5441
|
-
const base = document.createElement("canvas");
|
|
5442
5674
|
base.width = canvas.width;
|
|
5443
5675
|
base.height = canvas.height;
|
|
5444
|
-
this.
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
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
|
+
);
|
|
5448
5698
|
}
|
|
5449
5699
|
/** Repaint the static overview + rect (floor switch, live open/close). */
|
|
5450
5700
|
refreshMinimap() {
|
|
5451
5701
|
if (!this.miniBase) return;
|
|
5702
|
+
this.layoutMinimap();
|
|
5703
|
+
this.syncMinimapLabel();
|
|
5452
5704
|
this.drawMinimapStatic();
|
|
5453
5705
|
this.drawMinimapRect();
|
|
5454
5706
|
}
|
|
@@ -5456,8 +5708,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5456
5708
|
drawMinimapStatic() {
|
|
5457
5709
|
const base = this.miniBase;
|
|
5458
5710
|
const tf = this.miniTf;
|
|
5459
|
-
|
|
5460
|
-
if (!base || !tf || !doc) return;
|
|
5711
|
+
if (!base || !tf) return;
|
|
5461
5712
|
const ctx = base.getContext("2d");
|
|
5462
5713
|
if (!ctx) return;
|
|
5463
5714
|
ctx.clearRect(0, 0, base.width, base.height);
|
|
@@ -5466,34 +5717,42 @@ var SeatPicker = class _SeatPicker {
|
|
|
5466
5717
|
const line = this.cssVar("--sl-line") || "rgba(139,147,167,.5)";
|
|
5467
5718
|
const muted = this.cssVar("--sl-muted") || "#8b93a7";
|
|
5468
5719
|
const accent = this.cssVar("--sl-accent") || "#6e7bff";
|
|
5469
|
-
const
|
|
5470
|
-
|
|
5471
|
-
for (const
|
|
5472
|
-
if (
|
|
5473
|
-
drewSection = true;
|
|
5474
|
-
const closed = this.controller.isSectionClosed(o.id);
|
|
5475
|
-
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;
|
|
5476
5724
|
ctx.beginPath();
|
|
5477
|
-
|
|
5725
|
+
section.outline.forEach((point, index) => index === 0 ? ctx.moveTo(fx(point.x), fy(point.y)) : ctx.lineTo(fx(point.x), fy(point.y)));
|
|
5478
5726
|
ctx.closePath();
|
|
5479
|
-
ctx.globalAlpha =
|
|
5480
|
-
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;
|
|
5481
5729
|
ctx.fill();
|
|
5482
|
-
ctx.globalAlpha = 0.
|
|
5730
|
+
ctx.globalAlpha = 0.82;
|
|
5483
5731
|
ctx.lineWidth = Math.max(1, tf.dpr);
|
|
5484
5732
|
ctx.strokeStyle = line;
|
|
5485
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
|
+
}
|
|
5486
5744
|
}
|
|
5487
5745
|
ctx.globalAlpha = 1;
|
|
5488
|
-
if (!
|
|
5746
|
+
if (!snapshot.sections.length) {
|
|
5489
5747
|
const r = Math.max(1, tf.dpr);
|
|
5490
|
-
for (const seat of
|
|
5491
|
-
|
|
5492
|
-
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;
|
|
5493
5751
|
ctx.beginPath();
|
|
5494
5752
|
ctx.arc(fx(seat.x), fy(seat.y), r, 0, Math.PI * 2);
|
|
5495
5753
|
ctx.fill();
|
|
5496
5754
|
}
|
|
5755
|
+
ctx.globalAlpha = 1;
|
|
5497
5756
|
}
|
|
5498
5757
|
}
|
|
5499
5758
|
/** Blit the base overview, then stroke the current viewport rectangle on top. */
|
|
@@ -5508,41 +5767,106 @@ var SeatPicker = class _SeatPicker {
|
|
|
5508
5767
|
ctx.drawImage(base, 0, 0);
|
|
5509
5768
|
const vp = this.controller.getViewport();
|
|
5510
5769
|
if (!vp) return;
|
|
5511
|
-
const
|
|
5512
|
-
const
|
|
5513
|
-
const
|
|
5514
|
-
const w = v.width * tf.scale;
|
|
5515
|
-
const h = v.height * tf.scale;
|
|
5516
|
-
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";
|
|
5517
5773
|
ctx.save();
|
|
5518
|
-
ctx.globalAlpha = 0.
|
|
5519
|
-
ctx.fillStyle =
|
|
5520
|
-
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);
|
|
5521
5782
|
ctx.globalAlpha = 1;
|
|
5522
|
-
ctx.lineWidth = Math.max(
|
|
5523
|
-
ctx.strokeStyle =
|
|
5524
|
-
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
|
+
}
|
|
5525
5790
|
ctx.restore();
|
|
5526
5791
|
}
|
|
5527
|
-
|
|
5528
|
-
minimapJump(e) {
|
|
5792
|
+
minimapWorldPoint(event) {
|
|
5529
5793
|
const canvas = this.miniCanvas;
|
|
5530
5794
|
const tf = this.miniTf;
|
|
5531
|
-
if (!canvas || !tf) return;
|
|
5532
|
-
const
|
|
5533
|
-
const px = (
|
|
5534
|
-
const py = (
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
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);
|
|
5542
5865
|
return;
|
|
5543
5866
|
}
|
|
5544
5867
|
}
|
|
5545
|
-
this.controller.
|
|
5868
|
+
this.controller.clearSectionFocus();
|
|
5869
|
+
this.controller.panToWorld(point);
|
|
5546
5870
|
}
|
|
5547
5871
|
// ---- F4 price-band filter -------------------------------------------------
|
|
5548
5872
|
/** Effective display price of a category: host pricing override → first tier → base. */
|
|
@@ -5551,32 +5875,39 @@ var SeatPicker = class _SeatPicker {
|
|
|
5551
5875
|
if (chart === void 0 || !c.key) return chart;
|
|
5552
5876
|
return this.paidPrice(c.key, c.tiers?.[0]?.id ?? null, chart);
|
|
5553
5877
|
}
|
|
5554
|
-
/**
|
|
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. */
|
|
5555
5885
|
priceBands() {
|
|
5556
5886
|
const doc = this.controller.doc;
|
|
5557
5887
|
if (!doc) return [];
|
|
5558
|
-
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);
|
|
5559
5889
|
if (!priced.length) return [];
|
|
5560
|
-
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);
|
|
5561
5891
|
if (distinct.length <= 5) {
|
|
5562
|
-
return distinct.map((
|
|
5563
|
-
id: `p${
|
|
5564
|
-
label: this.money(
|
|
5565
|
-
keys: priced.filter((
|
|
5566
|
-
min:
|
|
5567
|
-
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
|
|
5568
5898
|
}));
|
|
5569
5899
|
}
|
|
5570
|
-
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);
|
|
5571
5902
|
const bands = [];
|
|
5572
|
-
for (let i = 0; i <
|
|
5573
|
-
const slice =
|
|
5574
|
-
const lo = slice
|
|
5575
|
-
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));
|
|
5576
5907
|
bands.push({
|
|
5577
5908
|
id: `b${i}`,
|
|
5578
5909
|
label: lo === hi ? this.money(lo) : `${this.money(lo)}\u2013${this.money(hi)}`,
|
|
5579
|
-
keys:
|
|
5910
|
+
keys: slice.map((item) => item.key),
|
|
5580
5911
|
min: lo,
|
|
5581
5912
|
max: hi
|
|
5582
5913
|
});
|
|
@@ -5631,7 +5962,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5631
5962
|
this.syncProjection();
|
|
5632
5963
|
}
|
|
5633
5964
|
if (hasSections) {
|
|
5634
|
-
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"];
|
|
5635
5972
|
const pills = document.createElement("div");
|
|
5636
5973
|
pills.className = "sl-rungs on";
|
|
5637
5974
|
pills.setAttribute("role", "group");
|
|
@@ -5664,18 +6001,38 @@ var SeatPicker = class _SeatPicker {
|
|
|
5664
6001
|
const floors = this.controller.getFloors();
|
|
5665
6002
|
const rail = document.createElement("div");
|
|
5666
6003
|
rail.className = "sl-floors on";
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
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();
|
|
5678
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);
|
|
5679
6036
|
this.regions["left-rail"].appendChild(rail);
|
|
5680
6037
|
this.floorsEl = rail;
|
|
5681
6038
|
this.syncFloors();
|
|
@@ -5721,19 +6078,59 @@ var SeatPicker = class _SeatPicker {
|
|
|
5721
6078
|
syncFloors() {
|
|
5722
6079
|
if (!this.floorsEl) return;
|
|
5723
6080
|
const active = this.controller.getActiveFloorId();
|
|
5724
|
-
this.
|
|
5725
|
-
|
|
5726
|
-
|
|
6081
|
+
const overview = this.controller.isFloorOverview();
|
|
6082
|
+
const select = this.floorsEl.querySelector("select");
|
|
6083
|
+
if (select) select.value = overview ? "" : active;
|
|
5727
6084
|
}
|
|
5728
6085
|
/** Show (or clear, on null) the tapped-section summary card. */
|
|
5729
6086
|
showSectionCard(summary) {
|
|
5730
6087
|
this.lastSection = summary;
|
|
5731
6088
|
this.secCardEl?.remove();
|
|
5732
6089
|
this.secCardEl = null;
|
|
5733
|
-
if (!summary)
|
|
6090
|
+
if (!summary) {
|
|
6091
|
+
this.refreshMinimap();
|
|
6092
|
+
return;
|
|
6093
|
+
}
|
|
5734
6094
|
this.secCardCollapsed = this.controller.getRung() === "seats";
|
|
5735
6095
|
this.secCardShownAt = Date.now();
|
|
5736
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
|
+
});
|
|
5737
6134
|
}
|
|
5738
6135
|
/**
|
|
5739
6136
|
* Render the section card in the form the layout + state want: expanded card
|
|
@@ -5747,26 +6144,32 @@ var SeatPicker = class _SeatPicker {
|
|
|
5747
6144
|
this.lastSection = null;
|
|
5748
6145
|
return;
|
|
5749
6146
|
}
|
|
5750
|
-
const
|
|
5751
|
-
|
|
5752
|
-
|
|
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));
|
|
5753
6153
|
const priceLabel = paidMin === paidMax ? this.money(paidMin) : `${this.money(paidMin)}\u2013${this.money(paidMax)}`;
|
|
5754
6154
|
const leftLabel = (0, import_core2.tCount)("picker.seatsLeftInSection", summary.seatsLeft);
|
|
5755
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);
|
|
5756
6157
|
const card = document.createElement("div");
|
|
5757
6158
|
const narrow = this.root?.dataset.layout === "narrow";
|
|
5758
6159
|
if (narrow) {
|
|
5759
6160
|
card.className = "sl-seccard strip on";
|
|
5760
6161
|
card.setAttribute("role", "status");
|
|
5761
6162
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5762
|
-
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);
|
|
5763
6165
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5764
6166
|
(this.els.sheetHead ?? this.els.side ?? this.els.map).appendChild(card);
|
|
5765
6167
|
} else if (this.secCardCollapsed) {
|
|
5766
6168
|
card.className = "sl-seccard mini on";
|
|
5767
6169
|
card.setAttribute("role", "button");
|
|
5768
6170
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5769
|
-
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);
|
|
5770
6173
|
card.addEventListener("click", (e) => {
|
|
5771
6174
|
if (e.target.closest(".sl-seccard-x")) return;
|
|
5772
6175
|
this.secCardCollapsed = false;
|
|
@@ -5781,9 +6184,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
5781
6184
|
card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
|
|
5782
6185
|
const mix = summary.categories.map((c) => {
|
|
5783
6186
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
5784
|
-
|
|
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>`;
|
|
5785
6191
|
}).join("");
|
|
5786
|
-
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);
|
|
5787
6194
|
card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
|
|
5788
6195
|
card.querySelector(".sl-seccard-overview").addEventListener("click", () => this.controller.overview());
|
|
5789
6196
|
(this.regions["top-center"] ?? this.els.map).appendChild(card);
|
|
@@ -5848,13 +6255,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
5848
6255
|
const cat = this.controller.doc?.categories.find((c) => c.key === seat.categoryKey);
|
|
5849
6256
|
const status = this.controller.getStatus(seat.id) ?? "free";
|
|
5850
6257
|
const statusText = status === "free" ? this.tf("picker.statusAvailable", "available") : status === "held" ? this.tf("picker.statusOnHold", "on hold") : this.tf("picker.statusTaken2", "taken");
|
|
5851
|
-
const
|
|
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;
|
|
5852
6260
|
const table = this.controller.tableSelection(seat.id);
|
|
5853
6261
|
const details = table ?? this.controller.seatDetails(seat.id);
|
|
5854
6262
|
const typeWord = this.rowTypeWord(details);
|
|
5855
6263
|
const buyerName = details?.rowLabel ?? details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
5856
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 });
|
|
5857
|
-
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${
|
|
6265
|
+
this.srEl.textContent = `${identity}, ${cat?.label ?? seat.categoryKey}${priceText ? `, ${priceText}` : ""}, ${statusText}`;
|
|
5858
6266
|
}
|
|
5859
6267
|
// ---- atomic table confirmation / guest quantity ---------------------------
|
|
5860
6268
|
showTableDialog(table, held, returnFocus) {
|
|
@@ -6021,12 +6429,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
6021
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>` : "",
|
|
6022
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>` : ""
|
|
6023
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>` : "";
|
|
6024
6439
|
const el2 = document.createElement("div");
|
|
6025
6440
|
el2.className = "sl-confirm";
|
|
6026
6441
|
el2.setAttribute("role", "dialog");
|
|
6027
6442
|
el2.setAttribute("aria-label", (0, import_core2.t)("picker.confirmSeatLabel", { label: seat.label }));
|
|
6028
|
-
|
|
6029
|
-
|
|
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>`;
|
|
6030
6448
|
this.els.map.appendChild(el2);
|
|
6031
6449
|
this.confirmEl = el2;
|
|
6032
6450
|
const thumb = el2.querySelector(".sl-confirm-thumb");
|
|
@@ -6042,6 +6460,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
6042
6460
|
this.openSeatConfidencePassport(seat, event.currentTarget instanceof HTMLElement ? event.currentTarget : null);
|
|
6043
6461
|
});
|
|
6044
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
|
+
});
|
|
6045
6479
|
el2.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
6046
6480
|
if (this.buyerView === "venue3d") {
|
|
6047
6481
|
this.dismissConfirm();
|
|
@@ -6418,21 +6852,23 @@ var SeatPicker = class _SeatPicker {
|
|
|
6418
6852
|
const doc = this.controller.doc;
|
|
6419
6853
|
if (!doc || !this.els.prices) return;
|
|
6420
6854
|
const left = this.controller.categoryAvailability();
|
|
6421
|
-
|
|
6422
|
-
this.
|
|
6855
|
+
const sellable = doc.categories.filter((c) => !c.notForSale);
|
|
6856
|
+
this.narrateAvailability(sellable, left);
|
|
6857
|
+
this.syncSoldout(sellable, left);
|
|
6423
6858
|
const PRICE_LIMIT = 5;
|
|
6424
|
-
const overflow =
|
|
6859
|
+
const overflow = sellable.length - PRICE_LIMIT;
|
|
6425
6860
|
const collapsed = overflow > 1 && !this.pricesExpanded;
|
|
6426
|
-
const shown = collapsed ?
|
|
6861
|
+
const shown = collapsed ? sellable.slice(0, PRICE_LIMIT) : sellable;
|
|
6427
6862
|
this.els.prices.classList.toggle("sl-expanded", overflow > 1 && this.pricesExpanded);
|
|
6428
6863
|
this.els.prices.innerHTML = shown.map((c) => {
|
|
6429
6864
|
const price = this.catPrice(c);
|
|
6865
|
+
const range = this.catPriceRange(c);
|
|
6430
6866
|
const offer = this.offerPrice(c.key);
|
|
6431
6867
|
const previous = offer?.previousPrice != null && offer.previousPrice > offer.price ? offer.previousPrice : null;
|
|
6432
6868
|
const active = this.focusedCatKey === c.key;
|
|
6433
6869
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
6434
|
-
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>` : "") + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
6435
|
-
}).join("") + (overflow > 1 ? `<button type="button" class="sl-price-more" aria-expanded="${!collapsed}">` + (collapsed ? (0, import_core2.t)("picker.showAllTicketTypes", { count:
|
|
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>`;
|
|
6436
6872
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
6437
6873
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
6438
6874
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
@@ -6536,7 +6972,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6536
6972
|
const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
|
|
6537
6973
|
if (noPicks) this.baReopen = false;
|
|
6538
6974
|
if (!this.salesClosed && !this.hold && (noPicks || this.baReopen || this.bestAvailableBusy || this.bestAvailableConfirm)) {
|
|
6539
|
-
const cats = this.controller.doc?.categories ?? [];
|
|
6975
|
+
const cats = (this.controller.doc?.categories ?? []).filter((c) => !c.notForSale);
|
|
6540
6976
|
const zones = this.controller.getBestAvailableZones();
|
|
6541
6977
|
if (this.baZone && !zones.some((zone) => zone.id === this.baZone)) this.baZone = "";
|
|
6542
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
|
|
@@ -6592,8 +7028,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
6592
7028
|
nextTrayKeys.add(itemKey);
|
|
6593
7029
|
const cat = this.controller.doc?.categories.find((c) => c.key === s.categoryKey);
|
|
6594
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") : "");
|
|
6595
7033
|
parts.push(
|
|
6596
|
-
`<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
|
|
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>`
|
|
6597
7035
|
);
|
|
6598
7036
|
}
|
|
6599
7037
|
for (const area of gaAreas) {
|
|
@@ -6906,7 +7344,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6906
7344
|
const problem = err;
|
|
6907
7345
|
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
6908
7346
|
if (problem.reason === "event_closed") this.setSalesClosed(true);
|
|
6909
|
-
const message = problem.reason === "event_closed" ? this.tf("picker.seatSalesClosedForEvent", "Seat sales have closed for this event.") : 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.");
|
|
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.");
|
|
6910
7348
|
this.toast(message, "error");
|
|
6911
7349
|
this.setCtaPhase("idle");
|
|
6912
7350
|
} finally {
|
|
@@ -7235,13 +7673,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
7235
7673
|
const isBooth = details.objectType === "booth";
|
|
7236
7674
|
const hasLoc = details.sectionLabel || details.rowLabel || details.seatNumber;
|
|
7237
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>`;
|
|
7238
|
-
const
|
|
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>`;
|
|
7239
7678
|
const limited = this.limitedViewLabel(details.commercial);
|
|
7240
7679
|
const cxLine = limited ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u25D0</span>${esc(limited)}</div>` : "";
|
|
7241
7680
|
const wheelchair = this.wheelchairProvisionLabel(details.wheelchairSpaceType);
|
|
7242
7681
|
const wheelchairLine = wheelchair ? `<div class="sl-tip-cx"><span class="g" aria-hidden="true">\u267F</span>${esc(wheelchair)}</div>` : "";
|
|
7243
7682
|
this.tipEl.style.setProperty("--sl-cat", details.categoryColor);
|
|
7244
|
-
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;
|
|
7245
7684
|
this.tipEl.style.display = "block";
|
|
7246
7685
|
this.placeTooltip();
|
|
7247
7686
|
}
|
|
@@ -7464,13 +7903,16 @@ var SeatPicker = class _SeatPicker {
|
|
|
7464
7903
|
sourceLabel: this.tf("picker.chartDerivedModel", "Chart-derived model")
|
|
7465
7904
|
};
|
|
7466
7905
|
}
|
|
7467
|
-
/** Route
|
|
7468
|
-
|
|
7906
|
+
/** Route decoupled analytics into the host callback, tagged buyer. */
|
|
7907
|
+
emitAnalytics(event, props) {
|
|
7469
7908
|
try {
|
|
7470
7909
|
this.opts.onAnalytics?.(event, { ...props, surface: "buyer" });
|
|
7471
7910
|
} catch {
|
|
7472
7911
|
}
|
|
7473
7912
|
}
|
|
7913
|
+
emit3dAnalytics(event, props) {
|
|
7914
|
+
this.emitAnalytics(event, props);
|
|
7915
|
+
}
|
|
7474
7916
|
/** A 3D seat tap runs the SAME selection path as a 2D tap: toggle through the
|
|
7475
7917
|
* controller, then raise the shared confirm card (bottom-sheeted in 3D). */
|
|
7476
7918
|
onView3dSeatPick(seatId) {
|
|
@@ -7489,9 +7931,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
7489
7931
|
this.syncSelectionTo3d();
|
|
7490
7932
|
return;
|
|
7491
7933
|
}
|
|
7492
|
-
const
|
|
7493
|
-
if (
|
|
7494
|
-
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);
|
|
7495
7937
|
this.syncSelectionTo3d();
|
|
7496
7938
|
return;
|
|
7497
7939
|
}
|
|
@@ -7513,14 +7955,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
7513
7955
|
else this.syncTray();
|
|
7514
7956
|
this.syncSelectionTo3d();
|
|
7515
7957
|
}
|
|
7516
|
-
/** Explain a
|
|
7517
|
-
*
|
|
7518
|
-
*
|
|
7519
|
-
|
|
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) {
|
|
7520
7963
|
const overlay = this.view3dEl;
|
|
7521
7964
|
if (!overlay) return;
|
|
7522
7965
|
this.dismissUnavailable3dSeat();
|
|
7523
|
-
const status = this.controller.getStatus(seat.id);
|
|
7524
7966
|
const details = this.controller.seatDetails(seat.id);
|
|
7525
7967
|
const identity = details?.displayLabel ?? seat.displayLabel ?? seat.label;
|
|
7526
7968
|
const section = details?.sectionLabel;
|
|
@@ -7532,12 +7974,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
7532
7974
|
} : status === "booked" ? {
|
|
7533
7975
|
title: this.tf("picker.sold", "Sold"),
|
|
7534
7976
|
message: this.tf("picker.soldSeatExplanation", "This seat has already been booked.")
|
|
7535
|
-
} :
|
|
7977
|
+
} : {
|
|
7536
7978
|
title: this.tf("picker.notForSale", "Not for sale"),
|
|
7537
7979
|
message: this.tf("picker.notForSaleExplanation", "This seat is not included in the current sale.")
|
|
7538
|
-
} : {
|
|
7539
|
-
title: this.tf("picker.filteredSeat", "Unavailable with current filters"),
|
|
7540
|
-
message: this.tf("picker.filteredSeatExplanation", "Change the active price or view filters to make this seat selectable.")
|
|
7541
7980
|
};
|
|
7542
7981
|
const card = document.createElement("div");
|
|
7543
7982
|
card.className = "sl-view3d-unavailable";
|
|
@@ -7915,7 +8354,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7915
8354
|
const floors = handle.floors();
|
|
7916
8355
|
const floorLabels = new Set(floors.map((floor) => floor.label.trim().toLocaleLowerCase()));
|
|
7917
8356
|
const zones = handle.zones().filter((zone) => zone.seatCount > 0 && !floorLabels.has(zone.label.trim().toLocaleLowerCase()));
|
|
7918
|
-
const allSections = handle.sections().filter((
|
|
8357
|
+
const allSections = handle.sections().filter((section) => section.seatCount > 0 && !floorLabels.has(section.label.trim().toLocaleLowerCase()));
|
|
7919
8358
|
const sections = zones.length > 1 ? [] : allSections;
|
|
7920
8359
|
const wantFloors = floors.length > 1;
|
|
7921
8360
|
const wantZones = zones.length > 1;
|
|
@@ -7942,16 +8381,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
7942
8381
|
});
|
|
7943
8382
|
setFinderOpen(false);
|
|
7944
8383
|
nav.appendChild(finderToggle);
|
|
8384
|
+
let syncAreasForFloor = (_index) => void 0;
|
|
8385
|
+
let syncPressedFloor = (_index) => void 0;
|
|
7945
8386
|
if (wantFloors) {
|
|
7946
8387
|
const row = document.createElement("div");
|
|
7947
8388
|
row.setAttribute("role", "group");
|
|
7948
8389
|
row.setAttribute("aria-label", this.tf("picker.levels", "Levels"));
|
|
7949
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
|
+
};
|
|
7950
8398
|
const select = (index) => {
|
|
7951
8399
|
if (!handle.focusFloor(index)) return;
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
});
|
|
8400
|
+
syncPressedFloor(index);
|
|
8401
|
+
syncAreasForFloor(index);
|
|
7955
8402
|
};
|
|
7956
8403
|
const add = (label, index) => {
|
|
7957
8404
|
const b = document.createElement("button");
|
|
@@ -7976,39 +8423,55 @@ var SeatPicker = class _SeatPicker {
|
|
|
7976
8423
|
row.setAttribute("aria-label", this.tf("picker.areas", "Areas"));
|
|
7977
8424
|
const entries = wantZones ? zones.map((z) => ({ id: z.id, label: z.label || z.id, go: () => {
|
|
7978
8425
|
handle.focusZone(z.id);
|
|
7979
|
-
} })) : sections.map((s) => ({
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
placeholder.value = "";
|
|
7987
|
-
placeholder.textContent = this.tf("picker.jumpToSection", "Jump to section");
|
|
7988
|
-
select.appendChild(placeholder);
|
|
7989
|
-
for (const entry of entries) {
|
|
7990
|
-
const option = document.createElement("option");
|
|
7991
|
-
option.value = entry.id;
|
|
7992
|
-
option.textContent = entry.label;
|
|
7993
|
-
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);
|
|
7994
8433
|
}
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
row.
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
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();
|
|
8440
|
+
return;
|
|
8441
|
+
}
|
|
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);
|
|
8460
|
+
return;
|
|
8461
|
+
}
|
|
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();
|
|
8006
8468
|
setFinderOpen(false);
|
|
8007
8469
|
});
|
|
8008
|
-
row.appendChild(
|
|
8470
|
+
row.appendChild(button);
|
|
8009
8471
|
}
|
|
8010
|
-
}
|
|
8011
|
-
|
|
8472
|
+
};
|
|
8473
|
+
syncAreasForFloor = renderEntries;
|
|
8474
|
+
renderEntries(null);
|
|
8012
8475
|
}
|
|
8013
8476
|
overlay.appendChild(nav);
|
|
8014
8477
|
}
|
|
@@ -8207,7 +8670,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8207
8670
|
} catch (err) {
|
|
8208
8671
|
this.opts.onError?.(err);
|
|
8209
8672
|
const reason = err?.reason;
|
|
8210
|
-
const message = reason === "not_enough_together" ? (0, import_core2.t)("picker.couldNotFindSeatsTogether", { count: qty }) : 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.");
|
|
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.");
|
|
8211
8674
|
this.toast(message, "error");
|
|
8212
8675
|
return null;
|
|
8213
8676
|
} finally {
|