@seatlayer/js 0.15.1 → 0.16.1
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 +308 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.js +308 -123
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -476,24 +476,26 @@ var CSS = `
|
|
|
476
476
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
477
477
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
478
478
|
.sl-map-host{position:absolute;inset:0}
|
|
479
|
-
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow
|
|
479
|
+
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow:hidden}
|
|
480
480
|
|
|
481
481
|
/* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
|
|
482
482
|
container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
|
|
483
483
|
mobile pattern). data-sheet on the root: "peek" (default: grab handle + one
|
|
484
|
-
summary line) / "open" (
|
|
484
|
+
summary line) / "open" (room for rows + checkout, swipe up to open).
|
|
485
485
|
Swipe handling lives on the sheet head ONLY \u2014 never the map host, so the
|
|
486
486
|
map's raw-pointer gesture pipeline is untouched. */
|
|
487
487
|
.sl-picker[data-layout="narrow"] .sl-body{flex-direction:column}
|
|
488
488
|
.sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
|
|
489
489
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
490
|
-
flex:none;height:
|
|
490
|
+
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
491
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
|
|
491
492
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:86px;overflow:hidden}
|
|
492
493
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
493
|
-
.sl-picker[data-layout="narrow"] .sl-tray{flex:
|
|
494
|
-
.sl-picker[data-layout="narrow"] .sl-foot{position:
|
|
494
|
+
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
|
|
495
|
+
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
496
|
+
.sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
|
|
495
497
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
|
|
496
|
-
.sl-picker[data-layout="narrow"] .sl-seats-sec{
|
|
498
|
+
.sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
|
|
497
499
|
.sl-picker[data-layout="narrow"] .sl-tray{order:2}
|
|
498
500
|
.sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
|
|
499
501
|
.sl-picker[data-layout="narrow"] .sl-filters{order:4}
|
|
@@ -501,13 +503,21 @@ var CSS = `
|
|
|
501
503
|
.sl-picker[data-layout="narrow"] .sl-pricef{order:6}
|
|
502
504
|
.sl-picker[data-layout="narrow"] .sl-prices{order:7}
|
|
503
505
|
.sl-picker[data-layout="narrow"] .sl-foot{order:8}
|
|
506
|
+
.sl-picker[data-layout="narrow"] .sl-tray-hint,
|
|
507
|
+
.sl-picker[data-layout="narrow"] .sl-filtersec,
|
|
508
|
+
.sl-picker[data-layout="narrow"] .sl-filters,
|
|
509
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec,
|
|
510
|
+
.sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
|
|
511
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
|
|
512
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
|
|
513
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-prices-sec{display:none}
|
|
504
514
|
/* touch chrome: pinch-zoom exists \u2014 hide +/\u2212 on the sheet layout (keep fit) */
|
|
505
515
|
.sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zin"],
|
|
506
516
|
.sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zout"]{display:none}
|
|
507
517
|
|
|
508
518
|
/* bottom-sheet head: grab handle + one-line summary (narrow only). The WHOLE
|
|
509
519
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
510
|
-
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px
|
|
520
|
+
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
511
521
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
512
522
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex}
|
|
513
523
|
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:2px auto 7px}
|
|
@@ -522,54 +532,72 @@ var CSS = `
|
|
|
522
532
|
/* state chevron: points UP while peeking, rotates to point DOWN when open.
|
|
523
533
|
Base keeps an explicit rotate(0) \u2014 transitioning to/from a bare 'none' leaves
|
|
524
534
|
the value stuck in some engines, so both endpoints must be real transforms. */
|
|
525
|
-
.sl-sheet-
|
|
526
|
-
|
|
527
|
-
.sl-sheet-
|
|
535
|
+
.sl-sheet-toggle{width:44px;height:44px;margin:-8px -8px -8px 0;border-radius:999px;flex:none;display:flex;
|
|
536
|
+
align-items:center;justify-content:center;color:var(--sl-muted);transition:color .15s,background .15s}
|
|
537
|
+
.sl-sheet-toggle:hover,.sl-sheet-toggle:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-line) 44%,transparent)}
|
|
538
|
+
.sl-sheet-toggle svg{width:21px;height:21px;stroke:currentColor;stroke-width:2.4;fill:none;
|
|
528
539
|
stroke-linecap:round;stroke-linejoin:round}
|
|
529
|
-
.sl-
|
|
540
|
+
.sl-sheet-toggle svg{transform:rotate(0deg);transition:transform .24s cubic-bezier(.2,.8,.2,1)}
|
|
541
|
+
.sl-picker[data-sheet="open"] .sl-sheet-toggle svg{transform:rotate(180deg)}
|
|
530
542
|
|
|
531
543
|
/* consolidated Filters row inside the sheet (a11y chips + colorblind toggle
|
|
532
544
|
dock here on narrow; they live on the map / zoom column on wide) */
|
|
533
545
|
.sl-filtersec{display:none}
|
|
534
546
|
.sl-filters{display:none;gap:6px;flex-wrap:wrap;align-items:center;padding:2px 16px 10px}
|
|
535
|
-
.sl-picker[data-layout="narrow"] .sl-filtersec.has
|
|
536
|
-
.sl-picker[data-layout="narrow"] .sl-filters.has{display:
|
|
547
|
+
.sl-picker[data-layout="narrow"] .sl-filtersec.has,
|
|
548
|
+
.sl-picker[data-layout="narrow"] .sl-filters.has{display:none}
|
|
549
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec.has,
|
|
550
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters.has{display:none}
|
|
537
551
|
.sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
538
552
|
color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
539
553
|
.sl-cbbtn:hover{border-color:var(--sl-muted)}
|
|
540
554
|
.sl-cbbtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
541
555
|
|
|
542
|
-
/* price panel */
|
|
543
|
-
.sl-sec{padding:14px
|
|
544
|
-
.sl-prices{
|
|
545
|
-
.sl-price-
|
|
556
|
+
/* price panel \u2014 one compact filter control replaces the wrapping price-chip row. */
|
|
557
|
+
.sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
558
|
+
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
559
|
+
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
560
|
+
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
561
|
+
.sl-prices{padding:5px 14px 10px;border-bottom:1px solid var(--sl-line)}
|
|
562
|
+
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
563
|
+
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px}
|
|
546
564
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
547
565
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
548
566
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
549
567
|
.sl-price-amt{font-weight:800;font-variant-numeric:tabular-nums}
|
|
550
|
-
.sl-status-key{display:flex;gap:
|
|
568
|
+
.sl-status-key{display:flex;gap:13px;flex-wrap:wrap;padding:8px 0 2px;margin-top:5px;border-top:1px solid var(--sl-line);color:var(--sl-muted);font-size:10.5px}
|
|
551
569
|
.sl-status-item{display:inline-flex;align-items:center;gap:6px}
|
|
552
570
|
.sl-status-icon{width:17px;height:17px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;
|
|
553
|
-
color:#fff;background:#6b7280;
|
|
554
|
-
.sl-status-icon
|
|
571
|
+
color:#fff;background:#6b7280;line-height:1}
|
|
572
|
+
.sl-status-icon svg{width:10px;height:10px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
573
|
+
.sl-status-icon.sold{background:#8b93a0}
|
|
574
|
+
.sl-status-icon.sold svg{width:11px;height:11px;stroke-width:2.4}
|
|
555
575
|
|
|
556
576
|
/* tray */
|
|
557
|
-
.sl-
|
|
577
|
+
.sl-seats-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
578
|
+
.sl-seat-summary{font-size:10px;letter-spacing:0;text-transform:none;white-space:nowrap}
|
|
579
|
+
.sl-tray{flex:1;padding:10px 14px 14px;display:flex;flex-direction:column;gap:7px;min-height:0;overflow-y:auto;
|
|
580
|
+
overscroll-behavior:contain;scrollbar-gutter:stable}
|
|
558
581
|
.sl-tray-hint{font-size:12.5px;color:var(--sl-muted);line-height:1.5}
|
|
559
|
-
.sl-chip{display:
|
|
560
|
-
|
|
582
|
+
.sl-chip{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto 30px;align-items:center;gap:8px;
|
|
583
|
+
min-height:53px;padding:7px 7px 7px 9px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm);
|
|
584
|
+
background:var(--sl-surface);font-size:13px;transform-origin:center;transition:border-color .15s,background .15s}
|
|
585
|
+
.sl-chip:hover{border-color:color-mix(in srgb,var(--sl-accent) 38%,var(--sl-line))}
|
|
561
586
|
.sl-chip.sl-enter{animation:slChipIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
562
587
|
.sl-chip.sl-leave{pointer-events:none;animation:slChipOut .16s ease-in both}
|
|
563
588
|
.sl-chip.sl-held{border-color:var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
564
589
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
|
|
565
|
-
.sl-
|
|
566
|
-
|
|
590
|
+
.sl-ticket-state{width:23px;height:23px;border-radius:999px;display:flex;align-items:center;justify-content:center;
|
|
591
|
+
background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
592
|
+
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent)}
|
|
593
|
+
.sl-ticket-state svg{width:13px;height:13px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
594
|
+
.sl-chip-main{min-width:0}
|
|
595
|
+
.sl-chip b{display:block;font-weight:800;min-width:max-content;white-space:nowrap}
|
|
596
|
+
.sl-chip-sub{display:flex;align-items:center;gap:5px;min-width:0;margin-top:2px}
|
|
597
|
+
.sl-chip .cat{color:var(--sl-muted);font-size:10.5px;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
567
598
|
.sl-chip .amt{font-weight:700;font-variant-numeric:tabular-nums;flex:none;white-space:nowrap}
|
|
568
|
-
.sl-chip
|
|
569
|
-
|
|
570
|
-
.sl-chip-state.sl-pending{background:transparent;color:var(--sl-muted);border:1px solid var(--sl-line)}
|
|
571
|
-
.sl-chip .rm{width:22px;height:22px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
|
|
572
|
-
.sl-chip .rm:hover{color:var(--sl-text)}
|
|
599
|
+
.sl-chip .rm{width:29px;height:29px;border-radius:8px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
|
|
600
|
+
.sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
|
|
573
601
|
.sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
|
|
574
602
|
|
|
575
603
|
/* GA rows */
|
|
@@ -584,16 +612,22 @@ var CSS = `
|
|
|
584
612
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
585
613
|
|
|
586
614
|
/* footer */
|
|
587
|
-
.sl-foot{padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none
|
|
588
|
-
|
|
615
|
+
.sl-foot{position:relative;z-index:2;padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none;
|
|
616
|
+
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
617
|
+
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
589
618
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
590
619
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent);
|
|
591
620
|
font-size:11.5px;line-height:1.35;color:var(--sl-muted)}
|
|
592
621
|
.sl-hold-note.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
593
622
|
.sl-hold-note svg{width:16px;height:16px;flex:none;stroke:var(--sl-accent);stroke-width:2.4;fill:none;
|
|
594
623
|
stroke-linecap:round;stroke-linejoin:round}
|
|
595
|
-
.sl-hold-note b{display:block;color:var(--sl-text);font-size:
|
|
596
|
-
.sl-hold-copy{display:block}
|
|
624
|
+
.sl-hold-note b{display:block;color:var(--sl-text);font-size:11.5px;white-space:nowrap}
|
|
625
|
+
.sl-hold-copy{display:block;white-space:nowrap;font-size:10.5px}
|
|
626
|
+
.sl-hold-note>span{flex:1;min-width:0}
|
|
627
|
+
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
628
|
+
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
629
|
+
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
630
|
+
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
597
631
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
598
632
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
599
633
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
@@ -651,6 +685,9 @@ var CSS = `
|
|
|
651
685
|
font-size:12.5px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .22s,transform .22s;white-space:nowrap;
|
|
652
686
|
overflow:hidden;text-overflow:ellipsis}
|
|
653
687
|
.sl-toast.on{opacity:1;transform:translateY(0) scale(1)}
|
|
688
|
+
.sl-toast.has-action{pointer-events:auto;display:flex;align-items:center;gap:12px;padding-right:8px}
|
|
689
|
+
.sl-toast-action{min-height:30px;padding:5px 10px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
690
|
+
font:inherit;font-weight:800}
|
|
654
691
|
.sl-toast[data-tone="error"]{border-color:#ef4444}
|
|
655
692
|
.sl-toast[data-tone="warning"]{border-color:var(--sl-accent)}
|
|
656
693
|
.sl-toast[data-tone="success"]{border-color:#22c55e}
|
|
@@ -733,29 +770,49 @@ var CSS = `
|
|
|
733
770
|
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(342px,calc(100% - 24px));
|
|
734
771
|
transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
735
772
|
|
|
736
|
-
/*
|
|
737
|
-
.sl-ba{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
.sl-ba
|
|
741
|
-
.sl-ba-
|
|
742
|
-
|
|
773
|
+
/* Best available is a first-class shortcut, not an anonymous utility row. */
|
|
774
|
+
.sl-ba{position:relative;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:7px;
|
|
775
|
+
padding:13px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));border-radius:13px;
|
|
776
|
+
background:linear-gradient(135deg,color-mix(in srgb,var(--sl-accent) 5%,var(--sl-surface)),color-mix(in srgb,var(--sl-accent) 11%,var(--sl-surface)))}
|
|
777
|
+
.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}
|
|
778
|
+
.sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
|
|
779
|
+
.sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
|
|
780
|
+
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
781
|
+
.sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
782
|
+
.sl-ba-copy .narrow{display:none}
|
|
783
|
+
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
784
|
+
font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
|
|
785
|
+
.sl-ba-qty{display:flex;align-items:center;gap:7px;padding:3px;border:1px solid var(--sl-line);border-radius:9px;background:var(--sl-surface)}
|
|
786
|
+
.sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
|
|
787
|
+
font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
|
|
743
788
|
.sl-ba-qty span{min-width:14px;text-align:center;font-weight:800}
|
|
744
|
-
.sl-ba-go{grid-column:1/-1;width:100%;min-height:
|
|
745
|
-
transition:
|
|
746
|
-
|
|
747
|
-
.sl-ba-go:
|
|
789
|
+
.sl-picker .sl-ba-go{grid-column:1/-1;width:100%;min-height:37px;padding:7px 12px;border-radius:9px;background:var(--sl-accent);
|
|
790
|
+
color:var(--sl-accent-ink);font-weight:800;font-size:12px;transition:filter .15s,opacity .15s;display:flex;align-items:center;justify-content:center;gap:6px;
|
|
791
|
+
box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
|
|
792
|
+
.sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
|
|
793
|
+
.sl-picker .sl-ba-go:disabled{opacity:.62;cursor:wait}
|
|
794
|
+
.sl-ba-replace{grid-column:1/-1;padding:3px 0 1px}
|
|
795
|
+
.sl-ba-replace b{display:block;font-size:12.5px}
|
|
796
|
+
.sl-ba-replace span{display:block;margin-top:3px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
797
|
+
.sl-ba-actions{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:7px}
|
|
798
|
+
.sl-ba-actions button{min-height:36px;border-radius:9px;border:1px solid var(--sl-line);font-size:11.5px;font-weight:800}
|
|
799
|
+
.sl-ba-actions .replace{border-color:var(--sl-accent);background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
800
|
+
.sl-picker[data-layout="narrow"] .sl-ba{padding:11px}
|
|
801
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
802
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
748
803
|
|
|
749
804
|
/* screen-reader live region */
|
|
750
805
|
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
751
806
|
|
|
752
807
|
/* per-seat ticket-tier select + view-from-seat button in tray chips */
|
|
753
|
-
.sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:
|
|
754
|
-
font:inherit;font-size:
|
|
755
|
-
.sl-chip .view{width:
|
|
756
|
-
color:var(--sl-muted);transition:color .15s}
|
|
808
|
+
.sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:6px;
|
|
809
|
+
font:inherit;font-size:10px;padding:2px 4px;min-width:0;max-width:100%;cursor:pointer}
|
|
810
|
+
.sl-chip .view{width:20px;height:20px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
811
|
+
color:var(--sl-muted);opacity:.36;transition:color .15s,opacity .15s}
|
|
757
812
|
.sl-chip .view:hover{color:var(--sl-text)}
|
|
758
|
-
.sl-chip .view
|
|
813
|
+
.sl-chip:hover .view,.sl-chip .view:focus-visible{opacity:1;color:var(--sl-text)}
|
|
814
|
+
.sl-chip .view svg{width:12px;height:12px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
815
|
+
@media(pointer:coarse){.sl-chip .view{opacity:.58}}
|
|
759
816
|
|
|
760
817
|
/* arena: LOD rung pills (flow within the top-center region) */
|
|
761
818
|
.sl-rungs{display:none;background:var(--sl-surface);border:1px solid var(--sl-line);border-radius:999px;padding:3px}
|
|
@@ -842,10 +899,6 @@ var CSS = `
|
|
|
842
899
|
.sl-minimap canvas{display:block}
|
|
843
900
|
.sl-picker[data-layout="narrow"] .sl-minimap{display:none}
|
|
844
901
|
|
|
845
|
-
/* F4 price-band filter \u2014 chip row in the side panel, above the price legend it
|
|
846
|
-
* dims (keeps clear of the map's top-center rungs / top-left a11y chips). */
|
|
847
|
-
.sl-pricef{display:flex;gap:6px;flex-wrap:wrap;padding:2px 16px 10px}
|
|
848
|
-
.sl-pricef .sl-chip-f{padding:6px 11px;font-size:11.5px}
|
|
849
902
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
850
903
|
.sl-price-row.sl-dim{opacity:.4}
|
|
851
904
|
.sl-seccard-mix-item.sl-dim{opacity:.4}
|
|
@@ -935,6 +988,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
935
988
|
this.a11yFilter = "all";
|
|
936
989
|
this.baQty = 2;
|
|
937
990
|
this.baCat = "";
|
|
991
|
+
this.bestAvailableConfirm = false;
|
|
992
|
+
this.releasingHold = false;
|
|
938
993
|
// arena / multi-floor / seat-view chrome
|
|
939
994
|
this.rungsEl = null;
|
|
940
995
|
this.floorsEl = null;
|
|
@@ -982,10 +1037,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
982
1037
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
983
1038
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
984
1039
|
this.api = new PubApi(this.apiBase);
|
|
1040
|
+
this.maxTickets = Math.max(1, Math.floor(options.maxSelection ?? DEFAULT_MAX_SELECTION2));
|
|
985
1041
|
this.controller = new PickerController2({
|
|
986
1042
|
transport: this.api,
|
|
987
1043
|
eventKey: options.event,
|
|
988
|
-
maxSelection:
|
|
1044
|
+
maxSelection: this.maxTickets,
|
|
989
1045
|
currency: options.currency,
|
|
990
1046
|
flashOnLiveChange: true,
|
|
991
1047
|
colorblindSafe: options.colorblindSafe,
|
|
@@ -1020,6 +1076,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
1020
1076
|
onDeselect: (seat) => {
|
|
1021
1077
|
if (this.confirmSeat?.id === seat.id) this.dismissConfirm();
|
|
1022
1078
|
},
|
|
1079
|
+
onSelectionLimit: () => {
|
|
1080
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1081
|
+
},
|
|
1023
1082
|
onViewChange: () => {
|
|
1024
1083
|
this.reanchorConfirm();
|
|
1025
1084
|
this.syncRung();
|
|
@@ -1082,6 +1141,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
1082
1141
|
if (picker.confirmSeat) {
|
|
1083
1142
|
e.preventDefault();
|
|
1084
1143
|
picker.cancelConfirm();
|
|
1144
|
+
} else if (picker.bestAvailableConfirm) {
|
|
1145
|
+
e.preventDefault();
|
|
1146
|
+
picker.bestAvailableConfirm = false;
|
|
1147
|
+
picker.syncTray();
|
|
1085
1148
|
} else {
|
|
1086
1149
|
close();
|
|
1087
1150
|
}
|
|
@@ -1105,10 +1168,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
1105
1168
|
this.root = root;
|
|
1106
1169
|
mount.appendChild(root);
|
|
1107
1170
|
root.addEventListener("keydown", (e) => {
|
|
1108
|
-
if (e.key !== "Escape"
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1171
|
+
if (e.key !== "Escape") return;
|
|
1172
|
+
if (this.confirmSeat) {
|
|
1173
|
+
e.preventDefault();
|
|
1174
|
+
e.stopPropagation();
|
|
1175
|
+
this.cancelConfirm();
|
|
1176
|
+
} else if (this.bestAvailableConfirm) {
|
|
1177
|
+
e.preventDefault();
|
|
1178
|
+
e.stopPropagation();
|
|
1179
|
+
this.bestAvailableConfirm = false;
|
|
1180
|
+
this.syncTray();
|
|
1181
|
+
}
|
|
1112
1182
|
});
|
|
1113
1183
|
Object.entries(resolveTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
1114
1184
|
root.innerHTML = `
|
|
@@ -1141,21 +1211,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
1141
1211
|
<div class="sl-sheet-grab"></div>
|
|
1142
1212
|
<div class="sl-sheet-bar">
|
|
1143
1213
|
<div class="sl-sheet-peek" data-ref="peek"></div>
|
|
1144
|
-
<
|
|
1214
|
+
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
|
|
1145
1215
|
<svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
|
|
1146
|
-
</
|
|
1216
|
+
</button>
|
|
1147
1217
|
</div>
|
|
1148
1218
|
</div>
|
|
1149
1219
|
<div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
|
|
1150
1220
|
<div class="sl-filters" data-ref="filters"></div>
|
|
1151
|
-
<div class="sl-sec sl-prices-sec" data-ref="pricesSec">
|
|
1221
|
+
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
|
|
1152
1222
|
<div class="sl-prices" data-ref="prices"></div>
|
|
1153
|
-
<div class="sl-sec sl-seats-sec">Your seats</div>
|
|
1223
|
+
<div class="sl-sec sl-seats-sec"><span>Your seats</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
|
|
1154
1224
|
<div class="sl-tray" data-ref="tray"></div>
|
|
1155
|
-
<div class="sl-foot">
|
|
1225
|
+
<div class="sl-foot" data-ref="foot">
|
|
1156
1226
|
<div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
|
|
1157
1227
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
1158
1228
|
<span><b data-ref="holdTitle">Seats secured</b><span class="sl-hold-copy" data-ref="holdCopy">Checkout timer is running.</span></span>
|
|
1229
|
+
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats">Change</button>
|
|
1159
1230
|
</div>
|
|
1160
1231
|
<div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
|
|
1161
1232
|
<button type="button" class="sl-cta" data-ref="cta" disabled></button>
|
|
@@ -1184,9 +1255,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
1184
1255
|
this.els.zfit.addEventListener("click", () => this.controller.zoomToFit());
|
|
1185
1256
|
const head = this.els.sheetHead;
|
|
1186
1257
|
if (head) {
|
|
1258
|
+
const toggle = this.els.sheetToggle;
|
|
1187
1259
|
const setSheet = (open) => {
|
|
1188
1260
|
root.dataset.sheet = open ? "open" : "peek";
|
|
1261
|
+
toggle?.setAttribute("aria-expanded", String(open));
|
|
1262
|
+
toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
|
|
1189
1263
|
};
|
|
1264
|
+
setSheet(root.dataset.sheet === "open");
|
|
1265
|
+
toggle?.addEventListener("click", (e) => {
|
|
1266
|
+
e.stopPropagation();
|
|
1267
|
+
setSheet(root.dataset.sheet !== "open");
|
|
1268
|
+
});
|
|
1190
1269
|
let startY = 0;
|
|
1191
1270
|
let swiped = false;
|
|
1192
1271
|
let tracking = false;
|
|
@@ -1209,7 +1288,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1209
1288
|
});
|
|
1210
1289
|
head.addEventListener("pointerup", (e) => {
|
|
1211
1290
|
if (tracking && !swiped && Math.abs(e.clientY - startY) < 6) {
|
|
1212
|
-
if (!e.target.closest(".sl-seccard")) setSheet(root.dataset.sheet !== "open");
|
|
1291
|
+
if (!e.target.closest(".sl-seccard,.sl-sheet-toggle")) setSheet(root.dataset.sheet !== "open");
|
|
1213
1292
|
}
|
|
1214
1293
|
tracking = false;
|
|
1215
1294
|
head.releasePointerCapture?.(e.pointerId);
|
|
@@ -1225,6 +1304,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1225
1304
|
if (this.tipEl && this.tipEl.style.display !== "none") this.placeTooltip();
|
|
1226
1305
|
});
|
|
1227
1306
|
this.els.cta.addEventListener("click", () => void this.handleCta());
|
|
1307
|
+
this.els.holdChange?.addEventListener("click", () => void this.handleChangeSeats());
|
|
1228
1308
|
const canvasHost = document.createElement("div");
|
|
1229
1309
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
1230
1310
|
this.mapHost.appendChild(canvasHost);
|
|
@@ -1431,15 +1511,51 @@ var SeatPicker = class _SeatPicker {
|
|
|
1431
1511
|
const heldItems = this.hold?.items ?? [];
|
|
1432
1512
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
1433
1513
|
const pendingSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1514
|
+
return pendingSeats + this.pendingGACount();
|
|
1515
|
+
}
|
|
1516
|
+
heldGACounts() {
|
|
1434
1517
|
const heldGA = /* @__PURE__ */ new Map();
|
|
1435
|
-
for (const item of
|
|
1518
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1436
1519
|
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1437
1520
|
}
|
|
1438
|
-
|
|
1521
|
+
return heldGA;
|
|
1522
|
+
}
|
|
1523
|
+
pendingGACount() {
|
|
1524
|
+
const heldGA = this.heldGACounts();
|
|
1525
|
+
return [...this.gaQty.entries()].reduce(
|
|
1439
1526
|
(sum, [areaId, qty]) => sum + Math.max(0, qty - (heldGA.get(areaId) ?? 0)),
|
|
1440
1527
|
0
|
|
1441
1528
|
);
|
|
1442
|
-
|
|
1529
|
+
}
|
|
1530
|
+
heldTicketCount() {
|
|
1531
|
+
return (this.hold?.items ?? []).reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
1532
|
+
}
|
|
1533
|
+
totalTicketCount() {
|
|
1534
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1535
|
+
const freshSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1536
|
+
return this.heldTicketCount() + freshSeats + this.pendingGACount();
|
|
1537
|
+
}
|
|
1538
|
+
/** Held tickets and standing quantities consume the same order-wide cap. */
|
|
1539
|
+
updateSelectionCapacity() {
|
|
1540
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1541
|
+
const selectedHeld = this.committedSelection().filter((seat) => heldLabels.has(seat.label)).length;
|
|
1542
|
+
const remaining = Math.max(0, this.maxTickets - this.heldTicketCount() - this.pendingGACount());
|
|
1543
|
+
this.controller.setMaxSelection(selectedHeld + remaining);
|
|
1544
|
+
}
|
|
1545
|
+
canAddTicket() {
|
|
1546
|
+
if (this.totalTicketCount() < this.maxTickets) return true;
|
|
1547
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
pendingGATotal(gaAreas) {
|
|
1551
|
+
const heldGA = /* @__PURE__ */ new Map();
|
|
1552
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1553
|
+
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1554
|
+
}
|
|
1555
|
+
return gaAreas.reduce(
|
|
1556
|
+
(sum, area) => sum + area.price * Math.max(0, (this.gaQty.get(area.id) ?? 0) - (heldGA.get(area.id) ?? 0)),
|
|
1557
|
+
0
|
|
1558
|
+
);
|
|
1443
1559
|
}
|
|
1444
1560
|
syncCta(count = this.lastTrayCount, pending = this.pendingSelectionCount()) {
|
|
1445
1561
|
const cta = this.els.cta;
|
|
@@ -1728,39 +1844,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
1728
1844
|
}
|
|
1729
1845
|
return bands;
|
|
1730
1846
|
}
|
|
1731
|
-
/**
|
|
1732
|
-
*
|
|
1733
|
-
* header and above the legend it dims. Living in the panel (not a map overlay)
|
|
1734
|
-
* keeps it clear of the top-center rung pills and top-left a11y chips, and it
|
|
1735
|
-
* rides the bottom sheet on narrow layouts. Skipped when there's <2 bands.
|
|
1736
|
-
*/
|
|
1847
|
+
/** Build the compact price selector in the panel header. Choosing a band both
|
|
1848
|
+
* filters availability and smoothly frames the matching seats on the map. */
|
|
1737
1849
|
buildPriceFilter() {
|
|
1738
1850
|
if (!this.els.prices || !this.els.pricesSec) return;
|
|
1739
1851
|
const bands = this.priceBands();
|
|
1740
1852
|
if (bands.length < 2) return;
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
this.controller.setCategoryFilter(null);
|
|
1756
|
-
} else {
|
|
1757
|
-
const bd = bands.find((x) => x.id === id);
|
|
1758
|
-
this.priceBandKeys = bd ? new Set(bd.keys) : null;
|
|
1759
|
-
this.controller.setCategoryFilter(bd ? bd.keys : null);
|
|
1760
|
-
}
|
|
1761
|
-
this.syncPrices();
|
|
1762
|
-
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
1763
|
-
});
|
|
1853
|
+
const select = document.createElement("select");
|
|
1854
|
+
select.className = "sl-price-select";
|
|
1855
|
+
select.setAttribute("aria-label", "Filter and focus seats by price");
|
|
1856
|
+
select.innerHTML = `<option value="all">All prices</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
|
|
1857
|
+
this.els.pricesSec.appendChild(select);
|
|
1858
|
+
this.priceFilterEl = select;
|
|
1859
|
+
select.addEventListener("change", () => {
|
|
1860
|
+
const band = bands.find((candidate) => candidate.id === select.value);
|
|
1861
|
+
const keys = band?.keys ?? null;
|
|
1862
|
+
this.priceBandKeys = keys ? new Set(keys) : null;
|
|
1863
|
+
this.controller.setCategoryFilter(keys);
|
|
1864
|
+
this.controller.focusCategoryFilter(keys);
|
|
1865
|
+
this.syncPrices();
|
|
1866
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
1764
1867
|
});
|
|
1765
1868
|
}
|
|
1766
1869
|
// ---- arena / multi-floor chrome -------------------------------------------
|
|
@@ -2160,7 +2263,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2160
2263
|
const price = c.tiers?.length ? c.tiers[0].price : c.price;
|
|
2161
2264
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
2162
2265
|
return `<div class="sl-price-row${dim ? " sl-dim" : ""}" data-cat="${c.key}"><span class="sl-dot" style="background:${c.color}"></span><span class="sl-price-label">${c.label}</span><span class="sl-price-left">${left[c.key] ?? 0} left</span>` + (price != null ? `<span class="sl-price-amt">${this.money(price)}</span>` : "") + `</div>`;
|
|
2163
|
-
}).join("") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon"
|
|
2266
|
+
}).join("") + `<div class="sl-status-key" aria-label="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>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>Sold</span></div>`;
|
|
2164
2267
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
2165
2268
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
2166
2269
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
@@ -2179,6 +2282,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2179
2282
|
}
|
|
2180
2283
|
syncTray() {
|
|
2181
2284
|
if (!this.els.tray) return;
|
|
2285
|
+
this.updateSelectionCapacity();
|
|
2182
2286
|
const seats = this.committedSelection();
|
|
2183
2287
|
const gaAreas = this.controller.getGAAreas();
|
|
2184
2288
|
const heldItems = this.hold?.items ?? [];
|
|
@@ -2189,14 +2293,19 @@ var SeatPicker = class _SeatPicker {
|
|
|
2189
2293
|
} else if (!seats.length && !heldItems.length) {
|
|
2190
2294
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
|
|
2191
2295
|
}
|
|
2296
|
+
if (!this.hold) {
|
|
2297
|
+
const cats = this.controller.doc?.categories ?? [];
|
|
2298
|
+
parts.push(this.bestAvailableConfirm ? `<div class="sl-ba" role="alert"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Replace your current choices?</div><div class="sl-ba-replace"><b>We\u2019ll find ${this.baQty} seats together.</b><span>Your manually selected tickets will be removed only after a new group is secured.</span></div><div class="sl-ba-actions"><button type="button" data-ba-cancel>Keep mine</button><button type="button" class="replace" data-ba-replace>Find new seats</button></div></div>` : `<div class="sl-ba"><div class="sl-ba-title"><span class="spark" aria-hidden="true">\u2726</span>Find the best seats together</div><div class="sl-ba-copy"><span class="wide">We\u2019ll choose the closest available group for you.</span><span class="narrow">Closest available group, chosen instantly.</span></div>` + (cats.length > 1 ? `<select aria-label="Preferred ticket type" data-ba-cat><option value="">Any ticket type</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : `<span aria-hidden="true"></span>`) + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding the best seats\u2026` : `Find ${this.baQty} best ${this.baQty === 1 ? "seat" : "seats"}`) + `</button></div>`);
|
|
2299
|
+
}
|
|
2192
2300
|
for (const item of heldItems) {
|
|
2193
2301
|
const itemKey = `held:${item.label}`;
|
|
2194
2302
|
nextTrayKeys.add(itemKey);
|
|
2195
2303
|
const cat = this.controller.doc?.categories.find((c) => c.key === item.categoryKey);
|
|
2196
2304
|
const tierName = item.tierId ? cat?.tiers?.find((ti) => ti.id === item.tierId)?.name : void 0;
|
|
2197
2305
|
const canView2 = this.seatViewEnabled() && item.objectType !== "ga" && !!this.controller.seatByLabel(item.label);
|
|
2306
|
+
const viewBtn = canView2 ? `<button type="button" class="view" data-view-label="${item.label}" aria-label="${t2("picker.viewFromSeat", { label: item.label })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "";
|
|
2198
2307
|
parts.push(
|
|
2199
|
-
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"><
|
|
2308
|
+
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"><span class="sl-ticket-state held" aria-label="Held for you" title="Held for you"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></span><div class="sl-chip-main"><b>${item.label}</b><div class="sl-chip-sub"><span class="cat">${cat?.label ?? item.categoryKey}${tierName ? ` \xB7 ${tierName}` : ""}</span>${viewBtn}</div></div><span class="amt">${this.money(item.unitPrice * (item.quantity ?? 1))}</span><button type="button" class="rm" aria-label="Remove held ticket ${item.label}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div>`
|
|
2200
2309
|
);
|
|
2201
2310
|
}
|
|
2202
2311
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -2208,7 +2317,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2208
2317
|
const tierSelect = s.tiers && s.tiers.length ? `<select class="tier" data-tier="${s.id}" aria-label="${t2("picker.ticketTierFor", { label: s.label })}">` + s.tiers.map((ti) => `<option value="${ti.id}"${ti.id === s.tierId ? " selected" : ""}>${ti.name} \xB7 ${this.money(ti.price)}</option>`).join("") + `</select>` : "";
|
|
2209
2318
|
const viewBtn = canView ? `<button type="button" class="view" data-view-label="${s.label}" aria-label="${t2("picker.viewFromSeat", { label: s.label })}"><svg viewBox="0 0 24 24"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/></svg></button>` : "";
|
|
2210
2319
|
parts.push(
|
|
2211
|
-
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}"><b>${s.label}</b><span class="cat">${cat?.label ?? s.categoryKey}</span
|
|
2320
|
+
`<div class="sl-chip${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-seat="${s.id}"><span class="sl-ticket-state" aria-label="Selected" title="Selected"><svg viewBox="0 0 24 24"><path d="M5 12l4 4L19 6"/></svg></span><div class="sl-chip-main"><b>${s.label}</b><div class="sl-chip-sub"><span class="cat">${cat?.label ?? s.categoryKey}</span>${tierSelect}${viewBtn}</div></div><span class="amt">${this.money(s.price)}</span><button type="button" class="rm" aria-label="Remove ${s.label}"><svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button></div>`
|
|
2212
2321
|
);
|
|
2213
2322
|
}
|
|
2214
2323
|
for (const area of gaAreas) {
|
|
@@ -2217,17 +2326,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
2217
2326
|
`<div class="sl-ga" data-ga="${area.id}"><div class="sl-ga-info"><div class="sl-ga-name">${area.label}</div><div class="sl-ga-sub">${this.money(area.price)} \xB7 ${area.available} left</div></div><div class="sl-ga-qty"><button type="button" data-d="-1" aria-label="Fewer">\u2212</button><span>${qty}</span><button type="button" data-d="1" aria-label="More">+</button></div></div>`
|
|
2218
2327
|
);
|
|
2219
2328
|
}
|
|
2220
|
-
if (!this.hold) {
|
|
2221
|
-
const cats = this.controller.doc?.categories ?? [];
|
|
2222
|
-
parts.push(
|
|
2223
|
-
`<div class="sl-ba">` + (cats.length > 1 ? `<select aria-label="Category" data-ba-cat><option value="">Any tier</option>` + cats.map((c) => `<option value="${c.key}"${this.baCat === c.key ? " selected" : ""}>${c.label}</option>`).join("") + `</select>` : "") + `<div class="sl-ba-qty"><button type="button" data-ba="-1" aria-label="Fewer seats">\u2212</button><span>${this.baQty}</span><button type="button" data-ba="1" aria-label="More seats">+</button></div><button type="button" class="sl-ba-go"${this.bestAvailableBusy ? " disabled" : ""}>` + (this.bestAvailableBusy ? `<span class="sl-ba-spin" aria-hidden="true"></span>Finding\u2026` : "Best available") + `</button></div>`
|
|
2224
|
-
);
|
|
2225
|
-
}
|
|
2226
2329
|
this.els.tray.innerHTML = parts.join("");
|
|
2227
2330
|
this.lastTrayKeys = nextTrayKeys;
|
|
2228
2331
|
this.els.tray.querySelectorAll("[data-ba]").forEach((btn) => {
|
|
2229
2332
|
btn.addEventListener("click", () => {
|
|
2230
|
-
this.baQty = Math.max(1, Math.min(
|
|
2333
|
+
this.baQty = Math.max(1, Math.min(this.maxTickets, this.baQty + Number(btn.dataset.ba)));
|
|
2231
2334
|
this.syncTray();
|
|
2232
2335
|
});
|
|
2233
2336
|
});
|
|
@@ -2235,6 +2338,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
2235
2338
|
this.baCat = e.target.value;
|
|
2236
2339
|
});
|
|
2237
2340
|
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
2341
|
+
if (this.pendingSelectionCount() > 0) {
|
|
2342
|
+
this.bestAvailableConfirm = true;
|
|
2343
|
+
this.syncTray();
|
|
2344
|
+
this.els.tray.querySelector("[data-ba-replace]")?.focus();
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2348
|
+
});
|
|
2349
|
+
this.els.tray.querySelector("[data-ba-cancel]")?.addEventListener("click", () => {
|
|
2350
|
+
this.bestAvailableConfirm = false;
|
|
2351
|
+
this.syncTray();
|
|
2352
|
+
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
2353
|
+
});
|
|
2354
|
+
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
2355
|
+
this.bestAvailableConfirm = false;
|
|
2238
2356
|
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2239
2357
|
});
|
|
2240
2358
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
@@ -2245,12 +2363,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
2245
2363
|
return;
|
|
2246
2364
|
}
|
|
2247
2365
|
const id = chip.dataset.seat;
|
|
2248
|
-
|
|
2366
|
+
const label = chip.querySelector("b")?.textContent ?? "Seat";
|
|
2367
|
+
const remove = () => {
|
|
2249
2368
|
this.controller.deselect([id]);
|
|
2369
|
+
this.toast(`${label} removed.`, "neutral", {
|
|
2370
|
+
label: "Undo",
|
|
2371
|
+
onClick: () => {
|
|
2372
|
+
const restored = this.controller.select([id]);
|
|
2373
|
+
this.toast(
|
|
2374
|
+
restored.length ? `${label} restored.` : `${label} is no longer available.`,
|
|
2375
|
+
restored.length ? "success" : "warning"
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
};
|
|
2380
|
+
if (this.reducedMotion()) {
|
|
2381
|
+
remove();
|
|
2250
2382
|
return;
|
|
2251
2383
|
}
|
|
2252
2384
|
chip.classList.add("sl-leave");
|
|
2253
|
-
this.scheduleMotion(
|
|
2385
|
+
this.scheduleMotion(remove, 150);
|
|
2254
2386
|
});
|
|
2255
2387
|
});
|
|
2256
2388
|
this.els.tray.querySelectorAll(".sl-chip .tier").forEach((sel) => {
|
|
@@ -2267,13 +2399,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
2267
2399
|
const areaEl = btn.closest(".sl-ga");
|
|
2268
2400
|
const id = areaEl.dataset.ga;
|
|
2269
2401
|
const area = gaAreas.find((a) => a.id === id);
|
|
2270
|
-
const
|
|
2402
|
+
const delta = Number(btn.dataset.d);
|
|
2403
|
+
if (delta > 0 && !this.canAddTicket()) return;
|
|
2404
|
+
const next = Math.max(0, Math.min(area?.available ?? 0, (this.gaQty.get(id) ?? 0) + delta));
|
|
2271
2405
|
this.gaQty.set(id, next);
|
|
2272
2406
|
this.syncTray();
|
|
2273
2407
|
});
|
|
2274
2408
|
});
|
|
2275
|
-
const gaTotal =
|
|
2276
|
-
const gaCount =
|
|
2409
|
+
const gaTotal = this.pendingGATotal(gaAreas);
|
|
2410
|
+
const gaCount = this.pendingGACount();
|
|
2277
2411
|
const heldTotal = heldItems.reduce((sum, item) => sum + item.unitPrice * (item.quantity ?? 1), 0);
|
|
2278
2412
|
const heldCount = heldItems.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
2279
2413
|
const freshSeats = seats.filter((seat) => !heldLabels.has(seat.label));
|
|
@@ -2284,14 +2418,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
2284
2418
|
const previousTotal = this.lastTrayTotal;
|
|
2285
2419
|
this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
|
|
2286
2420
|
this.els.total.textContent = count ? this.money(total) : "";
|
|
2421
|
+
this.root?.setAttribute("data-has-selection", String(count > 0));
|
|
2422
|
+
this.els.foot?.classList.toggle("empty", count === 0);
|
|
2423
|
+
if (this.els.seatSummary) {
|
|
2424
|
+
this.els.seatSummary.textContent = count ? `${count} selected` : "";
|
|
2425
|
+
}
|
|
2287
2426
|
this.syncCta(count, pendingCount);
|
|
2288
2427
|
if (this.hold) {
|
|
2289
2428
|
const securedCount = heldCount || this.hold.seats?.length || 0;
|
|
2290
2429
|
if (this.els.holdTitle) {
|
|
2291
|
-
this.els.holdTitle.textContent = `${securedCount}
|
|
2430
|
+
this.els.holdTitle.textContent = `${securedCount} secured`;
|
|
2292
2431
|
}
|
|
2293
2432
|
if (this.els.holdCopy) {
|
|
2294
|
-
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected
|
|
2433
|
+
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected` : "Checkout timer running";
|
|
2434
|
+
}
|
|
2435
|
+
const change = this.els.holdChange;
|
|
2436
|
+
if (change) {
|
|
2437
|
+
change.disabled = this.releasingHold;
|
|
2438
|
+
change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
|
|
2295
2439
|
}
|
|
2296
2440
|
}
|
|
2297
2441
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
@@ -2302,7 +2446,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2302
2446
|
this.els.peek.innerHTML = `<span>${count} ${count === 1 ? "ticket" : "tickets"} \xB7 ${this.money(total)}</span><span class="go">${this.hold ? pendingCount ? "Secure more" : "Continue" : "Review"}</span>`;
|
|
2303
2447
|
} else {
|
|
2304
2448
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
2305
|
-
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<span class="
|
|
2449
|
+
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<span class="go">\u2726 Best seats</span>`;
|
|
2306
2450
|
}
|
|
2307
2451
|
}
|
|
2308
2452
|
this.lastTrayCount = count;
|
|
@@ -2343,7 +2487,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
2343
2487
|
if (button?.isConnected) button.disabled = false;
|
|
2344
2488
|
}
|
|
2345
2489
|
}
|
|
2490
|
+
async handleChangeSeats() {
|
|
2491
|
+
if (!this.hold || this.releasingHold) return;
|
|
2492
|
+
this.releasingHold = true;
|
|
2493
|
+
const button = this.els.holdChange;
|
|
2494
|
+
if (button) {
|
|
2495
|
+
button.disabled = true;
|
|
2496
|
+
button.textContent = "Releasing\u2026";
|
|
2497
|
+
}
|
|
2498
|
+
try {
|
|
2499
|
+
await this.release();
|
|
2500
|
+
if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
|
|
2501
|
+
} finally {
|
|
2502
|
+
this.releasingHold = false;
|
|
2503
|
+
if (button?.isConnected) {
|
|
2504
|
+
button.disabled = false;
|
|
2505
|
+
button.textContent = "Change";
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2346
2509
|
async handleCta() {
|
|
2510
|
+
if (this.totalTicketCount() > this.maxTickets) {
|
|
2511
|
+
this.toast(`Remove tickets until your order has ${this.maxTickets} or fewer.`, "warning");
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2347
2514
|
const committed = this.committedSelection();
|
|
2348
2515
|
if (this.hold && !committed.some((s) => !(this.hold.items ?? []).some((i) => i.label === s.label))) {
|
|
2349
2516
|
const seats = this.hold.seats ?? committed;
|
|
@@ -2381,7 +2548,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
2381
2548
|
this.opts.onCheckout?.(hold, hold.seats ?? chosenSeats, this.buildHandoff(hold));
|
|
2382
2549
|
} catch (err) {
|
|
2383
2550
|
this.opts.onError?.(err);
|
|
2384
|
-
|
|
2551
|
+
const problem = err;
|
|
2552
|
+
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
2553
|
+
const message = problem.reason === "event_closed" ? "Seat sales have closed for this event." : labels.length ? `${labels.join(", ")} ${labels.length === 1 ? "is" : "are"} no longer available. Choose another ${labels.length === 1 ? "seat" : "group"}.` : "One or more seats were just taken. Please pick again.";
|
|
2554
|
+
this.toast(message, "error");
|
|
2385
2555
|
this.setCtaPhase("idle");
|
|
2386
2556
|
} finally {
|
|
2387
2557
|
this.holdingLabels.clear();
|
|
@@ -2504,15 +2674,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
2504
2674
|
hold ? this.buildHandoff(hold) : null
|
|
2505
2675
|
);
|
|
2506
2676
|
}
|
|
2507
|
-
toast(msg, tone = "neutral") {
|
|
2677
|
+
toast(msg, tone = "neutral", action) {
|
|
2508
2678
|
const el = this.els.toast;
|
|
2509
2679
|
if (!el) return;
|
|
2510
|
-
el.
|
|
2680
|
+
el.replaceChildren();
|
|
2681
|
+
const copy = document.createElement("span");
|
|
2682
|
+
copy.textContent = msg;
|
|
2683
|
+
el.appendChild(copy);
|
|
2684
|
+
el.classList.toggle("has-action", !!action);
|
|
2685
|
+
if (action) {
|
|
2686
|
+
const button = document.createElement("button");
|
|
2687
|
+
button.type = "button";
|
|
2688
|
+
button.className = "sl-toast-action";
|
|
2689
|
+
button.textContent = action.label;
|
|
2690
|
+
button.addEventListener("click", action.onClick, { once: true });
|
|
2691
|
+
el.appendChild(button);
|
|
2692
|
+
}
|
|
2511
2693
|
el.dataset.tone = tone;
|
|
2512
2694
|
el.classList.add("on");
|
|
2513
2695
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
2514
2696
|
this.toastTimer = setTimeout(() => {
|
|
2515
2697
|
el.classList.remove("on");
|
|
2698
|
+
el.classList.remove("has-action");
|
|
2516
2699
|
el.dataset.tone = "neutral";
|
|
2517
2700
|
}, 4200);
|
|
2518
2701
|
}
|
|
@@ -2562,7 +2745,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
2562
2745
|
}
|
|
2563
2746
|
async bestAvailable(qty, categoryKey) {
|
|
2564
2747
|
if (this.bestAvailableBusy) return null;
|
|
2748
|
+
qty = Math.max(1, Math.min(this.maxTickets, Math.floor(qty)));
|
|
2565
2749
|
if (this.confirmSeat) this.cancelConfirm();
|
|
2750
|
+
this.bestAvailableConfirm = false;
|
|
2566
2751
|
this.bestAvailableBusy = true;
|
|
2567
2752
|
const button = this.els.tray?.querySelector(".sl-ba-go");
|
|
2568
2753
|
if (button) {
|
|
@@ -2575,6 +2760,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2575
2760
|
this.hold = { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items };
|
|
2576
2761
|
this.handedOff = false;
|
|
2577
2762
|
this.bookedShown = false;
|
|
2763
|
+
this.gaQty.clear();
|
|
2578
2764
|
this.startHoldTimer(h.expiresAt);
|
|
2579
2765
|
this.flashHeldSeats(this.hold);
|
|
2580
2766
|
this.syncTray();
|
|
@@ -2584,14 +2770,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
2584
2770
|
return null;
|
|
2585
2771
|
} catch (err) {
|
|
2586
2772
|
this.opts.onError?.(err);
|
|
2587
|
-
|
|
2773
|
+
const reason = err?.reason;
|
|
2774
|
+
const message = reason === "not_enough_together" ? `We couldn't find ${qty} seats together. Try fewer seats or another ticket type.` : reason === "sold_out" ? "That ticket type is sold out. Try another ticket type." : reason === "event_closed" ? "Seat sales have closed for this event." : "Those seats are no longer available. Try another quantity or ticket type.";
|
|
2775
|
+
this.toast(message, "error");
|
|
2588
2776
|
return null;
|
|
2589
2777
|
} finally {
|
|
2590
2778
|
this.bestAvailableBusy = false;
|
|
2591
|
-
|
|
2592
|
-
button.disabled = false;
|
|
2593
|
-
button.textContent = "Best available";
|
|
2594
|
-
}
|
|
2779
|
+
this.syncTray();
|
|
2595
2780
|
}
|
|
2596
2781
|
}
|
|
2597
2782
|
async release() {
|