@seatlayer/core 0.18.0 → 0.18.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.d.cts CHANGED
@@ -139,6 +139,10 @@ interface SeatOverride {
139
139
  commercial?: SeatCommercialAttributes;
140
140
  /** Seat-specific view photo; falls back to the row photo. */
141
141
  viewFromSeatUrl?: string;
142
+ /** Per-seat label size/color override; falls back to the row/theme default.
143
+ * Size is clamped to LABEL_STYLE_MIN_SIZE..MAX_SIZE; color is passed through
144
+ * the shared auto-contrast rule at paint time (see {@link LabelStyle}). */
145
+ labelStyle?: LabelStyle;
142
146
  }
143
147
  interface SeatCommercialAttributes {
144
148
  restrictedView?: boolean;
@@ -640,6 +644,8 @@ interface ExpandedSeat {
640
644
  commercial?: SeatCommercialAttributes;
641
645
  /** Organizer-supplied view-from-seat image (inherited from the row). */
642
646
  viewUrl?: string;
647
+ /** Per-seat label size/color override; absent = inherit the row/theme default. */
648
+ labelStyle?: LabelStyle;
643
649
  }
644
650
  type SeatStatus = 'free' | 'held' | 'booked' | 'not_for_sale';
645
651
  interface RendererCallbacks {
@@ -1063,6 +1069,7 @@ interface RowSeatSlot {
1063
1069
  accessibility: AccessibilityType[];
1064
1070
  commercial?: RowObject['commercial'];
1065
1071
  viewUrl?: string;
1072
+ labelStyle?: LabelStyle;
1066
1073
  }
1067
1074
  declare function expandRowSlots(row: RowObject): RowSeatSlot[];
1068
1075
  declare function expandRow(row: RowObject): ExpandedSeat[];
@@ -1489,11 +1496,16 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1489
1496
  private renderBoothUnit;
1490
1497
  /** The category's display color — Okabe-Ito hue when colorblind-safe is on. */
1491
1498
  private seatBaseColor;
1492
- /** Resolve label ink against the paint that is actually visible. The theme
1493
- * ink remains preferred, but mixed category palettes cannot always share one
1494
- * accessible text colour, so every free and transient state gets the same
1495
- * deterministic dark/light fallback used by Designer. */
1496
- private renderedBookableLabelInk;
1499
+ /** Per-seat authored label size relative to the neutral default (1 = default),
1500
+ * mirroring the section convention (labelStyle.size ?? 18) / 18. Only real
1501
+ * seats carry labelStyle, so booths and unstyled seats resolve to 1. */
1502
+ private seatLabelScale;
1503
+ /** Preferred label ink for a seat: the authored per-seat colour when set,
1504
+ * otherwise today's theme default. Fed through auto-contrast by seatLabelInk. */
1505
+ private seatPreferredLabelInk;
1506
+ /** Resolve a seat's label ink against the paint actually visible, honouring an
1507
+ * authored per-seat colour through the same auto-contrast rule as sections. */
1508
+ private seatLabelInk;
1497
1509
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
1498
1510
  private paintSeat;
1499
1511
  /** True when a seat sits in a section/zone currently marked `closed`. */
@@ -2170,6 +2182,13 @@ declare class PickerController {
2170
2182
  private applySeatsMap;
2171
2183
  private clearBookedHoldIfSettled;
2172
2184
  private resnapshot;
2185
+ /**
2186
+ * Re-pull the status snapshot on demand. Live surfaces rarely need this (the
2187
+ * socket keeps them fresh); local/mock transports with no socket call it
2188
+ * after they mutate state (e.g. the demo books a hold) so the renderer and
2189
+ * every onStatusChange consumer repaint from the new truth.
2190
+ */
2191
+ refresh(): Promise<void>;
2173
2192
  /**
2174
2193
  * Buyer catch-up on tab regain. While a tab is hidden Chrome pauses rAF, so
2175
2194
  * seat-status deltas that arrived over the WS mutated the scene graph but the
package/dist/index.d.ts CHANGED
@@ -139,6 +139,10 @@ interface SeatOverride {
139
139
  commercial?: SeatCommercialAttributes;
140
140
  /** Seat-specific view photo; falls back to the row photo. */
141
141
  viewFromSeatUrl?: string;
142
+ /** Per-seat label size/color override; falls back to the row/theme default.
143
+ * Size is clamped to LABEL_STYLE_MIN_SIZE..MAX_SIZE; color is passed through
144
+ * the shared auto-contrast rule at paint time (see {@link LabelStyle}). */
145
+ labelStyle?: LabelStyle;
142
146
  }
143
147
  interface SeatCommercialAttributes {
144
148
  restrictedView?: boolean;
@@ -640,6 +644,8 @@ interface ExpandedSeat {
640
644
  commercial?: SeatCommercialAttributes;
641
645
  /** Organizer-supplied view-from-seat image (inherited from the row). */
642
646
  viewUrl?: string;
647
+ /** Per-seat label size/color override; absent = inherit the row/theme default. */
648
+ labelStyle?: LabelStyle;
643
649
  }
644
650
  type SeatStatus = 'free' | 'held' | 'booked' | 'not_for_sale';
645
651
  interface RendererCallbacks {
@@ -1063,6 +1069,7 @@ interface RowSeatSlot {
1063
1069
  accessibility: AccessibilityType[];
1064
1070
  commercial?: RowObject['commercial'];
1065
1071
  viewUrl?: string;
1072
+ labelStyle?: LabelStyle;
1066
1073
  }
1067
1074
  declare function expandRowSlots(row: RowObject): RowSeatSlot[];
1068
1075
  declare function expandRow(row: RowObject): ExpandedSeat[];
@@ -1489,11 +1496,16 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
1489
1496
  private renderBoothUnit;
1490
1497
  /** The category's display color — Okabe-Ito hue when colorblind-safe is on. */
1491
1498
  private seatBaseColor;
1492
- /** Resolve label ink against the paint that is actually visible. The theme
1493
- * ink remains preferred, but mixed category palettes cannot always share one
1494
- * accessible text colour, so every free and transient state gets the same
1495
- * deterministic dark/light fallback used by Designer. */
1496
- private renderedBookableLabelInk;
1499
+ /** Per-seat authored label size relative to the neutral default (1 = default),
1500
+ * mirroring the section convention (labelStyle.size ?? 18) / 18. Only real
1501
+ * seats carry labelStyle, so booths and unstyled seats resolve to 1. */
1502
+ private seatLabelScale;
1503
+ /** Preferred label ink for a seat: the authored per-seat colour when set,
1504
+ * otherwise today's theme default. Fed through auto-contrast by seatLabelInk. */
1505
+ private seatPreferredLabelInk;
1506
+ /** Resolve a seat's label ink against the paint actually visible, honouring an
1507
+ * authored per-seat colour through the same auto-contrast rule as sections. */
1508
+ private seatLabelInk;
1497
1509
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
1498
1510
  private paintSeat;
1499
1511
  /** True when a seat sits in a section/zone currently marked `closed`. */
@@ -2170,6 +2182,13 @@ declare class PickerController {
2170
2182
  private applySeatsMap;
2171
2183
  private clearBookedHoldIfSettled;
2172
2184
  private resnapshot;
2185
+ /**
2186
+ * Re-pull the status snapshot on demand. Live surfaces rarely need this (the
2187
+ * socket keeps them fresh); local/mock transports with no socket call it
2188
+ * after they mutate state (e.g. the demo books a hold) so the renderer and
2189
+ * every onStatusChange consumer repaint from the new truth.
2190
+ */
2191
+ refresh(): Promise<void>;
2173
2192
  /**
2174
2193
  * Buyer catch-up on tab regain. While a tab is hidden Chrome pauses rAF, so
2175
2194
  * seat-status deltas that arrived over the WS mutated the scene graph but the
package/dist/index.js CHANGED
@@ -187,7 +187,8 @@ function expandRowSlots(row) {
187
187
  accessible: accessibility.length > 0,
188
188
  accessibility,
189
189
  commercial: Object.values(commercial).some((value) => value !== void 0 && value !== false && value !== "") ? commercial : void 0,
190
- viewUrl: o?.viewFromSeatUrl ?? row.viewFromSeatUrl
190
+ viewUrl: o?.viewFromSeatUrl ?? row.viewFromSeatUrl,
191
+ labelStyle: o?.labelStyle
191
192
  };
192
193
  });
193
194
  }
@@ -206,7 +207,8 @@ function expandRow(row) {
206
207
  accessible: slot.accessible || void 0,
207
208
  accessibility: slot.accessibility.length ? slot.accessibility : void 0,
208
209
  commercial: slot.commercial,
209
- viewUrl: slot.viewUrl
210
+ viewUrl: slot.viewUrl,
211
+ labelStyle: slot.labelStyle
210
212
  });
211
213
  }
212
214
  return seats;
@@ -2402,14 +2404,22 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2402
2404
  const idx = this.catOrder.indexOf(categoryKey);
2403
2405
  return CB_PALETTE[(idx >= 0 ? idx : 0) % CB_PALETTE.length];
2404
2406
  }
2405
- /** Resolve label ink against the paint that is actually visible. The theme
2406
- * ink remains preferred, but mixed category palettes cannot always share one
2407
- * accessible text colour, so every free and transient state gets the same
2408
- * deterministic dark/light fallback used by Designer. */
2409
- renderedBookableLabelInk(shape) {
2410
- const preferred = this.theme.seatLabelColor ?? DEF_SEAT_LABEL;
2407
+ /** Per-seat authored label size relative to the neutral default (1 = default),
2408
+ * mirroring the section convention (labelStyle.size ?? 18) / 18. Only real
2409
+ * seats carry labelStyle, so booths and unstyled seats resolve to 1. */
2410
+ seatLabelScale(seat) {
2411
+ return (seat.labelStyle?.size ?? 18) / 18;
2412
+ }
2413
+ /** Preferred label ink for a seat: the authored per-seat colour when set,
2414
+ * otherwise today's theme default. Fed through auto-contrast by seatLabelInk. */
2415
+ seatPreferredLabelInk(seat) {
2416
+ return seat.labelStyle?.color ?? this.theme.seatLabelColor ?? DEF_SEAT_LABEL;
2417
+ }
2418
+ /** Resolve a seat's label ink against the paint actually visible, honouring an
2419
+ * authored per-seat colour through the same auto-contrast rule as sections. */
2420
+ seatLabelInk(seat, shape) {
2411
2421
  const fill = shape.fill();
2412
- return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", preferred);
2422
+ return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", this.seatPreferredLabelInk(seat));
2413
2423
  }
2414
2424
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
2415
2425
  paintSeat(c, id) {
@@ -2493,7 +2503,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2493
2503
  if (this.selectionFocusId && id !== this.selectionFocusId) c.opacity(Math.min(c.opacity(), 0.16));
2494
2504
  const bookableLabel = this.boothLabelById.get(id) ?? this.seatLabelById.get(id);
2495
2505
  if (bookableLabel) {
2496
- bookableLabel.fill(this.renderedBookableLabelInk(c));
2506
+ bookableLabel.fill(this.seatLabelInk(seat, c));
2497
2507
  bookableLabel.visible(
2498
2508
  isBookableLabelLegibleAtScale(bookableLabel.fontSize(), this.effScale()) && c.opacity() >= 0.5
2499
2509
  );
@@ -3873,7 +3883,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3873
3883
  const labels = this.seats.map((seat) => {
3874
3884
  const shape = this.circleById.get(seat.id);
3875
3885
  const label = this.boothLabelById.get(seat.id) ?? this.seatLabelById.get(seat.id);
3876
- const authoredFontSize = seat.kind === "booth" ? BOOTH_LABEL_FONT_SIZE : SEAT_LABEL_FONT_SIZE;
3886
+ const authoredFontSize = seat.kind === "booth" ? BOOTH_LABEL_FONT_SIZE : SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
3877
3887
  const renderedFontPx = rounded((label?.fontSize() ?? authoredFontSize) * effectiveScale);
3878
3888
  const screen = this.worldToScreen(seat);
3879
3889
  const outside = screen.x < 0 || screen.x > viewport.width || screen.y < 0 || screen.y > viewport.height;
@@ -4190,9 +4200,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4190
4200
  minimumFont = Math.min(minimumFont, BOOTH_LABEL_FONT_SIZE);
4191
4201
  continue;
4192
4202
  }
4193
- measure.fontSize(SEAT_LABEL_FONT_SIZE);
4203
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4204
+ measure.fontSize(authoredFontSize);
4194
4205
  measure.text(bookableMarkerLabel(seat.displayLabel ?? seat.label));
4195
- const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, SEAT_LABEL_FONT_SIZE * maxWidth / measure.width()) : SEAT_LABEL_FONT_SIZE;
4206
+ const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, authoredFontSize * maxWidth / measure.width()) : authoredFontSize;
4196
4207
  minimumFont = Math.min(minimumFont, fitted);
4197
4208
  }
4198
4209
  measure.destroy();
@@ -4342,19 +4353,20 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4342
4353
  if (++count >= MAX_LABELS) break;
4343
4354
  continue;
4344
4355
  }
4356
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4345
4357
  const t2 = new Text({
4346
4358
  x: seat.x,
4347
4359
  y: seat.y,
4348
4360
  text: bookableMarkerLabel(seat.displayLabel ?? seat.label),
4349
- fontSize: SEAT_LABEL_FONT_SIZE,
4361
+ fontSize: authoredFontSize,
4350
4362
  fontStyle: "600",
4351
4363
  fontFamily: this.labelFont(),
4352
- fill: shape ? this.renderedBookableLabelInk(shape) : this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
4364
+ fill: shape ? this.seatLabelInk(seat, shape) : this.seatPreferredLabelInk(seat),
4353
4365
  listening: false,
4354
4366
  perfectDrawEnabled: false
4355
4367
  });
4356
4368
  const maxW = this.seatR * 2 - 3;
4357
- if (t2.width() > maxW) t2.fontSize(Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, SEAT_LABEL_FONT_SIZE * maxW / t2.width()));
4369
+ if (t2.width() > maxW) t2.fontSize(Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, authoredFontSize * maxW / t2.width()));
4358
4370
  if (t2.width() > maxW + 0.01) {
4359
4371
  t2.destroy();
4360
4372
  continue;
@@ -5314,6 +5326,15 @@ var PickerController = class {
5314
5326
  } catch {
5315
5327
  }
5316
5328
  }
5329
+ /**
5330
+ * Re-pull the status snapshot on demand. Live surfaces rarely need this (the
5331
+ * socket keeps them fresh); local/mock transports with no socket call it
5332
+ * after they mutate state (e.g. the demo books a hold) so the renderer and
5333
+ * every onStatusChange consumer repaint from the new truth.
5334
+ */
5335
+ async refresh() {
5336
+ await this.resnapshot();
5337
+ }
5317
5338
  // ---- realtime socket ------------------------------------------------------
5318
5339
  /**
5319
5340
  * Buyer catch-up on tab regain. While a tab is hidden Chrome pauses rAF, so
@@ -5348,9 +5369,11 @@ var PickerController = class {
5348
5369
  }
5349
5370
  connect() {
5350
5371
  if (this.closed) return;
5372
+ const url = this.api.socketUrl(this.key);
5373
+ if (!url) return;
5351
5374
  let ws;
5352
5375
  try {
5353
- ws = new WebSocket(this.api.socketUrl(this.key));
5376
+ ws = new WebSocket(url);
5354
5377
  } catch {
5355
5378
  this.scheduleReconnect();
5356
5379
  return;