@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.d.cts CHANGED
@@ -522,6 +522,9 @@ interface ISeatmapRenderer {
522
522
  /** Price-band filter (F4): dim free seats whose category is NOT in `keys`
523
523
  * (null clears). The widget resolves which categories fall in the band. */
524
524
  setCategoryFilter?(keys: string[] | null): void;
525
+ /** Smoothly frame the currently available seats in these categories. `null`
526
+ * returns to the full chart. Used after an explicit buyer price-filter action. */
527
+ focusCategories?(keys: string[] | null): void;
525
528
  /** Dim the seats of these section/zone ids (organizer manager: held-back inventory). */
526
529
  setDimmedSections?(ids: string[] | null): void;
527
530
  /**
@@ -587,7 +590,7 @@ interface ISeatmapRenderer {
587
590
  sectionMembers?(id: string): string[];
588
591
  /**
589
592
  * Smoothly glide (pan+zoom) the camera to frame a section (by id) or a world-
590
- * space bounds rect over ~450ms easeInOutCubic. `prefers-reduced-motion` snaps.
593
+ * space bounds rect over a calm easeInOutCubic glide. `prefers-reduced-motion` snaps.
591
594
  * A pointer-down (grab/pan) cancels an in-flight glide. Slice 5 "glide in".
592
595
  */
593
596
  focusRegion?(target: string | {
@@ -597,6 +600,8 @@ interface ISeatmapRenderer {
597
600
  height: number;
598
601
  }, opts?: {
599
602
  animate?: boolean;
603
+ minScale?: number;
604
+ durationMs?: number;
600
605
  }): void;
601
606
  /** Current LOD rung derived from zoom (for the ZONES/SECTIONS/SEATS pill). */
602
607
  getRung?(): LodRung;
@@ -970,6 +975,9 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
970
975
  * widget resolves which categories fall inside the buyer's chosen band.
971
976
  */
972
977
  setCategoryFilter(keys: string[] | null): void;
978
+ /** Frame the currently available inventory that survived a buyer price
979
+ * filter. Clearing the filter glides back to the full venue. */
980
+ focusCategories(keys: string[] | null): void;
973
981
  /**
974
982
  * Switch the projection between flat top-down and the isometric "3D" view
975
983
  * (rotate + y-squash about the chart centre, plus per-elevation lift). Tweens
@@ -1214,7 +1222,7 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1214
1222
  private zoomToBounds;
1215
1223
  /**
1216
1224
  * Smoothly glide the camera to frame a section (by id) or a world-space bounds
1217
- * rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
1225
+ * rect — the Slice 5 "glide in". Pan+zoom tween over a calm easeInOutCubic; the
1218
1226
  * melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
1219
1227
  * `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
1220
1228
  * newer glide cancels an in-flight one.
@@ -1227,6 +1235,7 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1227
1235
  }, opts?: {
1228
1236
  animate?: boolean;
1229
1237
  minScale?: number;
1238
+ durationMs?: number;
1230
1239
  }): void;
1231
1240
  /** Cancel an in-flight camera glide (a grab or a newer glide interrupts it). */
1232
1241
  private cancelGlide;
@@ -1614,6 +1623,10 @@ declare class PickerController {
1614
1623
  /** Price-band filter (F4): dim free seats whose category is outside `keys`
1615
1624
  * (null clears). The widget resolves which categories fall in the band. */
1616
1625
  setCategoryFilter(keys: string[] | null): void;
1626
+ /** An explicit buyer price-filter action should not only dim the rest of the
1627
+ * chart: clear any older section drill-in and guide the camera to the seats
1628
+ * that remain relevant. Clearing the filter glides back to the full chart. */
1629
+ focusCategoryFilter(keys: string[] | null): void;
1617
1630
  /** World-space rect currently visible + full chart bounds (F3 minimap frame). */
1618
1631
  getViewport(): {
1619
1632
  visible: {
package/dist/index.d.ts CHANGED
@@ -522,6 +522,9 @@ interface ISeatmapRenderer {
522
522
  /** Price-band filter (F4): dim free seats whose category is NOT in `keys`
523
523
  * (null clears). The widget resolves which categories fall in the band. */
524
524
  setCategoryFilter?(keys: string[] | null): void;
525
+ /** Smoothly frame the currently available seats in these categories. `null`
526
+ * returns to the full chart. Used after an explicit buyer price-filter action. */
527
+ focusCategories?(keys: string[] | null): void;
525
528
  /** Dim the seats of these section/zone ids (organizer manager: held-back inventory). */
526
529
  setDimmedSections?(ids: string[] | null): void;
527
530
  /**
@@ -587,7 +590,7 @@ interface ISeatmapRenderer {
587
590
  sectionMembers?(id: string): string[];
588
591
  /**
589
592
  * Smoothly glide (pan+zoom) the camera to frame a section (by id) or a world-
590
- * space bounds rect over ~450ms easeInOutCubic. `prefers-reduced-motion` snaps.
593
+ * space bounds rect over a calm easeInOutCubic glide. `prefers-reduced-motion` snaps.
591
594
  * A pointer-down (grab/pan) cancels an in-flight glide. Slice 5 "glide in".
592
595
  */
593
596
  focusRegion?(target: string | {
@@ -597,6 +600,8 @@ interface ISeatmapRenderer {
597
600
  height: number;
598
601
  }, opts?: {
599
602
  animate?: boolean;
603
+ minScale?: number;
604
+ durationMs?: number;
600
605
  }): void;
601
606
  /** Current LOD rung derived from zoom (for the ZONES/SECTIONS/SEATS pill). */
602
607
  getRung?(): LodRung;
@@ -970,6 +975,9 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
970
975
  * widget resolves which categories fall inside the buyer's chosen band.
971
976
  */
972
977
  setCategoryFilter(keys: string[] | null): void;
978
+ /** Frame the currently available inventory that survived a buyer price
979
+ * filter. Clearing the filter glides back to the full venue. */
980
+ focusCategories(keys: string[] | null): void;
973
981
  /**
974
982
  * Switch the projection between flat top-down and the isometric "3D" view
975
983
  * (rotate + y-squash about the chart centre, plus per-elevation lift). Tweens
@@ -1214,7 +1222,7 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1214
1222
  private zoomToBounds;
1215
1223
  /**
1216
1224
  * Smoothly glide the camera to frame a section (by id) or a world-space bounds
1217
- * rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
1225
+ * rect — the Slice 5 "glide in". Pan+zoom tween over a calm easeInOutCubic; the
1218
1226
  * melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
1219
1227
  * `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
1220
1228
  * newer glide cancels an in-flight one.
@@ -1227,6 +1235,7 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1227
1235
  }, opts?: {
1228
1236
  animate?: boolean;
1229
1237
  minScale?: number;
1238
+ durationMs?: number;
1230
1239
  }): void;
1231
1240
  /** Cancel an in-flight camera glide (a grab or a newer glide interrupts it). */
1232
1241
  private cancelGlide;
@@ -1614,6 +1623,10 @@ declare class PickerController {
1614
1623
  /** Price-band filter (F4): dim free seats whose category is outside `keys`
1615
1624
  * (null clears). The widget resolves which categories fall in the band. */
1616
1625
  setCategoryFilter(keys: string[] | null): void;
1626
+ /** An explicit buyer price-filter action should not only dim the rest of the
1627
+ * chart: clear any older section drill-in and guide the camera to the seats
1628
+ * that remain relevant. Clearing the filter glides back to the full chart. */
1629
+ focusCategoryFilter(keys: string[] | null): void;
1617
1630
  /** World-space rect currently visible + full chart bounds (F3 minimap frame). */
1618
1631
  getViewport(): {
1619
1632
  visible: {
package/dist/index.js CHANGED
@@ -644,6 +644,7 @@ var ISO_ANGLE_DEG = -11.5;
644
644
  var ISO_SQUASH = 0.58;
645
645
  var LIFT_PER_STEP = 58;
646
646
  var ISO_TWEEN_MS = 320;
647
+ var CAMERA_GLIDE_MS = 650;
647
648
  var BLOCK_FILL_ALPHA = 0.85;
648
649
  var SOLD_DARKEN = 0.5;
649
650
  var SECTION_LABEL_PX = 20;
@@ -1598,6 +1599,40 @@ var _SeatmapRenderer = class _SeatmapRenderer {
1598
1599
  this.seatLayer.batchDraw();
1599
1600
  }
1600
1601
  }
1602
+ /** Frame the currently available inventory that survived a buyer price
1603
+ * filter. Clearing the filter glides back to the full venue. */
1604
+ focusCategories(keys) {
1605
+ if (!keys?.length) {
1606
+ this.focusRegion(this.bounds, { durationMs: CAMERA_GLIDE_MS });
1607
+ return;
1608
+ }
1609
+ const wanted = new Set(keys);
1610
+ const matching = this.seats.filter((seat) => {
1611
+ if (!wanted.has(seat.categoryKey) || this.seatInClosedSection(seat.id)) return false;
1612
+ const status = this.statusById.get(seat.id) ?? "free";
1613
+ return status === "free" || this.ownedHold.has(seat.id);
1614
+ });
1615
+ if (!matching.length) return;
1616
+ let minX = Infinity;
1617
+ let maxX = -Infinity;
1618
+ let minY = Infinity;
1619
+ let maxY = -Infinity;
1620
+ for (const seat of matching) {
1621
+ minX = Math.min(minX, seat.x);
1622
+ maxX = Math.max(maxX, seat.x);
1623
+ minY = Math.min(minY, seat.y);
1624
+ maxY = Math.max(maxY, seat.y);
1625
+ }
1626
+ const pad = Math.max(24, this.seatR * 3);
1627
+ minX -= pad;
1628
+ maxX += pad;
1629
+ minY -= pad;
1630
+ maxY += pad;
1631
+ this.focusRegion(
1632
+ { x: minX, y: minY, width: Math.max(40, maxX - minX), height: Math.max(40, maxY - minY) },
1633
+ { durationMs: CAMERA_GLIDE_MS }
1634
+ );
1635
+ }
1601
1636
  // ---- isometric ("3D") view mode -------------------------------------------
1602
1637
  /**
1603
1638
  * Switch the projection between flat top-down and the isometric "3D" view
@@ -3124,7 +3159,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3124
3159
  }
3125
3160
  /**
3126
3161
  * Smoothly glide the camera to frame a section (by id) or a world-space bounds
3127
- * rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
3162
+ * rect — the Slice 5 "glide in". Pan+zoom tween over a calm easeInOutCubic; the
3128
3163
  * melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
3129
3164
  * `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
3130
3165
  * newer glide cancels an in-flight one.
@@ -3156,13 +3191,13 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3156
3191
  return;
3157
3192
  }
3158
3193
  const start = performance.now();
3159
- const DUR = 450;
3194
+ const duration = Math.max(180, Math.min(1200, opts?.durationMs ?? CAMERA_GLIDE_MS));
3160
3195
  const step = (now) => {
3161
3196
  if (this.destroyed) {
3162
3197
  this.glideRaf = 0;
3163
3198
  return;
3164
3199
  }
3165
- const raw = Math.min(1, (now - start) / DUR);
3200
+ const raw = Math.min(1, (now - start) / duration);
3166
3201
  const e = raw < 0.5 ? 4 * raw * raw * raw : 1 - Math.pow(-2 * raw + 2, 3) / 2;
3167
3202
  const sc = fromScale + (toScale - fromScale) * e;
3168
3203
  this.stage.scale({ x: sc, y: sc });
@@ -3287,15 +3322,49 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3287
3322
  if (seat.kind === "booth") continue;
3288
3323
  if (seat.x < x0 || seat.x > x1 || seat.y < y0 || seat.y > y1) continue;
3289
3324
  const status = this.statusById.get(seat.id) ?? "free";
3290
- const statusCue = status === "booked" ? "\xD7" : status === "held" && !this.ownedHold.has(seat.id) ? "H" : null;
3325
+ const unavailable = status === "booked" || status === "held" && !this.ownedHold.has(seat.id);
3326
+ if (unavailable) {
3327
+ const cue = new Group({ x: seat.x, y: seat.y, listening: false });
3328
+ if (status === "held") {
3329
+ cue.add(new Rect({
3330
+ x: -4.2,
3331
+ y: -0.7,
3332
+ width: 8.4,
3333
+ height: 6.5,
3334
+ cornerRadius: 1.3,
3335
+ stroke: "#ffffff",
3336
+ strokeWidth: 1.25,
3337
+ listening: false
3338
+ }));
3339
+ cue.add(new Line({
3340
+ 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],
3341
+ stroke: "#ffffff",
3342
+ strokeWidth: 1.2,
3343
+ lineCap: "round",
3344
+ lineJoin: "round",
3345
+ listening: false
3346
+ }));
3347
+ } else {
3348
+ cue.add(new Line({
3349
+ points: [-4.2, 4.2, 4.2, -4.2],
3350
+ stroke: "#ffffff",
3351
+ strokeWidth: 1.8,
3352
+ lineCap: "round",
3353
+ listening: false
3354
+ }));
3355
+ }
3356
+ this.labelGroup.add(cue);
3357
+ if (++count >= MAX_LABELS) break;
3358
+ continue;
3359
+ }
3291
3360
  const t2 = new Text({
3292
3361
  x: seat.x,
3293
3362
  y: seat.y,
3294
- text: statusCue ?? seat.label,
3295
- fontSize: statusCue ? status === "booked" ? 13 : 8 : 7,
3296
- fontStyle: statusCue ? "800" : "600",
3363
+ text: seat.label,
3364
+ fontSize: 7,
3365
+ fontStyle: "600",
3297
3366
  fontFamily: this.labelFont(),
3298
- fill: statusCue ? "#ffffff" : this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
3367
+ fill: this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
3299
3368
  listening: false,
3300
3369
  perfectDrawEnabled: false
3301
3370
  });
@@ -3952,6 +4021,17 @@ var PickerController = class {
3952
4021
  setCategoryFilter(keys) {
3953
4022
  this.renderer?.setCategoryFilter?.(keys);
3954
4023
  }
4024
+ /** An explicit buyer price-filter action should not only dim the rest of the
4025
+ * chart: clear any older section drill-in and guide the camera to the seats
4026
+ * that remain relevant. Clearing the filter glides back to the full chart. */
4027
+ focusCategoryFilter(keys) {
4028
+ const renderer = this.renderer;
4029
+ if (!renderer) return;
4030
+ renderer.clearSectionFocus?.();
4031
+ this.opts.onSectionFocus?.(null);
4032
+ if (renderer.focusCategories) renderer.focusCategories(keys);
4033
+ else renderer.zoomToFit();
4034
+ }
3955
4035
  /** World-space rect currently visible + full chart bounds (F3 minimap frame). */
3956
4036
  getViewport() {
3957
4037
  const r = this.renderer;