@seatlayer/js 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -360,6 +360,7 @@ var TYPES = /* @__PURE__ */ new Set([
360
360
  "seatlayer.designer.error"
361
361
  ]);
362
362
  var DEFAULT_LOADING_TIMEOUT_MS = 2e4;
363
+ var DEFAULT_MIN_FILL_HEIGHT = 480;
363
364
  function resolveContainer2(container) {
364
365
  if (typeof container !== "string") return container;
365
366
  const element = document.querySelector(container);
@@ -407,12 +408,23 @@ var EmbeddedDesigner = class {
407
408
  this.fsKeyHandler = null;
408
409
  /** Latest height (px string) the Designer reported; re-applied after unpin. */
409
410
  this.lastAutoHeight = "";
411
+ // Viewport-fill sizing: pending rAF handle + whether listeners are attached.
412
+ this.fillRaf = null;
413
+ this.fillListening = false;
414
+ /** rAF-throttled fill recompute, so a burst of scroll/resize ticks coalesces. */
415
+ this.scheduleFill = () => {
416
+ if (this.fillRaf !== null) return;
417
+ this.fillRaf = requestAnimationFrame(() => {
418
+ this.fillRaf = null;
419
+ this.applyFill();
420
+ });
421
+ };
410
422
  this.handleMessage = (event) => {
411
423
  if (!this.frame || event.origin !== this.designerOrigin || event.source !== this.frame.contentWindow) return;
412
424
  if (!event.data || typeof event.data !== "object") return;
413
425
  const data = event.data;
414
426
  if (data.type === "seatlayer.designer.resize") {
415
- if (this.autoResizeEnabled() && typeof data.px === "number" && Number.isFinite(data.px) && data.px > 0) {
427
+ if (!this.fillEnabled() && this.autoResizeEnabled() && typeof data.px === "number" && Number.isFinite(data.px) && data.px > 0) {
416
428
  this.lastAutoHeight = `${Math.round(data.px)}px`;
417
429
  if (!this.pinned) this.frame.style.height = this.lastAutoHeight;
418
430
  }
@@ -470,11 +482,11 @@ var EmbeddedDesigner = class {
470
482
  this.designerOrigin = url.origin;
471
483
  const frame = document.createElement("iframe");
472
484
  frame.title = this.options.title ?? "Venue chart Designer";
473
- frame.allow = this.options.allow ?? "clipboard-write";
485
+ frame.allow = this.options.allow ?? "fullscreen; clipboard-write";
474
486
  frame.referrerPolicy = this.options.referrerPolicy ?? "origin";
475
487
  frame.src = url.toString();
476
488
  frame.style.width = "100%";
477
- frame.style.height = "100%";
489
+ frame.style.height = typeof this.options.height === "number" ? `${this.options.height}px` : "100%";
478
490
  frame.style.border = "0";
479
491
  Object.assign(frame.style, this.options.style);
480
492
  if (this.options.className) frame.className = this.options.className;
@@ -482,6 +494,7 @@ var EmbeddedDesigner = class {
482
494
  window.addEventListener("message", this.handleMessage);
483
495
  container.append(frame);
484
496
  this.frame = frame;
497
+ if (this.fillEnabled()) this.startFill();
485
498
  this.phase = "loading";
486
499
  if (this.loadingStateEnabled()) {
487
500
  this.ensureContainerPositioned(container);
@@ -505,6 +518,7 @@ var EmbeddedDesigner = class {
505
518
  }
506
519
  destroy() {
507
520
  window.removeEventListener("message", this.handleMessage);
521
+ this.stopFill();
508
522
  this.unpinFullscreen();
509
523
  this.clearTimeoutTimer();
510
524
  this.removeOverlay();
@@ -521,6 +535,41 @@ var EmbeddedDesigner = class {
521
535
  autoResizeEnabled() {
522
536
  return this.options.autoResize !== false;
523
537
  }
538
+ /** Fill mode is the default; a numeric `height` opts into a fixed pixel box. */
539
+ fillEnabled() {
540
+ return typeof this.options.height !== "number";
541
+ }
542
+ /**
543
+ * Size the iframe so its bottom edge meets the bottom of the viewport
544
+ * (`window.innerHeight - top`), clamped to `minHeight`. No-op while pinned
545
+ * fullscreen (the pin fills the viewport itself).
546
+ */
547
+ applyFill() {
548
+ if (!this.frame || this.pinned) return;
549
+ const min = this.options.minHeight ?? DEFAULT_MIN_FILL_HEIGHT;
550
+ const top = this.frame.getBoundingClientRect().top;
551
+ const target = Math.max(min, Math.round(window.innerHeight - top));
552
+ this.frame.style.height = `${target}px`;
553
+ }
554
+ startFill() {
555
+ this.applyFill();
556
+ if (this.fillListening) return;
557
+ this.fillListening = true;
558
+ window.addEventListener("resize", this.scheduleFill);
559
+ window.addEventListener("orientationchange", this.scheduleFill);
560
+ window.addEventListener("scroll", this.scheduleFill, { passive: true });
561
+ }
562
+ stopFill() {
563
+ if (this.fillRaf !== null) {
564
+ cancelAnimationFrame(this.fillRaf);
565
+ this.fillRaf = null;
566
+ }
567
+ if (!this.fillListening) return;
568
+ this.fillListening = false;
569
+ window.removeEventListener("resize", this.scheduleFill);
570
+ window.removeEventListener("orientationchange", this.scheduleFill);
571
+ window.removeEventListener("scroll", this.scheduleFill);
572
+ }
524
573
  /**
525
574
  * Pin the iframe over the host page as a viewport-filling overlay. We save the
526
575
  * iframe's inline style and the document scroll state so `unpinFullscreen`
@@ -559,7 +608,8 @@ var EmbeddedDesigner = class {
559
608
  if (this.frame) {
560
609
  if (this.frameStyleBeforeFs === null) this.frame.removeAttribute("style");
561
610
  else this.frame.setAttribute("style", this.frameStyleBeforeFs);
562
- if (this.autoResizeEnabled() && this.lastAutoHeight) this.frame.style.height = this.lastAutoHeight;
611
+ if (this.fillEnabled()) this.applyFill();
612
+ else if (this.autoResizeEnabled() && this.lastAutoHeight) this.frame.style.height = this.lastAutoHeight;
563
613
  }
564
614
  this.frameStyleBeforeFs = null;
565
615
  if (this.docOverflowBeforeFs !== null) {
@@ -862,6 +912,11 @@ var CSS = `
862
912
  .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec,
863
913
  .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters,
864
914
  .sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-prices-sec{display:none}
915
+ /* Reclaim the bottom sheet once the cart has anything: the "Find best seats"
916
+ panel collapses too. EXCEPT the confirm ("Replace your current choices?") and
917
+ in-flight busy states, which legitimately show with a non-empty cart \u2014 those
918
+ set data-ba-active="true" (see setAttribute alongside data-has-selection). */
919
+ .sl-picker[data-layout="narrow"][data-has-selection="true"]:not([data-ba-active="true"]) .sl-ba{display:none}
865
920
  /* touch chrome: pinch-zoom exists \u2014 hide +/\u2212 on the sheet layout (keep fit) */
866
921
  .sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zin"],
867
922
  .sl-picker[data-layout="narrow"] .sl-zoom [data-ref="zout"]{display:none}
@@ -2712,6 +2767,7 @@ var SeatPicker = class _SeatPicker {
2712
2767
  if (narrow) {
2713
2768
  card.className = "sl-seccard strip on";
2714
2769
  card.setAttribute("role", "status");
2770
+ card.setAttribute("aria-label", (0, import_core2.t)("picker.sectionSummaryAria", { label: summary.label }));
2715
2771
  card.innerHTML = `<span class="sl-seccard-dot" style="background:${summary.color}"></span><span class="sl-seccard-name">${summary.label}</span><span class="sl-seccard-left">${leftLabel}</span>` + (summary.categories.length ? `<span class="sl-seccard-price">${priceLabel}</span>` : "") + xBtn;
2716
2772
  card.querySelector(".sl-seccard-x").addEventListener("click", () => this.controller.overview());
2717
2773
  (this.els.sheetHead ?? this.els.side ?? this.els.map).appendChild(card);
@@ -3122,7 +3178,7 @@ var SeatPicker = class _SeatPicker {
3122
3178
  } else if (!seats.length && !heldItems.length) {
3123
3179
  parts.push(`<div class="sl-tray-hint">Tap a seat on the map \u2014 or grab standing tickets below.</div>`);
3124
3180
  }
3125
- const noPicks = !seats.length && !heldItems.length;
3181
+ const noPicks = !seats.length && !heldItems.length && !this.pendingGACount();
3126
3182
  if (!this.hold && (noPicks || this.bestAvailableBusy || this.bestAvailableConfirm)) {
3127
3183
  const cats = this.controller.doc?.categories ?? [];
3128
3184
  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>`);
@@ -3266,6 +3322,10 @@ var SeatPicker = class _SeatPicker {
3266
3322
  this.els.count.textContent = count ? `${count} ${count === 1 ? "ticket" : "tickets"}` : "No seats selected";
3267
3323
  this.els.total.textContent = count ? this.money(total) : "";
3268
3324
  this.root?.setAttribute("data-has-selection", String(count > 0));
3325
+ this.root?.setAttribute(
3326
+ "data-ba-active",
3327
+ String(this.bestAvailableConfirm || this.bestAvailableBusy)
3328
+ );
3269
3329
  this.els.foot?.classList.toggle("empty", count === 0);
3270
3330
  if (this.els.seatSummary) {
3271
3331
  this.els.seatSummary.textContent = count ? `${count} selected` : "";