@seatlayer/core 0.15.1 → 0.16.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
@@ -963,6 +963,7 @@ var ISO_ANGLE_DEG = -11.5;
963
963
  var ISO_SQUASH = 0.58;
964
964
  var LIFT_PER_STEP = 58;
965
965
  var ISO_TWEEN_MS = 320;
966
+ var CAMERA_GLIDE_MS = 650;
966
967
  var BLOCK_FILL_ALPHA = 0.85;
967
968
  var SOLD_DARKEN = 0.5;
968
969
  var SECTION_LABEL_PX = 20;
@@ -1917,6 +1918,40 @@ var _SeatmapRenderer = class _SeatmapRenderer {
1917
1918
  this.seatLayer.batchDraw();
1918
1919
  }
1919
1920
  }
1921
+ /** Frame the currently available inventory that survived a buyer price
1922
+ * filter. Clearing the filter glides back to the full venue. */
1923
+ focusCategories(keys) {
1924
+ if (!keys?.length) {
1925
+ this.focusRegion(this.bounds, { durationMs: CAMERA_GLIDE_MS });
1926
+ return;
1927
+ }
1928
+ const wanted = new Set(keys);
1929
+ const matching = this.seats.filter((seat) => {
1930
+ if (!wanted.has(seat.categoryKey) || this.seatInClosedSection(seat.id)) return false;
1931
+ const status = this.statusById.get(seat.id) ?? "free";
1932
+ return status === "free" || this.ownedHold.has(seat.id);
1933
+ });
1934
+ if (!matching.length) return;
1935
+ let minX = Infinity;
1936
+ let maxX = -Infinity;
1937
+ let minY = Infinity;
1938
+ let maxY = -Infinity;
1939
+ for (const seat of matching) {
1940
+ minX = Math.min(minX, seat.x);
1941
+ maxX = Math.max(maxX, seat.x);
1942
+ minY = Math.min(minY, seat.y);
1943
+ maxY = Math.max(maxY, seat.y);
1944
+ }
1945
+ const pad = Math.max(24, this.seatR * 3);
1946
+ minX -= pad;
1947
+ maxX += pad;
1948
+ minY -= pad;
1949
+ maxY += pad;
1950
+ this.focusRegion(
1951
+ { x: minX, y: minY, width: Math.max(40, maxX - minX), height: Math.max(40, maxY - minY) },
1952
+ { durationMs: CAMERA_GLIDE_MS }
1953
+ );
1954
+ }
1920
1955
  // ---- isometric ("3D") view mode -------------------------------------------
1921
1956
  /**
1922
1957
  * Switch the projection between flat top-down and the isometric "3D" view
@@ -3443,7 +3478,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3443
3478
  }
3444
3479
  /**
3445
3480
  * Smoothly glide the camera to frame a section (by id) or a world-space bounds
3446
- * rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
3481
+ * rect — the Slice 5 "glide in". Pan+zoom tween over a calm easeInOutCubic; the
3447
3482
  * melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
3448
3483
  * `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
3449
3484
  * newer glide cancels an in-flight one.
@@ -3475,13 +3510,13 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3475
3510
  return;
3476
3511
  }
3477
3512
  const start = performance.now();
3478
- const DUR = 450;
3513
+ const duration = Math.max(180, Math.min(1200, opts?.durationMs ?? CAMERA_GLIDE_MS));
3479
3514
  const step = (now) => {
3480
3515
  if (this.destroyed) {
3481
3516
  this.glideRaf = 0;
3482
3517
  return;
3483
3518
  }
3484
- const raw = Math.min(1, (now - start) / DUR);
3519
+ const raw = Math.min(1, (now - start) / duration);
3485
3520
  const e = raw < 0.5 ? 4 * raw * raw * raw : 1 - Math.pow(-2 * raw + 2, 3) / 2;
3486
3521
  const sc = fromScale + (toScale - fromScale) * e;
3487
3522
  this.stage.scale({ x: sc, y: sc });
@@ -3606,15 +3641,49 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3606
3641
  if (seat.kind === "booth") continue;
3607
3642
  if (seat.x < x0 || seat.x > x1 || seat.y < y0 || seat.y > y1) continue;
3608
3643
  const status = this.statusById.get(seat.id) ?? "free";
3609
- const statusCue = status === "booked" ? "\xD7" : status === "held" && !this.ownedHold.has(seat.id) ? "H" : null;
3644
+ const unavailable = status === "booked" || status === "held" && !this.ownedHold.has(seat.id);
3645
+ if (unavailable) {
3646
+ const cue = new import_Group.Group({ x: seat.x, y: seat.y, listening: false });
3647
+ if (status === "held") {
3648
+ cue.add(new import_Rect.Rect({
3649
+ x: -4.2,
3650
+ y: -0.7,
3651
+ width: 8.4,
3652
+ height: 6.5,
3653
+ cornerRadius: 1.3,
3654
+ stroke: "#ffffff",
3655
+ strokeWidth: 1.25,
3656
+ listening: false
3657
+ }));
3658
+ cue.add(new import_Line.Line({
3659
+ points: [-2.4, -0.8, -2.4, -2.7, -1.2, -4, 0, -4.35, 1.2, -4, 2.4, -2.7, 2.4, -0.8],
3660
+ stroke: "#ffffff",
3661
+ strokeWidth: 1.2,
3662
+ lineCap: "round",
3663
+ lineJoin: "round",
3664
+ listening: false
3665
+ }));
3666
+ } else {
3667
+ cue.add(new import_Line.Line({
3668
+ points: [-4.2, 4.2, 4.2, -4.2],
3669
+ stroke: "#ffffff",
3670
+ strokeWidth: 1.8,
3671
+ lineCap: "round",
3672
+ listening: false
3673
+ }));
3674
+ }
3675
+ this.labelGroup.add(cue);
3676
+ if (++count >= MAX_LABELS) break;
3677
+ continue;
3678
+ }
3610
3679
  const t2 = new import_Text.Text({
3611
3680
  x: seat.x,
3612
3681
  y: seat.y,
3613
- text: statusCue ?? seat.label,
3614
- fontSize: statusCue ? status === "booked" ? 13 : 8 : 7,
3615
- fontStyle: statusCue ? "800" : "600",
3682
+ text: seat.label,
3683
+ fontSize: 7,
3684
+ fontStyle: "600",
3616
3685
  fontFamily: this.labelFont(),
3617
- fill: statusCue ? "#ffffff" : this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
3686
+ fill: this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
3618
3687
  listening: false,
3619
3688
  perfectDrawEnabled: false
3620
3689
  });
@@ -4271,6 +4340,17 @@ var PickerController = class {
4271
4340
  setCategoryFilter(keys) {
4272
4341
  this.renderer?.setCategoryFilter?.(keys);
4273
4342
  }
4343
+ /** An explicit buyer price-filter action should not only dim the rest of the
4344
+ * chart: clear any older section drill-in and guide the camera to the seats
4345
+ * that remain relevant. Clearing the filter glides back to the full chart. */
4346
+ focusCategoryFilter(keys) {
4347
+ const renderer = this.renderer;
4348
+ if (!renderer) return;
4349
+ renderer.clearSectionFocus?.();
4350
+ this.opts.onSectionFocus?.(null);
4351
+ if (renderer.focusCategories) renderer.focusCategories(keys);
4352
+ else renderer.zoomToFit();
4353
+ }
4274
4354
  /** World-space rect currently visible + full chart bounds (F3 minimap frame). */
4275
4355
  getViewport() {
4276
4356
  const r = this.renderer;