@seatlayer/core 0.17.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.cjs CHANGED
@@ -275,9 +275,12 @@ var init_fr = __esm({
275
275
  // src/index.ts
276
276
  var index_exports = {};
277
277
  __export(index_exports, {
278
+ ACCESSIBILITY_RING_COLOR: () => ACCESSIBILITY_RING_COLOR,
278
279
  ACCESSIBILITY_TYPES: () => ACCESSIBILITY_TYPES,
279
280
  CHART_STORAGE_KEY: () => CHART_STORAGE_KEY,
280
281
  DEFAULT_CURRENCY: () => DEFAULT_CURRENCY,
282
+ LABEL_STYLE_MAX_SIZE: () => LABEL_STYLE_MAX_SIZE,
283
+ LABEL_STYLE_MIN_SIZE: () => LABEL_STYLE_MIN_SIZE,
281
284
  MAX_EVENT_INVENTORY: () => MAX_EVENT_INVENTORY,
282
285
  MAX_GA_CAPACITY: () => MAX_GA_CAPACITY,
283
286
  PickerController: () => PickerController,
@@ -285,6 +288,7 @@ __export(index_exports, {
285
288
  SeatmapRenderer: () => SeatmapRenderer,
286
289
  UNGROUPED_ID: () => UNGROUPED_ID,
287
290
  accessibilityMeta: () => accessibilityMeta,
291
+ accessibilityRingColor: () => accessibilityRingColor,
288
292
  allObjects: () => allObjects,
289
293
  applyHidden: () => applyHidden,
290
294
  chartBounds: () => chartBounds,
@@ -339,6 +343,21 @@ var ACCESSIBILITY_LABEL = new Map(ACCESSIBILITY_TYPES.map((a) => [a.key, a]));
339
343
  function accessibilityMeta(key) {
340
344
  return ACCESSIBILITY_LABEL.get(key);
341
345
  }
346
+ var ACCESSIBILITY_RING_COLOR = {
347
+ wheelchair: "#3b82f6",
348
+ companion: "#8b5cf6",
349
+ "semi-ambulatory": "#0ea5e9",
350
+ hearing: "#14b8a6",
351
+ "sign-language": "#f59e0b",
352
+ "plus-size": "#ec4899",
353
+ "lift-armrest": "#22c55e"
354
+ };
355
+ function accessibilityRingColor(types) {
356
+ const primary = types?.[0];
357
+ return primary && ACCESSIBILITY_RING_COLOR[primary] || "#3b82f6";
358
+ }
359
+ var LABEL_STYLE_MIN_SIZE = 8;
360
+ var LABEL_STYLE_MAX_SIZE = 24;
342
361
  function layerOf(obj) {
343
362
  switch (obj.type) {
344
363
  case "row":
@@ -485,15 +504,22 @@ function expandRowSlots(row) {
485
504
  return rowSeatPositions(row).map((p, i) => {
486
505
  const o = ov.get(i);
487
506
  const accessibility = overrideAccessibility(o);
507
+ const inventoryLabel = o?.label ?? `${row.label}-${seatNumber(i)}`;
508
+ const displayPrefix = row.displayLabel ?? row.label;
509
+ const commercial = { ...row.commercial, ...o?.commercial };
488
510
  return {
489
511
  index: i,
490
512
  x: p.x + (o?.dx ?? 0),
491
513
  y: p.y + (o?.dy ?? 0),
492
- label: o?.label ?? `${row.label}-${seatNumber(i)}`,
514
+ label: inventoryLabel,
515
+ displayLabel: o?.displayLabel ?? `${displayPrefix}-${seatNumber(i)}`,
493
516
  categoryKey: o?.categoryKey ?? row.categoryKey,
494
517
  skipped: !!o?.skip,
495
518
  accessible: accessibility.length > 0,
496
- accessibility
519
+ accessibility,
520
+ commercial: Object.values(commercial).some((value) => value !== void 0 && value !== false && value !== "") ? commercial : void 0,
521
+ viewUrl: o?.viewFromSeatUrl ?? row.viewFromSeatUrl,
522
+ labelStyle: o?.labelStyle
497
523
  };
498
524
  });
499
525
  }
@@ -504,13 +530,16 @@ function expandRow(row) {
504
530
  seats.push({
505
531
  id: `${row.id}:${slot.index}`,
506
532
  label: slot.label,
533
+ displayLabel: slot.displayLabel === slot.label ? void 0 : slot.displayLabel,
507
534
  x: slot.x,
508
535
  y: slot.y,
509
536
  rowId: row.id,
510
537
  categoryKey: slot.categoryKey,
511
538
  accessible: slot.accessible || void 0,
512
539
  accessibility: slot.accessibility.length ? slot.accessibility : void 0,
513
- viewUrl: row.viewFromSeatUrl
540
+ commercial: slot.commercial,
541
+ viewUrl: slot.viewUrl,
542
+ labelStyle: slot.labelStyle
514
543
  });
515
544
  }
516
545
  return seats;
@@ -868,7 +897,7 @@ function computeSections(doc) {
868
897
  }
869
898
  nodes.set(logicalId, {
870
899
  id: logicalId,
871
- label: s.label || "Section",
900
+ label: s.displayLabel || s.label || "Section",
872
901
  zone: s.zone,
873
902
  seatCount: 0,
874
903
  objectIds: [],
@@ -1196,15 +1225,6 @@ var CLOSED_SEAT_OPACITY = 0.4;
1196
1225
  var FOCUS_DIM_OPACITY = 0.16;
1197
1226
  var FOCUS_BACKDROP_FILL = "rgba(244,246,248,0.06)";
1198
1227
  var CB_PALETTE = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"];
1199
- var ACCESS_RING = {
1200
- wheelchair: "#3b82f6",
1201
- companion: "#8b5cf6",
1202
- "semi-ambulatory": "#0ea5e9",
1203
- hearing: "#14b8a6",
1204
- "sign-language": "#f59e0b",
1205
- "plus-size": "#ec4899",
1206
- "lift-armrest": "#22c55e"
1207
- };
1208
1228
  function seatMatchesAccess(seat, filter) {
1209
1229
  if (filter.length === 0) return !!seat.accessible;
1210
1230
  return !!seat.accessibility?.some((t2) => filter.includes(t2));
@@ -2654,13 +2674,12 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2654
2674
  this.paintSeat(c, seat.id);
2655
2675
  target.add(c);
2656
2676
  if (seat.accessible) {
2657
- const primary = seat.accessibility?.[0];
2658
2677
  target.add(
2659
2678
  new import_Circle.Circle({
2660
2679
  x: seat.x,
2661
2680
  y: seat.y,
2662
2681
  radius: this.seatR + 1,
2663
- stroke: primary && ACCESS_RING[primary] || "#3b82f6",
2682
+ stroke: accessibilityRingColor(seat.accessibility),
2664
2683
  strokeWidth: 2,
2665
2684
  listening: false,
2666
2685
  perfectDrawEnabled: false,
@@ -2693,7 +2712,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2693
2712
  const t2 = new import_Text.Text({
2694
2713
  x: seat.x,
2695
2714
  y: seat.y,
2696
- text: seat.label,
2715
+ text: seat.displayLabel ?? seat.label,
2697
2716
  fontSize: BOOTH_LABEL_FONT_SIZE,
2698
2717
  fontStyle: "600",
2699
2718
  fontFamily: this.labelFont(),
@@ -2716,14 +2735,22 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2716
2735
  const idx = this.catOrder.indexOf(categoryKey);
2717
2736
  return CB_PALETTE[(idx >= 0 ? idx : 0) % CB_PALETTE.length];
2718
2737
  }
2719
- /** Authored free fills retain the chart's validated ink. Renderer-owned
2720
- * transient fills choose an ink against the paint that is actually visible. */
2721
- renderedBookableLabelInk(id, shape) {
2722
- const preferred = this.theme.seatLabelColor ?? DEF_SEAT_LABEL;
2723
- const status = this.statusById.get(id) ?? "free";
2724
- if (status === "free" && !this.selection.has(id)) return preferred;
2738
+ /** Per-seat authored label size relative to the neutral default (1 = default),
2739
+ * mirroring the section convention (labelStyle.size ?? 18) / 18. Only real
2740
+ * seats carry labelStyle, so booths and unstyled seats resolve to 1. */
2741
+ seatLabelScale(seat) {
2742
+ return (seat.labelStyle?.size ?? 18) / 18;
2743
+ }
2744
+ /** Preferred label ink for a seat: the authored per-seat colour when set,
2745
+ * otherwise today's theme default. Fed through auto-contrast by seatLabelInk. */
2746
+ seatPreferredLabelInk(seat) {
2747
+ return seat.labelStyle?.color ?? this.theme.seatLabelColor ?? DEF_SEAT_LABEL;
2748
+ }
2749
+ /** Resolve a seat's label ink against the paint actually visible, honouring an
2750
+ * authored per-seat colour through the same auto-contrast rule as sections. */
2751
+ seatLabelInk(seat, shape) {
2725
2752
  const fill = shape.fill();
2726
- return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", preferred);
2753
+ return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", this.seatPreferredLabelInk(seat));
2727
2754
  }
2728
2755
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
2729
2756
  paintSeat(c, id) {
@@ -2807,7 +2834,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2807
2834
  if (this.selectionFocusId && id !== this.selectionFocusId) c.opacity(Math.min(c.opacity(), 0.16));
2808
2835
  const bookableLabel = this.boothLabelById.get(id) ?? this.seatLabelById.get(id);
2809
2836
  if (bookableLabel) {
2810
- bookableLabel.fill(this.renderedBookableLabelInk(id, c));
2837
+ bookableLabel.fill(this.seatLabelInk(seat, c));
2811
2838
  bookableLabel.visible(
2812
2839
  isBookableLabelLegibleAtScale(bookableLabel.fontSize(), this.effScale()) && c.opacity() >= 0.5
2813
2840
  );
@@ -3348,14 +3375,19 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3348
3375
  listening: false
3349
3376
  }, obj.outlinePath);
3350
3377
  bgTarget.add(blockPoly);
3378
+ const labelStyle = obj.labelPresentation?.labelStyle;
3379
+ const preferredInk = labelStyle?.color ?? palette.sectionInk;
3380
+ const labelScale = (labelStyle?.size ?? 18) / 18;
3351
3381
  const nameLabel = new import_Text.Text({
3352
- x: centroid.x,
3353
- y: centroid.y,
3354
- text: obj.label,
3355
- fontSize: 22,
3382
+ x: obj.labelPresentation?.position?.x ?? centroid.x,
3383
+ y: obj.labelPresentation?.position?.y ?? centroid.y,
3384
+ text: obj.displayLabel ?? obj.label,
3385
+ rotation: obj.labelPresentation?.rotation ?? 0,
3386
+ visible: obj.labelPresentation?.visible !== false,
3387
+ fontSize: 22 * labelScale,
3356
3388
  fontStyle: "700",
3357
3389
  fontFamily: this.labelFont(),
3358
- fill: palette.sectionInk,
3390
+ fill: stateAwareBookableLabelInk(palette.sectionFill, preferredInk),
3359
3391
  listening: false,
3360
3392
  perfectDrawEnabled: false
3361
3393
  });
@@ -3379,7 +3411,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3379
3411
  const sec = {
3380
3412
  id: obj.id,
3381
3413
  logicalId: obj.logicalSectionId ?? obj.id,
3382
- label: obj.label,
3414
+ label: obj.displayLabel ?? obj.label,
3383
3415
  outline: obj.outline,
3384
3416
  ...obj.outlinePath ? { outlinePath: obj.outlinePath } : {},
3385
3417
  holes: obj.holes ?? [],
@@ -3395,6 +3427,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3395
3427
  blockPoly,
3396
3428
  nameLabel,
3397
3429
  subLabel,
3430
+ preferredInk,
3431
+ labelScale,
3398
3432
  nameLabelFits: true,
3399
3433
  subLabelFits: true,
3400
3434
  elevation,
@@ -3563,7 +3597,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3563
3597
  const sectionFill = sec.blockPoly.fill();
3564
3598
  const sectionInk = stateAwareBookableLabelInk(
3565
3599
  typeof sectionFill === "string" ? sectionFill : sec.baseFill,
3566
- palette.sectionInk
3600
+ sec.preferredInk
3567
3601
  );
3568
3602
  sec.nameLabel.fill(sectionInk);
3569
3603
  sec.subLabel.fill(sectionInk);
@@ -3659,7 +3693,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3659
3693
  fitSectionRungLabels(sec, sx) {
3660
3694
  const paddingPx = 8;
3661
3695
  sec.subLabelFits = false;
3662
- for (let fontPx = SECTION_LABEL_PX; fontPx >= MIN_SECTION_LABEL_PX; fontPx -= 1) {
3696
+ const maxPx = SECTION_LABEL_PX * sec.labelScale;
3697
+ const minPx = MIN_SECTION_LABEL_PX * sec.labelScale;
3698
+ for (let fontPx = maxPx; fontPx >= minPx; fontPx -= 1) {
3663
3699
  for (const rotation of [0, -90]) {
3664
3700
  sec.nameLabel.rotation(rotation);
3665
3701
  this.sizeLabel(sec.nameLabel, fontPx / sx, sec.nameLabel.y());
@@ -4178,7 +4214,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4178
4214
  const labels = this.seats.map((seat) => {
4179
4215
  const shape = this.circleById.get(seat.id);
4180
4216
  const label = this.boothLabelById.get(seat.id) ?? this.seatLabelById.get(seat.id);
4181
- const authoredFontSize = seat.kind === "booth" ? BOOTH_LABEL_FONT_SIZE : SEAT_LABEL_FONT_SIZE;
4217
+ const authoredFontSize = seat.kind === "booth" ? BOOTH_LABEL_FONT_SIZE : SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4182
4218
  const renderedFontPx = rounded((label?.fontSize() ?? authoredFontSize) * effectiveScale);
4183
4219
  const screen = this.worldToScreen(seat);
4184
4220
  const outside = screen.x < 0 || screen.x > viewport.width || screen.y < 0 || screen.y > viewport.height;
@@ -4495,9 +4531,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4495
4531
  minimumFont = Math.min(minimumFont, BOOTH_LABEL_FONT_SIZE);
4496
4532
  continue;
4497
4533
  }
4498
- measure.fontSize(SEAT_LABEL_FONT_SIZE);
4499
- measure.text(bookableMarkerLabel(seat.label));
4500
- const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, SEAT_LABEL_FONT_SIZE * maxWidth / measure.width()) : SEAT_LABEL_FONT_SIZE;
4534
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4535
+ measure.fontSize(authoredFontSize);
4536
+ measure.text(bookableMarkerLabel(seat.displayLabel ?? seat.label));
4537
+ const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, authoredFontSize * maxWidth / measure.width()) : authoredFontSize;
4501
4538
  minimumFont = Math.min(minimumFont, fitted);
4502
4539
  }
4503
4540
  measure.destroy();
@@ -4647,19 +4684,20 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4647
4684
  if (++count >= MAX_LABELS) break;
4648
4685
  continue;
4649
4686
  }
4687
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4650
4688
  const t2 = new import_Text.Text({
4651
4689
  x: seat.x,
4652
4690
  y: seat.y,
4653
- text: bookableMarkerLabel(seat.label),
4654
- fontSize: SEAT_LABEL_FONT_SIZE,
4691
+ text: bookableMarkerLabel(seat.displayLabel ?? seat.label),
4692
+ fontSize: authoredFontSize,
4655
4693
  fontStyle: "600",
4656
4694
  fontFamily: this.labelFont(),
4657
- fill: shape ? this.renderedBookableLabelInk(seat.id, shape) : this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
4695
+ fill: shape ? this.seatLabelInk(seat, shape) : this.seatPreferredLabelInk(seat),
4658
4696
  listening: false,
4659
4697
  perfectDrawEnabled: false
4660
4698
  });
4661
4699
  const maxW = this.seatR * 2 - 3;
4662
- if (t2.width() > maxW) t2.fontSize(Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, SEAT_LABEL_FONT_SIZE * maxW / t2.width()));
4700
+ if (t2.width() > maxW) t2.fontSize(Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, authoredFontSize * maxW / t2.width()));
4663
4701
  if (t2.width() > maxW + 0.01) {
4664
4702
  t2.destroy();
4665
4703
  continue;
@@ -5619,6 +5657,15 @@ var PickerController = class {
5619
5657
  } catch {
5620
5658
  }
5621
5659
  }
5660
+ /**
5661
+ * Re-pull the status snapshot on demand. Live surfaces rarely need this (the
5662
+ * socket keeps them fresh); local/mock transports with no socket call it
5663
+ * after they mutate state (e.g. the demo books a hold) so the renderer and
5664
+ * every onStatusChange consumer repaint from the new truth.
5665
+ */
5666
+ async refresh() {
5667
+ await this.resnapshot();
5668
+ }
5622
5669
  // ---- realtime socket ------------------------------------------------------
5623
5670
  /**
5624
5671
  * Buyer catch-up on tab regain. While a tab is hidden Chrome pauses rAF, so
@@ -5653,9 +5700,11 @@ var PickerController = class {
5653
5700
  }
5654
5701
  connect() {
5655
5702
  if (this.closed) return;
5703
+ const url = this.api.socketUrl(this.key);
5704
+ if (!url) return;
5656
5705
  let ws;
5657
5706
  try {
5658
- ws = new WebSocket(this.api.socketUrl(this.key));
5707
+ ws = new WebSocket(url);
5659
5708
  } catch {
5660
5709
  this.scheduleReconnect();
5661
5710
  return;
@@ -5946,9 +5995,12 @@ async function loadLocale(code) {
5946
5995
  }
5947
5996
  // Annotate the CommonJS export names for ESM import in node:
5948
5997
  0 && (module.exports = {
5998
+ ACCESSIBILITY_RING_COLOR,
5949
5999
  ACCESSIBILITY_TYPES,
5950
6000
  CHART_STORAGE_KEY,
5951
6001
  DEFAULT_CURRENCY,
6002
+ LABEL_STYLE_MAX_SIZE,
6003
+ LABEL_STYLE_MIN_SIZE,
5952
6004
  MAX_EVENT_INVENTORY,
5953
6005
  MAX_GA_CAPACITY,
5954
6006
  PickerController,
@@ -5956,6 +6008,7 @@ async function loadLocale(code) {
5956
6008
  SeatmapRenderer,
5957
6009
  UNGROUPED_ID,
5958
6010
  accessibilityMeta,
6011
+ accessibilityRingColor,
5959
6012
  allObjects,
5960
6013
  applyHidden,
5961
6014
  chartBounds,