@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.cjs
CHANGED
|
@@ -500,24 +500,26 @@ var CSS = `
|
|
|
500
500
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
501
501
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
502
502
|
.sl-map-host{position:absolute;inset:0}
|
|
503
|
-
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow
|
|
503
|
+
.sl-side{width:300px;flex:none;border-left:1px solid var(--sl-line);display:flex;flex-direction:column;min-height:0;overflow:hidden}
|
|
504
504
|
|
|
505
505
|
/* narrow (container < 640px): map-first \u2014 the map claims ~80-85% of the
|
|
506
506
|
container and the side panel becomes a PEEKING bottom sheet (AXS/Ticketmaster
|
|
507
507
|
mobile pattern). data-sheet on the root: "peek" (default: grab handle + one
|
|
508
|
-
summary line) / "open" (
|
|
508
|
+
summary line) / "open" (room for rows + checkout, swipe up to open).
|
|
509
509
|
Swipe handling lives on the sheet head ONLY \u2014 never the map host, so the
|
|
510
510
|
map's raw-pointer gesture pipeline is untouched. */
|
|
511
511
|
.sl-picker[data-layout="narrow"] .sl-body{flex-direction:column}
|
|
512
512
|
.sl-picker[data-layout="narrow"] .sl-map{min-height:0;flex:1}
|
|
513
513
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
514
|
-
flex:none;height:
|
|
514
|
+
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
515
|
+
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
|
|
515
516
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:86px;overflow:hidden}
|
|
516
517
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
517
|
-
.sl-picker[data-layout="narrow"] .sl-tray{flex:
|
|
518
|
-
.sl-picker[data-layout="narrow"] .sl-foot{position:
|
|
518
|
+
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
|
|
519
|
+
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
520
|
+
.sl-picker[data-layout="narrow"] .sl-foot.empty{display:none}
|
|
519
521
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{order:0}
|
|
520
|
-
.sl-picker[data-layout="narrow"] .sl-seats-sec{
|
|
522
|
+
.sl-picker[data-layout="narrow"] .sl-seats-sec{display:none}
|
|
521
523
|
.sl-picker[data-layout="narrow"] .sl-tray{order:2}
|
|
522
524
|
.sl-picker[data-layout="narrow"] .sl-filtersec{order:3}
|
|
523
525
|
.sl-picker[data-layout="narrow"] .sl-filters{order:4}
|
|
@@ -525,13 +527,21 @@ var CSS = `
|
|
|
525
527
|
.sl-picker[data-layout="narrow"] .sl-pricef{order:6}
|
|
526
528
|
.sl-picker[data-layout="narrow"] .sl-prices{order:7}
|
|
527
529
|
.sl-picker[data-layout="narrow"] .sl-foot{order:8}
|
|
530
|
+
.sl-picker[data-layout="narrow"] .sl-tray-hint,
|
|
531
|
+
.sl-picker[data-layout="narrow"] .sl-filtersec,
|
|
532
|
+
.sl-picker[data-layout="narrow"] .sl-filters,
|
|
533
|
+
.sl-picker[data-layout="narrow"] .sl-prices-sec,
|
|
534
|
+
.sl-picker[data-layout="narrow"] .sl-prices{display:none!important}
|
|
535
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
|
|
536
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
|
|
537
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-prices-sec{display:none}
|
|
528
538
|
/* touch chrome: pinch-zoom exists \u2014 hide +/\u2212 on the sheet layout (keep fit) */
|
|
529
539
|
.sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zin"],
|
|
530
540
|
.sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zout"]{display:none}
|
|
531
541
|
|
|
532
542
|
/* bottom-sheet head: grab handle + one-line summary (narrow only). The WHOLE
|
|
533
543
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
534
|
-
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px
|
|
544
|
+
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
535
545
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
536
546
|
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex}
|
|
537
547
|
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:2px auto 7px}
|
|
@@ -546,54 +556,72 @@ var CSS = `
|
|
|
546
556
|
/* state chevron: points UP while peeking, rotates to point DOWN when open.
|
|
547
557
|
Base keeps an explicit rotate(0) \u2014 transitioning to/from a bare 'none' leaves
|
|
548
558
|
the value stuck in some engines, so both endpoints must be real transforms. */
|
|
549
|
-
.sl-sheet-
|
|
550
|
-
|
|
551
|
-
.sl-sheet-
|
|
559
|
+
.sl-sheet-toggle{width:44px;height:44px;margin:-8px -8px -8px 0;border-radius:999px;flex:none;display:flex;
|
|
560
|
+
align-items:center;justify-content:center;color:var(--sl-muted);transition:color .15s,background .15s}
|
|
561
|
+
.sl-sheet-toggle:hover,.sl-sheet-toggle:focus-visible{color:var(--sl-text);background:color-mix(in srgb,var(--sl-line) 44%,transparent)}
|
|
562
|
+
.sl-sheet-toggle svg{width:21px;height:21px;stroke:currentColor;stroke-width:2.4;fill:none;
|
|
552
563
|
stroke-linecap:round;stroke-linejoin:round}
|
|
553
|
-
.sl-
|
|
564
|
+
.sl-sheet-toggle svg{transform:rotate(0deg);transition:transform .24s cubic-bezier(.2,.8,.2,1)}
|
|
565
|
+
.sl-picker[data-sheet="open"] .sl-sheet-toggle svg{transform:rotate(180deg)}
|
|
554
566
|
|
|
555
567
|
/* consolidated Filters row inside the sheet (a11y chips + colorblind toggle
|
|
556
568
|
dock here on narrow; they live on the map / zoom column on wide) */
|
|
557
569
|
.sl-filtersec{display:none}
|
|
558
570
|
.sl-filters{display:none;gap:6px;flex-wrap:wrap;align-items:center;padding:2px 16px 10px}
|
|
559
|
-
.sl-picker[data-layout="narrow"] .sl-filtersec.has
|
|
560
|
-
.sl-picker[data-layout="narrow"] .sl-filters.has{display:
|
|
571
|
+
.sl-picker[data-layout="narrow"] .sl-filtersec.has,
|
|
572
|
+
.sl-picker[data-layout="narrow"] .sl-filters.has{display:none}
|
|
573
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec.has,
|
|
574
|
+
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters.has{display:none}
|
|
561
575
|
.sl-cbbtn{width:32px;height:32px;border-radius:999px;background:var(--sl-surface);border:1px solid var(--sl-line);
|
|
562
576
|
color:var(--sl-text);display:flex;align-items:center;justify-content:center;transition:border-color .15s}
|
|
563
577
|
.sl-cbbtn:hover{border-color:var(--sl-muted)}
|
|
564
578
|
.sl-cbbtn svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
565
579
|
|
|
566
|
-
/* price panel */
|
|
567
|
-
.sl-sec{padding:14px
|
|
568
|
-
.sl-prices{
|
|
569
|
-
.sl-price-
|
|
580
|
+
/* price panel \u2014 one compact filter control replaces the wrapping price-chip row. */
|
|
581
|
+
.sl-sec{padding:14px 14px 4px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--sl-muted);font-weight:700}
|
|
582
|
+
.sl-prices-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
583
|
+
.sl-price-select{min-height:32px;max-width:130px;padding:5px 28px 5px 9px;border:1px solid var(--sl-line);border-radius:9px;
|
|
584
|
+
background:var(--sl-surface);color:var(--sl-text);font:inherit;font-size:11px;font-weight:750;letter-spacing:0;text-transform:none}
|
|
585
|
+
.sl-prices{padding:5px 14px 10px;border-bottom:1px solid var(--sl-line)}
|
|
586
|
+
.sl-prices-sec,.sl-prices,.sl-seats-sec{flex:none}
|
|
587
|
+
.sl-price-row{display:flex;align-items:center;gap:7px;min-height:28px;font-size:12px}
|
|
570
588
|
.sl-dot{width:9px;height:9px;border-radius:50%;flex:none}
|
|
571
589
|
.sl-price-label{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
|
|
572
590
|
.sl-price-left{font-size:11px;color:var(--sl-muted);font-variant-numeric:tabular-nums}
|
|
573
591
|
.sl-price-amt{font-weight:800;font-variant-numeric:tabular-nums}
|
|
574
|
-
.sl-status-key{display:flex;gap:
|
|
592
|
+
.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}
|
|
575
593
|
.sl-status-item{display:inline-flex;align-items:center;gap:6px}
|
|
576
594
|
.sl-status-icon{width:17px;height:17px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;
|
|
577
|
-
color:#fff;background:#6b7280;
|
|
578
|
-
.sl-status-icon
|
|
595
|
+
color:#fff;background:#6b7280;line-height:1}
|
|
596
|
+
.sl-status-icon svg{width:10px;height:10px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
597
|
+
.sl-status-icon.sold{background:#8b93a0}
|
|
598
|
+
.sl-status-icon.sold svg{width:11px;height:11px;stroke-width:2.4}
|
|
579
599
|
|
|
580
600
|
/* tray */
|
|
581
|
-
.sl-
|
|
601
|
+
.sl-seats-sec{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:13px}
|
|
602
|
+
.sl-seat-summary{font-size:10px;letter-spacing:0;text-transform:none;white-space:nowrap}
|
|
603
|
+
.sl-tray{flex:1;padding:10px 14px 14px;display:flex;flex-direction:column;gap:7px;min-height:0;overflow-y:auto;
|
|
604
|
+
overscroll-behavior:contain;scrollbar-gutter:stable}
|
|
582
605
|
.sl-tray-hint{font-size:12.5px;color:var(--sl-muted);line-height:1.5}
|
|
583
|
-
.sl-chip{display:
|
|
584
|
-
|
|
606
|
+
.sl-chip{position:relative;display:grid;grid-template-columns:24px minmax(0,1fr) auto 30px;align-items:center;gap:8px;
|
|
607
|
+
min-height:53px;padding:7px 7px 7px 9px;border:1px solid var(--sl-line);border-radius:var(--sl-r-sm);
|
|
608
|
+
background:var(--sl-surface);font-size:13px;transform-origin:center;transition:border-color .15s,background .15s}
|
|
609
|
+
.sl-chip:hover{border-color:color-mix(in srgb,var(--sl-accent) 38%,var(--sl-line))}
|
|
585
610
|
.sl-chip.sl-enter{animation:slChipIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
586
611
|
.sl-chip.sl-leave{pointer-events:none;animation:slChipOut .16s ease-in both}
|
|
587
612
|
.sl-chip.sl-held{border-color:var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
588
613
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent)}
|
|
589
|
-
.sl-
|
|
590
|
-
|
|
614
|
+
.sl-ticket-state{width:23px;height:23px;border-radius:999px;display:flex;align-items:center;justify-content:center;
|
|
615
|
+
background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
616
|
+
.sl-ticket-state.held{background:color-mix(in srgb,var(--sl-accent) 18%,var(--sl-surface));color:var(--sl-accent)}
|
|
617
|
+
.sl-ticket-state svg{width:13px;height:13px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
618
|
+
.sl-chip-main{min-width:0}
|
|
619
|
+
.sl-chip b{display:block;font-weight:800;min-width:max-content;white-space:nowrap}
|
|
620
|
+
.sl-chip-sub{display:flex;align-items:center;gap:5px;min-width:0;margin-top:2px}
|
|
621
|
+
.sl-chip .cat{color:var(--sl-muted);font-size:10.5px;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
591
622
|
.sl-chip .amt{font-weight:700;font-variant-numeric:tabular-nums;flex:none;white-space:nowrap}
|
|
592
|
-
.sl-chip
|
|
593
|
-
|
|
594
|
-
.sl-chip-state.sl-pending{background:transparent;color:var(--sl-muted);border:1px solid var(--sl-line)}
|
|
595
|
-
.sl-chip .rm{width:22px;height:22px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
|
|
596
|
-
.sl-chip .rm:hover{color:var(--sl-text)}
|
|
623
|
+
.sl-chip .rm{width:29px;height:29px;border-radius:8px;flex:none;display:flex;align-items:center;justify-content:center;color:var(--sl-muted)}
|
|
624
|
+
.sl-chip .rm:hover,.sl-chip .rm:focus-visible{color:var(--sl-accent);background:color-mix(in srgb,var(--sl-accent) 10%,transparent)}
|
|
597
625
|
.sl-chip .rm svg{width:11px;height:11px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
|
|
598
626
|
|
|
599
627
|
/* GA rows */
|
|
@@ -608,16 +636,22 @@ var CSS = `
|
|
|
608
636
|
.sl-ga-qty span{min-width:16px;text-align:center;font-weight:800;font-variant-numeric:tabular-nums}
|
|
609
637
|
|
|
610
638
|
/* footer */
|
|
611
|
-
.sl-foot{padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none
|
|
612
|
-
|
|
639
|
+
.sl-foot{position:relative;z-index:2;padding:12px 16px 14px;border-top:1px solid var(--sl-line);flex:none;
|
|
640
|
+
background:var(--sl-bg);box-shadow:0 -10px 24px -22px rgba(0,0,0,.72)}
|
|
641
|
+
.sl-hold-note{display:none;align-items:center;gap:7px;margin-bottom:8px;padding:7px 8px;border-radius:var(--sl-r-sm);
|
|
613
642
|
border:1px solid var(--sl-line);background:color-mix(in srgb,var(--sl-accent) 7%,var(--sl-surface));
|
|
614
643
|
box-shadow:inset 3px 0 0 color-mix(in srgb,var(--sl-accent) 72%,transparent);
|
|
615
644
|
font-size:11.5px;line-height:1.35;color:var(--sl-muted)}
|
|
616
645
|
.sl-hold-note.on{display:flex;animation:slNoticeIn .38s cubic-bezier(.2,.8,.2,1) both}
|
|
617
646
|
.sl-hold-note svg{width:16px;height:16px;flex:none;stroke:var(--sl-accent);stroke-width:2.4;fill:none;
|
|
618
647
|
stroke-linecap:round;stroke-linejoin:round}
|
|
619
|
-
.sl-hold-note b{display:block;color:var(--sl-text);font-size:
|
|
620
|
-
.sl-hold-copy{display:block}
|
|
648
|
+
.sl-hold-note b{display:block;color:var(--sl-text);font-size:11.5px;white-space:nowrap}
|
|
649
|
+
.sl-hold-copy{display:block;white-space:nowrap;font-size:10.5px}
|
|
650
|
+
.sl-hold-note>span{flex:1;min-width:0}
|
|
651
|
+
.sl-hold-change{flex:none;min-height:30px;padding:5px 8px;border-radius:8px;border:1px solid var(--sl-line);
|
|
652
|
+
color:var(--sl-text);font-size:10.5px;font-weight:750;white-space:nowrap}
|
|
653
|
+
.sl-hold-change:hover,.sl-hold-change:focus-visible{border-color:var(--sl-accent);color:var(--sl-accent)}
|
|
654
|
+
.sl-hold-change:disabled{opacity:.58;cursor:wait}
|
|
621
655
|
.sl-total{display:flex;justify-content:space-between;align-items:center;font-size:13px;margin-bottom:10px}
|
|
622
656
|
.sl-total b{font-size:17px;font-variant-numeric:tabular-nums}
|
|
623
657
|
.sl-value-pop{animation:slValuePop .32s cubic-bezier(.2,.8,.2,1)}
|
|
@@ -675,6 +709,9 @@ var CSS = `
|
|
|
675
709
|
font-size:12.5px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .22s,transform .22s;white-space:nowrap;
|
|
676
710
|
overflow:hidden;text-overflow:ellipsis}
|
|
677
711
|
.sl-toast.on{opacity:1;transform:translateY(0) scale(1)}
|
|
712
|
+
.sl-toast.has-action{pointer-events:auto;display:flex;align-items:center;gap:12px;padding-right:8px}
|
|
713
|
+
.sl-toast-action{min-height:30px;padding:5px 10px;border-radius:999px;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
714
|
+
font:inherit;font-weight:800}
|
|
678
715
|
.sl-toast[data-tone="error"]{border-color:#ef4444}
|
|
679
716
|
.sl-toast[data-tone="warning"]{border-color:var(--sl-accent)}
|
|
680
717
|
.sl-toast[data-tone="success"]{border-color:#22c55e}
|
|
@@ -757,29 +794,49 @@ var CSS = `
|
|
|
757
794
|
.sl-picker[data-layout="narrow"] .sl-confirm{left:50%!important;top:auto!important;bottom:14px;width:min(342px,calc(100% - 24px));
|
|
758
795
|
transform:translateX(-50%);animation:slConfirmMobileIn .24s cubic-bezier(.2,.8,.2,1) both}
|
|
759
796
|
|
|
760
|
-
/*
|
|
761
|
-
.sl-ba{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
.sl-ba
|
|
765
|
-
.sl-ba-
|
|
766
|
-
|
|
797
|
+
/* Best available is a first-class shortcut, not an anonymous utility row. */
|
|
798
|
+
.sl-ba{position:relative;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:7px;
|
|
799
|
+
padding:13px;border:1px solid color-mix(in srgb,var(--sl-accent) 34%,var(--sl-line));border-radius:13px;
|
|
800
|
+
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)))}
|
|
801
|
+
.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}
|
|
802
|
+
.sl-ba-title,.sl-ba-copy,.sl-ba select,.sl-ba-qty,.sl-ba-go{position:relative;z-index:1}
|
|
803
|
+
.sl-ba-title{grid-column:1/-1;display:flex;align-items:center;gap:7px;font-size:13px;font-weight:850}
|
|
804
|
+
.sl-ba-title .spark{color:var(--sl-accent);font-size:16px}
|
|
805
|
+
.sl-ba-copy{grid-column:1/-1;margin:-4px 0 2px 23px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
806
|
+
.sl-ba-copy .narrow{display:none}
|
|
807
|
+
.sl-ba select{background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:8px;
|
|
808
|
+
font:inherit;font-size:11px;padding:7px 8px;min-width:0;width:100%;max-width:none}
|
|
809
|
+
.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)}
|
|
810
|
+
.sl-ba-qty button{width:25px;height:25px;border-radius:7px;background:color-mix(in srgb,var(--sl-line) 35%,transparent);border:0;
|
|
811
|
+
font-size:14px;font-weight:800;display:flex;align-items:center;justify-content:center}
|
|
767
812
|
.sl-ba-qty span{min-width:14px;text-align:center;font-weight:800}
|
|
768
|
-
.sl-ba-go{grid-column:1/-1;width:100%;min-height:
|
|
769
|
-
transition:
|
|
770
|
-
|
|
771
|
-
.sl-ba-go:
|
|
813
|
+
.sl-picker .sl-ba-go{grid-column:1/-1;width:100%;min-height:37px;padding:7px 12px;border-radius:9px;background:var(--sl-accent);
|
|
814
|
+
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;
|
|
815
|
+
box-shadow:0 8px 18px color-mix(in srgb,var(--sl-accent) 18%,transparent)}
|
|
816
|
+
.sl-picker .sl-ba-go:hover{filter:brightness(1.06)}
|
|
817
|
+
.sl-picker .sl-ba-go:disabled{opacity:.62;cursor:wait}
|
|
818
|
+
.sl-ba-replace{grid-column:1/-1;padding:3px 0 1px}
|
|
819
|
+
.sl-ba-replace b{display:block;font-size:12.5px}
|
|
820
|
+
.sl-ba-replace span{display:block;margin-top:3px;color:var(--sl-muted);font-size:10.5px;line-height:1.35}
|
|
821
|
+
.sl-ba-actions{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:7px}
|
|
822
|
+
.sl-ba-actions button{min-height:36px;border-radius:9px;border:1px solid var(--sl-line);font-size:11.5px;font-weight:800}
|
|
823
|
+
.sl-ba-actions .replace{border-color:var(--sl-accent);background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
824
|
+
.sl-picker[data-layout="narrow"] .sl-ba{padding:11px}
|
|
825
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
826
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
772
827
|
|
|
773
828
|
/* screen-reader live region */
|
|
774
829
|
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
775
830
|
|
|
776
831
|
/* per-seat ticket-tier select + view-from-seat button in tray chips */
|
|
777
|
-
.sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:
|
|
778
|
-
font:inherit;font-size:
|
|
779
|
-
.sl-chip .view{width:
|
|
780
|
-
color:var(--sl-muted);transition:color .15s}
|
|
832
|
+
.sl-chip .tier{background:var(--sl-bg);color:var(--sl-text);border:1px solid var(--sl-line);border-radius:6px;
|
|
833
|
+
font:inherit;font-size:10px;padding:2px 4px;min-width:0;max-width:100%;cursor:pointer}
|
|
834
|
+
.sl-chip .view{width:20px;height:20px;border-radius:999px;flex:none;display:flex;align-items:center;justify-content:center;
|
|
835
|
+
color:var(--sl-muted);opacity:.36;transition:color .15s,opacity .15s}
|
|
781
836
|
.sl-chip .view:hover{color:var(--sl-text)}
|
|
782
|
-
.sl-chip .view
|
|
837
|
+
.sl-chip:hover .view,.sl-chip .view:focus-visible{opacity:1;color:var(--sl-text)}
|
|
838
|
+
.sl-chip .view svg{width:12px;height:12px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
|
|
839
|
+
@media(pointer:coarse){.sl-chip .view{opacity:.58}}
|
|
783
840
|
|
|
784
841
|
/* arena: LOD rung pills (flow within the top-center region) */
|
|
785
842
|
.sl-rungs{display:none;background:var(--sl-surface);border:1px solid var(--sl-line);border-radius:999px;padding:3px}
|
|
@@ -866,10 +923,6 @@ var CSS = `
|
|
|
866
923
|
.sl-minimap canvas{display:block}
|
|
867
924
|
.sl-picker[data-layout="narrow"] .sl-minimap{display:none}
|
|
868
925
|
|
|
869
|
-
/* F4 price-band filter \u2014 chip row in the side panel, above the price legend it
|
|
870
|
-
* dims (keeps clear of the map's top-center rungs / top-left a11y chips). */
|
|
871
|
-
.sl-pricef{display:flex;gap:6px;flex-wrap:wrap;padding:2px 16px 10px}
|
|
872
|
-
.sl-pricef .sl-chip-f{padding:6px 11px;font-size:11.5px}
|
|
873
926
|
/* F4 legend reflection: rows + counts for out-of-band categories read muted */
|
|
874
927
|
.sl-price-row.sl-dim{opacity:.4}
|
|
875
928
|
.sl-seccard-mix-item.sl-dim{opacity:.4}
|
|
@@ -959,6 +1012,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
959
1012
|
this.a11yFilter = "all";
|
|
960
1013
|
this.baQty = 2;
|
|
961
1014
|
this.baCat = "";
|
|
1015
|
+
this.bestAvailableConfirm = false;
|
|
1016
|
+
this.releasingHold = false;
|
|
962
1017
|
// arena / multi-floor / seat-view chrome
|
|
963
1018
|
this.rungsEl = null;
|
|
964
1019
|
this.floorsEl = null;
|
|
@@ -1006,10 +1061,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
1006
1061
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
1007
1062
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
1008
1063
|
this.api = new PubApi(this.apiBase);
|
|
1064
|
+
this.maxTickets = Math.max(1, Math.floor(options.maxSelection ?? DEFAULT_MAX_SELECTION2));
|
|
1009
1065
|
this.controller = new import_core2.PickerController({
|
|
1010
1066
|
transport: this.api,
|
|
1011
1067
|
eventKey: options.event,
|
|
1012
|
-
maxSelection:
|
|
1068
|
+
maxSelection: this.maxTickets,
|
|
1013
1069
|
currency: options.currency,
|
|
1014
1070
|
flashOnLiveChange: true,
|
|
1015
1071
|
colorblindSafe: options.colorblindSafe,
|
|
@@ -1044,6 +1100,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
1044
1100
|
onDeselect: (seat) => {
|
|
1045
1101
|
if (this.confirmSeat?.id === seat.id) this.dismissConfirm();
|
|
1046
1102
|
},
|
|
1103
|
+
onSelectionLimit: () => {
|
|
1104
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1105
|
+
},
|
|
1047
1106
|
onViewChange: () => {
|
|
1048
1107
|
this.reanchorConfirm();
|
|
1049
1108
|
this.syncRung();
|
|
@@ -1106,6 +1165,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
1106
1165
|
if (picker.confirmSeat) {
|
|
1107
1166
|
e.preventDefault();
|
|
1108
1167
|
picker.cancelConfirm();
|
|
1168
|
+
} else if (picker.bestAvailableConfirm) {
|
|
1169
|
+
e.preventDefault();
|
|
1170
|
+
picker.bestAvailableConfirm = false;
|
|
1171
|
+
picker.syncTray();
|
|
1109
1172
|
} else {
|
|
1110
1173
|
close();
|
|
1111
1174
|
}
|
|
@@ -1129,10 +1192,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
1129
1192
|
this.root = root;
|
|
1130
1193
|
mount.appendChild(root);
|
|
1131
1194
|
root.addEventListener("keydown", (e) => {
|
|
1132
|
-
if (e.key !== "Escape"
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1195
|
+
if (e.key !== "Escape") return;
|
|
1196
|
+
if (this.confirmSeat) {
|
|
1197
|
+
e.preventDefault();
|
|
1198
|
+
e.stopPropagation();
|
|
1199
|
+
this.cancelConfirm();
|
|
1200
|
+
} else if (this.bestAvailableConfirm) {
|
|
1201
|
+
e.preventDefault();
|
|
1202
|
+
e.stopPropagation();
|
|
1203
|
+
this.bestAvailableConfirm = false;
|
|
1204
|
+
this.syncTray();
|
|
1205
|
+
}
|
|
1136
1206
|
});
|
|
1137
1207
|
Object.entries(resolveTokens(void 0, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
1138
1208
|
root.innerHTML = `
|
|
@@ -1165,21 +1235,22 @@ var SeatPicker = class _SeatPicker {
|
|
|
1165
1235
|
<div class="sl-sheet-grab"></div>
|
|
1166
1236
|
<div class="sl-sheet-bar">
|
|
1167
1237
|
<div class="sl-sheet-peek" data-ref="peek"></div>
|
|
1168
|
-
<
|
|
1238
|
+
<button type="button" class="sl-sheet-toggle" data-ref="sheetToggle" aria-label="Open ticket panel" aria-expanded="false">
|
|
1169
1239
|
<svg viewBox="0 0 24 24"><path d="M6 15l6-6 6 6"/></svg>
|
|
1170
|
-
</
|
|
1240
|
+
</button>
|
|
1171
1241
|
</div>
|
|
1172
1242
|
</div>
|
|
1173
1243
|
<div class="sl-sec sl-filtersec" data-ref="filtersSec">Filters</div>
|
|
1174
1244
|
<div class="sl-filters" data-ref="filters"></div>
|
|
1175
|
-
<div class="sl-sec sl-prices-sec" data-ref="pricesSec">
|
|
1245
|
+
<div class="sl-sec sl-prices-sec" data-ref="pricesSec"><span>Ticket prices</span></div>
|
|
1176
1246
|
<div class="sl-prices" data-ref="prices"></div>
|
|
1177
|
-
<div class="sl-sec sl-seats-sec">Your seats</div>
|
|
1247
|
+
<div class="sl-sec sl-seats-sec"><span>Your seats</span><span class="sl-seat-summary" data-ref="seatSummary"></span></div>
|
|
1178
1248
|
<div class="sl-tray" data-ref="tray"></div>
|
|
1179
|
-
<div class="sl-foot">
|
|
1249
|
+
<div class="sl-foot" data-ref="foot">
|
|
1180
1250
|
<div class="sl-hold-note" data-ref="holdNote" role="status" aria-live="polite">
|
|
1181
1251
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>
|
|
1182
1252
|
<span><b data-ref="holdTitle">Seats secured</b><span class="sl-hold-copy" data-ref="holdCopy">Checkout timer is running.</span></span>
|
|
1253
|
+
<button type="button" class="sl-hold-change" data-ref="holdChange" aria-label="Release held tickets and choose different seats">Change</button>
|
|
1183
1254
|
</div>
|
|
1184
1255
|
<div class="sl-total"><span data-ref="count"></span><b data-ref="total"></b></div>
|
|
1185
1256
|
<button type="button" class="sl-cta" data-ref="cta" disabled></button>
|
|
@@ -1208,9 +1279,17 @@ var SeatPicker = class _SeatPicker {
|
|
|
1208
1279
|
this.els.zfit.addEventListener("click", () => this.controller.zoomToFit());
|
|
1209
1280
|
const head = this.els.sheetHead;
|
|
1210
1281
|
if (head) {
|
|
1282
|
+
const toggle = this.els.sheetToggle;
|
|
1211
1283
|
const setSheet = (open) => {
|
|
1212
1284
|
root.dataset.sheet = open ? "open" : "peek";
|
|
1285
|
+
toggle?.setAttribute("aria-expanded", String(open));
|
|
1286
|
+
toggle?.setAttribute("aria-label", open ? "Collapse ticket panel" : "Open ticket panel");
|
|
1213
1287
|
};
|
|
1288
|
+
setSheet(root.dataset.sheet === "open");
|
|
1289
|
+
toggle?.addEventListener("click", (e) => {
|
|
1290
|
+
e.stopPropagation();
|
|
1291
|
+
setSheet(root.dataset.sheet !== "open");
|
|
1292
|
+
});
|
|
1214
1293
|
let startY = 0;
|
|
1215
1294
|
let swiped = false;
|
|
1216
1295
|
let tracking = false;
|
|
@@ -1233,7 +1312,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1233
1312
|
});
|
|
1234
1313
|
head.addEventListener("pointerup", (e) => {
|
|
1235
1314
|
if (tracking && !swiped && Math.abs(e.clientY - startY) < 6) {
|
|
1236
|
-
if (!e.target.closest(".sl-seccard")) setSheet(root.dataset.sheet !== "open");
|
|
1315
|
+
if (!e.target.closest(".sl-seccard,.sl-sheet-toggle")) setSheet(root.dataset.sheet !== "open");
|
|
1237
1316
|
}
|
|
1238
1317
|
tracking = false;
|
|
1239
1318
|
head.releasePointerCapture?.(e.pointerId);
|
|
@@ -1249,6 +1328,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
1249
1328
|
if (this.tipEl && this.tipEl.style.display !== "none") this.placeTooltip();
|
|
1250
1329
|
});
|
|
1251
1330
|
this.els.cta.addEventListener("click", () => void this.handleCta());
|
|
1331
|
+
this.els.holdChange?.addEventListener("click", () => void this.handleChangeSeats());
|
|
1252
1332
|
const canvasHost = document.createElement("div");
|
|
1253
1333
|
canvasHost.style.cssText = "position:absolute;inset:0";
|
|
1254
1334
|
this.mapHost.appendChild(canvasHost);
|
|
@@ -1455,15 +1535,51 @@ var SeatPicker = class _SeatPicker {
|
|
|
1455
1535
|
const heldItems = this.hold?.items ?? [];
|
|
1456
1536
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
1457
1537
|
const pendingSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1538
|
+
return pendingSeats + this.pendingGACount();
|
|
1539
|
+
}
|
|
1540
|
+
heldGACounts() {
|
|
1458
1541
|
const heldGA = /* @__PURE__ */ new Map();
|
|
1459
|
-
for (const item of
|
|
1542
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1460
1543
|
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1461
1544
|
}
|
|
1462
|
-
|
|
1545
|
+
return heldGA;
|
|
1546
|
+
}
|
|
1547
|
+
pendingGACount() {
|
|
1548
|
+
const heldGA = this.heldGACounts();
|
|
1549
|
+
return [...this.gaQty.entries()].reduce(
|
|
1463
1550
|
(sum, [areaId, qty]) => sum + Math.max(0, qty - (heldGA.get(areaId) ?? 0)),
|
|
1464
1551
|
0
|
|
1465
1552
|
);
|
|
1466
|
-
|
|
1553
|
+
}
|
|
1554
|
+
heldTicketCount() {
|
|
1555
|
+
return (this.hold?.items ?? []).reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
1556
|
+
}
|
|
1557
|
+
totalTicketCount() {
|
|
1558
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1559
|
+
const freshSeats = this.committedSelection().filter((seat) => !heldLabels.has(seat.label)).length;
|
|
1560
|
+
return this.heldTicketCount() + freshSeats + this.pendingGACount();
|
|
1561
|
+
}
|
|
1562
|
+
/** Held tickets and standing quantities consume the same order-wide cap. */
|
|
1563
|
+
updateSelectionCapacity() {
|
|
1564
|
+
const heldLabels = new Set((this.hold?.items ?? []).map((item) => item.label));
|
|
1565
|
+
const selectedHeld = this.committedSelection().filter((seat) => heldLabels.has(seat.label)).length;
|
|
1566
|
+
const remaining = Math.max(0, this.maxTickets - this.heldTicketCount() - this.pendingGACount());
|
|
1567
|
+
this.controller.setMaxSelection(selectedHeld + remaining);
|
|
1568
|
+
}
|
|
1569
|
+
canAddTicket() {
|
|
1570
|
+
if (this.totalTicketCount() < this.maxTickets) return true;
|
|
1571
|
+
this.toast(`You can select up to ${this.maxTickets} tickets for this order.`, "warning");
|
|
1572
|
+
return false;
|
|
1573
|
+
}
|
|
1574
|
+
pendingGATotal(gaAreas) {
|
|
1575
|
+
const heldGA = /* @__PURE__ */ new Map();
|
|
1576
|
+
for (const item of (this.hold?.items ?? []).filter((candidate) => candidate.objectType === "ga")) {
|
|
1577
|
+
heldGA.set(item.objectId, (heldGA.get(item.objectId) ?? 0) + (item.quantity ?? 1));
|
|
1578
|
+
}
|
|
1579
|
+
return gaAreas.reduce(
|
|
1580
|
+
(sum, area) => sum + area.price * Math.max(0, (this.gaQty.get(area.id) ?? 0) - (heldGA.get(area.id) ?? 0)),
|
|
1581
|
+
0
|
|
1582
|
+
);
|
|
1467
1583
|
}
|
|
1468
1584
|
syncCta(count = this.lastTrayCount, pending = this.pendingSelectionCount()) {
|
|
1469
1585
|
const cta = this.els.cta;
|
|
@@ -1752,39 +1868,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
1752
1868
|
}
|
|
1753
1869
|
return bands;
|
|
1754
1870
|
}
|
|
1755
|
-
/**
|
|
1756
|
-
*
|
|
1757
|
-
* header and above the legend it dims. Living in the panel (not a map overlay)
|
|
1758
|
-
* keeps it clear of the top-center rung pills and top-left a11y chips, and it
|
|
1759
|
-
* rides the bottom sheet on narrow layouts. Skipped when there's <2 bands.
|
|
1760
|
-
*/
|
|
1871
|
+
/** Build the compact price selector in the panel header. Choosing a band both
|
|
1872
|
+
* filters availability and smoothly frames the matching seats on the map. */
|
|
1761
1873
|
buildPriceFilter() {
|
|
1762
1874
|
if (!this.els.prices || !this.els.pricesSec) return;
|
|
1763
1875
|
const bands = this.priceBands();
|
|
1764
1876
|
if (bands.length < 2) return;
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
this.controller.setCategoryFilter(null);
|
|
1780
|
-
} else {
|
|
1781
|
-
const bd = bands.find((x) => x.id === id);
|
|
1782
|
-
this.priceBandKeys = bd ? new Set(bd.keys) : null;
|
|
1783
|
-
this.controller.setCategoryFilter(bd ? bd.keys : null);
|
|
1784
|
-
}
|
|
1785
|
-
this.syncPrices();
|
|
1786
|
-
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
1787
|
-
});
|
|
1877
|
+
const select = document.createElement("select");
|
|
1878
|
+
select.className = "sl-price-select";
|
|
1879
|
+
select.setAttribute("aria-label", "Filter and focus seats by price");
|
|
1880
|
+
select.innerHTML = `<option value="all">All prices</option>` + bands.map((band) => `<option value="${band.id}">${band.label}</option>`).join("");
|
|
1881
|
+
this.els.pricesSec.appendChild(select);
|
|
1882
|
+
this.priceFilterEl = select;
|
|
1883
|
+
select.addEventListener("change", () => {
|
|
1884
|
+
const band = bands.find((candidate) => candidate.id === select.value);
|
|
1885
|
+
const keys = band?.keys ?? null;
|
|
1886
|
+
this.priceBandKeys = keys ? new Set(keys) : null;
|
|
1887
|
+
this.controller.setCategoryFilter(keys);
|
|
1888
|
+
this.controller.focusCategoryFilter(keys);
|
|
1889
|
+
this.syncPrices();
|
|
1890
|
+
if (this.lastSection) this.showSectionCard(this.lastSection);
|
|
1788
1891
|
});
|
|
1789
1892
|
}
|
|
1790
1893
|
// ---- arena / multi-floor chrome -------------------------------------------
|
|
@@ -2184,7 +2287,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2184
2287
|
const price = c.tiers?.length ? c.tiers[0].price : c.price;
|
|
2185
2288
|
const dim = this.priceBandKeys != null && !this.priceBandKeys.has(c.key);
|
|
2186
2289
|
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>`;
|
|
2187
|
-
}).join("") + `<div class="sl-status-key" aria-label="Seat status legend"><span class="sl-status-item"><i class="sl-status-icon"
|
|
2290
|
+
}).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>`;
|
|
2188
2291
|
this.els.prices.querySelectorAll(".sl-price-row").forEach((row) => {
|
|
2189
2292
|
row.addEventListener("mouseenter", () => this.controller.getRenderer()?.setCategoryHighlight?.(row.dataset.cat ?? null));
|
|
2190
2293
|
row.addEventListener("mouseleave", () => this.controller.getRenderer()?.setCategoryHighlight?.(null));
|
|
@@ -2203,6 +2306,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2203
2306
|
}
|
|
2204
2307
|
syncTray() {
|
|
2205
2308
|
if (!this.els.tray) return;
|
|
2309
|
+
this.updateSelectionCapacity();
|
|
2206
2310
|
const seats = this.committedSelection();
|
|
2207
2311
|
const gaAreas = this.controller.getGAAreas();
|
|
2208
2312
|
const heldItems = this.hold?.items ?? [];
|
|
@@ -2213,14 +2317,19 @@ var SeatPicker = class _SeatPicker {
|
|
|
2213
2317
|
} else if (!seats.length && !heldItems.length) {
|
|
2214
2318
|
parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
|
|
2215
2319
|
}
|
|
2320
|
+
if (!this.hold) {
|
|
2321
|
+
const cats = this.controller.doc?.categories ?? [];
|
|
2322
|
+
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>`);
|
|
2323
|
+
}
|
|
2216
2324
|
for (const item of heldItems) {
|
|
2217
2325
|
const itemKey = `held:${item.label}`;
|
|
2218
2326
|
nextTrayKeys.add(itemKey);
|
|
2219
2327
|
const cat = this.controller.doc?.categories.find((c) => c.key === item.categoryKey);
|
|
2220
2328
|
const tierName = item.tierId ? cat?.tiers?.find((ti) => ti.id === item.tierId)?.name : void 0;
|
|
2221
2329
|
const canView2 = this.seatViewEnabled() && item.objectType !== "ga" && !!this.controller.seatByLabel(item.label);
|
|
2330
|
+
const viewBtn = canView2 ? `<button type="button" class="view" data-view-label="${item.label}" aria-label="${(0, import_core2.t)("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>` : "";
|
|
2222
2331
|
parts.push(
|
|
2223
|
-
`<div class="sl-chip sl-held${this.lastTrayKeys.has(itemKey) ? "" : " sl-enter"}" data-key="${itemKey}" data-held="${encodeURIComponent(item.label)}"><
|
|
2332
|
+
`<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>`
|
|
2224
2333
|
);
|
|
2225
2334
|
}
|
|
2226
2335
|
const heldLabels = new Set(heldItems.map((item) => item.label));
|
|
@@ -2232,7 +2341,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2232
2341
|
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(ti.price)}</option>`).join("") + `</select>` : "";
|
|
2233
2342
|
const viewBtn = canView ? `<button type="button" class="view" data-view-label="${s.label}" aria-label="${(0, import_core2.t)("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>` : "";
|
|
2234
2343
|
parts.push(
|
|
2235
|
-
`<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
|
|
2344
|
+
`<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>`
|
|
2236
2345
|
);
|
|
2237
2346
|
}
|
|
2238
2347
|
for (const area of gaAreas) {
|
|
@@ -2241,17 +2350,11 @@ var SeatPicker = class _SeatPicker {
|
|
|
2241
2350
|
`<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>`
|
|
2242
2351
|
);
|
|
2243
2352
|
}
|
|
2244
|
-
if (!this.hold) {
|
|
2245
|
-
const cats = this.controller.doc?.categories ?? [];
|
|
2246
|
-
parts.push(
|
|
2247
|
-
`<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>`
|
|
2248
|
-
);
|
|
2249
|
-
}
|
|
2250
2353
|
this.els.tray.innerHTML = parts.join("");
|
|
2251
2354
|
this.lastTrayKeys = nextTrayKeys;
|
|
2252
2355
|
this.els.tray.querySelectorAll("[data-ba]").forEach((btn) => {
|
|
2253
2356
|
btn.addEventListener("click", () => {
|
|
2254
|
-
this.baQty = Math.max(1, Math.min(
|
|
2357
|
+
this.baQty = Math.max(1, Math.min(this.maxTickets, this.baQty + Number(btn.dataset.ba)));
|
|
2255
2358
|
this.syncTray();
|
|
2256
2359
|
});
|
|
2257
2360
|
});
|
|
@@ -2259,6 +2362,21 @@ var SeatPicker = class _SeatPicker {
|
|
|
2259
2362
|
this.baCat = e.target.value;
|
|
2260
2363
|
});
|
|
2261
2364
|
this.els.tray.querySelector(".sl-ba-go")?.addEventListener("click", () => {
|
|
2365
|
+
if (this.pendingSelectionCount() > 0) {
|
|
2366
|
+
this.bestAvailableConfirm = true;
|
|
2367
|
+
this.syncTray();
|
|
2368
|
+
this.els.tray.querySelector("[data-ba-replace]")?.focus();
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2372
|
+
});
|
|
2373
|
+
this.els.tray.querySelector("[data-ba-cancel]")?.addEventListener("click", () => {
|
|
2374
|
+
this.bestAvailableConfirm = false;
|
|
2375
|
+
this.syncTray();
|
|
2376
|
+
this.els.tray.querySelector(".sl-ba-go")?.focus();
|
|
2377
|
+
});
|
|
2378
|
+
this.els.tray.querySelector("[data-ba-replace]")?.addEventListener("click", () => {
|
|
2379
|
+
this.bestAvailableConfirm = false;
|
|
2262
2380
|
void this.bestAvailable(this.baQty, this.baCat || void 0);
|
|
2263
2381
|
});
|
|
2264
2382
|
this.els.tray.querySelectorAll(".sl-chip .rm").forEach((btn) => {
|
|
@@ -2269,12 +2387,26 @@ var SeatPicker = class _SeatPicker {
|
|
|
2269
2387
|
return;
|
|
2270
2388
|
}
|
|
2271
2389
|
const id = chip.dataset.seat;
|
|
2272
|
-
|
|
2390
|
+
const label = chip.querySelector("b")?.textContent ?? "Seat";
|
|
2391
|
+
const remove = () => {
|
|
2273
2392
|
this.controller.deselect([id]);
|
|
2393
|
+
this.toast(`${label} removed.`, "neutral", {
|
|
2394
|
+
label: "Undo",
|
|
2395
|
+
onClick: () => {
|
|
2396
|
+
const restored = this.controller.select([id]);
|
|
2397
|
+
this.toast(
|
|
2398
|
+
restored.length ? `${label} restored.` : `${label} is no longer available.`,
|
|
2399
|
+
restored.length ? "success" : "warning"
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
};
|
|
2404
|
+
if (this.reducedMotion()) {
|
|
2405
|
+
remove();
|
|
2274
2406
|
return;
|
|
2275
2407
|
}
|
|
2276
2408
|
chip.classList.add("sl-leave");
|
|
2277
|
-
this.scheduleMotion(
|
|
2409
|
+
this.scheduleMotion(remove, 150);
|
|
2278
2410
|
});
|
|
2279
2411
|
});
|
|
2280
2412
|
this.els.tray.querySelectorAll(".sl-chip .tier").forEach((sel) => {
|
|
@@ -2291,13 +2423,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
2291
2423
|
const areaEl = btn.closest(".sl-ga");
|
|
2292
2424
|
const id = areaEl.dataset.ga;
|
|
2293
2425
|
const area = gaAreas.find((a) => a.id === id);
|
|
2294
|
-
const
|
|
2426
|
+
const delta = Number(btn.dataset.d);
|
|
2427
|
+
if (delta > 0 && !this.canAddTicket()) return;
|
|
2428
|
+
const next = Math.max(0, Math.min(area?.available ?? 0, (this.gaQty.get(id) ?? 0) + delta));
|
|
2295
2429
|
this.gaQty.set(id, next);
|
|
2296
2430
|
this.syncTray();
|
|
2297
2431
|
});
|
|
2298
2432
|
});
|
|
2299
|
-
const gaTotal =
|
|
2300
|
-
const gaCount =
|
|
2433
|
+
const gaTotal = this.pendingGATotal(gaAreas);
|
|
2434
|
+
const gaCount = this.pendingGACount();
|
|
2301
2435
|
const heldTotal = heldItems.reduce((sum, item) => sum + item.unitPrice * (item.quantity ?? 1), 0);
|
|
2302
2436
|
const heldCount = heldItems.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
2303
2437
|
const freshSeats = seats.filter((seat) => !heldLabels.has(seat.label));
|
|
@@ -2308,14 +2442,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
2308
2442
|
const previousTotal = this.lastTrayTotal;
|
|
2309
2443
|
this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
|
|
2310
2444
|
this.els.total.textContent = count ? this.money(total) : "";
|
|
2445
|
+
this.root?.setAttribute("data-has-selection", String(count > 0));
|
|
2446
|
+
this.els.foot?.classList.toggle("empty", count === 0);
|
|
2447
|
+
if (this.els.seatSummary) {
|
|
2448
|
+
this.els.seatSummary.textContent = count ? `${count} selected` : "";
|
|
2449
|
+
}
|
|
2311
2450
|
this.syncCta(count, pendingCount);
|
|
2312
2451
|
if (this.hold) {
|
|
2313
2452
|
const securedCount = heldCount || this.hold.seats?.length || 0;
|
|
2314
2453
|
if (this.els.holdTitle) {
|
|
2315
|
-
this.els.holdTitle.textContent = `${securedCount}
|
|
2454
|
+
this.els.holdTitle.textContent = `${securedCount} secured`;
|
|
2316
2455
|
}
|
|
2317
2456
|
if (this.els.holdCopy) {
|
|
2318
|
-
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected
|
|
2457
|
+
this.els.holdCopy.textContent = pendingCount ? `${pendingCount} more selected` : "Checkout timer running";
|
|
2458
|
+
}
|
|
2459
|
+
const change = this.els.holdChange;
|
|
2460
|
+
if (change) {
|
|
2461
|
+
change.disabled = this.releasingHold;
|
|
2462
|
+
change.textContent = this.releasingHold ? "Releasing\u2026" : "Change";
|
|
2319
2463
|
}
|
|
2320
2464
|
}
|
|
2321
2465
|
if (count !== previousCount) this.animateOnce(this.els.count, "sl-value-pop", 380);
|
|
@@ -2326,7 +2470,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2326
2470
|
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>`;
|
|
2327
2471
|
} else {
|
|
2328
2472
|
const prices = (this.controller.doc?.categories ?? []).map((c) => this.catPrice(c)).filter((p) => p != null);
|
|
2329
|
-
this.els.peek.innerHTML = (prices.length ? `<span>From ${this.money(Math.min(...prices))}</span>` : "<span>Pick your seats</span>") + `<span class="
|
|
2473
|
+
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>`;
|
|
2330
2474
|
}
|
|
2331
2475
|
}
|
|
2332
2476
|
this.lastTrayCount = count;
|
|
@@ -2367,7 +2511,30 @@ var SeatPicker = class _SeatPicker {
|
|
|
2367
2511
|
if (button?.isConnected) button.disabled = false;
|
|
2368
2512
|
}
|
|
2369
2513
|
}
|
|
2514
|
+
async handleChangeSeats() {
|
|
2515
|
+
if (!this.hold || this.releasingHold) return;
|
|
2516
|
+
this.releasingHold = true;
|
|
2517
|
+
const button = this.els.holdChange;
|
|
2518
|
+
if (button) {
|
|
2519
|
+
button.disabled = true;
|
|
2520
|
+
button.textContent = "Releasing\u2026";
|
|
2521
|
+
}
|
|
2522
|
+
try {
|
|
2523
|
+
await this.release();
|
|
2524
|
+
if (!this.hold) this.toast("Held tickets released. Choose your new seats.", "success");
|
|
2525
|
+
} finally {
|
|
2526
|
+
this.releasingHold = false;
|
|
2527
|
+
if (button?.isConnected) {
|
|
2528
|
+
button.disabled = false;
|
|
2529
|
+
button.textContent = "Change";
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2370
2533
|
async handleCta() {
|
|
2534
|
+
if (this.totalTicketCount() > this.maxTickets) {
|
|
2535
|
+
this.toast(`Remove tickets until your order has ${this.maxTickets} or fewer.`, "warning");
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2371
2538
|
const committed = this.committedSelection();
|
|
2372
2539
|
if (this.hold && !committed.some((s) => !(this.hold.items ?? []).some((i) => i.label === s.label))) {
|
|
2373
2540
|
const seats = this.hold.seats ?? committed;
|
|
@@ -2405,7 +2572,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
2405
2572
|
this.opts.onCheckout?.(hold, hold.seats ?? chosenSeats, this.buildHandoff(hold));
|
|
2406
2573
|
} catch (err) {
|
|
2407
2574
|
this.opts.onError?.(err);
|
|
2408
|
-
|
|
2575
|
+
const problem = err;
|
|
2576
|
+
const labels = (problem.conflicts ?? []).map((conflict) => conflict.label).filter(Boolean).slice(0, 3);
|
|
2577
|
+
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.";
|
|
2578
|
+
this.toast(message, "error");
|
|
2409
2579
|
this.setCtaPhase("idle");
|
|
2410
2580
|
} finally {
|
|
2411
2581
|
this.holdingLabels.clear();
|
|
@@ -2528,15 +2698,28 @@ var SeatPicker = class _SeatPicker {
|
|
|
2528
2698
|
hold ? this.buildHandoff(hold) : null
|
|
2529
2699
|
);
|
|
2530
2700
|
}
|
|
2531
|
-
toast(msg, tone = "neutral") {
|
|
2701
|
+
toast(msg, tone = "neutral", action) {
|
|
2532
2702
|
const el = this.els.toast;
|
|
2533
2703
|
if (!el) return;
|
|
2534
|
-
el.
|
|
2704
|
+
el.replaceChildren();
|
|
2705
|
+
const copy = document.createElement("span");
|
|
2706
|
+
copy.textContent = msg;
|
|
2707
|
+
el.appendChild(copy);
|
|
2708
|
+
el.classList.toggle("has-action", !!action);
|
|
2709
|
+
if (action) {
|
|
2710
|
+
const button = document.createElement("button");
|
|
2711
|
+
button.type = "button";
|
|
2712
|
+
button.className = "sl-toast-action";
|
|
2713
|
+
button.textContent = action.label;
|
|
2714
|
+
button.addEventListener("click", action.onClick, { once: true });
|
|
2715
|
+
el.appendChild(button);
|
|
2716
|
+
}
|
|
2535
2717
|
el.dataset.tone = tone;
|
|
2536
2718
|
el.classList.add("on");
|
|
2537
2719
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
2538
2720
|
this.toastTimer = setTimeout(() => {
|
|
2539
2721
|
el.classList.remove("on");
|
|
2722
|
+
el.classList.remove("has-action");
|
|
2540
2723
|
el.dataset.tone = "neutral";
|
|
2541
2724
|
}, 4200);
|
|
2542
2725
|
}
|
|
@@ -2586,7 +2769,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
2586
2769
|
}
|
|
2587
2770
|
async bestAvailable(qty, categoryKey) {
|
|
2588
2771
|
if (this.bestAvailableBusy) return null;
|
|
2772
|
+
qty = Math.max(1, Math.min(this.maxTickets, Math.floor(qty)));
|
|
2589
2773
|
if (this.confirmSeat) this.cancelConfirm();
|
|
2774
|
+
this.bestAvailableConfirm = false;
|
|
2590
2775
|
this.bestAvailableBusy = true;
|
|
2591
2776
|
const button = this.els.tray?.querySelector(".sl-ba-go");
|
|
2592
2777
|
if (button) {
|
|
@@ -2599,6 +2784,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
2599
2784
|
this.hold = { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items };
|
|
2600
2785
|
this.handedOff = false;
|
|
2601
2786
|
this.bookedShown = false;
|
|
2787
|
+
this.gaQty.clear();
|
|
2602
2788
|
this.startHoldTimer(h.expiresAt);
|
|
2603
2789
|
this.flashHeldSeats(this.hold);
|
|
2604
2790
|
this.syncTray();
|
|
@@ -2608,14 +2794,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
2608
2794
|
return null;
|
|
2609
2795
|
} catch (err) {
|
|
2610
2796
|
this.opts.onError?.(err);
|
|
2611
|
-
|
|
2797
|
+
const reason = err?.reason;
|
|
2798
|
+
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.";
|
|
2799
|
+
this.toast(message, "error");
|
|
2612
2800
|
return null;
|
|
2613
2801
|
} finally {
|
|
2614
2802
|
this.bestAvailableBusy = false;
|
|
2615
|
-
|
|
2616
|
-
button.disabled = false;
|
|
2617
|
-
button.textContent = "Best available";
|
|
2618
|
-
}
|
|
2803
|
+
this.syncTray();
|
|
2619
2804
|
}
|
|
2620
2805
|
}
|
|
2621
2806
|
async release() {
|