@seatlayer/core 0.27.0 → 0.28.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
@@ -1313,7 +1313,30 @@ function isBookableLabelLegibleAtScale(fontSize, effectiveScale) {
1313
1313
  return fontSize * effectiveScale >= MIN_VISIBLE_BOOKABLE_LABEL_PX;
1314
1314
  }
1315
1315
  var ACCESS_GLYPH_VIEWBOX = 24;
1316
- var ACCESS_GLYPH_PATH = "M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z";
1316
+ var ACCESS_TYPE_GLYPH_PATHS = {
1317
+ // ISO-style wheelchair: seated figure (head + L-shaped body) over a ring wheel.
1318
+ wheelchair: "M7.8 4.5a2.2 2.2 0 1 0 4.4 0a2.2 2.2 0 1 0 -4.4 0zM8 7L10.5 7L10.5 15L8 15ZM8 12.5L18 12.5L18 15L8 15ZM16 15L18 15L18 18L16 18ZM5.5 16a5.5 5.5 0 1 0 11 0a5.5 5.5 0 1 0 -11 0zM8 16a3 3 0 1 1 6 0a3 3 0 1 1 -6 0z",
1319
+ // Companion: two overlapping head-and-shoulders figures (seat + a guest).
1320
+ companion: "M5.5 6.5a3 3 0 1 0 6 0a3 3 0 1 0 -6 0zM4.5 18.5L6 11.5L11 11.5L12.5 18.5ZM12.5 7.5a3 3 0 1 0 6 0a3 3 0 1 0 -6 0zM11.5 19L13 12.5L18 12.5L19.5 19Z",
1321
+ // Semi-ambulatory: walking figure leaning on a cane at its right side.
1322
+ "semi-ambulatory": "M8.3 3.8a2.2 2.2 0 1 0 4.4 0a2.2 2.2 0 1 0 -4.4 0zM9 6.2L12 6.6L11 13.5L8.6 13L9 6.2ZM8.6 13L11 13L9.5 21L7 21ZM10.4 13L12.6 13L13.6 21L11.2 21ZM11.2 7.2L14 8L14.6 9.6L11.9 8.9ZM14.4 6.8L15.9 6.8L16.4 21L14.8 21Z",
1323
+ // Hearing: a bold ear silhouette (assistive listening).
1324
+ hearing: "M15.5 3.5C9 2.2 5.5 6 5.5 11.5C5.5 16 8 19.5 10.5 21C13.2 22.6 16.4 20.8 15.4 18C14.8 16.4 12.9 16.1 13 14C13.1 11.9 15.8 11.4 15.8 8.5C15.8 5.4 13 4 15.5 3.5Z",
1325
+ // CART captions: a rounded caption bubble with two bold caption bars cut out.
1326
+ cart: "M5.5 6L18.5 6a2.5 2.5 0 0 1 2.5 2.5L21 15.5a2.5 2.5 0 0 1 -2.5 2.5L11 18L7 20.5L7.5 18L5.5 18a2.5 2.5 0 0 1 -2.5 -2.5L3 8.5a2.5 2.5 0 0 1 2.5 -2.5zM6.5 11L17.5 11L17.5 12.6L6.5 12.6zM6.5 14L17.5 14L17.5 15.6L6.5 15.6z",
1327
+ // Sign language: a raised open hand (palm with four fingers and a thumb).
1328
+ "sign-language": "M7 11L17 11L17 18a2 2 0 0 1 -2 2L9 20a2 2 0 0 1 -2 -2zM7.6 5L9.4 5L9.4 12L7.6 12zM9.9 3.5L11.7 3.5L11.7 12L9.9 12zM12.3 3.5L14.1 3.5L14.1 12L12.3 12zM14.6 5L16.4 5L16.4 12L14.6 12zM7.2 12.5L5 10.2L6.4 8.9L8.6 11.2Z",
1329
+ // Plus-size: a broad-shouldered wide figure.
1330
+ "plus-size": "M9.4 4.3a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0zM5 20L4 13C4 10.5 7 9.5 12 9.5C17 9.5 20 10.5 20 13L19 20Z",
1331
+ // Lift-up armrest: a chair side-profile with an up arrow over the armrest.
1332
+ "lift-armrest": "M5 6L8 6L8 19L5 19zM5 16L18 16L18 19L5 19zM8 12L17 12L17 14.5L8 14.5zM14 7L16 7L16 12L14 12zM12 7L18 7L15 3Z"
1333
+ };
1334
+ var ACCESS_GLYPH_PATH = ACCESS_TYPE_GLYPH_PATHS.wheelchair;
1335
+ function accessGlyphPath(accessibility) {
1336
+ if (!accessibility?.length) return null;
1337
+ const primary = accessibility.includes("wheelchair") ? "wheelchair" : accessibility[0];
1338
+ return ACCESS_TYPE_GLYPH_PATHS[primary] ?? null;
1339
+ }
1317
1340
  function bookableMarkerLabel(publicLabel) {
1318
1341
  return /-(\d{1,5})$/.exec(publicLabel)?.[1] ?? publicLabel;
1319
1342
  }
