@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.js CHANGED
@@ -12,6 +12,21 @@ var ACCESSIBILITY_LABEL = new Map(ACCESSIBILITY_TYPES.map((a) => [a.key, a]));
12
12
  function accessibilityMeta(key) {
13
13
  return ACCESSIBILITY_LABEL.get(key);
14
14
  }
15
+ var ACCESSIBILITY_RING_COLOR = {
16
+ wheelchair: "#3b82f6",
17
+ companion: "#8b5cf6",
18
+ "semi-ambulatory": "#0ea5e9",
19
+ hearing: "#14b8a6",
20
+ "sign-language": "#f59e0b",
21
+ "plus-size": "#ec4899",
22
+ "lift-armrest": "#22c55e"
23
+ };
24
+ function accessibilityRingColor(types) {
25
+ const primary = types?.[0];
26
+ return primary && ACCESSIBILITY_RING_COLOR[primary] || "#3b82f6";
27
+ }
28
+ var LABEL_STYLE_MIN_SIZE = 8;
29
+ var LABEL_STYLE_MAX_SIZE = 24;
15
30
  function layerOf(obj) {
16
31
  switch (obj.type) {
17
32
  case "row":
@@ -158,15 +173,22 @@ function expandRowSlots(row) {
158
173
  return rowSeatPositions(row).map((p, i) => {
159
174
  const o = ov.get(i);
160
175
  const accessibility = overrideAccessibility(o);
176
+ const inventoryLabel = o?.label ?? `${row.label}-${seatNumber(i)}`;
177
+ const displayPrefix = row.displayLabel ?? row.label;
178
+ const commercial = { ...row.commercial, ...o?.commercial };
161
179
  return {
162
180
  index: i,
163
181
  x: p.x + (o?.dx ?? 0),
164
182
  y: p.y + (o?.dy ?? 0),
165
- label: o?.label ?? `${row.label}-${seatNumber(i)}`,
183
+ label: inventoryLabel,
184
+ displayLabel: o?.displayLabel ?? `${displayPrefix}-${seatNumber(i)}`,
166
185
  categoryKey: o?.categoryKey ?? row.categoryKey,
167
186
  skipped: !!o?.skip,
168
187
  accessible: accessibility.length > 0,
169
- accessibility
188
+ accessibility,
189
+ commercial: Object.values(commercial).some((value) => value !== void 0 && value !== false && value !== "") ? commercial : void 0,
190
+ viewUrl: o?.viewFromSeatUrl ?? row.viewFromSeatUrl,
191
+ labelStyle: o?.labelStyle
170
192
  };
171
193
  });
172
194
  }
@@ -177,13 +199,16 @@ function expandRow(row) {
177
199
  seats.push({
178
200
  id: `${row.id}:${slot.index}`,
179
201
  label: slot.label,
202
+ displayLabel: slot.displayLabel === slot.label ? void 0 : slot.displayLabel,
180
203
  x: slot.x,
181
204
  y: slot.y,
182
205
  rowId: row.id,
183
206
  categoryKey: slot.categoryKey,
184
207
  accessible: slot.accessible || void 0,
185
208
  accessibility: slot.accessibility.length ? slot.accessibility : void 0,
186
- viewUrl: row.viewFromSeatUrl
209
+ commercial: slot.commercial,
210
+ viewUrl: slot.viewUrl,
211
+ labelStyle: slot.labelStyle
187
212
  });
188
213
  }
189
214
  return seats;
@@ -541,7 +566,7 @@ function computeSections(doc) {
541
566
  }
542
567
  nodes.set(logicalId, {
543
568
  id: logicalId,
544
- label: s.label || "Section",
569
+ label: s.displayLabel || s.label || "Section",
545
570
  zone: s.zone,
546
571
  seatCount: 0,
547
572
  objectIds: [],
@@ -869,15 +894,6 @@ var CLOSED_SEAT_OPACITY = 0.4;
869
894
  var FOCUS_DIM_OPACITY = 0.16;
870
895
  var FOCUS_BACKDROP_FILL = "rgba(244,246,248,0.06)";
871
896
  var CB_PALETTE = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"];
872
- var ACCESS_RING = {
873
- wheelchair: "#3b82f6",
874
- companion: "#8b5cf6",
875
- "semi-ambulatory": "#0ea5e9",
876
- hearing: "#14b8a6",
877
- "sign-language": "#f59e0b",
878
- "plus-size": "#ec4899",
879
- "lift-armrest": "#22c55e"
880
- };
881
897
  function seatMatchesAccess(seat, filter) {
882
898
  if (filter.length === 0) return !!seat.accessible;
883
899
  return !!seat.accessibility?.some((t2) => filter.includes(t2));
@@ -2327,13 +2343,12 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2327
2343
  this.paintSeat(c, seat.id);
2328
2344
  target.add(c);
2329
2345
  if (seat.accessible) {
2330
- const primary = seat.accessibility?.[0];
2331
2346
  target.add(
2332
2347
  new Circle({
2333
2348
  x: seat.x,
2334
2349
  y: seat.y,
2335
2350
  radius: this.seatR + 1,
2336
- stroke: primary && ACCESS_RING[primary] || "#3b82f6",
2351
+ stroke: accessibilityRingColor(seat.accessibility),
2337
2352
  strokeWidth: 2,
2338
2353
  listening: false,
2339
2354
  perfectDrawEnabled: false,
@@ -2366,7 +2381,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2366
2381
  const t2 = new Text({
2367
2382
  x: seat.x,
2368
2383
  y: seat.y,
2369
- text: seat.label,
2384
+ text: seat.displayLabel ?? seat.label,
2370
2385
  fontSize: BOOTH_LABEL_FONT_SIZE,
2371
2386
  fontStyle: "600",
2372
2387
  fontFamily: this.labelFont(),
@@ -2389,14 +2404,22 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2389
2404
  const idx = this.catOrder.indexOf(categoryKey);
2390
2405
  return CB_PALETTE[(idx >= 0 ? idx : 0) % CB_PALETTE.length];
2391
2406
  }
2392
- /** Authored free fills retain the chart's validated ink. Renderer-owned
2393
- * transient fills choose an ink against the paint that is actually visible. */
2394
- renderedBookableLabelInk(id, shape) {
2395
- const preferred = this.theme.seatLabelColor ?? DEF_SEAT_LABEL;
2396
- const status = this.statusById.get(id) ?? "free";
2397
- if (status === "free" && !this.selection.has(id)) return preferred;
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) {
2398
2421
  const fill = shape.fill();
2399
- return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", preferred);
2422
+ return stateAwareBookableLabelInk(typeof fill === "string" ? fill : "", this.seatPreferredLabelInk(seat));
2400
2423
  }
2401
2424
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
2402
2425
  paintSeat(c, id) {
@@ -2480,7 +2503,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
2480
2503
  if (this.selectionFocusId && id !== this.selectionFocusId) c.opacity(Math.min(c.opacity(), 0.16));
2481
2504
  const bookableLabel = this.boothLabelById.get(id) ?? this.seatLabelById.get(id);
2482
2505
  if (bookableLabel) {
2483
- bookableLabel.fill(this.renderedBookableLabelInk(id, c));
2506
+ bookableLabel.fill(this.seatLabelInk(seat, c));
2484
2507
  bookableLabel.visible(
2485
2508
  isBookableLabelLegibleAtScale(bookableLabel.fontSize(), this.effScale()) && c.opacity() >= 0.5
2486
2509
  );
@@ -3021,14 +3044,19 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3021
3044
  listening: false
3022
3045
  }, obj.outlinePath);
3023
3046
  bgTarget.add(blockPoly);
3047
+ const labelStyle = obj.labelPresentation?.labelStyle;
3048
+ const preferredInk = labelStyle?.color ?? palette.sectionInk;
3049
+ const labelScale = (labelStyle?.size ?? 18) / 18;
3024
3050
  const nameLabel = new Text({
3025
- x: centroid.x,
3026
- y: centroid.y,
3027
- text: obj.label,
3028
- fontSize: 22,
3051
+ x: obj.labelPresentation?.position?.x ?? centroid.x,
3052
+ y: obj.labelPresentation?.position?.y ?? centroid.y,
3053
+ text: obj.displayLabel ?? obj.label,
3054
+ rotation: obj.labelPresentation?.rotation ?? 0,
3055
+ visible: obj.labelPresentation?.visible !== false,
3056
+ fontSize: 22 * labelScale,
3029
3057
  fontStyle: "700",
3030
3058
  fontFamily: this.labelFont(),
3031
- fill: palette.sectionInk,
3059
+ fill: stateAwareBookableLabelInk(palette.sectionFill, preferredInk),
3032
3060
  listening: false,
3033
3061
  perfectDrawEnabled: false
3034
3062
  });
@@ -3052,7 +3080,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3052
3080
  const sec = {
3053
3081
  id: obj.id,
3054
3082
  logicalId: obj.logicalSectionId ?? obj.id,
3055
- label: obj.label,
3083
+ label: obj.displayLabel ?? obj.label,
3056
3084
  outline: obj.outline,
3057
3085
  ...obj.outlinePath ? { outlinePath: obj.outlinePath } : {},
3058
3086
  holes: obj.holes ?? [],
@@ -3068,6 +3096,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3068
3096
  blockPoly,
3069
3097
  nameLabel,
3070
3098
  subLabel,
3099
+ preferredInk,
3100
+ labelScale,
3071
3101
  nameLabelFits: true,
3072
3102
  subLabelFits: true,
3073
3103
  elevation,
@@ -3236,7 +3266,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3236
3266
  const sectionFill = sec.blockPoly.fill();
3237
3267
  const sectionInk = stateAwareBookableLabelInk(
3238
3268
  typeof sectionFill === "string" ? sectionFill : sec.baseFill,
3239
- palette.sectionInk
3269
+ sec.preferredInk
3240
3270
  );
3241
3271
  sec.nameLabel.fill(sectionInk);
3242
3272
  sec.subLabel.fill(sectionInk);
@@ -3332,7 +3362,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3332
3362
  fitSectionRungLabels(sec, sx) {
3333
3363
  const paddingPx = 8;
3334
3364
  sec.subLabelFits = false;
3335
- for (let fontPx = SECTION_LABEL_PX; fontPx >= MIN_SECTION_LABEL_PX; fontPx -= 1) {
3365
+ const maxPx = SECTION_LABEL_PX * sec.labelScale;
3366
+ const minPx = MIN_SECTION_LABEL_PX * sec.labelScale;
3367
+ for (let fontPx = maxPx; fontPx >= minPx; fontPx -= 1) {
3336
3368
  for (const rotation of [0, -90]) {
3337
3369
  sec.nameLabel.rotation(rotation);
3338
3370
  this.sizeLabel(sec.nameLabel, fontPx / sx, sec.nameLabel.y());
@@ -3851,7 +3883,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
3851
3883
  const labels = this.seats.map((seat) => {
3852
3884
  const shape = this.circleById.get(seat.id);
3853
3885
  const label = this.boothLabelById.get(seat.id) ?? this.seatLabelById.get(seat.id);
3854
- 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);
3855
3887
  const renderedFontPx = rounded((label?.fontSize() ?? authoredFontSize) * effectiveScale);
3856
3888
  const screen = this.worldToScreen(seat);
3857
3889
  const outside = screen.x < 0 || screen.x > viewport.width || screen.y < 0 || screen.y > viewport.height;
@@ -4168,9 +4200,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4168
4200
  minimumFont = Math.min(minimumFont, BOOTH_LABEL_FONT_SIZE);
4169
4201
  continue;
4170
4202
  }
4171
- measure.fontSize(SEAT_LABEL_FONT_SIZE);
4172
- measure.text(bookableMarkerLabel(seat.label));
4173
- const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, SEAT_LABEL_FONT_SIZE * maxWidth / measure.width()) : SEAT_LABEL_FONT_SIZE;
4203
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4204
+ measure.fontSize(authoredFontSize);
4205
+ measure.text(bookableMarkerLabel(seat.displayLabel ?? seat.label));
4206
+ const fitted = measure.width() > maxWidth ? Math.max(MIN_FITTED_SEAT_LABEL_FONT_SIZE, authoredFontSize * maxWidth / measure.width()) : authoredFontSize;
4174
4207
  minimumFont = Math.min(minimumFont, fitted);
4175
4208
  }
4176
4209
  measure.destroy();
@@ -4320,19 +4353,20 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4320
4353
  if (++count >= MAX_LABELS) break;
4321
4354
  continue;
4322
4355
  }
4356
+ const authoredFontSize = SEAT_LABEL_FONT_SIZE * this.seatLabelScale(seat);
4323
4357
  const t2 = new Text({
4324
4358
  x: seat.x,
4325
4359
  y: seat.y,
4326
- text: bookableMarkerLabel(seat.label),
4327
- fontSize: SEAT_LABEL_FONT_SIZE,
4360
+ text: bookableMarkerLabel(seat.displayLabel ?? seat.label),
4361
+ fontSize: authoredFontSize,
4328
4362
  fontStyle: "600",
4329
4363
  fontFamily: this.labelFont(),
4330
- fill: shape ? this.renderedBookableLabelInk(seat.id, shape) : this.theme.seatLabelColor ?? DEF_SEAT_LABEL,
4364
+ fill: shape ? this.seatLabelInk(seat, shape) : this.seatPreferredLabelInk(seat),
4331
4365
  listening: false,
4332
4366
  perfectDrawEnabled: false
4333
4367
  });
4334
4368
  const maxW = this.seatR * 2 - 3;
4335
- 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()));
4336
4370
  if (t2.width() > maxW + 0.01) {
4337
4371
  t2.destroy();
4338
4372
  continue;
@@ -5292,6 +5326,15 @@ var PickerController = class {
5292
5326
  } catch {
5293
5327
  }
5294
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
+ }
5295
5338
  // ---- realtime socket ------------------------------------------------------
5296
5339
  /**
5297
5340
  * Buyer catch-up on tab regain. While a tab is hidden Chrome pauses rAF, so
@@ -5326,9 +5369,11 @@ var PickerController = class {
5326
5369
  }
5327
5370
  connect() {
5328
5371
  if (this.closed) return;
5372
+ const url = this.api.socketUrl(this.key);
5373
+ if (!url) return;
5329
5374
  let ws;
5330
5375
  try {
5331
- ws = new WebSocket(this.api.socketUrl(this.key));
5376
+ ws = new WebSocket(url);
5332
5377
  } catch {
5333
5378
  this.scheduleReconnect();
5334
5379
  return;
@@ -5618,9 +5663,12 @@ async function loadLocale(code) {
5618
5663
  }
5619
5664
  }
5620
5665
  export {
5666
+ ACCESSIBILITY_RING_COLOR,
5621
5667
  ACCESSIBILITY_TYPES,
5622
5668
  CHART_STORAGE_KEY,
5623
5669
  DEFAULT_CURRENCY,
5670
+ LABEL_STYLE_MAX_SIZE,
5671
+ LABEL_STYLE_MIN_SIZE,
5624
5672
  MAX_EVENT_INVENTORY,
5625
5673
  MAX_GA_CAPACITY,
5626
5674
  PickerController,
@@ -5628,6 +5676,7 @@ export {
5628
5676
  SeatmapRenderer,
5629
5677
  UNGROUPED_ID,
5630
5678
  accessibilityMeta,
5679
+ accessibilityRingColor,
5631
5680
  allObjects,
5632
5681
  applyHidden,
5633
5682
  chartBounds,