@@ -1880,6 +1903,12 @@ var VENUE_ICONS = [
1880
1903
  group: "facilities",
1881
1904
  path: "M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14 M16 16l5 5 M9 8.5a2 2 0 1 1 3 1.7c-.8.5-1 .8-1 1.8 M11 15h.01"
1882
1905
  },
1906
+ {
1907
+ key: "restrooms",
1908
+ label: "Restrooms",
1909
+ group: "facilities",
1910
+ path: "M6 3h12a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z M12 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4 M9 18v-3.5a3 3 0 0 1 6 0V18"
1911
+ },
1883
1912
  {
1884
1913
  key: "charging",
1885
1914
  label: "Charging point",
@@ -1929,6 +1958,18 @@ var VENUE_ICONS = [
1929
1958
  group: "food-drink",
1930
1959
  path: "M6 8h12l-1 12H7L6 8Z M9 8V6a3 3 0 0 1 6 0v2"
1931
1960
  },
1961
+ {
1962
+ key: "screen",
1963
+ label: "Screen",
1964
+ group: "facilities",
1965
+ path: "M3 4h18v12H3V4Z M9 20h6 M12 16v4 M10 8l4 2-4 2V8Z"
1966
+ },
1967
+ {
1968
+ key: "sound-booth",
1969
+ label: "Sound booth",
1970
+ group: "facilities",
1971
+ path: "M6 4v16 M6 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4 M12 4v16 M12 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4 M18 4v16 M18 12a2 2 0 1 0 0 4 2 2 0 0 0 0-4"
1972
+ },
1932
1973
  // ---- Getting around ----------------------------------------------------
1933
1974
  {
1934
1975
  key: "entrance",
@@ -4587,7 +4628,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4587
4628
  this.accessRingById.set(seat.id, ring);
4588
4629
  target.add(ring);
4589
4630
  }
4590
- if (seat.accessibility?.includes("wheelchair")) {
4631
+ if (accessGlyphPath(seat.accessibility ?? [])) {
4591
4632
  const glyph = this.buildAccessGlyph(seat, typeof c.fill() === "string" ? c.fill() : "");
4592
4633
  this.accessGlyphById.set(seat.id, glyph);
4593
4634
  target.add(glyph);
@@ -4661,7 +4702,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
4661
4702
  return new import_Path.Path({
4662
4703
  x: seat.x,
4663
4704
  y: seat.y,
4664
- data: ACCESS_GLYPH_PATH,
4705
+ // Non-null: buildAccessGlyph is only called for seats the gate confirmed
4706
+ // resolve to a primary-accommodation glyph.
4707
+ data: accessGlyphPath(seat.accessibility ?? []) ?? "",
4665
4708
  offsetX: ACCESS_GLYPH_VIEWBOX / 2,
4666
4709
  offsetY: ACCESS_GLYPH_VIEWBOX / 2,
4667
4710
  scaleX: k,
@@ -6794,7 +6837,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6794
6837
  if (shouldCache && !this.cached) {
6795
6838
  this.cacheSeatLayer();
6796
6839
  } else if (!shouldCache && (this.cached || !this.seatLayer.listening())) {
6797
- if (this.cached) this.seatLayer.clearCache();
6840
+ if (this.cached) this.releaseSeatLayerBitmap();
6798
6841
  this.seatLayer.listening(true);
6799
6842
  this.cached = false;
6800
6843
  this.seatLayer.batchDraw();
@@ -6814,6 +6857,28 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6814
6857
  this.rebuildSeatCache();
6815
6858
  this.seatLayer.batchDraw();
6816
6859
  }
6860
+ /**
6861
+ * Drop the seat-layer overview bitmap while PRESERVING every descendant's
6862
+ * cached local transform + clientRect.
6863
+ *
6864
+ * Konva's public `Layer.clearCache()` releases the bitmap and then runs a deep
6865
+ * `_clearSelfAndDescendantCache()` that clears those caches on all 14k seat
6866
+ * nodes. Recomputing them on the single frame the melt first reveals a section's
6867
+ * live seats is the ~480ms first-drill stall (OV-80). A camera glide only marks
6868
+ * each seat's ABSOLUTE_TRANSFORM dirty (recomputed lazily at ordinary pan cost)
6869
+ * and never moves a seat's LOCAL transform, so the local caches stay valid —
6870
+ * keeping them lets the first melt paint at pan cost, matching the ~17ms of
6871
+ * every subsequent glide on the same section. Any edit that actually moves a
6872
+ * seat invalidates its own transform cache through the attr setter, so only
6873
+ * still-valid caches survive here.
6874
+ */
6875
+ releaseSeatLayerBitmap() {
6876
+ const layer = this.seatLayer;
6877
+ const entry = layer._cache.get("canvas");
6878
+ if (!entry) return;
6879
+ import_Core.Konva.Util.releaseCanvas(entry.scene._canvas, entry.filter._canvas, entry.hit._canvas);
6880
+ layer._cache.delete("canvas");
6881
+ }
6817
6882
  /**
6818
6883
  * SYNCHRONOUS repaint that bypasses requestAnimationFrame — see the
6819
6884
  * ISeatmapRenderer.forceDraw() contract. Chrome pauses rAF (and therefore
@@ -6832,7 +6897,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
6832
6897
  if (this.effScale() < CACHE_THRESHOLD) {
6833
6898
  this.rebuildSeatCache();
6834
6899
  } else if (this.cached) {
6835
- this.seatLayer.clearCache();
6900
+ this.releaseSeatLayerBitmap();
6836
6901
  this.seatLayer.listening(true);
6837
6902
  this.cached = false;
6838
6903
  }
@@ -7353,6 +7418,36 @@ var PickerController = class {
7353
7418
  const group = this.groupedTableBySeatId.get(seatIdOrLabel) ?? this.groupedTablesByLabel.get(seatIdOrLabel);
7354
7419
  return group ? this.toTableSeat(group) : null;
7355
7420
  }
7421
+ /**
7422
+ * Buyer-facing name + type word for a checkout line item, resolved from the
7423
+ * SAME author overrides the tray/tooltip already render. `label` stays the
7424
+ * booking identity; a host builds its own order summary from `displayLabel`/
7425
+ * `displayType` so the buyer sees the designer's copy, not the internal label.
7426
+ * Absent fields = no override (host falls back to `label`).
7427
+ */
7428
+ lineItemDisplay(item) {
7429
+ if (item.objectType === "ga") {
7430
+ const doc = this.visibleDoc();
7431
+ const area = gaAreasOf(doc).find((candidate) => candidate.id === item.objectId);
7432
+ return {
7433
+ ...area?.displayLabel ? { displayLabel: area.displayLabel } : {},
7434
+ ...area?.displayType ? { displayType: area.displayType } : {}
7435
+ };
7436
+ }
7437
+ if (item.objectType === "table") {
7438
+ const group = this.groupedTablesByLabel.get(item.label);
7439
+ return {
7440
+ ...group?.displayLabel && group.displayLabel !== group.label ? { displayLabel: group.displayLabel } : {},
7441
+ ...group?.displayType ? { displayType: group.displayType } : {}
7442
+ };
7443
+ }
7444
+ const seat = this.labelToSeat.get(item.label);
7445
+ const context = seat ? this.seatContext.get(seat.id) : void 0;
7446
+ return {
7447
+ ...seat?.displayLabel ? { displayLabel: seat.displayLabel } : {},
7448
+ ...context?.displayType ? { displayType: context.displayType } : {}
7449
+ };
7450
+ }
7356
7451
  /** Fetch chart, build label maps, mount the renderer, seed statuses, go live. */
7357
7452
  async render(host) {
7358
7453
  if (this.renderer) return null;